summaryrefslogtreecommitdiffstats
path: root/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/.gitignore5
-rw-r--r--src/shared/libosmocore/.gitignore72
-rw-r--r--src/shared/libosmocore/COPYING339
-rw-r--r--src/shared/libosmocore/Doxyfile.codec.in1716
-rw-r--r--src/shared/libosmocore/Doxyfile.core.in1716
-rw-r--r--src/shared/libosmocore/Doxyfile.gsm.in1716
-rw-r--r--src/shared/libosmocore/Doxyfile.vty.in1716
-rw-r--r--src/shared/libosmocore/Makefile.am60
-rw-r--r--src/shared/libosmocore/configure.ac177
-rw-r--r--src/shared/libosmocore/debian/changelog17
-rw-r--r--src/shared/libosmocore/debian/compat1
-rw-r--r--src/shared/libosmocore/debian/control27
-rw-r--r--src/shared/libosmocore/debian/copyright54
-rw-r--r--src/shared/libosmocore/debian/docs0
-rw-r--r--src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log6
-rw-r--r--src/shared/libosmocore/debian/libosmocore-dbg.dirs1
-rw-r--r--src/shared/libosmocore/debian/libosmocore-dbg.install1
-rw-r--r--src/shared/libosmocore/debian/libosmocore-dev.dirs8
-rw-r--r--src/shared/libosmocore/debian/libosmocore-dev.install5
-rw-r--r--src/shared/libosmocore/debian/libosmocore.dirs8
-rw-r--r--src/shared/libosmocore/debian/libosmocore.install1
-rw-r--r--src/shared/libosmocore/debian/patches/debian-changes-0.1.17-146
-rw-r--r--src/shared/libosmocore/debian/patches/series1
-rwxr-xr-xsrc/shared/libosmocore/debian/rules19
-rw-r--r--src/shared/libosmocore/debian/source/format1
-rw-r--r--src/shared/libosmocore/doc/.empty0
-rwxr-xr-xsrc/shared/libosmocore/git-version-gen151
-rw-r--r--src/shared/libosmocore/include/Makefile.am1
-rw-r--r--src/shared/libosmocore/include/osmocom/Makefile.am5
-rw-r--r--src/shared/libosmocore/include/osmocom/codec/Makefile.am3
-rw-r--r--src/shared/libosmocore/include/osmocom/codec/codec.h20
-rw-r--r--src/shared/libosmocore/include/osmocom/core/Makefile.am31
-rw-r--r--src/shared/libosmocore/include/osmocom/core/application.h23
-rw-r--r--src/shared/libosmocore/include/osmocom/core/backtrace.h6
-rw-r--r--src/shared/libosmocore/include/osmocom/core/bits.h78
-rw-r--r--src/shared/libosmocore/include/osmocom/core/bitvec.h70
-rw-r--r--src/shared/libosmocore/include/osmocom/core/conv.h146
-rw-r--r--src/shared/libosmocore/include/osmocom/core/crc16.h34
-rw-r--r--src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl59
-rw-r--r--src/shared/libosmocore/include/osmocom/core/crcgen.h41
-rw-r--r--src/shared/libosmocore/include/osmocom/core/gsmtap.h151
-rw-r--r--src/shared/libosmocore/include/osmocom/core/gsmtap_util.h57
-rw-r--r--src/shared/libosmocore/include/osmocom/core/linuxlist.h360
-rw-r--r--src/shared/libosmocore/include/osmocom/core/linuxrbtree.h160
-rw-r--r--src/shared/libosmocore/include/osmocom/core/logging.h211
-rw-r--r--src/shared/libosmocore/include/osmocom/core/msgb.h370
-rw-r--r--src/shared/libosmocore/include/osmocom/core/msgfile.h49
-rw-r--r--src/shared/libosmocore/include/osmocom/core/panic.h20
-rw-r--r--src/shared/libosmocore/include/osmocom/core/plugin.h6
-rw-r--r--src/shared/libosmocore/include/osmocom/core/prim.h58
-rw-r--r--src/shared/libosmocore/include/osmocom/core/process.h2
-rw-r--r--src/shared/libosmocore/include/osmocom/core/rate_ctr.h88
-rw-r--r--src/shared/libosmocore/include/osmocom/core/select.h45
-rw-r--r--src/shared/libosmocore/include/osmocom/core/serial.h43
-rw-r--r--src/shared/libosmocore/include/osmocom/core/signal.h45
-rw-r--r--src/shared/libosmocore/include/osmocom/core/socket.h34
-rw-r--r--src/shared/libosmocore/include/osmocom/core/statistics.h53
-rw-r--r--src/shared/libosmocore/include/osmocom/core/talloc.h192
-rw-r--r--src/shared/libosmocore/include/osmocom/core/timer.h87
-rw-r--r--src/shared/libosmocore/include/osmocom/core/timer_compat.h79
-rw-r--r--src/shared/libosmocore/include/osmocom/core/utils.h56
-rw-r--r--src/shared/libosmocore/include/osmocom/core/write_queue.h63
-rw-r--r--src/shared/libosmocore/include/osmocom/crypt/Makefile.am3
-rw-r--r--src/shared/libosmocore/include/osmocom/crypt/auth.h90
-rw-r--r--src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h54
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/Makefile.am8
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/a5.h63
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/abis_nm.h40
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/comp128.h22
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h62
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h45
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h36
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0480.h26
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0502.h38
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0808.h50
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm48.h38
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h117
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h150
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/lapd_core.h171
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/lapdm.h162
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/mncc.h71
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/prim.h13
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/Makefile.am6
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h51
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h1265
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h190
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h31
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h126
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h303
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h561
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h748
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_44_318.h153
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h94
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/rsl.h55
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h22
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/sysinfo.h43
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/tlv.h319
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/Makefile.am4
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/buffer.h102
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/command.h370
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/logging.h12
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/misc.h10
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/telnet_interface.h55
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/vector.h64
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/vty.h188
-rw-r--r--src/shared/libosmocore/libosmocodec.pc.in11
-rw-r--r--src/shared/libosmocore/libosmocore.pc.in11
-rw-r--r--src/shared/libosmocore/libosmogsm.pc.in11
-rw-r--r--src/shared/libosmocore/libosmovty.pc.in11
-rw-r--r--src/shared/libosmocore/m4/DUMMY1
-rw-r--r--src/shared/libosmocore/src/Makefile.am43
-rw-r--r--src/shared/libosmocore/src/application.c154
-rw-r--r--src/shared/libosmocore/src/backtrace.c64
-rw-r--r--src/shared/libosmocore/src/bits.c188
-rw-r--r--src/shared/libosmocore/src/bitvec.c264
-rw-r--r--src/shared/libosmocore/src/codec/Makefile.am11
-rw-r--r--src/shared/libosmocore/src/codec/gsm610.c294
-rw-r--r--src/shared/libosmocore/src/codec/gsm620.c262
-rw-r--r--src/shared/libosmocore/src/codec/gsm660.c256
-rw-r--r--src/shared/libosmocore/src/codec/gsm690.c210
-rw-r--r--src/shared/libosmocore/src/conv.c631
-rw-r--r--src/shared/libosmocore/src/crc16.c62
-rw-r--r--src/shared/libosmocore/src/crcXXgen.c.tpl120
-rw-r--r--src/shared/libosmocore/src/gsm/Makefile.am25
-rw-r--r--src/shared/libosmocore/src/gsm/a5.c367
-rw-r--r--src/shared/libosmocore/src/gsm/abis_nm.c455
-rw-r--r--src/shared/libosmocore/src/gsm/auth_comp128v1.c47
-rw-r--r--src/shared/libosmocore/src/gsm/auth_core.c121
-rw-r--r--src/shared/libosmocore/src/gsm/auth_milenage.c109
-rw-r--r--src/shared/libosmocore/src/gsm/comp128.c230
-rw-r--r--src/shared/libosmocore/src/gsm/gprs_cipher_core.c99
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0411_smc.c539
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0411_smr.c451
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0411_utils.c306
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0480.c461
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0502.c43
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0808.c402
-rw-r--r--src/shared/libosmocore/src/gsm/gsm48.c428
-rw-r--r--src/shared/libosmocore/src/gsm/gsm48_ie.c1107
-rw-r--r--src/shared/libosmocore/src/gsm/gsm_utils.c606
-rw-r--r--src/shared/libosmocore/src/gsm/lapd_core.c2170
-rw-r--r--src/shared/libosmocore/src/gsm/lapdm.c1245
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/aes-encblock.c38
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/aes-internal-enc.c121
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/aes-internal.c805
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/aes.h27
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/aes_i.h122
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/aes_wrap.h48
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/common.h20
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/crypto.h0
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/includes.h0
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/milenage.c329
-rw-r--r--src/shared/libosmocore/src/gsm/milenage/milenage.h33
-rw-r--r--src/shared/libosmocore/src/gsm/rsl.c507
-rw-r--r--src/shared/libosmocore/src/gsm/rxlev_stat.c82
-rw-r--r--src/shared/libosmocore/src/gsm/sysinfo.c136
-rw-r--r--src/shared/libosmocore/src/gsm/tlv_parser.c189
-rw-r--r--src/shared/libosmocore/src/gsmtap_util.c362
-rw-r--r--src/shared/libosmocore/src/logging.c757
-rw-r--r--src/shared/libosmocore/src/logging_syslog.c92
-rw-r--r--src/shared/libosmocore/src/msgb.c156
-rw-r--r--src/shared/libosmocore/src/msgfile.c123
-rw-r--r--src/shared/libosmocore/src/panic.c82
-rw-r--r--src/shared/libosmocore/src/plugin.c62
-rw-r--r--src/shared/libosmocore/src/rate_ctr.c180
-rw-r--r--src/shared/libosmocore/src/rbtree.c383
-rw-r--r--src/shared/libosmocore/src/select.c172
-rw-r--r--src/shared/libosmocore/src/serial.c229
-rw-r--r--src/shared/libosmocore/src/signal.c109
-rw-r--r--src/shared/libosmocore/src/socket.c249
-rw-r--r--src/shared/libosmocore/src/statistics.c76
-rw-r--r--src/shared/libosmocore/src/talloc.c1804
-rw-r--r--src/shared/libosmocore/src/timer.c236
-rw-r--r--src/shared/libosmocore/src/utils.c215
-rw-r--r--src/shared/libosmocore/src/vty/Makefile.am15
-rw-r--r--src/shared/libosmocore/src/vty/buffer.c463
-rw-r--r--src/shared/libosmocore/src/vty/command.c3241
-rw-r--r--src/shared/libosmocore/src/vty/logging_vty.c604
-rw-r--r--src/shared/libosmocore/src/vty/telnet_interface.c236
-rw-r--r--src/shared/libosmocore/src/vty/utils.c63
-rw-r--r--src/shared/libosmocore/src/vty/vector.c192
-rw-r--r--src/shared/libosmocore/src/vty/vty.c1771
-rw-r--r--src/shared/libosmocore/src/write_queue.c118
-rw-r--r--src/shared/libosmocore/tests/Makefile.am47
-rw-r--r--src/shared/libosmocore/tests/a5/Makefile.am6
-rw-r--r--src/shared/libosmocore/tests/a5/a5_test.c98
-rw-r--r--src/shared/libosmocore/tests/a5/a5_test.ok6
-rw-r--r--src/shared/libosmocore/tests/auth/Makefile.am8
-rw-r--r--src/shared/libosmocore/tests/auth/milenage_test.c78
-rw-r--r--src/shared/libosmocore/tests/auth/milenage_test.ok8
-rw-r--r--src/shared/libosmocore/tests/bits/Makefile.am7
-rw-r--r--src/shared/libosmocore/tests/bits/bitrev_test.c36
-rw-r--r--src/shared/libosmocore/tests/bits/bitrev_test.ok24
-rw-r--r--src/shared/libosmocore/tests/conv/Makefile.am6
-rw-r--r--src/shared/libosmocore/tests/conv/conv_test.c486
-rw-r--r--src/shared/libosmocore/tests/conv/conv_test.ok55
-rw-r--r--src/shared/libosmocore/tests/gsm0808/Makefile.am6
-rw-r--r--src/shared/libosmocore/tests/gsm0808/gsm0808_test.c269
-rw-r--r--src/shared/libosmocore/tests/gsm0808/gsm0808_test.ok15
-rw-r--r--src/shared/libosmocore/tests/lapd/Makefile.am9
-rw-r--r--src/shared/libosmocore/tests/lapd/lapd_test.c319
-rw-r--r--src/shared/libosmocore/tests/lapd/lapd_test.ok20
-rw-r--r--src/shared/libosmocore/tests/msgfile/Makefile.am6
-rw-r--r--src/shared/libosmocore/tests/msgfile/msgconfig.cfg2
-rw-r--r--src/shared/libosmocore/tests/msgfile/msgfile_test.c50
-rw-r--r--src/shared/libosmocore/tests/msgfile/msgfile_test.ok1
-rw-r--r--src/shared/libosmocore/tests/sms/Makefile.am6
-rw-r--r--src/shared/libosmocore/tests/sms/sms_test.c319
-rw-r--r--src/shared/libosmocore/tests/sms/sms_test.ok2
-rw-r--r--src/shared/libosmocore/tests/smscb/Makefile.am6
-rw-r--r--src/shared/libosmocore/tests/smscb/smscb_test.c41
-rw-r--r--src/shared/libosmocore/tests/smscb/smscb_test.ok4
-rw-r--r--src/shared/libosmocore/tests/testsuite.at73
-rw-r--r--src/shared/libosmocore/tests/timer/Makefile.am7
-rw-r--r--src/shared/libosmocore/tests/timer/timer_test.c191
-rw-r--r--src/shared/libosmocore/tests/timer/timer_test.ok2
-rw-r--r--src/shared/libosmocore/tests/ussd/Makefile.am6
-rw-r--r--src/shared/libosmocore/tests/ussd/ussd_test.c97
-rw-r--r--src/shared/libosmocore/tests/ussd/ussd_test.ok53
-rw-r--r--src/shared/libosmocore/utils/Makefile.am10
-rwxr-xr-xsrc/shared/libosmocore/utils/gen_website_doc_tree.sh14
-rw-r--r--src/shared/libosmocore/utils/osmo-arfcn.c103
-rw-r--r--src/shared/libosmocore/utils/osmo-auc-gen.c183
-rwxr-xr-xsrc/shared/update-libosmocore.sh3
224 files changed, 45585 insertions, 0 deletions
diff --git a/src/shared/.gitignore b/src/shared/.gitignore
new file mode 100644
index 00000000..5058adb1
--- /dev/null
+++ b/src/shared/.gitignore
@@ -0,0 +1,5 @@
+libosmocore/build-host/tests/msgfile/msgfile_test
+libosmocore/build-host/tests/smscb/smscb_test
+libosmocore/build-host/tests/sms/sms_test
+libosmocore/build-host/tests/timer/timer_test
+libosmocore/build-host/tests/ussd/ussd_test
diff --git a/src/shared/libosmocore/.gitignore b/src/shared/libosmocore/.gitignore
new file mode 100644
index 00000000..82e43e3a
--- /dev/null
+++ b/src/shared/libosmocore/.gitignore
@@ -0,0 +1,72 @@
+Makefile
+Makefile.in
+.deps
+.libs
+*.o
+*.lo
+*.la
+*.pc
+aclocal.m4
+acinclude.m4
+aminclude.am
+m4/*.m4
+autom4te.cache
+config.h*
+config.sub
+config.log
+config.status
+config.guess
+configure
+depcomp
+missing
+ltmain.sh
+install-sh
+stamp-h1
+libtool
+libosmocore-*.tar.*
+
+Doxyfile.core
+Doxyfile.gsm
+Doxyfile.vty
+Doxyfile.codec
+
+.tarball-version
+.version
+
+#gnu autotest
+tests/package.m4
+tests/atconfig
+tests/atlocal
+tests/osmo-test
+tests/package.m4
+tests/testsuite
+tests/testsuite.dir/
+tests/testsuite.log
+
+tests/sms/sms_test
+tests/timer/timer_test
+tests/msgfile/msgfile_test
+tests/ussd/ussd_test
+tests/smscb/smscb_test
+tests/bits/bitrev_test
+tests/a5/a5_test
+tests/auth/milenage_test
+tests/conv/conv_test
+tests/lapd/lapd_test
+tests/gsm0808/gsm0808_test
+
+utils/osmo-arfcn
+utils/osmo-auc-gen
+
+doc/codec
+doc/core
+doc/vty
+doc/gsm
+doc/html.tar
+
+src/crc*gen.c
+include/osmocom/core/crc*gen.h
+
+
+# vi files
+*.sw?
diff --git a/src/shared/libosmocore/COPYING b/src/shared/libosmocore/COPYING
new file mode 100644
index 00000000..d511905c
--- /dev/null
+++ b/src/shared/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/src/shared/libosmocore/Doxyfile.codec.in b/src/shared/libosmocore/Doxyfile.codec.in
new file mode 100644
index 00000000..fcd5122e
--- /dev/null
+++ b/src/shared/libosmocore/Doxyfile.codec.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmocodec
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom codec library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/codec
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = include/osmocom/codec src/codec
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is adviced to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = NO
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/src/shared/libosmocore/Doxyfile.core.in b/src/shared/libosmocore/Doxyfile.core.in
new file mode 100644
index 00000000..18eb2265
--- /dev/null
+++ b/src/shared/libosmocore/Doxyfile.core.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmocore
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom core library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/core
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = include/osmocom/core src
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is adviced to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc/core
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = NO
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/src/shared/libosmocore/Doxyfile.gsm.in b/src/shared/libosmocore/Doxyfile.gsm.in
new file mode 100644
index 00000000..ab25b220
--- /dev/null
+++ b/src/shared/libosmocore/Doxyfile.gsm.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmogsm
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom GSM library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/gsm
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = include/osmocom/gsm include/osmocom/gsm/protocol src/gsm
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is adviced to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = NO
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/src/shared/libosmocore/Doxyfile.vty.in b/src/shared/libosmocore/Doxyfile.vty.in
new file mode 100644
index 00000000..57f19ad8
--- /dev/null
+++ b/src/shared/libosmocore/Doxyfile.vty.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmovty
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom VTY library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/vty
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = include/osmocom/vty src/vty
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is adviced to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = NO
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/src/shared/libosmocore/Makefile.am b/src/shared/libosmocore/Makefile.am
new file mode 100644
index 00000000..5a157ce7
--- /dev/null
+++ b/src/shared/libosmocore/Makefile.am
@@ -0,0 +1,60 @@
+AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
+ACLOCAL_AMFLAGS = -I m4
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+SUBDIRS = include src tests utils
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libosmocore.pc libosmocodec.pc libosmovty.pc libosmogsm.pc
+
+BUILT_SOURCES = $(top_srcdir)/.version
+$(top_srcdir)/.version:
+ echo $(VERSION) > $@-t && mv $@-t $@
+dist-hook:
+ echo $(VERSION) > $(distdir)/.tarball-version
+
+EXTRA_DIST = git-version-gen
+
+if HAVE_DOXYGEN
+
+pkgdocdir=$(docdir)/$(PACKAGE)-$(VERSION)
+doc_htmldir=$(pkgdocdir)/html
+
+doc_html_DATA = $(top_builddir)/doc/html.tar
+
+$(doc_html_DATA): $(top_builddir)/doc/core/html/index.html \
+ $(top_builddir)/doc/gsm/html/index.html \
+ $(top_builddir)/doc/vty/html/index.html \
+ $(top_builddir)/doc/codec/html/index.html
+ cd $(top_builddir)/doc && tar cf html.tar */html
+
+$(top_builddir)/doc/core/html/index.html: $(SOURCES) Doxyfile.core
+ @rm -rf doc/core
+ mkdir -p doc/core
+ $(DOXYGEN) Doxyfile.core
+
+$(top_builddir)/doc/gsm/html/index.html: $(SOURCES) Doxyfile.gsm
+ @rm -rf doc/gsm
+ mkdir -p doc/gsm
+ $(DOXYGEN) Doxyfile.gsm
+
+$(top_builddir)/doc/vty/html/index.html: $(SOURCES) Doxyfile.vty
+ @rm -rf doc/vty
+ mkdir -p doc/vty
+ $(DOXYGEN) Doxyfile.vty
+
+$(top_builddir)/doc/codec/html/index.html: $(SOURCES) Doxyfile.codec
+ @rm -rf doc/codec
+ mkdir -p doc/codec
+ $(DOXYGEN) Doxyfile.codec
+
+install-data-hook:
+ cd $(DESTDIR)$(doc_htmldir) && tar xf html.tar && rm -f html.tar
+
+uninstall-hook:
+ cd $(DESTDIR)$(doc_htmldir) && rm -rf {core,gsm,vty,codec}
+
+DX_CLEAN = doc/{core,gsm,vty,codec}/{html,latex}/* doc/html.tar
+endif
+
+MOSTLYCLEANFILES = $(DX_CLEAN)
diff --git a/src/shared/libosmocore/configure.ac b/src/shared/libosmocore/configure.ac
new file mode 100644
index 00000000..ec79a923
--- /dev/null
+++ b/src/shared/libosmocore/configure.ac
@@ -0,0 +1,177 @@
+AC_INIT([libosmocore],
+ m4_esyscmd([./git-version-gen .tarball-version]),
+ [openbsc-devel@lists.openbsc.org])
+
+AM_INIT_AUTOMAKE([dist-bzip2])
+AC_CONFIG_TESTDIR(tests)
+
+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 sys/socket.h syslog.h ctype.h)
+# for src/conv.c
+AC_FUNC_ALLOCA
+AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
+AC_SUBST(LIBRARY_DL)
+
+AC_PATH_PROG(DOXYGEN,doxygen,false)
+AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false)
+
+# 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([AC_LANG_SOURCE([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,
+ [AS_HELP_STRING(
+ [--disable-talloc],
+ [Disable building talloc memory allocator]
+ )],
+ [enable_talloc=$enableval], [enable_talloc="yes"])
+AM_CONDITIONAL(ENABLE_TALLOC, [test x"$enable_talloc" = x"yes"])
+
+AC_ARG_ENABLE(plugin,
+ [AS_HELP_STRING(
+ [--disable-plugin],
+ [Disable support for dlopen plugins],
+ )],
+ [enable_plugin=$enableval], [enable_plugin="yes"])
+AM_CONDITIONAL(ENABLE_PLUGIN, test x"$enable_plugin" = x"yes")
+
+AC_ARG_ENABLE(tests,
+ [AS_HELP_STRING(
+ [--disable-tests],
+ [Disable building test programs]
+ )],
+ [enable_tests=$enableval], [enable_tests="yes"])
+AM_CONDITIONAL(ENABLE_TESTS, test x"$enable_tests" = x"yes")
+
+AC_ARG_ENABLE(vty,
+ [AS_HELP_STRING(
+ [--disable-vty],
+ [Disable building VTY telnet interface]
+ )],
+ [enable_vty=$enableval], [enable_vty="yes"])
+AM_CONDITIONAL(ENABLE_VTY, test x"$enable_vty" = x"yes")
+
+AC_ARG_ENABLE(panic_infloop,
+ [AS_HELP_STRING(
+ [--enable-panic-infloop],
+ [Trigger infinite loop on panic rather than fprintf/abort]
+ )],
+ [panic_infloop=$enableval], [panic_infloop="no"])
+if test x"$panic_infloop" = x"yes"
+then
+ AC_DEFINE([PANIC_INFLOOP],[1],[Use infinite loop on panic rather than fprintf/abort])
+fi
+
+AC_ARG_ENABLE(bsc_fd_check,
+ [AS_HELP_STRING(
+ [--enable-bsc-fd-check],
+ [Instrument bsc_register_fd to check that the fd is registered]
+ )],
+ [fd_check=$enableval], [fd_check="no"])
+if test x"$fd_check" = x"no"
+then
+ AC_DEFINE([BSC_FD_CHECK],[1],[Instrument the bsc_register_fd])
+fi
+
+AC_ARG_ENABLE(msgfile,
+ [AS_HELP_STRING(
+ [--disable-msgfile],
+ [Disable support for the msgfile],
+ )],
+ [enable_msgfile=$enableval], [enable_msgfile="yes"])
+AM_CONDITIONAL(ENABLE_MSGFILE, test x"$enable_msgfile" = x"yes")
+
+AC_ARG_ENABLE(serial,
+ [AS_HELP_STRING(
+ [--disable-serial],
+ [Disable support for the serial helpers],
+ )],
+ [enable_serial=$enableval], [enable_serial="yes"])
+AM_CONDITIONAL(ENABLE_SERIAL, test x"$enable_serial" = x"yes")
+
+AC_ARG_ENABLE(utilities,
+ [AS_HELP_STRING(
+ [--disable-utilities],
+ [Disable building utility programs],
+ )],
+ [enable_utilities=$enableval], [enable_utilities="yes"])
+AM_CONDITIONAL(ENABLE_UTILITIES, test x"$enable_utilities" = x"yes")
+
+AC_ARG_ENABLE(embedded,
+ [AS_HELP_STRING(
+ [--enable-embedded],
+ [Enable building for embedded use and disable unsupported features]
+ )],
+ [embedded=$enableval], [embedded="no"])
+if test x"$embedded" = x"yes"
+then
+ AC_DEFINE([EMBEDDED],[1],[Select building for embedded use])
+ AM_CONDITIONAL(ENABLE_TESTS, false)
+ AM_CONDITIONAL(ENABLE_PLUGIN, false)
+ AM_CONDITIONAL(ENABLE_MSGFILE, false)
+ AM_CONDITIONAL(ENABLE_SERIAL, false)
+ AM_CONDITIONAL(ENABLE_VTY, false)
+ AM_CONDITIONAL(ENABLE_TALLOC, false)
+ AM_CONDITIONAL(ENABLE_UTILITIES, false)
+ AC_DEFINE([PANIC_INFLOOP],[1],[Use infinite loop on panic rather than fprintf/abort])
+fi
+
+
+AC_OUTPUT(
+ libosmocore.pc
+ libosmocodec.pc
+ libosmovty.pc
+ libosmogsm.pc
+ include/osmocom/Makefile
+ include/osmocom/vty/Makefile
+ include/osmocom/codec/Makefile
+ include/osmocom/crypt/Makefile
+ include/osmocom/gsm/Makefile
+ include/osmocom/gsm/protocol/Makefile
+ include/osmocom/core/Makefile
+ include/Makefile
+ src/Makefile
+ src/vty/Makefile
+ src/codec/Makefile
+ src/gsm/Makefile
+ tests/Makefile
+ tests/timer/Makefile
+ tests/sms/Makefile
+ tests/msgfile/Makefile
+ tests/ussd/Makefile
+ tests/smscb/Makefile
+ tests/bits/Makefile
+ tests/a5/Makefile
+ tests/auth/Makefile
+ tests/conv/Makefile
+ tests/lapd/Makefile
+ tests/gsm0808/Makefile
+ utils/Makefile
+ Doxyfile.core
+ Doxyfile.gsm
+ Doxyfile.vty
+ Doxyfile.codec
+ Makefile)
diff --git a/src/shared/libosmocore/debian/changelog b/src/shared/libosmocore/debian/changelog
new file mode 100644
index 00000000..20a04938
--- /dev/null
+++ b/src/shared/libosmocore/debian/changelog
@@ -0,0 +1,17 @@
+libosmocore (0.3.0) natty; urgency=low
+
+ * New upstream version of libosmocore
+
+ -- Harald Welte <laforge@gnumonks.org> Tue, 10 May 2011 17:28:24 +0200
+
+libosmocore (0.1.27) natty; urgency=low
+
+ * New upstream version of libosmocore.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Thu, 13 Jan 2011 18:07:36 +0800
+
+libosmocore (0.1.17-1) unstable; urgency=low
+
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+
+ -- Harald Welte <laforge@gnumonks.org> Tue, 24 Aug 2010 10:55:04 +0200
diff --git a/src/shared/libosmocore/debian/compat b/src/shared/libosmocore/debian/compat
new file mode 100644
index 00000000..7f8f011e
--- /dev/null
+++ b/src/shared/libosmocore/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/src/shared/libosmocore/debian/control b/src/shared/libosmocore/debian/control
new file mode 100644
index 00000000..3f92875c
--- /dev/null
+++ b/src/shared/libosmocore/debian/control
@@ -0,0 +1,27 @@
+Source: libosmocore
+Section: libs
+Priority: optional
+Maintainer: Harald Welte <laforge@gnumonks.org>
+Build-Depends: debhelper (>= 7.0.50~), autotools-dev, autoconf, automake, libtool, dh-autoreconf
+Standards-Version: 3.8.4
+Homepage: http://bb.osmocom.org/trac/wiki/libosmocore
+Vcs-Git: git://git.osmocom.org/libosmocore.git
+Vcs-Browser: http://git.osmocom.org/gitweb?p=libosmocore.git;a=summary
+
+Package: libosmocore
+Section: libs
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: Open Source MObile COMmunications CORE library
+
+Package: libosmocore-dev
+Section: libdevel
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, libosmocore
+Description: Development headers for Open Source MObile COMmunications CORE library
+
+#Package: libosmocore-dbg
+#Section: libdevel
+#Architecture: any
+#Depends: ${shlibs:Depends}, ${misc:Depends}
+#Description: Debug symbols for Open Source MObile COMmunications CORE library
diff --git a/src/shared/libosmocore/debian/copyright b/src/shared/libosmocore/debian/copyright
new file mode 100644
index 00000000..c450be58
--- /dev/null
+++ b/src/shared/libosmocore/debian/copyright
@@ -0,0 +1,54 @@
+This work was packaged for Debian by:
+
+ Harald Welte <laforge@gnumonks.org> on Tue, 24 Aug 2010 10:55:04 +0200
+
+It was downloaded from:
+
+ git://git.osmocom.org/libosmocore.git
+
+Upstream Author(s):
+
+ Harald Welte <laforge@gnumonks.org>
+ Holger Hans Peter Freyther <zecke@selfish.org>
+ Sylvain Munaut <tnt@246tNt.com>
+ Daniel Willmann <daniel@totalueberwachung.de>
+ Golde <nico@ngolde.de>
+ For src/talloc.c and include/osmocore/talloc.h:
+ Andrew Tridgell
+ Stefan Metzmacher
+ For src/vty/* and include/osmocom/vty/*
+ Kunihiro Ishiguro
+
+Copyright:
+
+ Copyright (C) 2008-2010 Harald Welte <laforge@gnumonks.org>
+ Copyright (C) 2008-2010 Holger Hans Peter Freyther <zecke@selfish.org>
+ Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
+ Copyright (C) 2009-2010 On-Waves
+ Copyright (C) 2008 Daniel Willmann <daniel@totalueberwachung.de>
+ Copyright (C) 2010 Nico Golde <nico@ngolde.de>
+ For src/talloc.c and include/osmocore/talloc.h:
+ Copyright (C) 2004 Andrew Tridgell
+ Copyright (C) 2006 Stefan Metzmacher
+ For src/vty/* and include/osmocom/vty/*
+ Copyright (C) 1998 Kunihiro Ishiguro
+
+License:
+
+ GNU General Public License, Version 2 or later
+
+The Debian packaging is:
+
+ Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
+
+# Please chose a license for your packaging work. If the program you package
+# uses a mainstream license, using the same license is the safest choice.
+# Please avoid to pick license terms that are more restrictive than the
+# packaged work, as it may make Debian's contributions unacceptable upstream.
+# If you just want it to be GPL version 3, leave the following lines in.
+
+and is licensed under the GPL version 3,
+see "/usr/share/common-licenses/GPL-3".
+
+# Please also look if there are files or directories which have a
+# different copyright/license attached and list them here.
diff --git a/src/shared/libosmocore/debian/docs b/src/shared/libosmocore/debian/docs
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/shared/libosmocore/debian/docs
diff --git a/src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log b/src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log
new file mode 100644
index 00000000..2742cb47
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log
@@ -0,0 +1,6 @@
+dh_auto_configure
+dh_auto_build
+dh_auto_test
+dh_prep
+dh_installdirs
+dh_auto_install
diff --git a/src/shared/libosmocore/debian/libosmocore-dbg.dirs b/src/shared/libosmocore/debian/libosmocore-dbg.dirs
new file mode 100644
index 00000000..af59b0a9
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore-dbg.dirs
@@ -0,0 +1 @@
+usr/lib/debug/lib
diff --git a/src/shared/libosmocore/debian/libosmocore-dbg.install b/src/shared/libosmocore/debian/libosmocore-dbg.install
new file mode 100644
index 00000000..7ce02127
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore-dbg.install
@@ -0,0 +1 @@
+usr/lib/debug/lib/*
diff --git a/src/shared/libosmocore/debian/libosmocore-dev.dirs b/src/shared/libosmocore/debian/libosmocore-dev.dirs
new file mode 100644
index 00000000..94090a39
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore-dev.dirs
@@ -0,0 +1,8 @@
+usr/lib
+usr/include
+usr/include/osmocom
+usr/include/osmocom/codec
+usr/include/osmocom/core
+usr/include/osmocom/crypt
+usr/include/osmocom/gsm
+usr/include/osmocom/vty
diff --git a/src/shared/libosmocore/debian/libosmocore-dev.install b/src/shared/libosmocore/debian/libosmocore-dev.install
new file mode 100644
index 00000000..eec0e15e
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore-dev.install
@@ -0,0 +1,5 @@
+usr/include/*
+usr/lib/lib*.a
+usr/lib/lib*.so
+usr/lib/lib*.la
+usr/lib/pkgconfig/*
diff --git a/src/shared/libosmocore/debian/libosmocore.dirs b/src/shared/libosmocore/debian/libosmocore.dirs
new file mode 100644
index 00000000..94090a39
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore.dirs
@@ -0,0 +1,8 @@
+usr/lib
+usr/include
+usr/include/osmocom
+usr/include/osmocom/codec
+usr/include/osmocom/core
+usr/include/osmocom/crypt
+usr/include/osmocom/gsm
+usr/include/osmocom/vty
diff --git a/src/shared/libosmocore/debian/libosmocore.install b/src/shared/libosmocore/debian/libosmocore.install
new file mode 100644
index 00000000..d0dbfd18
--- /dev/null
+++ b/src/shared/libosmocore/debian/libosmocore.install
@@ -0,0 +1 @@
+usr/lib/lib*.so.*
diff --git a/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1 b/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1
new file mode 100644
index 00000000..c0a54bd7
--- /dev/null
+++ b/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1
@@ -0,0 +1,46 @@
+Description: Upstream changes introduced in version 0.1.17-1
+ This patch has been created by dpkg-source during the package build.
+ Here's the last changelog entry, hopefully it gives details on why
+ those changes were made:
+ .
+ libosmocore (0.1.17-1) unstable; urgency=low
+ .
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+ .
+ The person named in the Author field signed this changelog entry.
+Author: Harald Welte <laforge@gnumonks.org>
+
+---
+The information above should follow the Patch Tagging Guidelines, please
+checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
+are templates for supplementary fields that you might want to add:
+
+Origin: <vendor|upstream|other>, <url of original patch>
+Bug: <url in upstream bugtracker>
+Bug-Debian: http://bugs.debian.org/<bugnumber>
+Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
+Forwarded: <no|not-needed|url proving that it has been forwarded>
+Reviewed-By: <name and email of someone who approved the patch>
+Last-Update: <YYYY-MM-DD>
+
+--- /dev/null
++++ libosmocore-0.1.17/.version
+@@ -0,0 +1 @@
++0.1.17
+--- /dev/null
++++ libosmocore-0.1.17/copyright
+@@ -0,0 +1,14 @@
++Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
++Name: libosmocore
++Maintainer: Harald Welte <laforge@gnumonks.org>
++Source: git://git.osmocom.org/libosmocore.git
++
++Copyright: 2008-2010 Harald Welte <laforge@gnumonks.org>
++License: GPL-2+
++
++Files: src/talloc.c include/osmocore/talloc.h
++Copyright: 2004 Andrew Tridgell
++License: LGPL-3+
++
++Files: include/osmocore/linuxlist.h
++License: GPL-2
diff --git a/src/shared/libosmocore/debian/patches/series b/src/shared/libosmocore/debian/patches/series
new file mode 100644
index 00000000..0ca407b1
--- /dev/null
+++ b/src/shared/libosmocore/debian/patches/series
@@ -0,0 +1 @@
+debian-changes-0.1.17-1
diff --git a/src/shared/libosmocore/debian/rules b/src/shared/libosmocore/debian/rules
new file mode 100755
index 00000000..15f78f2c
--- /dev/null
+++ b/src/shared/libosmocore/debian/rules
@@ -0,0 +1,19 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+CFLAGS = -Wall -g
+
+%:
+ dh --with autoreconf $@
+
+#override_dh_strip:
+# dh_strip --dbg-package=libosmocore-dbg
+
diff --git a/src/shared/libosmocore/debian/source/format b/src/shared/libosmocore/debian/source/format
new file mode 100644
index 00000000..af745b31
--- /dev/null
+++ b/src/shared/libosmocore/debian/source/format
@@ -0,0 +1 @@
+3.0 (git)
diff --git a/src/shared/libosmocore/doc/.empty b/src/shared/libosmocore/doc/.empty
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/shared/libosmocore/doc/.empty
diff --git a/src/shared/libosmocore/git-version-gen b/src/shared/libosmocore/git-version-gen
new file mode 100755
index 00000000..42cf3d2b
--- /dev/null
+++ b/src/shared/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/src/shared/libosmocore/include/Makefile.am b/src/shared/libosmocore/include/Makefile.am
new file mode 100644
index 00000000..3578a80e
--- /dev/null
+++ b/src/shared/libosmocore/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = osmocom
diff --git a/src/shared/libosmocore/include/osmocom/Makefile.am b/src/shared/libosmocore/include/osmocom/Makefile.am
new file mode 100644
index 00000000..21f4f2d0
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/Makefile.am
@@ -0,0 +1,5 @@
+if ENABLE_VTY
+SUBDIRS = vty codec crypt gsm core
+else
+SUBDIRS = codec crypt gsm core
+endif
diff --git a/src/shared/libosmocore/include/osmocom/codec/Makefile.am b/src/shared/libosmocore/include/osmocom/codec/Makefile.am
new file mode 100644
index 00000000..c2136023
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/codec/Makefile.am
@@ -0,0 +1,3 @@
+osmocodec_HEADERS = codec.h
+
+osmocodecdir = $(includedir)/osmocom/codec
diff --git a/src/shared/libosmocore/include/osmocom/codec/codec.h b/src/shared/libosmocore/include/osmocom/codec/codec.h
new file mode 100644
index 00000000..6f9ffea5
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/codec/codec.h
@@ -0,0 +1,20 @@
+#ifndef _OSMOCOM_CODEC_H
+#define _OSMOCOM_CODEC_H
+
+#include <stdint.h>
+
+extern uint16_t gsm610_bitorder[]; /* FR */
+extern uint16_t gsm620_unvoiced_bitorder[]; /* HR unvoiced */
+extern uint16_t gsm620_voiced_bitorder[]; /* HR voiced */
+extern uint16_t gsm660_bitorder[]; /* EFR */
+
+extern uint16_t gsm690_12_2_bitorder[]; /* AMR 12.2 kbits */
+extern uint16_t gsm690_10_2_bitorder[]; /* AMR 10.2 kbits */
+extern uint16_t gsm690_7_95_bitorder[]; /* AMR 7.95 kbits */
+extern uint16_t gsm690_7_4_bitorder[]; /* AMR 7.4 kbits */
+extern uint16_t gsm690_6_7_bitorder[]; /* AMR 6.7 kbits */
+extern uint16_t gsm690_5_9_bitorder[]; /* AMR 5.9 kbits */
+extern uint16_t gsm690_5_15_bitorder[]; /* AMR 5.15 kbits */
+extern uint16_t gsm690_4_75_bitorder[]; /* AMR 4.75 kbits */
+
+#endif /* _OSMOCOM_CODEC_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/Makefile.am b/src/shared/libosmocore/include/osmocom/core/Makefile.am
new file mode 100644
index 00000000..1df111af
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/Makefile.am
@@ -0,0 +1,31 @@
+osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h bits.h \
+ bitvec.h statistics.h utils.h socket.h \
+ gsmtap.h write_queue.h prim.h \
+ logging.h rate_ctr.h gsmtap_util.h \
+ crc16.h panic.h process.h linuxrbtree.h \
+ backtrace.h conv.h application.h \
+ crcgen.h crc8gen.h crc16gen.h crc32gen.h crc64gen.h
+
+noinst_HEADERS = timer_compat.h
+
+if ENABLE_PLUGIN
+osmocore_HEADERS += plugin.h
+endif
+
+if ENABLE_TALLOC
+osmocore_HEADERS += talloc.h
+endif
+
+if ENABLE_MSGFILE
+osmocore_HEADERS += msgfile.h
+endif
+
+if ENABLE_SERIAL
+osmocore_HEADERS += serial.h
+endif
+
+osmocoredir = $(includedir)/osmocom/core
+
+crc%gen.h: crcXXgen.h.tpl
+ @echo " SED $< -> $@"
+ @sed -e's/XX/$*/g' $< > $@
diff --git a/src/shared/libosmocore/include/osmocom/core/application.h b/src/shared/libosmocore/include/osmocom/core/application.h
new file mode 100644
index 00000000..34571698
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/application.h
@@ -0,0 +1,23 @@
+#ifndef OSMO_APPLICATION_H
+#define OSMO_APPLICATION_H
+
+/*!
+ * \file application.h
+ * \brief Routines for helping with the osmocom application setup.
+ */
+
+/*! \brief information containing the available logging subsystems */
+struct log_info;
+
+/*! \brief one instance of a logging target (file, stderr, ...) */
+struct log_target;
+
+/*! \brief the default logging target, logging to stderr */
+extern struct log_target *osmo_stderr_target;
+
+void osmo_init_ignore_signals(void);
+int osmo_init_logging(const struct log_info *);
+
+int osmo_daemonize(void);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/backtrace.h b/src/shared/libosmocore/include/osmocom/core/backtrace.h
new file mode 100644
index 00000000..1ed089ad
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/backtrace.h
@@ -0,0 +1,6 @@
+#ifndef _OSMO_BACKTRACE_H_
+#define _OSMO_BACKTRACE_H_
+
+void osmo_generate_backtrace(void);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/bits.h b/src/shared/libosmocore/include/osmocom/core/bits.h
new file mode 100644
index 00000000..b541b9ce
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/bits.h
@@ -0,0 +1,78 @@
+#ifndef _OSMO_BITS_H
+#define _OSMO_BITS_H
+
+#include <stdint.h>
+
+/*! \defgroup bits soft, unpacked and packed bits
+ * @{
+ */
+
+/*! \file bits.h
+ * \brief Osmocom bit level support code
+ */
+
+typedef int8_t sbit_t; /*!< \brief soft bit (-127...127) */
+typedef uint8_t ubit_t; /*!< \brief unpacked bit (0 or 1) */
+typedef uint8_t pbit_t; /*!< \brief packed bis (8 bits in a byte) */
+
+/*
+ NOTE on the endianess of pbit_t:
+ Bits in a pbit_t are ordered MSB first, i.e. 0x80 is the first bit.
+ Bit i in a pbit_t array is array[i/8] & (1<<(7-i%8))
+*/
+
+/*! \brief determine how many bytes we would need for \a num_bits packed bits
+ * \param[in] num_bits Number of packed bits
+ */
+static inline unsigned int osmo_pbit_bytesize(unsigned int num_bits)
+{
+ unsigned int pbit_bytesize = num_bits / 8;
+
+ if (num_bits % 8)
+ pbit_bytesize++;
+
+ return pbit_bytesize;
+}
+
+int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits);
+
+int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits);
+
+int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
+ const ubit_t *in, unsigned int in_ofs,
+ unsigned int num_bits, int lsb_mode);
+
+int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
+ const pbit_t *in, unsigned int in_ofs,
+ unsigned int num_bits, int lsb_mode);
+
+
+/* BIT REVERSAL */
+
+/*! \brief bit-reversal mode for osmo_bit_reversal() */
+enum osmo_br_mode {
+ /*! \brief reverse all bits in a 32bit dword */
+ OSMO_BR_BITS_IN_DWORD = 31,
+ /*! \brief reverse byte order in a 32bit dword */
+ OSMO_BR_BYTES_IN_DWORD = 24,
+ /*! \brief reverse bits of each byte in a 32bit dword */
+ OSMO_BR_BITS_IN_BYTE = 7,
+ /*! \brief swap the two 16bit words in a 32bit dword */
+ OSMO_BR_WORD_SWAP = 16,
+};
+
+/*! \brief generic bit reversal function */
+uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k);
+
+/* \brief reverse the bits within each byte of a 32bit word */
+uint32_t osmo_revbytebits_32(uint32_t x);
+
+/* \brief reverse the bits within a byte */
+uint32_t osmo_revbytebits_8(uint8_t x);
+
+/* \brief reverse the bits of each byte in a given buffer */
+void osmo_revbytebits_buf(uint8_t *buf, int len);
+
+/*! }@ */
+
+#endif /* _OSMO_BITS_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/bitvec.h b/src/shared/libosmocore/include/osmocom/core/bitvec.h
new file mode 100644
index 00000000..c2422e6d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/bitvec.h
@@ -0,0 +1,70 @@
+#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.
+ *
+ */
+
+/*! \defgroup bitvec Bit vectors
+ * @{
+ */
+
+/*! \file bitvec.h
+ * \brief Osmocom bit vector abstraction
+ */
+
+#include <stdint.h>
+
+/*! \brief A single GSM bit
+ *
+ * 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, /*!< \brief A zero (0) bit */
+ ONE = 1, /*!< \brief A one (1) bit */
+ L = 2, /*!< \brief A CSN.1 "L" bit */
+ H = 3, /*!< \brief A CSN.1 "H" bit */
+};
+
+/*! \brief structure describing a bit vector */
+struct bitvec {
+ unsigned int cur_bit; /*!< \brief curser to the next unused bit */
+ unsigned int data_len; /*!< \brief length of data array in bytes */
+ uint8_t *data; /*!< \brief pointer to data array */
+};
+
+enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr);
+enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv,
+ unsigned int bitnr);
+unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n);
+int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum,
+ enum bit_value bit);
+int bitvec_set_bit(struct bitvec *bv, enum bit_value bit);
+int bitvec_get_bit_high(struct bitvec *bv);
+int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count);
+int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count);
+int bitvec_get_uint(struct bitvec *bv, int num_bits);
+int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value val);
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
+
+/*! }@ */
+
+#endif /* _BITVEC_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/conv.h b/src/shared/libosmocore/include/osmocom/core/conv.h
new file mode 100644
index 00000000..e76a5c61
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/conv.h
@@ -0,0 +1,146 @@
+/*
+ * conv.h
+ *
+ * Copyright (C) 2011 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.
+ */
+
+/*! \defgroup conv Convolutional encoding and decoding routines
+ * @{
+ */
+
+/*! \file conv.h
+ * \file Osmocom convolutional encoder and decoder
+ */
+
+#ifndef __OSMO_CONV_H__
+#define __OSMO_CONV_H__
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+
+/*! \brief possibe termination types
+ *
+ * The termination type will determine which state the encoder/decoder
+ * can start/end with. This is mostly taken care of in the high level API
+ * call. So if you use the low level API, you must take care of making the
+ * proper calls yourself.
+ */
+enum osmo_conv_term {
+ CONV_TERM_FLUSH = 0, /*!< \brief Flush encoder state */
+ CONV_TERM_TRUNCATION, /*!< \brief Direct truncation */
+ CONV_TERM_TAIL_BITING, /*!< \brief Tail biting */
+};
+
+/*! \brief structure describing a given convolutional code
+ *
+ * The only required fields are N,K and the next_output/next_state arrays. The
+ * other can be left to default value of zero depending on what the code does.
+ * If 'len' is left at 0 then only the low level API can be used.
+ */
+struct osmo_conv_code {
+ int N; /*!< \brief Inverse of code rate */
+ int K; /*!< \brief Constraint length */
+ int len; /*!< \brief # of data bits */
+
+ enum osmo_conv_term term; /*!< \brief Termination type */
+
+ const uint8_t (*next_output)[2];/*!< \brief Next output array */
+ const uint8_t (*next_state)[2]; /*!< \brief Next state array */
+
+ const uint8_t *next_term_output;/*!< \brief Flush termination output */
+ const uint8_t *next_term_state; /*!< \brief Flush termination state */
+
+ const int *puncture; /*!< \brief Punctured bits indexes */
+};
+
+
+/* Common */
+
+int osmo_conv_get_input_length(const struct osmo_conv_code *code, int len);
+int osmo_conv_get_output_length(const struct osmo_conv_code *code, int len);
+
+
+/* Encoding */
+
+ /* Low level API */
+
+/*! \brief convolutional encoder state */
+struct osmo_conv_encoder {
+ const struct osmo_conv_code *code; /*!< \brief for which code? */
+ int i_idx; /*!< \brief Next input bit index */
+ int p_idx; /*!< \brief Current puncture index */
+ uint8_t state; /*!< \brief Current state */
+};
+
+void osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
+ const struct osmo_conv_code *code);
+void osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder,
+ const ubit_t *input);
+int osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
+ const ubit_t *input, ubit_t *output, int n);
+int osmo_conv_encode_flush(struct osmo_conv_encoder *encoder, ubit_t *output);
+
+ /* All-in-one */
+int osmo_conv_encode(const struct osmo_conv_code *code,
+ const ubit_t *input, ubit_t *output);
+
+
+/* Decoding */
+
+ /* Low level API */
+
+/*! \brief convolutional decoder state */
+struct osmo_conv_decoder {
+ const struct osmo_conv_code *code; /*!< \brief for which code? */
+
+ int n_states; /*!< \brief number of states */
+
+ int len; /*!< \brief Max o_idx (excl. termination) */
+
+ int o_idx; /*!< \brief output index */
+ int p_idx; /*!< \brief puncture index */
+
+ unsigned int *ae; /*!< \brief accumulated error */
+ unsigned int *ae_next; /*!< \brief next accumulated error (tmp in scan) */
+ uint8_t *state_history; /*!< \brief state history [len][n_states] */
+};
+
+void osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
+ const struct osmo_conv_code *code,
+ int len, int start_state);
+void osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state);
+void osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder);
+void osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder);
+
+int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
+ const sbit_t *input, int n);
+int osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
+ const sbit_t *input);
+int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
+ ubit_t *output, int has_flush, int end_state);
+
+ /* All-in-one */
+int osmo_conv_decode(const struct osmo_conv_code *code,
+ const sbit_t *input, ubit_t *output);
+
+
+/*! }@ */
+
+#endif /* __OSMO_CONV_H__ */
diff --git a/src/shared/libosmocore/include/osmocom/core/crc16.h b/src/shared/libosmocore/include/osmocom/core/crc16.h
new file mode 100644
index 00000000..0e524176
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/crc16.h
@@ -0,0 +1,34 @@
+/*
+ * This was copied from the linux kernel and adjusted for our types.
+ */
+/*
+ * crc16.h - CRC-16 routine
+ *
+ * Implements the standard CRC-16:
+ * Width 16
+ * Poly 0x8005 (x^16 + x^15 + x^2 + 1)
+ * Init 0
+ *
+ * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#ifndef __CRC16_H
+#define __CRC16_H
+
+#include <stdint.h>
+
+#include <sys/types.h>
+
+extern uint16_t const osmo_crc16_table[256];
+
+extern uint16_t osmo_crc16(uint16_t crc, const uint8_t *buffer, size_t len);
+
+static inline uint16_t osmo_crc16_byte(uint16_t crc, const uint8_t data)
+{
+ return (crc >> 8) ^ osmo_crc16_table[(crc ^ data) & 0xff];
+}
+
+#endif /* __CRC16_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl b/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl
new file mode 100644
index 00000000..b411276e
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl
@@ -0,0 +1,59 @@
+/*
+ * crcXXgen.h
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef __OSMO_CRCXXGEN_H__
+#define __OSMO_CRCXXGEN_H__
+
+/*! \addtogroup crcgen
+ * @{
+ */
+
+/*! \file crcXXgen.h
+ * \file Osmocom generic CRC routines (for max XX bits poly) header
+ */
+
+
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+
+
+/*! \brief structure describing a given CRC code of max XX bits */
+struct osmo_crcXXgen_code {
+ int bits; /*!< \brief Actual number of bits of the CRC */
+ uintXX_t poly; /*!< \brief Polynom (normal representation, MSB omitted */
+ uintXX_t init; /*!< \brief Initialization value of the CRC state */
+ uintXX_t remainder; /*!< \brief Remainder of the CRC (final XOR) */
+};
+
+uintXX_t osmo_crcXXgen_compute_bits(const struct osmo_crcXXgen_code *code,
+ const ubit_t *in, int len);
+int osmo_crcXXgen_check_bits(const struct osmo_crcXXgen_code *code,
+ const ubit_t *in, int len, const ubit_t *crc_bits);
+void osmo_crcXXgen_set_bits(const struct osmo_crcXXgen_code *code,
+ const ubit_t *in, int len, ubit_t *crc_bits);
+
+
+/*! }@ */
+
+#endif /* __OSMO_CRCXXGEN_H__ */
+
+/* vim: set syntax=c: */
diff --git a/src/shared/libosmocore/include/osmocom/core/crcgen.h b/src/shared/libosmocore/include/osmocom/core/crcgen.h
new file mode 100644
index 00000000..cd916c76
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/crcgen.h
@@ -0,0 +1,41 @@
+/*
+ * crcgen.h
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef __OSMO_CRCGEN_H__
+#define __OSMO_CRCGEN_H__
+
+/*! \defgroup crcgen Osmocom generic CRC routines
+ * @{
+ */
+
+/*! \file crcgen.h
+ * \file Osmocom generic CRC routines global header
+ */
+
+#include <osmocom/core/crc8gen.h>
+#include <osmocom/core/crc16gen.h>
+#include <osmocom/core/crc32gen.h>
+#include <osmocom/core/crc64gen.h>
+
+/*! }@ */
+
+#endif /* __OSMO_CRCGEN_H__ */
diff --git a/src/shared/libosmocore/include/osmocom/core/gsmtap.h b/src/shared/libosmocore/include/osmocom/core/gsmtap.h
new file mode 100644
index 00000000..5dc869d4
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/gsmtap.h
@@ -0,0 +1,151 @@
+#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>
+
+/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
+
+/* The GSMTAP format definition is maintained in libosmocore,
+ * specifically the latest version can always be obtained from
+ * http://cgit.osmocom.org/cgit/libosmocore/tree/include/osmocom/core/gsmtap.h
+ *
+ * If you want to introduce new protocol/burst/channel types or extend
+ * GSMTAP in any way, please contact the GSMTAP maintainer at either the
+ * public openbsc@lists.osmocom.org mailing list, or privately at
+ * Harald Welte <laforge@gnumonks.org>.
+ *
+ * Your cooperation ensures that all projects will use the same GSMTAP
+ * definitions and remain compatible with each other.
+ */
+
+#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_TYPE_SIM 0x04
+#define GSMTAP_TYPE_TETRA_I1 0x05 /* tetra air interface */
+#define GSMTAP_TYPE_TETRA_I1_BURST 0x06 /* tetra air interface */
+#define GSMTAP_TYPE_WMX_BURST 0x07 /* WiMAX burst */
+#define GSMTAP_TYPE_GB_LLC 0x08 /* GPRS Gb interface: LLC */
+#define GSMTAP_TYPE_GB_SNDCP 0x09 /* GPRS Gb interface: SNDCP */
+#define GSMTAP_TYPE_GMR1_UM 0x0a /* GMR-1 L2 packets */
+
+/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
+
+/* sub-types for TYPE_UM_BURST */
+#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
+/* WiMAX bursts */
+#define GSMTAP_BURST_CDMA_CODE 0x10 /* WiMAX CDMA Code Attribute burst */
+#define GSMTAP_BURST_FCH 0x11 /* WiMAX FCH burst */
+#define GSMTAP_BURST_FFB 0x12 /* WiMAX Fast Feedback burst */
+#define GSMTAP_BURST_PDU 0x13 /* WiMAX PDU burst */
+#define GSMTAP_BURST_HACK 0x14 /* WiMAX HARQ ACK burst */
+#define GSMTAP_BURST_PHY_ATTRIBUTES 0x15 /* WiMAX PHY Attributes burst */
+
+/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
+
+/* sub-types for TYPE_UM */
+#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_PACCH 0x0b
+#define GSMTAP_CHANNEL_CBCH52 0x0c
+#define GSMTAP_CHANNEL_PDCH 0x0d
+#define GSMTAP_CHANNEL_PTCCH 0x0e
+#define GSMTAP_CHANNEL_CBCH51 0x0f
+#define GSMTAP_CHANNEL_ACCH 0x80
+
+/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
+
+/* sub-types for TYPE_TETRA_AIR */
+#define GSMTAP_TETRA_BSCH 0x01
+#define GSMTAP_TETRA_AACH 0x02
+#define GSMTAP_TETRA_SCH_HU 0x03
+#define GSMTAP_TETRA_SCH_HD 0x04
+#define GSMTAP_TETRA_SCH_F 0x05
+#define GSMTAP_TETRA_BNCH 0x06
+#define GSMTAP_TETRA_STCH 0x07
+#define GSMTAP_TETRA_TCH_F 0x08
+
+/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
+
+/* sub-types for TYPE_GMR1_UM */
+#define GSMTAP_GMR1_UNKNOWN 0x00
+#define GSMTAP_GMR1_BCCH 0x01
+#define GSMTAP_GMR1_CCCH 0x02 /* either AGCH or PCH */
+#define GSMTAP_GMR1_PCH 0x03
+#define GSMTAP_GMR1_AGCH 0x04
+#define GSMTAP_GMR1_BACH 0x05
+#define GSMTAP_GMR1_RACH 0x06
+#define GSMTAP_GMR1_CBCH 0x07
+#define GSMTAP_GMR1_SDCCH 0x08
+#define GSMTAP_GMR1_TACCH 0x09
+#define GSMTAP_GMR1_GBCH 0x0a
+
+#define GSMTAP_GMR1_SACCH 0x01 /* to be combined with _TCH{6,9} */
+#define GSMTAP_GMR1_FACCH 0x02 /* to be combines with _TCH{3,6,9} */
+#define GSMTAP_GMR1_DKAB 0x03 /* to be combined with _TCH3 */
+#define GSMTAP_GMR1_TCH3 0x10
+#define GSMTAP_GMR1_TCH6 0x14
+#define GSMTAP_GMR1_TCH9 0x18
+
+/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
+
+/* flags for the ARFCN */
+#define GSMTAP_ARFCN_F_PCS 0x8000
+#define GSMTAP_ARFCN_F_UPLINK 0x4000
+#define GSMTAP_ARFCN_MASK 0x3fff
+
+/* IANA-assigned well-known UDP port for GSMTAP messages */
+#define GSMTAP_UDP_PORT 4729
+
+/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
+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/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h b/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h
new file mode 100644
index 00000000..5609381f
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h
@@ -0,0 +1,57 @@
+#ifndef _GSMTAP_UTIL_H
+#define _GSMTAP_UTIL_H
+
+#include <stdint.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/select.h>
+
+/*! \defgroup gsmtap GSMTAP
+ * @{
+ */
+/*! \file gsmtap_util.h */
+
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id);
+
+struct msgb *gsmtap_makemsg_ex(uint8_t type, uint16_t arfcn, uint8_t ts, uint8_t chan_type,
+ uint8_t ss, uint32_t fn, int8_t signal_dbm,
+ uint8_t snr, const uint8_t *data, unsigned int len);
+
+struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
+ uint8_t ss, uint32_t fn, int8_t signal_dbm,
+ uint8_t snr, const uint8_t *data, unsigned int len);
+
+/*! \brief one gsmtap instance */
+struct gsmtap_inst {
+ int ofd_wq_mode; /*!< \brief wait queue mode? */
+ struct osmo_wqueue wq; /*!< \brief the wait queue */
+ struct osmo_fd sink_ofd;/*!< \brief file descriptor */
+};
+
+/*! \brief obtain the file descriptor associated with a gsmtap instance */
+static inline int gsmtap_inst_fd(struct gsmtap_inst *gti)
+{
+ return gti->wq.bfd.fd;
+}
+
+int gsmtap_source_init_fd(const char *host, uint16_t port);
+
+int gsmtap_source_add_sink_fd(int gsmtap_fd);
+
+struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
+ int ofd_wq_mode);
+
+int gsmtap_source_add_sink(struct gsmtap_inst *gti);
+
+int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg);
+
+int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, uint16_t arfcn, uint8_t ts,
+ uint8_t chan_type, uint8_t ss, uint32_t fn,
+ int8_t signal_dbm, uint8_t snr, const uint8_t *data,
+ unsigned int len);
+
+int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts,
+ uint8_t chan_type, uint8_t ss, uint32_t fn,
+ int8_t signal_dbm, uint8_t snr, const uint8_t *data,
+ unsigned int len);
+
+#endif /* _GSMTAP_UTIL_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/linuxlist.h b/src/shared/libosmocore/include/osmocom/core/linuxlist.h
new file mode 100644
index 00000000..ff2c4915
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/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(__attribute__((unused)) 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/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h
new file mode 100644
index 00000000..44e00a16
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h
@@ -0,0 +1,160 @@
+/*
+ Red Black Trees
+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ linux/include/linux/rbtree.h
+
+ To use rbtrees you'll have to implement your own insert and search cores.
+ This will avoid us to use callbacks and to drop drammatically performances.
+ I know it's not the cleaner way, but in C (not in C++) to get
+ performances and genericity...
+
+ Some example of insert and search follows here. The search is a plain
+ normal search over an ordered tree. The insert instead must be implemented
+ int two steps: as first thing the code must insert the element in
+ order as a red leaf in the tree, then the support library function
+ rb_insert_color() must be called. Such function will do the
+ not trivial work to rebalance the rbtree if necessary.
+
+-----------------------------------------------------------------------
+static inline struct page * rb_search_page_cache(struct inode * inode,
+ unsigned long offset)
+{
+ struct rb_node * n = inode->i_rb_page_cache.rb_node;
+ struct page * page;
+
+ while (n)
+ {
+ page = rb_entry(n, struct page, rb_page_cache);
+
+ if (offset < page->offset)
+ n = n->rb_left;
+ else if (offset > page->offset)
+ n = n->rb_right;
+ else
+ return page;
+ }
+ return NULL;
+}
+
+static inline struct page * __rb_insert_page_cache(struct inode * inode,
+ unsigned long offset,
+ struct rb_node * node)
+{
+ struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
+ struct rb_node * parent = NULL;
+ struct page * page;
+
+ while (*p)
+ {
+ parent = *p;
+ page = rb_entry(parent, struct page, rb_page_cache);
+
+ if (offset < page->offset)
+ p = &(*p)->rb_left;
+ else if (offset > page->offset)
+ p = &(*p)->rb_right;
+ else
+ return page;
+ }
+
+ rb_link_node(node, parent, p);
+
+ return NULL;
+}
+
+static inline struct page * rb_insert_page_cache(struct inode * inode,
+ unsigned long offset,
+ struct rb_node * node)
+{
+ struct page * ret;
+ if ((ret = __rb_insert_page_cache(inode, offset, node)))
+ goto out;
+ rb_insert_color(node, &inode->i_rb_page_cache);
+ out:
+ return ret;
+}
+-----------------------------------------------------------------------
+*/
+
+#ifndef _LINUX_RBTREE_H
+#define _LINUX_RBTREE_H
+
+#include <stdlib.h>
+
+struct rb_node
+{
+ unsigned long rb_parent_color;
+#define RB_RED 0
+#define RB_BLACK 1
+ struct rb_node *rb_right;
+ struct rb_node *rb_left;
+} __attribute__((aligned(sizeof(long))));
+ /* The alignment might seem pointless, but allegedly CRIS needs it */
+
+struct rb_root
+{
+ struct rb_node *rb_node;
+};
+
+
+#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
+#define rb_color(r) ((r)->rb_parent_color & 1)
+#define rb_is_red(r) (!rb_color(r))
+#define rb_is_black(r) rb_color(r)
+#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
+#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
+
+static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
+{
+ rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
+}
+static inline void rb_set_color(struct rb_node *rb, int color)
+{
+ rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
+}
+
+#define RB_ROOT { NULL, }
+#define rb_entry(ptr, type, member) container_of(ptr, type, member)
+
+#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
+#define RB_EMPTY_NODE(node) (rb_parent(node) == node)
+#define RB_CLEAR_NODE(node) (rb_set_parent(node, node))
+
+extern void rb_insert_color(struct rb_node *, struct rb_root *);
+extern void rb_erase(struct rb_node *, struct rb_root *);
+
+/* Find logical next and previous nodes in a tree */
+extern struct rb_node *rb_next(const struct rb_node *);
+extern struct rb_node *rb_prev(const struct rb_node *);
+extern struct rb_node *rb_first(const struct rb_root *);
+extern struct rb_node *rb_last(const struct rb_root *);
+
+/* Fast replacement of a single node without remove/rebalance/add/rebalance */
+extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
+ struct rb_root *root);
+
+static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
+ struct rb_node ** rb_link)
+{
+ node->rb_parent_color = (unsigned long )parent;
+ node->rb_left = node->rb_right = NULL;
+
+ *rb_link = node;
+}
+
+#endif /* _LINUX_RBTREE_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/logging.h b/src/shared/libosmocore/include/osmocom/core/logging.h
new file mode 100644
index 00000000..043a8509
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/logging.h
@@ -0,0 +1,211 @@
+#ifndef _OSMOCORE_LOGGING_H
+#define _OSMOCORE_LOGGING_H
+
+/*! \defgroup logging Osmocom logging framework
+ * @{
+ */
+
+/*! \file logging.h */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <osmocom/core/linuxlist.h>
+
+/*! \brief Maximum number of logging contexts */
+#define LOG_MAX_CTX 8
+/*! \brief Maximum number of logging filters */
+#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
+
+
+void osmo_vlogp(int subsys, int level, char *file, int line,
+ int cont, const char *format, va_list ap);
+
+void logp(int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
+
+/*! \brief Log a new message through the Osmocom logging framework
+ * \param[in] ss logging subsystem (e.g. \ref DLGLOBAL)
+ * \param[in] level logging level (e.g. \ref LOGL_NOTICE)
+ * \param[in] fmt format string
+ * \param[in] args variable argument list
+ */
+#define LOGP(ss, level, fmt, args...) \
+ logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
+
+/*! \brief Continue a log message through the Osmocom logging framework
+ * \param[in] ss logging subsystem (e.g. \ref DLGLOBAL)
+ * \param[in] level logging level (e.g. \ref LOGL_NOTICE)
+ * \param[in] fmt format string
+ * \param[in] args variable argument list
+ */
+#define LOGPC(ss, level, fmt, args...) \
+ logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
+
+/*! \brief different log levels */
+#define LOGL_DEBUG 1 /*!< \brief debugging information */
+#define LOGL_INFO 3
+#define LOGL_NOTICE 5 /*!< \brief abnormal/unexpected condition */
+#define LOGL_ERROR 7 /*!< \brief error condition, requires user action */
+#define LOGL_FATAL 8 /*!< \brief fatal, program aborted */
+
+#define LOG_FILTER_ALL 0x0001
+
+/* logging levels defined by the library itself */
+#define DLGLOBAL -1
+#define DLLAPD -2
+#define DLINP -3
+#define DLMUX -4
+#define DLMI -5
+#define DLMIB -6
+#define DLSMS -7
+#define OSMO_NUM_DLIB 7
+
+struct log_category {
+ uint8_t loglevel;
+ uint8_t enabled;
+};
+
+/*! \brief Information regarding one logging category */
+struct log_info_cat {
+ const char *name; /*!< name of category */
+ const char *color; /*!< color string for cateyory */
+ const char *description; /*!< description text */
+ uint8_t loglevel; /*!< currently selected log-level */
+ uint8_t enabled; /*!< is this category enabled or not */
+};
+
+/*! \brief Log context information, passed to filter */
+struct log_context {
+ void *ctx[LOG_MAX_CTX+1];
+};
+
+struct log_target;
+
+/*! \brief Log filter function */
+typedef int log_filter(const struct log_context *ctx,
+ struct log_target *target);
+
+/*! \brief Logging configuration, passed to \ref log_init */
+struct log_info {
+ /* \brief filter callback function */
+ log_filter *filter_fn;
+
+ /*! \brief per-category information */
+ struct log_info_cat *cat;
+ /*! \brief total number of categories */
+ unsigned int num_cat;
+ /*! \brief total number of user categories (not library) */
+ unsigned int num_cat_user;
+};
+
+/*! \brief Type of logging target */
+enum log_target_type {
+ LOG_TGT_TYPE_VTY, /*!< \brief VTY logging */
+ LOG_TGT_TYPE_SYSLOG, /*!< \brief syslog based logging */
+ LOG_TGT_TYPE_FILE, /*!< \brief text file logging */
+ LOG_TGT_TYPE_STDERR, /*!< \brief stderr logging */
+};
+
+/*! \brief structure representing a logging target */
+struct log_target {
+ struct llist_head entry; /*!< \brief linked list */
+
+ /*! \brief Internal data for filtering */
+ int filter_map;
+ /*! \brief Internal data for filtering */
+ void *filter_data[LOG_MAX_FILTERS+1];
+
+ /*! \brief logging categories */
+ struct log_category *categories;
+
+ /*! \brief global log level */
+ uint8_t loglevel;
+ /*! \brief should color be used when printing log messages? */
+ unsigned int use_color:1;
+ /*! \brief should log messages be prefixed with a timestamp? */
+ unsigned int print_timestamp:1;
+
+ /*! \brief the type of this log taget */
+ enum log_target_type type;
+
+ union {
+ struct {
+ FILE *out;
+ const char *fname;
+ } tgt_file;
+
+ struct {
+ int priority;
+ int facility;
+ } tgt_syslog;
+
+ struct {
+ void *vty;
+ } tgt_vty;
+ };
+
+ /*! \brief call-back function to be called when the logging framework
+ * wants to log somethnig.
+ * \param[[in] target logging target
+ * \param[in] level log level of currnet message
+ * \param[in] string the string that is to be written to the log
+ */
+ void (*output) (struct log_target *target, unsigned int level,
+ const char *string);
+};
+
+/* use the above macros */
+void logp2(int subsys, unsigned int level, char *file,
+ int line, int cont, const char *format, ...)
+ __attribute__ ((format (printf, 6, 7)));
+int log_init(const struct log_info *inf, void *talloc_ctx);
+
+/* 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);
+const char *log_level_str(unsigned int 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);
+void log_target_destroy(struct log_target *target);
+struct log_target *log_target_create_stderr(void);
+struct log_target *log_target_create_file(const char *fname);
+struct log_target *log_target_create_syslog(const char *ident, int option,
+ int facility);
+int log_target_file_reopen(struct log_target *tgt);
+
+void log_add_target(struct log_target *target);
+void log_del_target(struct log_target *target);
+
+/* Generate command string for VTY use */
+const char *log_vty_command_string(const struct log_info *info);
+const char *log_vty_command_description(const struct log_info *info);
+
+struct log_target *log_target_find(int type, const char *fname);
+extern struct llist_head osmo_log_target_list;
+
+/*! }@ */
+
+#endif /* _OSMOCORE_LOGGING_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/msgb.h b/src/shared/libosmocore/include/osmocom/core/msgb.h
new file mode 100644
index 00000000..e465ec2b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/msgb.h
@@ -0,0 +1,370 @@
+#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 <osmocom/core/linuxlist.h>
+#include <osmocom/core/utils.h>
+
+/*! \defgroup msgb Message buffers
+ * @{
+ */
+
+/*! \file msgb.h
+ * \brief Osmocom message buffers
+ * The Osmocom message buffers are modelled after the 'struct skb'
+ * inside the Linux kernel network stack. As they exist in userspace,
+ * they are much simplified. However, terminology such as headroom,
+ * tailroom, push/pull/put etc. remains the same.
+ */
+
+#define MSGB_DEBUG
+
+/*! \brief Osmocom message buffer */
+struct msgb {
+ struct llist_head list; /*!< \brief linked list header */
+
+
+ /* Part of which TRX logical channel we were received / transmitted */
+ /* FIXME: move them into the control buffer */
+ union {
+ void *dst; /*!< \brief reference of origin/destination */
+ struct gsm_bts_trx *trx;
+ };
+ struct gsm_lchan *lchan; /*!< \brief logical channel */
+
+ unsigned char *l1h; /*!< \brief pointer to Layer1 header (if any) */
+ unsigned char *l2h; /*!< \brief pointer to A-bis layer 2 header: OML, RSL(RLL), NS */
+ unsigned char *l3h; /*!< \brief pointer to Layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */
+ unsigned char *l4h; /*!< \brief pointer to layer 4 header */
+
+ unsigned long cb[5]; /*!< \brief control buffer */
+
+ uint16_t data_len; /*!< \brief length of underlying data array */
+ uint16_t len; /*!< \brief length of bytes used in msgb */
+
+ unsigned char *head; /*!< \brief start of underlying memory buffer */
+ unsigned char *tail; /*!< \brief end of message in buffer */
+ unsigned char *data; /*!< \brief start of message in buffer */
+ unsigned char _data[0]; /*!< \brief optional immediate data array */
+};
+
+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);
+
+#ifdef MSGB_DEBUG
+#include <osmocom/core/panic.h>
+#define MSGB_ABORT(msg, fmt, args ...) do { \
+ osmo_panic("msgb(%p): " fmt, msg, ## args); \
+ } while(0)
+#else
+#define MSGB_ABORT(msg, fmt, args ...)
+#endif
+
+/*! \brief obtain L1 header of msgb */
+#define msgb_l1(m) ((void *)(m->l1h))
+/*! \brief obtain L2 header of msgb */
+#define msgb_l2(m) ((void *)(m->l2h))
+/*! \brief obtain L3 header of msgb */
+#define msgb_l3(m) ((void *)(m->l3h))
+/*! \brief obtain SMS header of msgb */
+#define msgb_sms(m) ((void *)(m->l4h))
+
+/*! \brief determine length of L1 message
+ * \param[in] msgb message buffer
+ * \returns size of L1 message in bytes
+ *
+ * This function computes the number of bytes between the tail of the
+ * message and the layer 1 header.
+ */
+static inline unsigned int msgb_l1len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l1(msgb);
+}
+
+/*! \brief determine length of L2 message
+ * \param[in] msgb message buffer
+ * \returns size of L2 message in bytes
+ *
+ * This function computes the number of bytes between the tail of the
+ * message and the layer 2 header.
+ */
+static inline unsigned int msgb_l2len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l2(msgb);
+}
+
+/*! \brief determine length of L3 message
+ * \param[in] msgb message buffer
+ * \returns size of L3 message in bytes
+ *
+ * This function computes the number of bytes between the tail of the
+ * message and the layer 3 header.
+ */
+static inline unsigned int msgb_l3len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l3(msgb);
+}
+
+/*! \brief determine the length of the header
+ * \param[in] msgb message buffer
+ * \returns number of bytes between start of buffer and start of msg
+ *
+ * This function computes the length difference between the underlying
+ * data buffer and the used section of the \a msgb.
+ */
+static inline unsigned int msgb_headlen(const struct msgb *msgb)
+{
+ return msgb->len - msgb->data_len;
+}
+
+/*! \brief determine how much tail room is left in msgb
+ * \param[in] msgb message buffer
+ * \returns number of bytes remaining at end of msgb
+ *
+ * This function computes the amount of octets left in the underlying
+ * data buffer after the end of the message.
+ */
+static inline int msgb_tailroom(const struct msgb *msgb)
+{
+ return (msgb->head + msgb->data_len) - msgb->tail;
+}
+
+/*! \brief determine the amount of headroom in msgb
+ * \param[in] msgb message buffer
+ * \returns number of bytes left ahead of message start in msgb
+ *
+ * This function computes the amount of bytes left in the underlying
+ * data buffer before the start of the actual message.
+ */
+static inline int msgb_headroom(const struct msgb *msgb)
+{
+ return (msgb->data - msgb->head);
+}
+
+/*! \brief append data to end of message buffer
+ * \param[in] msgb message buffer
+ * \param[in] len number of bytes to append to message
+ * \returns pointer to start of newly-appended data
+ *
+ * This function will move the \a tail pointer of the message buffer \a
+ * len bytes further, thus enlarging the message by \a len bytes.
+ *
+ * The return value is a pointer to start of the newly added section at
+ * the end of the message and can be used for actually filling/copying
+ * data into it.
+ */
+static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->tail;
+ if (msgb_tailroom(msgb) < (int) len)
+ MSGB_ABORT(msgb, "Not enough tailroom msgb_push (%u < %u)\n",
+ msgb_tailroom(msgb), len);
+ msgb->tail += len;
+ msgb->len += len;
+ return tmp;
+}
+
+/*! \brief append a uint8 value to the end of the message
+ * \param[in] msgb message buffer
+ * \param[in] word unsigned 8bit byte to be appended
+ */
+static inline void msgb_put_u8(struct msgb *msgb, uint8_t word)
+{
+ uint8_t *space = msgb_put(msgb, 1);
+ space[0] = word & 0xFF;
+}
+
+/*! \brief append a uint16 value to the end of the message
+ * \param[in] msgb message buffer
+ * \param[in] word unsigned 16bit byte to be appended
+ */
+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;
+}
+
+/*! \brief append a uint32 value to the end of the message
+ * \param[in] msgb message buffer
+ * \param[in] word unsigned 32bit byte to be appended
+ */
+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;
+}
+
+/*! \brief remove data from end of message
+ * \param[in] msgb message buffer
+ * \param[in] len number of bytes to remove from end
+ */
+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;
+}
+/*! \brief remove uint8 from end of message
+ * \param[in] msgb message buffer
+ * \returns 8bit value taken from end of msgb
+ */
+static inline uint8_t msgb_get_u8(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 1);
+ return space[0];
+}
+/*! \brief remove uint16 from end of message
+ * \param[in] msgb message buffer
+ * \returns 16bit value taken from end of msgb
+ */
+static inline uint16_t msgb_get_u16(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 2);
+ return space[0] << 8 | space[1];
+}
+/*! \brief remove uint32 from end of message
+ * \param[in] msgb message buffer
+ * \returns 32bit value taken from end of msgb
+ */
+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];
+}
+
+/*! \brief prepend (push) some data to start of message
+ * \param[in] msgb message buffer
+ * \param[in] len number of bytes to pre-pend
+ * \returns pointer to newly added portion at start of \a msgb
+ *
+ * This function moves the \a data pointer of the \ref msgb further
+ * to the front (by \a len bytes), thereby enlarging the message by \a
+ * len bytes.
+ *
+ * The return value is a pointer to the newly added section in the
+ * beginning of the message. It can be used to fill/copy data into it.
+ */
+static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
+{
+ if (msgb_headroom(msgb) < (int) len)
+ MSGB_ABORT(msgb, "Not enough headroom msgb_push (%u < %u)\n",
+ msgb_headroom(msgb), len);
+ msgb->data -= len;
+ msgb->len += len;
+ return msgb->data;
+}
+/*! \brief remove (pull) a header from the front of the message buffer
+ * \param[in] msgb message buffer
+ * \param[in] len number of octets to be pulled
+ * \returns pointer to new start of msgb
+ *
+ * This function moves the \a data pointer of the \ref msgb further back
+ * in the message, thereby shrinking the size of the message by \a len
+ * bytes.
+ */
+static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
+{
+ msgb->len -= len;
+ return msgb->data += len;
+}
+
+/*! \brief Increase headroom of empty msgb, reducing the tailroom
+ * \param[in] msg message buffer
+ * \param[in] len amount of extra octets to be reserved as headroom
+ *
+ * This function reserves some memory at the beginning of the underlying
+ * data buffer. The idea is to reserve space in case further headers
+ * have to be pushed to the \ref msgb during further processing.
+ *
+ * Calling this function leads to undefined reusults if it is called on
+ * a non-empty \ref msgb.
+ */
+static inline void msgb_reserve(struct msgb *msg, int len)
+{
+ msg->data += len;
+ msg->tail += len;
+}
+
+/*! \brief Trim the msgb to a given absolute length
+ * \param[in] msg message buffer
+ * \param[in] len new total length of buffer
+ * \returns 0 in case of success, negative in case of error
+ */
+static inline int msgb_trim(struct msgb *msg, int len)
+{
+ if (len > msg->data_len)
+ return -1;
+
+ msg->len = len;
+ msg->tail = msg->data + len;
+
+ return 0;
+}
+
+/*! \brief Trim the msgb to a given layer3 length
+ * \pram[in] msg message buffer
+ * \param[in] l3len new layer3 length
+ * \returns 0 in case of success, negative in case of error
+ */
+static inline int msgb_l3trim(struct msgb *msg, int l3len)
+{
+ return msgb_trim(msg, (msg->l3h - msg->data) + l3len);
+}
+
+/*! \brief Allocate message buffer with specified headroom
+ * \param[in] size size in bytes, including headroom
+ * \param[in] headroom headroom in bytes
+ * \param[in] name human-readable name
+ * \returns allocated message buffer with specified headroom
+ *
+ * This function is a convenience wrapper around \ref msgb_alloc
+ * followed by \ref msgb_reserve in order to create a new \ref msgb with
+ * user-specified amount of headroom.
+ */
+static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
+ const char *name)
+{
+ osmo_static_assert(size > headroom, headroom_bigger);
+
+ struct msgb *msg = msgb_alloc(size, name);
+ if (msg)
+ msgb_reserve(msg, headroom);
+ return msg;
+}
+
+/* non inline functions to ease binding */
+
+uint8_t *msgb_data(const struct msgb *msg);
+uint16_t msgb_length(const struct msgb *msg);
+void msgb_set_talloc_ctx(void *ctx);
+
+/*! }@ */
+
+#endif /* _MSGB_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/msgfile.h b/src/shared/libosmocore/include/osmocom/core/msgfile.h
new file mode 100644
index 00000000..c5e67a45
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/msgfile.h
@@ -0,0 +1,49 @@
+/*
+ * (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 MSG_FILE_H
+#define MSG_FILE_H
+
+#include <osmocom/core/linuxlist.h>
+
+/**
+ * One message in the list.
+ */
+struct osmo_config_entry {
+ struct llist_head list;
+
+ /* number for everyone to use */
+ int nr;
+
+ /* data from the file */
+ char *mcc;
+ char *mnc;
+ char *option;
+ char *text;
+};
+
+struct osmo_config_list {
+ struct llist_head entry;
+};
+
+struct osmo_config_list* osmo_config_list_parse(void *ctx, const char *filename);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/panic.h b/src/shared/libosmocore/include/osmocom/core/panic.h
new file mode 100644
index 00000000..fd5cf208
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/panic.h
@@ -0,0 +1,20 @@
+#ifndef OSMOCORE_PANIC_H
+#define OSMOCORE_PANIC_H
+
+/*! \addtogroup utils
+ * @{
+ */
+
+/*! \file panic.h */
+
+#include <stdarg.h>
+
+/*! \brief panic handler callback function type */
+typedef void (*osmo_panic_handler_t)(const char *fmt, va_list args);
+
+extern void osmo_panic(const char *fmt, ...);
+extern void osmo_set_panic_handler(osmo_panic_handler_t h);
+
+/*! @} */
+
+#endif /* OSMOCORE_PANIC_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/plugin.h b/src/shared/libosmocore/include/osmocom/core/plugin.h
new file mode 100644
index 00000000..6c0eccc6
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/plugin.h
@@ -0,0 +1,6 @@
+#ifndef _OSMO_PLUGIN_H
+#define _OSMO_PLUGIN_H
+
+int osmo_plugin_load_all(const char *directory);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/prim.h b/src/shared/libosmocore/include/osmocom/core/prim.h
new file mode 100644
index 00000000..b1026fe3
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/prim.h
@@ -0,0 +1,58 @@
+#ifndef OSMO_PRIMITIVE_H
+#define OSMO_PRIMITIVE_H
+
+/*! \defgroup prim Osmocom primitives
+ * @{
+ */
+
+/*! \file prim.c */
+
+#include <stdint.h>
+#include <osmocom/core/msgb.h>
+
+#define OSMO_PRIM(prim, op) ((prim << 8) | (op & 0xFF))
+#define OSMO_PRIM_HDR(oph) OSMO_PRIM((oph)->primitive, (oph)->operation)
+
+/*! \brief primitive operation */
+enum osmo_prim_operation {
+ PRIM_OP_REQUEST, /*!< \brief request */
+ PRIM_OP_RESPONSE, /*!< \brief response */
+ PRIM_OP_INDICATION, /*!< \brief indication */
+ PRIM_OP_CONFIRM, /*!< \brief cofirm */
+};
+
+#define _SAP_GSM_SHIFT 24
+
+#define _SAP_GSM_BASE (0x01 << _SAP_GSM_SHIFT)
+#define _SAP_TETRA_BASE (0x02 << _SAP_GSM_SHIFT)
+
+/*! \brief primitive header */
+struct osmo_prim_hdr {
+ unsigned int sap; /*!< \brief Service Access Point */
+ unsigned int primitive; /*!< \brief Primitive number */
+ enum osmo_prim_operation operation; /*! \brief Primitive Operation */
+ struct msgb *msg; /*!< \brief \ref msgb containing associated data */
+};
+
+/*! \brief initialize a primitive header
+ * \param[in,out] oph primitive header
+ * \param[in] sap Service Access Point
+ * \param[in] primtive Primitive Number
+ * \param[in] operation Primitive Operation (REQ/RESP/IND/CONF)
+ * \param[in] msg Message
+ */
+static inline void
+osmo_prim_init(struct osmo_prim_hdr *oph, unsigned int sap,
+ unsigned int primitive, enum osmo_prim_operation operation,
+ struct msgb *msg)
+{
+ oph->sap = sap;
+ oph->primitive = primitive;
+ oph->operation = operation;
+ oph->msg = msg;
+}
+
+/*! \brief primitive handler callback type */
+typedef int (*osmo_prim_cb)(struct osmo_prim_hdr *oph, void *ctx);
+
+#endif /* OSMO_PRIMITIVE_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/process.h b/src/shared/libosmocore/include/osmocom/core/process.h
new file mode 100644
index 00000000..1dde0219
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/process.h
@@ -0,0 +1,2 @@
+#warning "Update from osmocom/core/process.h to osmocom/core/application.h"
+#include <osmocom/core/application.h>
diff --git a/src/shared/libosmocore/include/osmocom/core/rate_ctr.h b/src/shared/libosmocore/include/osmocom/core/rate_ctr.h
new file mode 100644
index 00000000..c38e5f9b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/rate_ctr.h
@@ -0,0 +1,88 @@
+#ifndef _RATE_CTR_H
+#define _RATE_CTR_H
+
+/*! \defgroup rate_ctr Rate counters
+ * @{
+ */
+
+/*! \file rate_ctr.h */
+
+#include <stdint.h>
+
+#include <osmocom/core/linuxlist.h>
+
+/*! \brief Number of rate counter intervals */
+#define RATE_CTR_INTV_NUM 4
+
+/*! \brief Rate counter interval */
+enum rate_ctr_intv {
+ RATE_CTR_INTV_SEC, /*!< \brief last second */
+ RATE_CTR_INTV_MIN, /*!< \brief last minute */
+ RATE_CTR_INTV_HOUR, /*!< \brief last hour */
+ RATE_CTR_INTV_DAY, /*!< \brief last day */
+};
+
+/*! \brief data we keep for each of the intervals */
+struct rate_ctr_per_intv {
+ uint64_t last; /*!< \brief counter value in last interval */
+ uint64_t rate; /*!< \brief counter rate */
+};
+
+/*! \brief data we keep for each actual value */
+struct rate_ctr {
+ uint64_t current; /*!< \brief current value */
+ /*! \brief per-interval data */
+ struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM];
+};
+
+/*! \brief rate counter description */
+struct rate_ctr_desc {
+ const char *name; /*!< \brief name of the counter */
+ const char *description;/*!< \brief description of the counter */
+};
+
+/*! \brief description of a rate counter group */
+struct rate_ctr_group_desc {
+ /*! \brief The prefix to the name of all counters in this group */
+ const char *group_name_prefix;
+ /*! \brief The human-readable description of the group */
+ const char *group_description;
+ /*! \brief The number of counters in this group */
+ const unsigned int num_ctr;
+ /*! \brief Pointer to array of counter names */
+ const struct rate_ctr_desc *ctr_desc;
+};
+
+/*! \brief One instance of a counter group class */
+struct rate_ctr_group {
+ /*! \brief Linked list of all counter groups in the system */
+ struct llist_head list;
+ /*! \brief Pointer to the counter group class */
+ const struct rate_ctr_group_desc *desc;
+ /*! \brief The index of this ctr_group within its class */
+ unsigned int idx;
+ /*! \brief Actual counter structures below */
+ struct rate_ctr ctr[0];
+};
+
+struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
+ const struct rate_ctr_group_desc *desc,
+ unsigned int idx);
+
+void rate_ctr_group_free(struct rate_ctr_group *grp);
+
+void rate_ctr_add(struct rate_ctr *ctr, int inc);
+
+/*! \brief Increment the counter by 1 */
+static inline void rate_ctr_inc(struct rate_ctr *ctr)
+{
+ rate_ctr_add(ctr, 1);
+}
+
+int rate_ctr_init(void *tall_ctx);
+
+struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx);
+const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name);
+
+/*! }@ */
+#endif /* RATE_CTR_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/select.h b/src/shared/libosmocore/include/osmocom/core/select.h
new file mode 100644
index 00000000..18aad35f
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/select.h
@@ -0,0 +1,45 @@
+#ifndef _BSC_SELECT_H
+#define _BSC_SELECT_H
+
+#include <osmocom/core/linuxlist.h>
+
+/*! \defgroup select Select loop abstraction
+ * @{
+ */
+
+/*! \file select.h
+ * \brief select loop abstraction
+ */
+
+/*! \brief Indicate interest in reading from the file descriptor */
+#define BSC_FD_READ 0x0001
+/*! \brief Indicate interest in writing to the file descriptor */
+#define BSC_FD_WRITE 0x0002
+/*! \brief Indicate interest in exceptions from the file descriptor */
+#define BSC_FD_EXCEPT 0x0004
+
+/*! \brief Structure representing a file dsecriptor */
+struct osmo_fd {
+ /*! linked list for internal management */
+ struct llist_head list;
+ /*! actual operating-system level file decriptor */
+ int fd;
+ /*! bit-mask or of \ref BSC_FD_READ, \ref BSC_FD_WRITE and/or
+ * \ref BSC_FD_EXCEPT */
+ unsigned int when;
+ /*! call-back function to be called once file descriptor becomes
+ * available */
+ int (*cb)(struct osmo_fd *fd, unsigned int what);
+ /*! data pointer passed through to call-back function */
+ void *data;
+ /*! private number, extending \a data */
+ unsigned int priv_nr;
+};
+
+int osmo_fd_register(struct osmo_fd *fd);
+void osmo_fd_unregister(struct osmo_fd *fd);
+int osmo_select_main(int polling);
+
+/*! }@ */
+
+#endif /* _BSC_SELECT_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/serial.h b/src/shared/libosmocore/include/osmocom/core/serial.h
new file mode 100644
index 00000000..73854251
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/serial.h
@@ -0,0 +1,43 @@
+/*
+ * serial.h
+ *
+ * Copyright (C) 2011 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.
+ */
+
+/*! \defgroup serial Utility functions to deal with serial ports
+ * @{
+ */
+
+/*! \file serial.h
+ * \file Osmocom serial port helpers
+ */
+
+#ifndef __OSMO_SERIAL_H__
+#define __OSMO_SERIAL_H__
+
+#include <termios.h>
+
+int osmo_serial_init(const char *dev, speed_t baudrate);
+int osmo_serial_set_baudrate(int fd, speed_t baudrate);
+int osmo_serial_set_custom_baudrate(int fd, int baudrate);
+int osmo_serial_clear_custom_baudrate(int fd);
+
+/*! }@ */
+
+#endif /* __OSMO_SERIAL_H__ */
diff --git a/src/shared/libosmocore/include/osmocom/core/signal.h b/src/shared/libosmocore/include/osmocom/core/signal.h
new file mode 100644
index 00000000..07d0e6fb
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/signal.h
@@ -0,0 +1,45 @@
+#ifndef OSMO_SIGNAL_H
+#define OSMO_SIGNAL_H
+
+#include <stdint.h>
+
+/*! \defgroup signal Intra-application signals
+ * @{
+ */
+/*! \file signal.h */
+
+/* subsystem signaling numbers: we split the numberspace for applications and
+ * libraries: from 0 to UINT_MAX/2 for applications, from UINT_MAX/2 to
+ * UINT_MAX for libraries. */
+#define OSMO_SIGNAL_SS_APPS 0
+#define OSMO_SIGNAL_SS_RESERVED 2147483648u
+
+/*! \brief signal subsystems */
+enum {
+ SS_L_GLOBAL = OSMO_SIGNAL_SS_RESERVED,
+ SS_L_INPUT,
+};
+
+/* application-defined signal types. */
+#define OSMO_SIGNAL_T_APPS 0
+#define OSMO_SIGNAL_T_RESERVED 2147483648u
+
+/*! \brief signal types. */
+enum {
+ S_L_GLOBAL_SHUTDOWN = OSMO_SIGNAL_T_RESERVED,
+};
+
+/*! signal callback function type */
+typedef int osmo_signal_cbfn(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data);
+
+
+/* Management */
+int osmo_signal_register_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data);
+void osmo_signal_unregister_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data);
+
+/* Dispatch */
+void osmo_signal_dispatch(unsigned int subsys, unsigned int signal, void *signal_data);
+
+/*! }@ */
+
+#endif /* OSMO_SIGNAL_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/socket.h b/src/shared/libosmocore/include/osmocom/core/socket.h
new file mode 100644
index 00000000..c5288dfc
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/socket.h
@@ -0,0 +1,34 @@
+#ifndef _OSMOCORE_SOCKET_H
+#define _OSMOCORE_SOCKET_H
+
+/*! \defgroup socket Socket convenience functions
+ * @{
+ */
+
+/*! \file socket.h
+ * \brief Osmocom socket convenience functions
+ */
+
+#include <stdint.h>
+
+struct sockaddr;
+
+/* flags for osmo_sock_init. */
+#define OSMO_SOCK_F_CONNECT (1 << 0)
+#define OSMO_SOCK_F_BIND (1 << 1)
+#define OSMO_SOCK_F_NONBLOCK (1 << 2)
+
+int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
+ const char *host, uint16_t port, unsigned int flags);
+
+int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
+ const char *host, uint16_t port, unsigned int flags);
+
+int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
+ uint8_t proto, unsigned int flags);
+
+int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen);
+
+/*! }@ */
+
+#endif /* _OSMOCORE_SOCKET_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/statistics.h b/src/shared/libosmocore/include/osmocom/core/statistics.h
new file mode 100644
index 00000000..04816c16
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/statistics.h
@@ -0,0 +1,53 @@
+#ifndef _STATISTICS_H
+#define _STATISTICS_H
+
+/*! \file statistics.h
+ * \brief Common routines regarding statistics */
+
+/*! structure representing a single counter */
+struct osmo_counter {
+ struct llist_head list; /*!< \brief internal list head */
+ const char *name; /*!< \brief human-readable name */
+ const char *description; /*!< \brief humn-readable description */
+ unsigned long value; /*!< \brief current value */
+};
+
+/*! \brief Increment counter */
+static inline void osmo_counter_inc(struct osmo_counter *ctr)
+{
+ ctr->value++;
+}
+
+/*! \brief Get current value of counter */
+static inline unsigned long osmo_counter_get(struct osmo_counter *ctr)
+{
+ return ctr->value;
+}
+
+/*! \brief Reset current value of counter to 0 */
+static inline void osmo_counter_reset(struct osmo_counter *ctr)
+{
+ ctr->value = 0;
+}
+
+/*! \brief Allocate a new counter */
+struct osmo_counter *osmo_counter_alloc(const char *name);
+
+/*! \brief Free the specified counter
+ * \param[ctr] Counter
+ */
+void osmo_counter_free(struct osmo_counter *ctr);
+
+/*! \brief Iteate over all counters
+ * \param[in] handle_counter Call-back function
+ * \param[in] data Private dtata handed through to \a handle_counter
+ */
+int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data);
+
+/*! \brief Resolve counter by human-readable name
+ * \param[in] name human-readable name of counter
+ * \returns pointer to counter (\ref osmo_counter) or NULL otherwise
+ */
+struct osmo_counter *osmo_counter_get_by_name(const char *name);
+
+#endif /* _STATISTICS_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/talloc.h b/src/shared/libosmocore/include/osmocom/core/talloc.h
new file mode 100644
index 00000000..f7f7643b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/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/src/shared/libosmocore/include/osmocom/core/timer.h b/src/shared/libosmocore/include/osmocom/core/timer.h
new file mode 100644
index 00000000..30f558b4
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/timer.h
@@ -0,0 +1,87 @@
+/*
+ * (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.
+ *
+ */
+
+/*! \defgroup timer Osmocom timers
+ * @{
+ */
+
+/*! \file timer.h
+ * \brief Osmocom timer handling routines
+ */
+
+#ifndef TIMER_H
+#define TIMER_H
+
+#include <sys/time.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/linuxrbtree.h>
+
+/**
+ * Timer management:
+ * - Create a struct osmo_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.
+ *
+ */
+/*! \brief A structure representing a single instance of a timer */
+struct osmo_timer_list {
+ struct rb_node node; /*!< \brief rb-tree node header */
+ struct llist_head list; /*!< \brief internal list header */
+ struct timeval timeout; /*!< \brief expiration time */
+ unsigned int active : 1; /*!< \brief is it active? */
+
+ void (*cb)(void*); /*!< \brief call-back called at timeout */
+ void *data; /*!< \brief user data for callback */
+};
+
+/**
+ * timer management
+ */
+
+void osmo_timer_add(struct osmo_timer_list *timer);
+
+void osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds);
+
+void osmo_timer_del(struct osmo_timer_list *timer);
+
+int osmo_timer_pending(struct osmo_timer_list *timer);
+
+
+/*
+ * internal timer list management
+ */
+struct timeval *osmo_timers_nearest(void);
+void osmo_timers_prepare(void);
+int osmo_timers_update(void);
+int osmo_timers_check(void);
+
+/*! }@ */
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/timer_compat.h b/src/shared/libosmocore/include/osmocom/core/timer_compat.h
new file mode 100644
index 00000000..209e84a3
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/timer_compat.h
@@ -0,0 +1,79 @@
+/*
+ * (C) 2011 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.
+ *
+ */
+
+/*! \defgroup timer Osmocom timers
+ * @{
+ */
+
+/*! \file timer_compat.h
+ * \brief Compatibility header with some helpers
+ */
+
+#ifndef TIMER_COMPAT_H
+#define TIMER_COMPAT_H
+
+
+/* Convenience macros for operations on timevals.
+ NOTE: `timercmp' does not work for >= or <=. */
+
+#ifndef timerisset
+# define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#endif
+
+#ifndef timerclear
+# define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
+#endif
+
+#ifndef timercmp
+# define timercmp(a, b, CMP) \
+ (((a)->tv_sec == (b)->tv_sec) ? \
+ ((a)->tv_usec CMP (b)->tv_usec) : \
+ ((a)->tv_sec CMP (b)->tv_sec))
+#endif
+
+#ifndef timeradd
+# define timeradd(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
+ if ((result)->tv_usec >= 1000000) \
+ { \
+ ++(result)->tv_sec; \
+ (result)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#endif
+
+#ifndef timersub
+# define timersub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif
+
+
+/*! }@ */
+
+#endif /* TIMER_COMPAT_H */
diff --git a/src/shared/libosmocore/include/osmocom/core/utils.h b/src/shared/libosmocore/include/osmocom/core/utils.h
new file mode 100644
index 00000000..f1843d9d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/utils.h
@@ -0,0 +1,56 @@
+#ifndef OSMOCORE_UTIL_H
+#define OSMOCORE_UTIL_H
+
+/*! \defgroup utils General-purpose utility functions
+ * @{
+ */
+
+/*! \file utils.h */
+
+/*! \brief Determine number of elements in an array of static size */
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+/*! \brief Return the maximum of two specified values */
+#define OSMO_MAX(a, b) (a) >= (b) ? (a) : (b)
+/*! \brief Return the minimum of two specified values */
+#define OSMO_MIN(a, b) (a) >= (b) ? (b) : (a)
+
+#include <stdint.h>
+
+/*! \brief A mapping between human-readable string and numeric value */
+struct value_string {
+ unsigned int value; /*!< \brief numeric value */
+ const char *str; /*!< \brief human-readable string */
+};
+
+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 osmo_bcd2char(uint8_t bcd);
+/* only works for numbers in ascci */
+uint8_t osmo_char2bcd(char c);
+
+int osmo_hexparse(const char *str, uint8_t *b, int max_len);
+
+char *osmo_ubit_dump(const uint8_t *bits, unsigned int len);
+char *osmo_hexdump(const unsigned char *buf, int len);
+char *osmo_hexdump_nospc(const unsigned char *buf, int len);
+char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) __attribute__((__deprecated__));
+
+#define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
+
+void osmo_str2lower(char *out, const char *in);
+void osmo_str2upper(char *out, const char *in);
+
+#define OSMO_SNPRINTF_RET(ret, rem, offset, len) \
+do { \
+ len += ret; \
+ if (ret > rem) \
+ ret = rem; \
+ offset += ret; \
+ rem -= ret; \
+} while (0)
+
+/*! }@ */
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/core/write_queue.h b/src/shared/libosmocore/include/osmocom/core/write_queue.h
new file mode 100644
index 00000000..8b896d15
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/core/write_queue.h
@@ -0,0 +1,63 @@
+/* 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 OSMO_WQUEUE_H
+#define OSMO_WQUEUE_H
+
+/*! \defgroup write_queue Osmocom msgb write queues
+ * @{
+ */
+
+/*! \file write_queue.h
+ */
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/msgb.h>
+
+/*! write queue instance */
+struct osmo_wqueue {
+ /*! \brief osmocom file descriptor */
+ struct osmo_fd bfd;
+ /*! \brief maximum length of write queue */
+ unsigned int max_length;
+ /*! \brief current length of write queue */
+ unsigned int current_length;
+
+ /*! \brief actual linked list implementing the queue */
+ struct llist_head msg_queue;
+
+ /*! \brief call-back in case qeueue is readable */
+ int (*read_cb)(struct osmo_fd *fd);
+ /*! \brief call-back in case qeueue is writable */
+ int (*write_cb)(struct osmo_fd *fd, struct msgb *msg);
+ /*! \brief call-back in case qeueue has exceptions */
+ int (*except_cb)(struct osmo_fd *fd);
+};
+
+void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length);
+void osmo_wqueue_clear(struct osmo_wqueue *queue);
+int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data);
+int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what);
+
+/*! }@ */
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/crypt/Makefile.am b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am
new file mode 100644
index 00000000..e4a6e538
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am
@@ -0,0 +1,3 @@
+osmocrypt_HEADERS = gprs_cipher.h auth.h
+
+osmocryptdir = $(includedir)/osmocom/crypt
diff --git a/src/shared/libosmocore/include/osmocom/crypt/auth.h b/src/shared/libosmocore/include/osmocom/crypt/auth.h
new file mode 100644
index 00000000..30e16e82
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/crypt/auth.h
@@ -0,0 +1,90 @@
+#ifndef _OSMOCRYPTO_AUTH_H
+#define _OSMOCRYPTO_AUTH_H
+
+#include <stdint.h>
+
+#include <osmocom/core/linuxlist.h>
+
+/*! \brief Authentication Type */
+enum osmo_sub_auth_type {
+ OSMO_AUTH_TYPE_NONE = 0x00,
+ OSMO_AUTH_TYPE_GSM = 0x01,
+ OSMO_AUTH_TYPE_UMTS = 0x02,
+};
+
+/*! \brief Authentication Algorithm */
+enum osmo_auth_algo {
+ OSMO_AUTH_ALG_NONE,
+ OSMO_AUTH_ALG_COMP128v1,
+ OSMO_AUTH_ALG_COMP128v2,
+ OSMO_AUTH_ALG_COMP128v3,
+ OSMO_AUTH_ALG_XOR,
+ OSMO_AUTH_ALG_MILENAGE,
+ _OSMO_AUTH_ALG_NUM,
+};
+
+/*! \brief permanent (secret) subscriber auth data */
+struct osmo_sub_auth_data {
+ enum osmo_sub_auth_type type;
+ enum osmo_auth_algo algo;
+ union {
+ struct {
+ uint8_t opc[16];
+ uint8_t k[16];
+ uint8_t amf[2];
+ uint64_t sqn;
+ } umts;
+ struct {
+ uint8_t ki[16];
+ } gsm;
+ } u;
+};
+
+/* data structure describing a computed auth vector, generated by AuC */
+struct osmo_auth_vector {
+ uint8_t rand[16];
+ uint8_t autn[16];
+ uint8_t ck[16];
+ uint8_t ik[16];
+ uint8_t res[16];
+ uint8_t res_len;
+ uint8_t kc[8];
+ uint8_t sres[4];
+ uint32_t auth_types; /*!< bitmask of OSMO_AUTH_TYPE_* */
+};
+
+/* \brief An implementation of an authentication algorithm */
+struct osmo_auth_impl {
+ struct llist_head list;
+ enum osmo_auth_algo algo;
+ const char *name;
+ unsigned int priority;
+
+ int (*gen_vec)(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *_rand);
+
+ int (*gen_vec_auts)(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *rand_auts, const uint8_t *auts,
+ const uint8_t *_rand);
+};
+
+int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud, const uint8_t *_rand);
+
+int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *rand_auts, const uint8_t *auts,
+ const uint8_t *_rand);
+
+int osmo_auth_register(struct osmo_auth_impl *impl);
+
+int osmo_auth_load(const char *path);
+
+int osmo_auth_supported(enum osmo_auth_algo algo);
+
+const char *osmo_auth_alg_name(enum osmo_auth_algo alg);
+enum osmo_auth_algo osmo_auth_alg_parse(const char *name);
+
+#endif /* _OSMOCRYPTO_AUTH_H */
diff --git a/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h
new file mode 100644
index 00000000..30510711
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h
@@ -0,0 +1,54 @@
+#ifndef _GPRS_CIPHER_H
+#define _GPRS_CIPHER_H
+
+#include <osmocom/core/linuxlist.h>
+
+#define GSM0464_CIPH_MAX_BLOCK 1523
+
+enum gprs_ciph_algo {
+ GPRS_ALGO_GEA0,
+ GPRS_ALGO_GEA1,
+ GPRS_ALGO_GEA2,
+ GPRS_ALGO_GEA3,
+ _GPRS_ALGO_NUM
+};
+
+enum gprs_cipher_direction {
+ GPRS_CIPH_MS2SGSN,
+ GPRS_CIPH_SGSN2MS,
+};
+
+/* An implementation of a GPRS cipher */
+struct gprs_cipher_impl {
+ struct llist_head list;
+ enum gprs_ciph_algo algo;
+ const char *name;
+ unsigned int priority;
+
+ /* As specified in 04.64 Annex A. Uses Kc, IV and direction
+ * to generate the 1523 bytes cipher stream that need to be
+ * XORed wit the plaintext for encrypt / ciphertext for decrypt */
+ int (*run)(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv,
+ enum gprs_cipher_direction direction);
+};
+
+/* register a cipher with the core (from a plugin) */
+int gprs_cipher_register(struct gprs_cipher_impl *ciph);
+
+/* load all available GPRS cipher plugins */
+int gprs_cipher_load(const char *path);
+
+/* function to be called by core code */
+int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo,
+ uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir);
+
+/* Do we have an implementation for this cipher? */
+int gprs_cipher_supported(enum gprs_ciph_algo algo);
+
+/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
+uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc);
+
+/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
+uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc);
+
+#endif /* _GPRS_CIPHER_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/Makefile.am b/src/shared/libosmocore/include/osmocom/gsm/Makefile.am
new file mode 100644
index 00000000..fc1abfe8
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/Makefile.am
@@ -0,0 +1,8 @@
+osmogsm_HEADERS = a5.h comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \
+ gsm0480.h gsm48.h gsm_utils.h rsl.h tlv.h abis_nm.h \
+ sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h \
+ gsm0411_utils.h gsm0411_smc.h gsm0411_smr.h
+
+SUBDIRS = protocol
+
+osmogsmdir = $(includedir)/osmocom/gsm
diff --git a/src/shared/libosmocore/include/osmocom/gsm/a5.h b/src/shared/libosmocore/include/osmocom/gsm/a5.h
new file mode 100644
index 00000000..7e6a17c4
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/a5.h
@@ -0,0 +1,63 @@
+/*
+ * a5.h
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef __OSMO_A5_H__
+#define __OSMO_A5_H__
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+
+/*! \defgroup a5 GSM A5 ciphering algorithm
+ * @{
+ */
+
+/*! \file gsm/a5.h
+ * \brief Osmocom GSM A5 ciphering algorithm header
+ */
+
+/*! \brief Converts a frame number into the 22 bit number used in A5/x
+ * \param[in] fn The true framenumber
+ * \return 22 bit word
+ */
+static inline uint32_t
+osmo_a5_fn_count(uint32_t fn)
+{
+ int t1 = fn / (26 * 51);
+ int t2 = fn % 26;
+ int t3 = fn % 51;
+ return (t1 << 11) | (t3 << 5) | t2;
+}
+
+ /* Notes:
+ * - key must be 8 bytes long (or NULL for A5/0)
+ * - the dl and ul pointer must be either NULL or 114 bits long
+ * - fn is the _real_ GSM frame number.
+ * (converted internally to fn_count)
+ */
+void osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
+void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
+void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
+
+/*! }@ */
+
+#endif /* __OSMO_A5_H__ */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h b/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h
new file mode 100644
index 00000000..5b5bff0e
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h
@@ -0,0 +1,40 @@
+#ifndef _OSMO_GSM_ABIS_NM_H
+#define _OSMO_GSM_ABIS_NM_H
+
+/*! \defgroup oml A-bis OML
+ * @{
+ */
+
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+/*! \file abis_nm.h */
+
+enum abis_nm_msgtype;
+enum gsm_phys_chan_config;
+
+const enum abis_nm_msgtype abis_nm_reports[4];
+const enum abis_nm_msgtype abis_nm_no_ack_nack[3];
+const enum abis_nm_msgtype abis_nm_sw_load_msgs[9];
+const enum abis_nm_msgtype abis_nm_nacks[33];
+
+extern const struct value_string abis_nm_obj_class_names[];
+extern const struct value_string abis_nm_adm_state_names[];
+
+const char *abis_nm_nack_cause_name(uint8_t cause);
+const char *abis_nm_nack_name(uint8_t nack);
+const char *abis_nm_event_type_name(uint8_t cause);
+const char *abis_nm_severity_name(uint8_t cause);
+const struct tlv_definition abis_nm_att_tlvdef;
+const char *abis_nm_opstate_name(uint8_t os);
+const char *abis_nm_avail_name(uint8_t avail);
+const char *abis_nm_test_name(uint8_t test);
+void abis_nm_debugp_foh(int ss, struct abis_om_fom_hdr *foh);
+
+int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan);
+enum abis_nm_chan_comb abis_nm_pchan4chcomb(uint8_t chcomb);
+
+/*! }@ */
+
+#endif /* _OSMO_GSM_ABIS_NM_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/comp128.h b/src/shared/libosmocore/include/osmocom/gsm/comp128.h
new file mode 100644
index 00000000..e4587d4f
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/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(const uint8_t *ki, const uint8_t *srand, uint8_t *sres, uint8_t *kc);
+
+#endif /* __COMP128_H__ */
+
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h
new file mode 100644
index 00000000..e1508a2d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smc.h
@@ -0,0 +1,62 @@
+#ifndef _GSM0411_SMC_H
+#define _GSM0411_SMC_H
+
+#include <osmocom/gsm/protocol/gsm_04_11.h>
+
+#define GSM411_MMSMS_EST_REQ 0x310
+#define GSM411_MMSMS_EST_IND 0x312
+#define GSM411_MMSMS_EST_CNF 0x311
+#define GSM411_MMSMS_REL_REQ 0x320
+#define GSM411_MMSMS_REL_IND 0x322
+#define GSM411_MMSMS_DATA_REQ 0x330
+#define GSM411_MMSMS_DATA_IND 0x332
+#define GSM411_MMSMS_UNIT_DATA_REQ 0x340
+#define GSM411_MMSMS_UNIT_DATA_IND 0x342
+#define GSM411_MMSMS_ERR_IND 0x372
+
+#define GSM411_MNSMS_ABORT_REQ 0x101
+#define GSM411_MNSMS_DATA_REQ 0x102
+#define GSM411_MNSMS_DATA_IND 0x103
+#define GSM411_MNSMS_EST_REQ 0x104
+#define GSM411_MNSMS_EST_IND 0x105
+#define GSM411_MNSMS_ERROR_IND 0x106
+#define GSM411_MNSMS_REL_REQ 0x107
+
+struct gsm411_smc_inst {
+ int network; /* is this a MO (0) or MT (1) transfer */
+ int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg);
+ int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg, int cp_msg_type);
+
+ enum gsm411_cp_state cp_state;
+ struct osmo_timer_list cp_timer;
+ struct msgb *cp_msg; /* store pending message */
+ int cp_rel; /* store pending release */
+ int cp_retx; /* retry counter */
+ int cp_max_retr; /* maximum retry */
+ int cp_tc1; /* timer value TC1* */
+
+};
+
+extern const struct value_string gsm411_cp_cause_strs[];
+
+/* init a new instance */
+void gsm411_smc_init(struct gsm411_smc_inst *inst, int network,
+ int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg),
+ int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg, int cp_msg_type));
+
+/* clear instance */
+void gsm411_smc_clear(struct gsm411_smc_inst *inst);
+
+/* message from upper layer */
+int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg);
+
+/* message from lower layer */
+int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg, int cp_msg_type);
+
+#endif /* _GSM0411_SMC_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h
new file mode 100644
index 00000000..5ea8584d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_smr.h
@@ -0,0 +1,45 @@
+#ifndef _GSM0411_SMR_H
+#define _GSM0411_SMR_H
+
+#include <osmocom/gsm/protocol/gsm_04_11.h>
+
+#define GSM411_SM_RL_DATA_REQ 0x401
+#define GSM411_SM_RL_DATA_IND 0x402
+#define GSM411_SM_RL_MEM_AVAIL_REQ 0x403
+#define GSM411_SM_RL_MEM_AVAIL_IND 0x404
+#define GSM411_SM_RL_REPORT_REQ 0x405
+#define GSM411_SM_RL_REPORT_IND 0x406
+
+struct gsm411_smr_inst {
+ int network; /* is this a MO (0) or MT (1) transfer */
+ int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg);
+ int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg);
+
+ enum gsm411_rp_state rp_state;
+ struct osmo_timer_list rp_timer;
+};
+
+extern const struct value_string gsm411_rp_cause_strs[];
+
+/* init a new instance */
+void gsm411_smr_init(struct gsm411_smr_inst *inst, int network,
+ int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg),
+ int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg));
+
+/* clear instance */
+void gsm411_smr_clear(struct gsm411_smr_inst *inst);
+
+/* message from upper layer */
+int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg);
+
+/* message from lower layer */
+int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg);
+
+#endif /* _GSM0411_SMR_H */
+
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h
new file mode 100644
index 00000000..d29ca631
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0411_utils.h
@@ -0,0 +1,36 @@
+#ifndef _GSM0411_UTILS_H
+#define _GSM0411_UTILS_H
+
+/* Turn int into semi-octet representation: 98 => 0x89 */
+uint8_t gsm411_bcdify(uint8_t value);
+
+/* Turn semi-octet representation into int: 0x89 => 98 */
+uint8_t gsm411_unbcdify(uint8_t value);
+
+struct msgb *gsm411_msgb_alloc(void);
+
+/* Generate 03.40 TP-SCTS */
+void gsm340_gen_scts(uint8_t *scts, time_t time);
+
+/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
+time_t gsm340_scts(uint8_t *scts);
+
+/* decode validity period. return minutes */
+unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp);
+
+/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
+enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs);
+
+/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
+int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
+ uint8_t plan, const char *number);
+
+/* Prefix msg with a RP header */
+int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
+ uint8_t rp_msg_ref);
+
+/* Prefix msg with a 04.08/04.11 CP header */
+int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
+ uint8_t msg_type);
+
+#endif /* _GSM0411_UTILS_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h
new file mode 100644
index 00000000..f6c37340
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h
@@ -0,0 +1,26 @@
+#ifndef gsm0480_h
+#define gsm0480_h
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_04_80.h>
+
+#define MAX_LEN_USSD_STRING 31
+
+struct ussd_request {
+ uint8_t text[MAX_LEN_USSD_STRING + 1];
+ uint8_t transaction_id;
+ uint8_t invoke_id;
+};
+
+int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len,
+ struct ussd_request *request);
+
+struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text);
+struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text);
+struct msgb *gsm0480_create_notifySS(const char *text);
+
+int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id);
+int gsm0480_wrap_facility(struct msgb *msg);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0502.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0502.h
new file mode 100644
index 00000000..46b629e4
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0502.h
@@ -0,0 +1,38 @@
+#ifndef OSMOCOM_GSM_0502_H
+#define OSMOCOM_GSM_0502_H
+
+#include <stdint.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+/* Table 5 Clause 7 TS 05.02 */
+static inline unsigned int
+gsm0502_get_n_pag_blocks(struct gsm48_control_channel_descr *chan_desc)
+{
+ if (chan_desc->ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
+ return 3 - chan_desc->bs_ag_blks_res;
+ else
+ return 9 - chan_desc->bs_ag_blks_res;
+}
+
+/* Chapter 6.5.2 of TS 05.02 */
+static inline unsigned int
+gsm0502_get_ccch_group(uint64_t imsi, unsigned int bs_cc_chans,
+ unsigned int n_pag_blocks)
+{
+ return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) / n_pag_blocks;
+}
+
+/* Chapter 6.5.2 of TS 05.02 */
+static inline unsigned int
+gsm0502_get_paging_group(uint64_t imsi, unsigned int bs_cc_chans,
+ int n_pag_blocks)
+{
+ return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) % n_pag_blocks;
+}
+
+unsigned int
+gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h
new file mode 100644
index 00000000..5380dd9e
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h
@@ -0,0 +1,50 @@
+/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009,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 OSMOCORE_GSM0808_H
+#define OSMOCORE_GSM0808_H
+
+#include "tlv.h"
+
+struct msgb;
+
+struct msgb *gsm0808_create_layer3(struct msgb *msg, uint16_t netcode, uint16_t countrycode, int lac, uint16_t ci);
+struct msgb *gsm0808_create_reset(void);
+struct msgb *gsm0808_create_clear_command(uint8_t reason);
+struct msgb *gsm0808_create_clear_complete(void);
+struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id);
+struct msgb *gsm0808_create_cipher_reject(uint8_t cause);
+struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len,
+ const uint8_t *cm3, uint8_t cm3_len);
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id);
+struct msgb *gsm0808_create_assignment_completed(uint8_t rr_cause,
+ uint8_t chosen_channel, uint8_t encr_alg_id,
+ uint8_t speech_mode);
+struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause);
+struct msgb *gsm0808_create_clear_rqst(uint8_t cause);
+
+struct msgb *gsm0808_create_dtap(struct msgb *msg, uint8_t link_id);
+void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id);
+
+const struct tlv_definition *gsm0808_att_tlvdef(void);
+
+const char *gsm0808_bssmap_name(uint8_t msg_type);
+const char *gsm0808_bssap_name(uint8_t msg_type);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm48.h b/src/shared/libosmocore/include/osmocom/gsm/gsm48.h
new file mode 100644
index 00000000..16a625aa
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm48.h
@@ -0,0 +1,38 @@
+#ifndef _OSMOCORE_GSM48_H
+#define _OSMOCORE_GSM48_H
+
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm48_ie.h>
+
+/* A parsed GPRS routing area */
+struct gprs_ra_id {
+ uint16_t mnc;
+ uint16_t mcc;
+ uint16_t lac;
+ uint8_t rac;
+};
+
+extern const struct tlv_definition gsm48_att_tlvdef;
+extern const struct tlv_definition gsm48_rr_att_tlvdef;
+extern const struct tlv_definition gsm48_mm_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);
+
+/* Convert Mobile Identity (10.5.1.4) to string */
+int gsm48_mi_to_string(char *string, const int str_len,
+ const uint8_t *mi, const int mi_len);
+
+/* Parse Routeing Area Identifier */
+void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf);
+int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid);
+
+int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h b/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h
new file mode 100644
index 00000000..2e576429
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h
@@ -0,0 +1,117 @@
+#ifndef _OSMOCORE_GSM48_IE_H
+#define _OSMOCORE_GSM48_IE_H
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/mncc.h>
+#include <osmocom/gsm/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);
+
+/* structure of one frequency */
+struct gsm_sysinfo_freq {
+ /* if the frequency included in the sysinfo */
+ uint8_t mask;
+} __attribute__ ((packed));
+
+/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
+int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
+ uint8_t len, uint8_t mask, uint8_t frqt);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h
new file mode 100644
index 00000000..c9a31016
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h
@@ -0,0 +1,150 @@
+/* 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>
+
+#define ADD_MODULO(sum, delta, modulo) do { \
+ if ((sum += delta) >= modulo) \
+ sum -= modulo; \
+ } while (0)
+
+#define GSM_MAX_FN (26*51*2048)
+
+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_decode_hdr(char *decoded, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind);
+int gsm_7bit_encode(uint8_t *result, const char *data);
+
+int gsm_septets2octets(uint8_t *result, uint8_t *rdata, uint8_t septet_len, uint8_t padding);
+int gsm_septet_encode(uint8_t *result, const char *data);
+uint8_t gsm_get_octet_len(const uint8_t sept_len);
+
+unsigned int ms_class_gmsk_dbm(enum gsm_band band, int ms_class);
+
+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.1.6 */
+static inline int ms_cm2_a5n_support(uint8_t *cm2, int n) {
+ switch (n) {
+ case 0: return 1;
+ case 1: return (cm2[0] & (1<<3)) ? 0 : 1;
+ case 2: return (cm2[2] & (1<<0)) ? 1 : 0;
+ case 3: return (cm2[2] & (1<<1)) ? 1 : 0;
+ default:
+ return 0;
+ }
+}
+
+/* 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
+#define ARFCN_FLAG_MASK 0xf000 /* Reserve the upper 5 bits for flags */
+
+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);
+
+/* GSM TS 03.03 Chapter 2.6 */
+enum gprs_tlli_type {
+ TLLI_LOCAL,
+ TLLI_FOREIGN,
+ TLLI_RANDOM,
+ TLLI_AUXILIARY,
+ TLLI_RESERVED,
+};
+
+/* TS 03.03 Chapter 2.6 */
+int gprs_tlli_type(uint32_t tlli);
+
+uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type);
+
+/* Osmocom internal, not part of any gsm spec */
+enum gsm_phys_chan_config {
+ GSM_PCHAN_NONE,
+ GSM_PCHAN_CCCH,
+ GSM_PCHAN_CCCH_SDCCH4,
+ GSM_PCHAN_TCH_F,
+ GSM_PCHAN_TCH_H,
+ GSM_PCHAN_SDCCH8_SACCH8C,
+ GSM_PCHAN_PDCH, /* GPRS PDCH */
+ GSM_PCHAN_TCH_F_PDCH, /* TCH/F if used, PDCH otherwise */
+ GSM_PCHAN_UNKNOWN,
+ _GSM_PCHAN_MAX
+};
+
+/* Osmocom internal, not part of any gsm spec */
+enum gsm_chan_t {
+ GSM_LCHAN_NONE,
+ GSM_LCHAN_SDCCH,
+ GSM_LCHAN_TCH_F,
+ GSM_LCHAN_TCH_H,
+ GSM_LCHAN_UNKNOWN,
+ GSM_LCHAN_CCCH,
+ _GSM_LCHAN_MAX
+};
+
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/lapd_core.h b/src/shared/libosmocore/include/osmocom/gsm/lapd_core.h
new file mode 100644
index 00000000..0f4e8899
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/lapd_core.h
@@ -0,0 +1,171 @@
+#ifndef _OSMOCOM_LAPD_H
+#define _OSMOCOM_LAPD_H
+
+#include <stdint.h>
+
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/prim.h>
+
+/*! \defgroup lapd LAPD implementation common part
+ * @{
+ */
+
+/*! \file lapd.h */
+
+/* primitive related sutff */
+
+/*! \brief LAPD related primitives (L2<->L3 SAP)*/
+enum osmo_dl_prim {
+ PRIM_DL_UNIT_DATA, /*!< \brief DL-UNIT-DATA */
+ PRIM_DL_DATA, /*!< \brief DL-DATA */
+ PRIM_DL_EST, /*!< \brief DL-ESTABLISH */
+ PRIM_DL_REL, /*!< \brief DL-RLEEASE */
+ PRIM_DL_SUSP, /*!< \brief DL-SUSPEND */
+ PRIM_DL_RES, /*!< \brief DL-RESUME */
+ PRIM_DL_RECON, /*!< \brief DL-RECONNECT */
+ PRIM_MDL_ERROR, /*!< \brief MDL-ERROR */
+};
+
+/* Uses the same values as RLL, so no conversion for GSM is required. */
+#define MDL_CAUSE_T200_EXPIRED 0x01
+#define MDL_CAUSE_REEST_REQ 0x02
+#define MDL_CAUSE_UNSOL_UA_RESP 0x03
+#define MDL_CAUSE_UNSOL_DM_RESP 0x04
+#define MDL_CAUSE_UNSOL_DM_RESP_MF 0x05
+#define MDL_CAUSE_UNSOL_SPRV_RESP 0x06
+#define MDL_CAUSE_SEQ_ERR 0x07
+#define MDL_CAUSE_UFRM_INC_PARAM 0x08
+#define MDL_CAUSE_SFRM_INC_PARAM 0x09
+#define MDL_CAUSE_IFRM_INC_MBITS 0x0a
+#define MDL_CAUSE_IFRM_INC_LEN 0x0b
+#define MDL_CAUSE_FRM_UNIMPL 0x0c
+#define MDL_CAUSE_SABM_MF 0x0d
+#define MDL_CAUSE_SABM_INFO_NOTALL 0x0e
+#define MDL_CAUSE_FRMR 0x0f
+
+/*! \brief for MDL-ERROR.ind */
+struct mdl_error_ind_param {
+ uint8_t cause; /*!< \brief generic cause value */
+};
+
+/*! \brief for DL-REL.req */
+struct dl_rel_req_param {
+ uint8_t mode; /*!< \brief release mode */
+};
+
+/*! \brief primitive header for LAPD DL-SAP primitives */
+struct osmo_dlsap_prim {
+ struct osmo_prim_hdr oph; /*!< \brief generic primitive header */
+ union {
+ struct mdl_error_ind_param error_ind;
+ struct dl_rel_req_param rel_req;
+ } u; /*!< \brief request-specific data */
+};
+
+/*! \brief LAPD mode/role */
+enum lapd_mode {
+ LAPD_MODE_USER, /*!< \brief behave like user */
+ LAPD_MODE_NETWORK, /*!< \brief behave like network */
+};
+
+/*! \brief LAPD state (Figure B.2/Q.921)*/
+enum lapd_state {
+ LAPD_STATE_NULL = 0,
+ LAPD_STATE_TEI_UNASS,
+ LAPD_STATE_ASS_TEI_WAIT,
+ LAPD_STATE_EST_TEI_WAIT,
+ LAPD_STATE_IDLE,
+ LAPD_STATE_SABM_SENT,
+ LAPD_STATE_DISC_SENT,
+ LAPD_STATE_MF_EST,
+ LAPD_STATE_TIMER_RECOV,
+};
+
+/*! \brief LAPD message format (I / S / U) */
+enum lapd_format {
+ LAPD_FORM_UKN = 0,
+ LAPD_FORM_I,
+ LAPD_FORM_S,
+ LAPD_FORM_U,
+};
+
+/*! \brief LAPD message context */
+struct lapd_msg_ctx {
+ struct lapd_datalink *dl;
+ int n201;
+ /* address */
+ uint8_t cr;
+ uint8_t sapi;
+ uint8_t tei;
+ uint8_t lpd;
+ /* control */
+ uint8_t format;
+ uint8_t p_f; /* poll / final bit */
+ uint8_t n_send;
+ uint8_t n_recv;
+ uint8_t s_u; /* S or repectivly U function bits */
+ /* length */
+ int length;
+ uint8_t more;
+};
+
+struct lapd_cr_ent {
+ uint8_t cmd;
+ uint8_t resp;
+};
+
+struct lapd_history {
+ struct msgb *msg; /* message to be sent / NULL, if histoy is empty */
+ int more; /* if message is fragmented */
+};
+
+/*! \brief LAPD datalink */
+struct lapd_datalink {
+ int (*send_dlsap)(struct osmo_dlsap_prim *dp,
+ struct lapd_msg_ctx *lctx);
+ int (*send_ph_data_req)(struct lapd_msg_ctx *lctx, struct msgb *msg);
+ struct {
+ /*! \brief filled-in once we set the lapd_mode above */
+ struct lapd_cr_ent loc2rem;
+ struct lapd_cr_ent rem2loc;
+ } cr;
+ enum lapd_mode mode; /*!< \brief current mode of link */
+ int use_sabme; /*!< \brief use SABME instead of SABM */
+ int reestablish; /*!< \brief enable reestablish support */
+ int n200, n200_est_rel; /*!< \brief number of retranmissions */
+ struct lapd_msg_ctx lctx; /*!< \brief LAPD context */
+ int maxf; /*!< \brief maximum frame size (after defragmentation) */
+ uint8_t k; /*!< \brief maximum number of unacknowledged frames */
+ uint8_t v_range; /*!< \brief range of sequence numbers */
+ uint8_t v_send; /*!< \brief seq nr of next I frame to be transmitted */
+ uint8_t v_ack; /*!< \brief last frame ACKed by peer */
+ uint8_t v_recv; /*!< \brief seq nr of next I frame expected to be received */
+ uint32_t state; /*!< \brief LAPD state (\ref lapd_state) */
+ int seq_err_cond; /*!< \brief condition of sequence error */
+ uint8_t own_busy; /*!< \brief receiver busy on our side */
+ uint8_t peer_busy; /*!< \brief receiver busy on remote side */
+ int t200_sec, t200_usec; /*!< \brief retry timer (default 1 sec) */
+ int t203_sec, t203_usec; /*!< \brief retry timer (default 10 secs) */
+ struct osmo_timer_list t200; /*!< \brief T200 timer */
+ struct osmo_timer_list t203; /*!< \brief T203 timer */
+ uint8_t retrans_ctr; /*!< \brief re-transmission counter */
+ struct llist_head tx_queue; /*!< \brief frames to L1 */
+ struct llist_head send_queue; /*!< \brief frames from L3 */
+ struct msgb *send_buffer; /*!< \brief current frame transmitting */
+ int send_out; /*!< \brief how much was sent from send_buffer */
+ struct lapd_history *tx_hist; /*!< \brief tx history structure array */
+ uint8_t range_hist; /*!< \brief range of history buffer 2..2^n */
+ struct msgb *rcv_buffer; /*!< \brief buffer to assemble the received message */
+ struct msgb *cont_res; /*!< \brief buffer to store content resolution data on network side, to detect multiple phones on same channel */
+};
+
+void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range,
+ int maxf);
+void lapd_dl_exit(struct lapd_datalink *dl);
+void lapd_dl_reset(struct lapd_datalink *dl);
+int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode);
+int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx);
+int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
+
+#endif /* _OSMOCOM_LAPD_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/lapdm.h b/src/shared/libosmocore/include/osmocom/gsm/lapdm.h
new file mode 100644
index 00000000..52e8fc52
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/lapdm.h
@@ -0,0 +1,162 @@
+#ifndef _OSMOCOM_LAPDM_H
+#define _OSMOCOM_LAPDM_H
+
+#include <osmocom/gsm/lapd_core.h>
+
+/*! \defgroup lapdm LAPDm implementation according to GSM TS 04.06
+ * @{
+ */
+
+/*! \file lapdm.h */
+
+/* primitive related sutff */
+
+/*! \brief LAPDm related primitives (L1<->L2 SAP) */
+enum osmo_ph_prim {
+ PRIM_PH_DATA, /*!< \brief PH-DATA */
+ PRIM_PH_RACH, /*!< \brief PH-RANDOM_ACCESS */
+ PRIM_PH_CONN, /*!< \brief PH-CONNECT */
+ PRIM_PH_EMPTY_FRAME, /*!< \brief PH-EMPTY_FRAME */
+ PRIM_PH_RTS, /*!< \brief PH-RTS */
+};
+
+/*! \brief for PH-RANDOM_ACCESS.req */
+struct ph_rach_req_param {
+ uint8_t ra; /*!< \brief Random Access */
+ uint8_t ta; /*!< \brief Timing Advance */
+ uint8_t tx_power; /*!< \brief Transmit Power */
+ uint8_t is_combined_ccch;/*!< \brief Are we using a combined CCCH? */
+ uint16_t offset; /*!< \brief Timing Offset */
+};
+
+/*! \brief for PH-RANDOM_ACCESS.ind */
+struct ph_rach_ind_param {
+ uint8_t ra; /*!< \brief Random Access */
+ uint8_t acc_delay; /*!< \brief Delay in bit periods */
+ uint32_t fn; /*!< \brief GSM Frame Number at time of RA */
+};
+
+/*! \brief for PH-[UNIT]DATA.{req,ind} */
+struct ph_data_param {
+ uint8_t link_id; /*!< \brief Link Identifier (Like RSL) */
+ uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */
+};
+
+/*! \brief for PH-CONN.ind */
+struct ph_conn_ind_param {
+ uint32_t fn; /*!< \brief GSM Frame Number */
+};
+
+/*! \brief primitive header for LAPDm PH-SAP primitives */
+struct osmo_phsap_prim {
+ struct osmo_prim_hdr oph; /*!< \brief generic primitive header */
+ union {
+ struct ph_data_param data;
+ struct ph_rach_req_param rach_req;
+ struct ph_rach_ind_param rach_ind;
+ struct ph_conn_ind_param conn_ind;
+ } u; /*!< \brief request-specific data */
+};
+
+/*! \brief LAPDm mode/role */
+enum lapdm_mode {
+ LAPDM_MODE_MS, /*!< \brief behave like a MS (mobile phone) */
+ LAPDM_MODE_BTS, /*!< \brief behave like a BTS (network) */
+};
+
+struct lapdm_entity;
+
+/*! \brief LAPDm message context */
+struct lapdm_msg_ctx {
+ struct lapdm_datalink *dl;
+ int lapdm_fmt;
+ uint8_t chan_nr;
+ uint8_t link_id;
+ uint8_t ta_ind; /* TA indicated by network */
+ uint8_t tx_power_ind; /* MS power indicated by network */
+};
+
+/*! \brief LAPDm datalink like TS 04.06 / Section 3.5.2 */
+struct lapdm_datalink {
+ struct lapd_datalink dl; /* \brief common LAPD */
+ struct lapdm_msg_ctx mctx; /*!< \brief context of established connection */
+
+ struct lapdm_entity *entity; /*!< \brief LAPDm entity we are part of */
+};
+
+/*! \brief LAPDm datalink SAPIs */
+enum lapdm_dl_sapi {
+ DL_SAPI0 = 0, /*!< \brief SAPI 0 */
+ DL_SAPI3 = 1, /*!< \brief SAPI 1 */
+ _NR_DL_SAPI
+};
+
+typedef int (*lapdm_cb_t)(struct msgb *msg, struct lapdm_entity *le, void *ctx);
+
+#define LAPDM_ENT_F_EMPTY_FRAME 0x0001
+#define LAPDM_ENT_F_POLLING_ONLY 0x0002
+
+/*! \brief a LAPDm Entity */
+struct lapdm_entity {
+ /*! \brief the SAPIs of the LAPDm entity */
+ struct lapdm_datalink datalink[_NR_DL_SAPI];
+ int last_tx_dequeue; /*!< \brief last entity that was dequeued */
+ int tx_pending; /*!< \brief currently a pending frame not confirmed by L1 */
+ enum lapdm_mode mode; /*!< \brief are we in BTS mode or MS mode */
+ unsigned int flags;
+
+ void *l1_ctx; /*!< \brief context for layer1 instance */
+ void *l3_ctx; /*!< \brief context for layer3 instance */
+
+ osmo_prim_cb l1_prim_cb;/*!< \brief callback for sending prims to L1 */
+ lapdm_cb_t l3_cb; /*!< \brief callback for sending stuff to L3 */
+
+ /*! \brief pointer to \ref lapdm_channel of which we're part */
+ struct lapdm_channel *lapdm_ch;
+
+ uint8_t ta; /* TA used and indicated to network */
+ uint8_t tx_power; /* MS power used and indicated to network */
+};
+
+/*! \brief the two lapdm_entities that form a GSM logical channel (ACCH + DCCH) */
+struct lapdm_channel {
+ struct llist_head list; /*!< \brief internal linked list */
+ char *name; /*!< \brief human-readable name */
+ struct lapdm_entity lapdm_acch; /*!< \brief Associated Control Channel */
+ struct lapdm_entity lapdm_dcch; /*!< \brief Dedicated Control Channel */
+};
+
+const char *get_rsl_name(int value);
+extern const char *lapdm_state_names[];
+
+/* initialize a LAPDm entity */
+void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200);
+void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode);
+
+/* deinitialize a LAPDm entity */
+void lapdm_entity_exit(struct lapdm_entity *le);
+void lapdm_channel_exit(struct lapdm_channel *lc);
+
+/* input into layer2 (from layer 1) */
+int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le);
+
+/* input into layer2 (from layer 3) */
+int lapdm_rslms_recvmsg(struct msgb *msg, struct lapdm_channel *lc);
+
+void lapdm_channel_set_l3(struct lapdm_channel *lc, lapdm_cb_t cb, void *ctx);
+void lapdm_channel_set_l1(struct lapdm_channel *lc, osmo_prim_cb cb, void *ctx);
+
+int lapdm_entity_set_mode(struct lapdm_entity *le, enum lapdm_mode mode);
+int lapdm_channel_set_mode(struct lapdm_channel *lc, enum lapdm_mode mode);
+
+void lapdm_entity_reset(struct lapdm_entity *le);
+void lapdm_channel_reset(struct lapdm_channel *lc);
+
+void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags);
+void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags);
+
+int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp);
+
+/*! }@ */
+
+#endif /* _OSMOCOM_LAPDM_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/mncc.h b/src/shared/libosmocore/include/osmocom/gsm/mncc.h
new file mode 100644
index 00000000..a094bb9b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/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/src/shared/libosmocore/include/osmocom/gsm/prim.h b/src/shared/libosmocore/include/osmocom/gsm/prim.h
new file mode 100644
index 00000000..95cbb120
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/prim.h
@@ -0,0 +1,13 @@
+#ifndef OSMO_GSM_PRIM_H
+#define OSMO_GSM_PRIM_H
+
+#include <osmocom/core/prim.h>
+
+/* enumeration of GSM related SAPs */
+enum osmo_gsm_sap {
+ SAP_GSM_PH = _SAP_GSM_BASE,
+ SAP_GSM_DL,
+ SAP_GSM_MDL,
+};
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/Makefile.am b/src/shared/libosmocore/include/osmocom/gsm/protocol/Makefile.am
new file mode 100644
index 00000000..6ed55e46
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/Makefile.am
@@ -0,0 +1,6 @@
+osmogsm_proto_HEADERS = gsm_03_41.h \
+ gsm_04_08.h gsm_04_11.h gsm_04_12.h gsm_04_80.h \
+ gsm_08_08.h gsm_08_58.h gsm_44_318.h \
+ gsm_12_21.h ipaccess.h
+
+osmogsm_protodir = $(includedir)/osmocom/gsm/protocol
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h
new file mode 100644
index 00000000..54365cbc
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h
@@ -0,0 +1,51 @@
+#ifndef PROTO_GSM_03_41_H
+#define PROTO_GSM_03_41_H
+
+#include <stdint.h>
+
+/* GSM TS 03.41 definitions also TS 23.041*/
+
+/* Chapter 9.3.2 */
+struct gsm341_ms_message {
+ struct {
+ uint8_t code_hi:6;
+ uint8_t gs:2;
+ uint8_t update:4;
+ uint8_t code_lo:4;
+ } serial;
+ uint16_t msg_id;
+ struct {
+ uint8_t language:4;
+ uint8_t group:4;
+ } dcs;
+ struct {
+ uint8_t total:4;
+ uint8_t current:4;
+ } page;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 9.4.1.3 */
+struct gsm341_etws_message {
+ struct {
+ uint8_t code_hi:4;
+ uint8_t popup:1;
+ uint8_t alert:1;
+ uint8_t gs:2;
+ uint8_t update:4;
+ uint8_t code_lo:4;
+ } serial;
+ uint16_t msg_id;
+ uint16_t warning_type;
+ uint8_t data[0];
+} __attribute__((packed));
+
+#define GSM341_MSG_CODE(ms) ((ms)->serial.code_lo | ((ms)->serial.code_hi << 4))
+
+/* Section 9.3.2.1 - Geographical Scope */
+#define GSM341_GS_CELL_WIDE_IMMED 0
+#define GSM341_GS_PLMN_WIDE 1
+#define GSM341_GS_LA_WIDE 2
+#define GSM341_GS_CELL_WIDE 3
+
+#endif /* PROTO_GSM_03_41_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h
new file mode 100644
index 00000000..5057ada8
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h
@@ -0,0 +1,1265 @@
+#ifndef PROTO_GSM_04_08_H
+#define PROTO_GSM_04_08_H
+
+#include <stdint.h>
+
+/* GSM TS 04.08 definitions */
+struct gsm_lchan;
+
+/* Chapter 10.5.1.5 */
+struct gsm48_classmark1 {
+ uint8_t pwr_lev:3,
+ a5_1:1,
+ es_ind:1,
+ rev_lev:2,
+ spare:1;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.1.6 */
+struct gsm48_classmark2 {
+ uint8_t pwr_lev:3,
+ a5_1:1,
+ es_ind:1,
+ rev_lev:2,
+ spare:1;
+ uint8_t fc:1,
+ vgcs:1,
+ vbs:1,
+ sm_cap:1,
+ ss_scr:2,
+ ps_cap:1,
+ spare2:1;
+ uint8_t a5_2:1,
+ a5_3:1,
+ cmsp:1,
+ solsa:1,
+ spare3:1,
+ lcsva_cap:1,
+ spare4:1,
+ cm3:1;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.3 */
+struct gsm48_range_1024 {
+ uint8_t w1_hi:2,
+ f0:1,
+ form_id:5;
+ uint8_t w1_lo;
+ uint8_t w2_hi;
+ uint8_t w3_hi:7,
+ w2_lo:1;
+ uint8_t w4_hi:6,
+ w3_lo:2;
+ uint8_t w5_hi:6,
+ w4_lo:2;
+ uint8_t w6_hi:6,
+ w5_lo:2;
+ uint8_t w7_hi:6,
+ w6_lo:2;
+ uint8_t w8_hi:6,
+ w7_lo:2;
+ uint8_t w9:7,
+ w8_lo:1;
+ uint8_t w11_hi:1,
+ w10:7;
+ uint8_t w12_hi:2,
+ w11_lo:6;
+ uint8_t w13_hi:3,
+ w12_lo:5;
+ uint8_t w14_hi:4,
+ w13_lo:4;
+ uint8_t w15_hi:5,
+ w14_lo:3;
+ uint8_t w16:6,
+ w15_lo:2;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.4 */
+struct gsm48_range_512 {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t w1_hi:7,
+ orig_arfcn_lo:1;
+ uint8_t w2_hi:6,
+ w1_lo:2;
+ uint8_t w3_hi:6,
+ w2_lo:2;
+ uint8_t w4_hi:6,
+ w3_lo:2;
+ uint8_t w5:7,
+ w4_lo:1;
+ uint8_t w7_hi:1,
+ w6:7;
+ uint8_t w8_hi:2,
+ w7_lo:6;
+ uint8_t w9_hi:4,
+ w8_lo:4;
+ uint8_t w10:6,
+ w9_lo:2;
+ uint8_t w12_hi:2,
+ w11:6;
+ uint8_t w13_hi:4,
+ w12_lo:4;
+ uint8_t w14:6,
+ w13_lo:2;
+ uint8_t w16_hi:2,
+ w15:6;
+ uint8_t w17:5,
+ w16_lo:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.5 */
+struct gsm48_range_256 {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t w1_hi:7,
+ orig_arfcn_lo:1;
+ uint8_t w2:7,
+ w1_lo:1;
+ uint8_t w4_hi:1,
+ w3:7;
+ uint8_t w5_hi:3,
+ w4_lo:5;
+ uint8_t w6_hi:5,
+ w5_lo:3;
+ uint8_t w8_hi:1,
+ w7:6,
+ w6_lo:1;
+ uint8_t w9_hi:4,
+ w8_lo:4;
+ uint8_t w11_hi:2,
+ w10:5,
+ w9_lo:1;
+ uint8_t w12:5,
+ w11_lo:3;
+ uint8_t w14_hi:3,
+ w13:5;
+ uint8_t w16_hi:1,
+ w15:5,
+ w14_lo:2;
+ uint8_t w18_hi:1,
+ w17:4,
+ w16_lo:3;
+ uint8_t w20_hi:1,
+ w19:4,
+ w18_lo:3;
+ uint8_t spare:1,
+ w21:4,
+ w20_lo:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.6 */
+struct gsm48_range_128 {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t w1:7,
+ orig_arfcn_lo:1;
+ uint8_t w3_hi:2,
+ w2:6;
+ uint8_t w4_hi:4,
+ w3_lo:4;
+ uint8_t w6_hi:2,
+ w5:5,
+ w4_lo:1;
+ uint8_t w7:5,
+ w6_lo:3;
+ uint8_t w9:4,
+ w8:4;
+ uint8_t w11:4,
+ w10:4;
+ uint8_t w13:4,
+ w12:4;
+ uint8_t w15:4,
+ w14:4;
+ uint8_t w18_hi:2,
+ w17:3,
+ w16:3;
+ uint8_t w21_hi:1,
+ w20:3,
+ w19:3,
+ w18_lo:1;
+ uint8_t w23:3,
+ w22:3,
+ w21_lo:2;
+ uint8_t w26_hi:2,
+ w25:3,
+ w24:3;
+ uint8_t spare:1,
+ w28:3,
+ w27:3,
+ w26_lo:1;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.7 */
+struct gsm48_var_bit {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t rrfcn1_7:7,
+ orig_arfcn_lo:1;
+ uint8_t rrfcn8_111[13];
+} __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;
+ } __attribute__ ((packed)) h1;
+ struct {
+ uint8_t arfcn_high:2,
+ spare:2,
+ h:1,
+ tsc:3;
+ uint8_t arfcn_low;
+ } __attribute__ ((packed)) h0;
+ } __attribute__ ((packed));
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.20 */
+struct gsm48_meas_res {
+ uint8_t rxlev_full:6,
+ dtx_used:1,
+ ba_used:1;
+ uint8_t rxlev_sub:6,
+ meas_valid:1,
+ spare:1;
+ uint8_t no_nc_n_hi:1,
+ rxqual_sub:3,
+ rxqual_full:3,
+ spare2:1;
+ uint8_t rxlev_nc1:6,
+ no_nc_n_lo:2;
+ uint8_t bsic_nc1_hi:3,
+ bcch_f_nc1:5;
+ uint8_t rxlev_nc2_hi:5,
+ bsic_nc1_lo:3;
+ uint8_t bsic_nc2_hi:2,
+ bcch_f_nc2:5,
+ rxlev_nc2_lo:1;
+ uint8_t rxlev_nc3_hi:4,
+ bsic_nc2_lo:4;
+ uint8_t bsic_nc3_hi:1,
+ bcch_f_nc3:5,
+ rxlev_nc3_lo:2;
+ uint8_t rxlev_nc4_hi:3,
+ bsic_nc3_lo:5;
+ uint8_t bcch_f_nc4:5,
+ rxlev_nc4_lo:3;
+ uint8_t rxlev_nc5_hi:2,
+ bsic_nc4:6;
+ uint8_t bcch_f_nc5_hi:4,
+ rxlev_nc5_lo:4;
+ uint8_t rxlev_nc6_hi:1,
+ bsic_nc5:6,
+ bcch_f_nc5_lo:1;
+ uint8_t bcch_f_nc6_hi:3,
+ rxlev_nc6_lo:5;
+ uint8_t bsic_nc6:6,
+ bcch_f_nc6_lo:2;
+} __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.28(a) */
+struct gsm48_power_cmd {
+ uint8_t power_level:5,
+ spare:2,
+ atc:1;
+} __attribute__((packed));
+
+/* Chapter 10.5.2.29 */
+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));
+
+
+/* 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 10.5.2.38 */
+struct gsm48_start_time {
+ uint8_t t3_high:3,
+ t1:5;
+ uint8_t t2:5,
+ t3_low:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.39 */
+struct gsm48_sync_ind {
+ uint8_t si:2,
+ rot:1,
+ nci:1,
+ sync_ie:4;
+} __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 9.1.13 */
+struct gsm48_frq_redef {
+ /* Semantic is from 10.5.2.5a */
+ struct gsm48_chan_desc chan_desc;
+ uint8_t mob_alloc_len;
+ uint8_t mob_alloc[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 9.1.25 */
+struct gsm48_pag_resp {
+ uint8_t spare:4,
+ key_seq:4;
+ uint32_t classmark2;
+ uint8_t mi_len;
+ uint8_t mi[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.3 */
+struct gsm48_auth_resp {
+ uint8_t sres[4];
+} __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));
+
+/* 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.33 System information Type 2bis */
+struct gsm48_system_information_type_2bis {
+ struct gsm48_system_information_type_header header;
+ uint8_t bcch_frequency_list[16];
+ struct gsm48_rach_control rach_control;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.34 System information Type 2ter */
+struct gsm48_system_information_type_2ter {
+ struct gsm48_system_information_type_header header;
+ uint8_t ext_bcch_frequency_list[16];
+ uint8_t rest_octets[0];
+} __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.38 System information Type 5bis */
+struct gsm48_system_information_type_5bis {
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+ uint8_t bcch_frequency_list[16];
+} __attribute__ ((packed));
+
+/* Section 9.1.39 System information Type 5ter */
+struct gsm48_system_information_type_5ter {
+ 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 9.1.1 */
+struct gsm48_add_ass {
+ /* Semantic is from 10.5.2.5 */
+ struct gsm48_chan_desc chan_desc;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.3 */
+struct gsm48_ass_cpl {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 9.1.4 */
+struct gsm48_ass_fail {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 9.1.3 */
+struct gsm48_ho_cpl {
+ uint8_t rr_cause;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.4 */
+struct gsm48_ho_fail {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 9.1.7 */
+struct gsm48_chan_rel {
+ uint8_t rr_cause;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.9 */
+struct gsm48_cip_mode_cmd {
+ uint8_t sc:1,
+ alg_id:3,
+ cr:1,
+ spare:3;
+} __attribute__((packed));
+
+/* Section 9.1.11 */
+struct gsm48_cm_change {
+ uint8_t cm2_len;
+ struct gsm48_classmark2 cm2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.19 */
+struct gsm48_imm_ass_ext {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t page_mode;
+ struct gsm48_chan_desc chan_desc1;
+ struct gsm48_req_ref req_ref1;
+ uint8_t timing_advance1;
+ struct gsm48_chan_desc chan_desc2;
+ struct gsm48_req_ref req_ref2;
+ uint8_t timing_advance2;
+ uint8_t mob_alloc_len;
+ uint8_t mob_alloc[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.20 */
+struct gsm48_imm_ass_rej {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t page_mode;
+ struct gsm48_req_ref req_ref1;
+ uint8_t wait_ind1;
+ struct gsm48_req_ref req_ref2;
+ uint8_t wait_ind2;
+ struct gsm48_req_ref req_ref3;
+ uint8_t wait_ind3;
+ struct gsm48_req_ref req_ref4;
+ uint8_t wait_ind4;
+ uint8_t rest[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.22 */
+struct gsm48_paging1 {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t pag_mode:2,
+ spare:2,
+ cneed1:2,
+ cneed2:2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.23 */
+struct gsm48_paging2 {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t pag_mode:2,
+ spare:2,
+ cneed1:2,
+ cneed2:2;
+ uint32_t tmsi1;
+ uint32_t tmsi2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.24 */
+struct gsm48_paging3 {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t pag_mode:2,
+ spare:2,
+ cneed1:2,
+ cneed2:2;
+ uint32_t tmsi1;
+ uint32_t tmsi2;
+ uint32_t tmsi3;
+ uint32_t tmsi4;
+ uint8_t cneed3:2,
+ cneed4:2,
+ spare2:4;
+ uint8_t rest[0];
+} __attribute__((packed));
+
+/* Section 9.1.25 */
+struct gsm48_pag_rsp {
+ uint8_t key_seq:3,
+ spare:5;
+ uint8_t cm2_len;
+ struct gsm48_classmark2 cm2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.29 */
+struct gsm48_rr_status {
+ uint8_t rr_cause;
+} __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.3.3 CM service type */
+#define GSM48_CMSERV_MO_CALL_PACKET 1
+#define GSM48_CMSERV_EMERGENCY 2
+#define GSM48_CMSERV_SMS 4
+#define GSM48_CMSERV_SUP_SERV 8
+#define GSM48_CMSERV_VGCS 9
+#define GSM48_CMSERV_VBS 10
+#define GSM48_CMSERV_LOC_SERV 11
+
+/* 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_MOBILE_ID 0x17 /* 10.5.1.4 */
+#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 */
+/* RR elements */
+#define GSM48_IE_VGCS_TARGET 0x01
+//#define GSM48_IE_VGCS_T_MODE_I 0x01
+#define GSM48_IE_FRQSHORT_AFTER 0x02
+#define GSM48_IE_MUL_RATE_CFG 0x03 /* 10.5.2.21aa */
+#define GSM48_IE_FREQ_L_AFTER 0x05
+#define GSM48_IE_MSLOT_DESC 0x10
+#define GSM48_IE_CHANMODE_2 0x11
+#define GSM48_IE_FRQSHORT_BEFORE 0x12
+//#define GSM48_IE_FRQSHORT_BEFOR 0x12
+#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
+#define GSM48_IE_MA_AFTER 0x72
+#define GSM48_IE_START_TIME 0x7c
+#define GSM48_IE_FREQ_L_BEFORE 0x19
+//#define GSM48_IE_FRQLIST_BEFORE 0x19
+#define GSM48_IE_CH_DESC_1_BEFORE 0x1c
+//#define GSM48_IE_CHDES_1_BEFORE 0x1c
+#define GSM48_IE_CH_DESC_2_BEFORE 0x1d
+//#define GSM48_IE_CHDES_2_BEFORE 0x1d
+#define GSM48_IE_F_CH_SEQ_BEFORE 0x1e
+//#define GSM48_IE_FRQSEQ_BEFORE 0x1e
+#define GSM48_IE_CLASSMARK3 0x20
+#define GSM48_IE_MA_BEFORE 0x21
+#define GSM48_IE_RR_PACKET_UL 0x22
+#define GSM48_IE_RR_PACKET_DL 0x23
+#define GSM48_IE_CELL_CH_DESC 0x62
+#define GSM48_IE_CHANMODE_1 0x63
+#define GSM48_IE_CHDES_2_AFTER 0x64
+#define GSM48_IE_MODE_SEC_CH 0x66
+#define GSM48_IE_F_CH_SEQ_AFTER 0x69
+#define GSM48_IE_MA_AFTER 0x72
+#define GSM48_IE_BA_RANGE 0x73
+#define GSM48_IE_GROUP_CHDES 0x74
+#define GSM48_IE_BA_LIST_PREF 0x75
+#define GSM48_IE_MOB_OVSERV_DIF 0x77
+#define GSM48_IE_REALTIME_DIFF 0x7b
+#define GSM48_IE_START_TIME 0x7c
+#define GSM48_IE_TIMING_ADVANCE 0x7d
+#define GSM48_IE_GROUP_CIP_SEQ 0x80
+#define GSM48_IE_CIP_MODE_SET 0x90
+#define GSM48_IE_GPRS_RESUMPT 0xc0
+#define GSM48_IE_SYNC_IND 0xd0
+/* System Information 4 (types are equal IEs above) */
+#define GSM48_IE_CBCH_CHAN_DESC 0x64
+#define GSM48_IE_CBCH_MOB_AL 0x72
+
+/* Additional MM elements */
+#define GSM48_IE_LOCATION_AREA 0x13
+#define GSM48_IE_PRIORITY_LEV 0x80
+#define GSM48_IE_FOLLOW_ON_PROC 0xa1
+#define GSM48_IE_CTS_PERMISSION 0xa2
+
+/* 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
+#define GSM48_T303_MS 30, 0
+#define GSM48_T305_MS 30, 0
+#define GSM48_T308_MS 30, 0
+#define GSM48_T310_MS 30, 0
+#define GSM48_T313_MS 30, 0
+#define GSM48_T323_MS 30, 0
+#define GSM48_T332_MS 30, 0
+#define GSM48_T335_MS 30, 0
+
+/* Chapter 5.1.2.2 */
+#define GSM_CSTATE_NULL 0
+#define GSM_CSTATE_INITIATED 1
+#define GSM_CSTATE_MM_CONNECTION_PEND 2 /* see 10.5.4.6 */
+#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
+
+/* Chapter 10.4.4.15 */
+struct gsm48_ra_id {
+ uint8_t digits[3]; /* MCC + MNC BCD digits */
+ uint16_t lac; /* Location Area Code */
+ uint8_t rac; /* Routing Area Code */
+} __attribute__ ((packed));
+
+#define GSM48_CELL_CHAN_DESC_SIZE 16
+
+#define GSM_MACBLOCK_LEN 23
+#define GSM_MACBLOCK_PADDING 0x2b
+
+#endif /* PROTO_GSM_04_08_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h
new file mode 100644
index 00000000..f37152fe
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h
@@ -0,0 +1,190 @@
+#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 user/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 user/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,
+ GSM411_RPS_WAIT_FOR_RETRANS_T = 4,
+};
+
+/* 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 /* TR1M - 10 */
+#define GSM411_TMR_TC1A_SEC 30 /* TR1M - 10 */
+
+/* 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/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h
new file mode 100644
index 00000000..9b1538a5
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h
@@ -0,0 +1,31 @@
+#ifndef PROTO_GSM_04_12_H
+#define PROTO_GSM_04_12_H
+
+#include <stdint.h>
+
+/* GSM TS 04.12 definitions for Short Message Service Cell Broadcast */
+
+#define GSM412_SEQ_FST_BLOCK 0x0
+#define GSM412_SEQ_SND_BLOCK 0x1
+#define GSM412_SEQ_TRD_BLOCK 0x2
+#define GSM412_SEQ_FTH_BLOCK 0x3
+#define GSM412_SEQ_FST_SCHED_BLOCK 0x8
+#define GSM412_SEQ_NULL_MSG 0xf
+
+struct gsm412_block_type {
+ uint8_t seq_nr : 4,
+ lb : 1,
+ lpd : 2,
+ spare : 1;
+} __attribute__((packed));
+
+struct gsm412_sched_msg {
+ uint8_t beg_slot_nr : 6,
+ type : 2;
+ uint8_t end_slot_nr : 6,
+ spare1 : 1, spare2: 1;
+ uint8_t cbsms_msg_map[6];
+ uint8_t data[0];
+} __attribute__((packed));
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h
new file mode 100644
index 00000000..fa5c9451
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/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/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h
new file mode 100644
index 00000000..6b8f9359
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h
@@ -0,0 +1,303 @@
+/* From GSM08.08 */
+
+#ifndef GSM_0808_H
+#define GSM_0808_H
+
+#include <stdlib.h>
+
+/*
+ * this is from GSM 03.03 CGI but is copied in GSM 08.08
+ * in § 3.2.2.27 for Cell Identifier List
+ */
+enum CELL_IDENT {
+ CELL_IDENT_WHOLE_GLOBAL = 0,
+ CELL_IDENT_LAC_AND_CI = 1,
+ CELL_IDENT_CI = 2,
+ CELL_IDENT_NO_CELL = 3,
+ CELL_IDENT_LAI_AND_LAC = 4,
+ CELL_IDENT_LAC = 5,
+ CELL_IDENT_BSS = 6,
+ CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8,
+ CELL_IDENT_UTRAN_RNC = 9,
+ CELL_IDENT_UTRAN_LAC_RNC = 10,
+};
+
+
+/* GSM 08.06 § 6.3 */
+enum BSSAP_MSG_TYPE {
+ BSSAP_MSG_BSS_MANAGEMENT = 0x0,
+ BSSAP_MSG_DTAP = 0x1,
+};
+
+struct bssmap_header {
+ uint8_t type;
+ uint8_t length;
+} __attribute__((packed));
+
+struct dtap_header {
+ uint8_t type;
+ uint8_t link_id;
+ uint8_t length;
+} __attribute__((packed));
+
+
+enum BSS_MAP_MSG_TYPE {
+ BSS_MAP_MSG_RESERVED_0 = 0,
+
+ /* ASSIGNMENT MESSAGES */
+ BSS_MAP_MSG_ASSIGMENT_RQST = 1,
+ BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2,
+ BSS_MAP_MSG_ASSIGMENT_FAILURE = 3,
+
+ /* HANDOVER MESSAGES */
+ BSS_MAP_MSG_HANDOVER_RQST = 16,
+ BSS_MAP_MSG_HANDOVER_REQUIRED = 17,
+ BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18,
+ BSS_MAP_MSG_HANDOVER_CMD = 19,
+ BSS_MAP_MSG_HANDOVER_COMPLETE = 20,
+ BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21,
+ BSS_MAP_MSG_HANDOVER_FAILURE = 22,
+ BSS_MAP_MSG_HANDOVER_PERFORMED = 23,
+ BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24,
+ BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25,
+ BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26,
+ BSS_MAP_MSG_HANDOVER_DETECT = 27,
+
+ /* RELEASE MESSAGES */
+ BSS_MAP_MSG_CLEAR_CMD = 32,
+ BSS_MAP_MSG_CLEAR_COMPLETE = 33,
+ BSS_MAP_MSG_CLEAR_RQST = 34,
+ BSS_MAP_MSG_RESERVED_1 = 35,
+ BSS_MAP_MSG_RESERVED_2 = 36,
+ BSS_MAP_MSG_SAPI_N_REJECT = 37,
+ BSS_MAP_MSG_CONFUSION = 38,
+
+ /* OTHER CONNECTION RELATED MESSAGES */
+ BSS_MAP_MSG_SUSPEND = 40,
+ BSS_MAP_MSG_RESUME = 41,
+ BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42,
+ BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43,
+ BSS_MAP_MSG_LSA_INFORMATION = 44,
+ BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45,
+ BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46,
+ BSS_MAP_MSG_COMMON_ID = 47,
+
+ /* GENERAL MESSAGES */
+ BSS_MAP_MSG_RESET = 48,
+ BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49,
+ BSS_MAP_MSG_OVERLOAD = 50,
+ BSS_MAP_MSG_RESERVED_3 = 51,
+ BSS_MAP_MSG_RESET_CIRCUIT = 52,
+ BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53,
+ BSS_MAP_MSG_MSC_INVOKE_TRACE = 54,
+ BSS_MAP_MSG_BSS_INVOKE_TRACE = 55,
+ BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58,
+
+ /* TERRESTRIAL RESOURCE MESSAGES */
+ BSS_MAP_MSG_BLOCK = 64,
+ BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65,
+ BSS_MAP_MSG_UNBLOCK = 66,
+ BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67,
+ BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68,
+ BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69,
+ BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70,
+ BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71,
+ BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72,
+ BSS_MAP_MSG_CHANGE_CIRCUIT = 78,
+ BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79,
+
+ /* RADIO RESOURCE MESSAGES */
+ BSS_MAP_MSG_RESOURCE_RQST = 80,
+ BSS_MAP_MSG_RESOURCE_INDICATION = 81,
+ BSS_MAP_MSG_PAGING = 82,
+ BSS_MAP_MSG_CIPHER_MODE_CMD = 83,
+ BSS_MAP_MSG_CLASSMARK_UPDATE = 84,
+ BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85,
+ BSS_MAP_MSG_QUEUING_INDICATION = 86,
+ BSS_MAP_MSG_COMPLETE_LAYER_3 = 87,
+ BSS_MAP_MSG_CLASSMARK_RQST = 88,
+ BSS_MAP_MSG_CIPHER_MODE_REJECT = 89,
+ BSS_MAP_MSG_LOAD_INDICATION = 90,
+
+ /* VGCS/VBS */
+ BSS_MAP_MSG_VGCS_VBS_SETUP = 4,
+ BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5,
+ BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29,
+ BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30,
+ BSS_MAP_MSG_UPLINK_RQST = 31,
+ BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39,
+ BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73,
+ BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74,
+ BSS_MAP_MSG_UPLINK_REJECT_CMD = 75,
+ BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76,
+ BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77,
+};
+
+enum GSM0808_IE_CODING {
+ GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1,
+ GSM0808_IE_RESERVED_0 = 2,
+ GSM0808_IE_RESOURCE_AVAILABLE = 3,
+ GSM0808_IE_CAUSE = 4,
+ GSM0808_IE_CELL_IDENTIFIER = 5,
+ GSM0808_IE_PRIORITY = 6,
+ GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7,
+ GSM0808_IE_IMSI = 8,
+ GSM0808_IE_TMSI = 9,
+ GSM0808_IE_ENCRYPTION_INFORMATION = 10,
+ GSM0808_IE_CHANNEL_TYPE = 11,
+ GSM0808_IE_PERIODICITY = 12,
+ GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13,
+ GSM0808_IE_NUMBER_OF_MSS = 14,
+ GSM0808_IE_RESERVED_1 = 15,
+ GSM0808_IE_RESERVED_2 = 16,
+ GSM0808_IE_RESERVED_3 = 17,
+ GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18,
+ GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19,
+ GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20,
+ GSM0808_IE_RR_CAUSE = 21,
+ GSM0808_IE_RESERVED_4 = 22,
+ GSM0808_IE_LAYER_3_INFORMATION = 23,
+ GSM0808_IE_DLCI = 24,
+ GSM0808_IE_DOWNLINK_DTX_FLAG = 25,
+ GSM0808_IE_CELL_IDENTIFIER_LIST = 26,
+ GSM0808_IE_RESPONSE_RQST = 27,
+ GSM0808_IE_RESOURCE_INDICATION_METHOD = 28,
+ GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29,
+ GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30,
+ GSM0808_IE_DIAGNOSTIC = 31,
+ GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32,
+ GSM0808_IE_CHOSEN_CHANNEL = 33,
+ GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34,
+ GSM0808_IE_CIPHER_RESPONSE_MODE = 35,
+ GSM0808_IE_CHANNEL_NEEDED = 36,
+ GSM0808_IE_TRACE_TYPE = 37,
+ GSM0808_IE_TRIGGERID = 38,
+ GSM0808_IE_TRACE_REFERENCE = 39,
+ GSM0808_IE_TRANSACTIONID = 40,
+ GSM0808_IE_MOBILE_IDENTITY = 41,
+ GSM0808_IE_OMCID = 42,
+ GSM0808_IE_FORWARD_INDICATOR = 43,
+ GSM0808_IE_CHOSEN_ENCR_ALG = 44,
+ GSM0808_IE_CIRCUIT_POOL = 45,
+ GSM0808_IE_CIRCUIT_POOL_LIST = 46,
+ GSM0808_IE_TIME_INDICATION = 47,
+ GSM0808_IE_RESOURCE_SITUATION = 48,
+ GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49,
+ GSM0808_IE_QUEUEING_INDICATOR = 50,
+ GSM0808_IE_SPEECH_VERSION = 64,
+ GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51,
+ GSM0808_IE_TALKER_FLAG = 53,
+ GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54,
+ GSM0808_IE_GROUP_CALL_REFERENCE = 55,
+ GSM0808_IE_EMLPP_PRIORITY = 56,
+ GSM0808_IE_CONFIG_EVO_INDI = 57,
+ GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58,
+ GSM0808_IE_LSA_IDENTIFIER = 59,
+ GSM0808_IE_LSA_IDENTIFIER_LIST = 60,
+ GSM0808_IE_LSA_INFORMATION = 61,
+ GSM0808_IE_LCS_QOS = 62,
+ GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63,
+ GSM0808_IE_LCS_PRIORITY = 67,
+ GSM0808_IE_LOCATION_TYPE = 68,
+ GSM0808_IE_LOCATION_ESTIMATE = 69,
+ GSM0808_IE_POSITIONING_DATA = 70,
+ GSM0808_IE_LCS_CAUSE = 71,
+ GSM0808_IE_LCS_CLIENT_TYPE = 72,
+ GSM0808_IE_APDU = 73,
+ GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74,
+ GSM0808_IE_GPS_ASSISTANCE_DATA = 75,
+ GSM0808_IE_DECIPHERING_KEYS = 76,
+ GSM0808_IE_RETURN_ERROR_RQST = 77,
+ GSM0808_IE_RETURN_ERROR_CAUSE = 78,
+ GSM0808_IE_SEGMENTATION = 79,
+ GSM0808_IE_SERVICE_HANDOVER = 80,
+ GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81,
+ GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82,
+ GSM0808_IE_RESERVED_5 = 65,
+ GSM0808_IE_RESERVED_6 = 66,
+};
+
+enum gsm0808_cause {
+ GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0,
+ GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1,
+ GSM0808_CAUSE_UPLINK_QUALITY = 2,
+ GSM0808_CAUSE_UPLINK_STRENGTH = 3,
+ GSM0808_CAUSE_DOWNLINK_QUALITY = 4,
+ GSM0808_CAUSE_DOWNLINK_STRENGTH = 5,
+ GSM0808_CAUSE_DISTANCE = 6,
+ GSM0808_CAUSE_O_AND_M_INTERVENTION = 7,
+ GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8,
+ GSM0808_CAUSE_CALL_CONTROL = 9,
+ GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10,
+ GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11,
+ GSM0808_CAUSE_BETTER_CELL = 12,
+ GSM0808_CAUSE_DIRECTED_RETRY = 13,
+ GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14,
+ GSM0808_CAUSE_TRAFFIC = 15,
+ GSM0808_CAUSE_EQUIPMENT_FAILURE = 32,
+ GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33,
+ GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34,
+ GSM0808_CAUSE_CCCH_OVERLOAD = 35,
+ GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36,
+ GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37,
+ GSM0808_CAUSE_MS_NOT_EQUIPPED = 38,
+ GSM0808_CAUSE_INVALID_CELL = 39,
+ GSM0808_CAUSE_TRAFFIC_LOAD = 40,
+ GSM0808_CAUSE_PREEMPTION = 41,
+ GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48,
+ GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49,
+ GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50,
+ GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51,
+ GSM0808_CAUSE_LSA_NOT_ALLOWED = 52,
+ GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64,
+ GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80,
+ GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81,
+ GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82,
+ GSM0808_CAUSE_INCORRECT_VALUE = 83,
+ GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84,
+ GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85,
+ GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96,
+};
+
+/* GSM 08.08 3.2.2.11 Channel Type */
+enum gsm0808_chan_indicator {
+ GSM0808_CHAN_SPEECH = 1,
+ GSM0808_CHAN_DATA = 2,
+ GSM0808_CHAN_SIGN = 3,
+};
+
+enum gsm0808_chan_rate_type_data {
+ GSM0808_DATA_FULL_BM = 0x8,
+ GSM0808_DATA_HALF_LM = 0x9,
+ GSM0808_DATA_FULL_RPREF = 0xa,
+ GSM0808_DATA_HALF_PREF = 0xb,
+ GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a,
+ GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b,
+ GSM0808_DATA_MULTI_MASK = 0x20,
+ GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30,
+};
+
+enum gsm0808_chan_rate_type_speech {
+ GSM0808_SPEECH_FULL_BM = 0x8,
+ GSM0808_SPEECH_HALF_LM = 0x9,
+ GSM0808_SPEECH_FULL_PREF= 0xa,
+ GSM0808_SPEECH_HALF_PREF= 0xb,
+ GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a,
+ GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b,
+ GSM0808_SPEECH_PERM = 0xf,
+ GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f,
+};
+
+enum gsm0808_permitted_speech {
+ GSM0808_PERM_FR1 = 0x01,
+ GSM0808_PERM_FR2 = 0x11,
+ GSM0808_PERM_FR3 = 0x21,
+ GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4,
+ GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4,
+ GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4,
+};
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h
new file mode 100644
index 00000000..b6a5dd83
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h
@@ -0,0 +1,561 @@
+#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>
+
+/*! \addtogroup rsl
+ * @{
+ */
+
+/*! \file gsm_08_58.h */
+
+/*! \brief RSL common header */
+struct abis_rsl_common_hdr {
+ uint8_t msg_discr; /*!< \brief message discriminator (ABIS_RSL_MDISC_*) */
+ uint8_t msg_type; /*!< \brief message type (\ref abis_rsl_msgtype) */
+ uint8_t data[0]; /*!< \brief actual payload data */
+} __attribute__ ((packed));
+
+/* \brief RSL RLL header (Chapter 8.3) */
+struct abis_rsl_rll_hdr {
+ struct abis_rsl_common_hdr c;
+ uint8_t ie_chan; /*!< \brief \ref RSL_IE_CHAN_NR (tag) */
+ uint8_t chan_nr; /*!< \brief RSL channel number (value) */
+ uint8_t ie_link_id; /*!< \brief \ref RSL_IE_LINK_IDENT (tag) */
+ uint8_t link_id; /*!< \brief RSL link identifier (value) */
+ uint8_t data[0]; /*!< \brief message payload data */
+} __attribute__ ((packed));
+
+/* \brief RSL Dedicated Channel header (Chapter 8.3 and 8.4) */
+struct abis_rsl_dchan_hdr {
+ struct abis_rsl_common_hdr c;
+ uint8_t ie_chan; /*!< \brief \ref RSL_IE_CHAN_NR (tag) */
+ uint8_t chan_nr; /*!< \brief RSL channel number (value) */
+ uint8_t data[0]; /*!< \brief message payload data */
+} __attribute__ ((packed));
+
+/* \brief RSL Common Channel header (Chapter 8.5) */
+struct abis_rsl_cchan_hdr {
+ struct abis_rsl_common_hdr c;
+ uint8_t ie_chan; /*!< \brief \ref RSL_IE_CHAN_NR (tag) */
+ uint8_t chan_nr; /*!< \brief RSL channel number (value) */
+ uint8_t data[0]; /*!< \brief message payload data */
+} __attribute__ ((packed));
+
+
+/* Chapter 9.1 */
+/* \brief RSL Message Discriminator: RLL */
+#define ABIS_RSL_MDISC_RLL 0x02
+/* \brief RSL Message Discriminator: Dedicated Channel */
+#define ABIS_RSL_MDISC_DED_CHAN 0x08
+/* \brief RSL Message Discriminator: Common Channel */
+#define ABIS_RSL_MDISC_COM_CHAN 0x0c
+/* \brief RSL Message Discriminator: TRX Management */
+#define ABIS_RSL_MDISC_TRX 0x10
+/* \brief RSL Message Discriminator: Location Service */
+#define ABIS_RSL_MDISC_LOC 0x20
+/* \brief RSL Message Discriminator: ip.access */
+#define ABIS_RSL_MDISC_IPACCESS 0x7e
+#define ABIS_RSL_MDISC_TRANSP 0x01
+
+/* \brief Check if given RSL message discriminator is transparent */
+#define ABIS_RSL_MDISC_IS_TRANSP(x) (x & 0x01)
+
+/* \brief RSL Message Tyoe (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 */
+ RSL_MT_SUSP_REQ, /* non-standard elements */
+ RSL_MT_SUSP_CONF,
+ RSL_MT_RES_REQ,
+ RSL_MT_RECON_REQ, /* 0x0f */
+
+ /* 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,
+ RSL_MT_CHAN_CONF, /* non-standard element */
+ /* 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,
+};
+
+/*! \brief Siemens vendor-specific RSL message types */
+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,
+};
+
+/*! \brief RSL Information Element Identifiers (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
+
+/*! \brief RSL Channel Mode IF (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
+
+/*! \brief RSL Channel Identification IE (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 RSL_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
+
+/*! \brief RSL Cell Broadcast Command (Chapter 9.3.45) */
+struct rsl_ie_cb_cmd_type {
+ uint8_t last_block:2;
+ uint8_t spare:1;
+ uint8_t def_bcast:1;
+ uint8_t command:4;
+} __attribute__ ((packed));
+/* ->command */
+#define RSL_CB_CMD_TYPE_NORMAL 0x00
+#define RSL_CB_CMD_TYPE_SCHEDULE 0x08
+#define RSL_CB_CMD_TYPE_DEFAULT 0x0e
+#define RSL_CB_CMD_TYPE_NULL 0x0f
+/* ->def_bcast */
+#define RSL_CB_CMD_DEFBCAST_NORMAL 0
+#define RSL_CB_CMD_DEFBCAST_NULL 1
+/* ->last_block */
+#define RSL_CB_CMD_LASTBLOCK_4 0
+#define RSL_CB_CMD_LASTBLOCK_1 1
+#define RSL_CB_CMD_LASTBLOCK_2 2
+#define RSL_CB_CMD_LASTBLOCK_3 3
+
+/* 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/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h
new file mode 100644
index 00000000..061dcb63
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h
@@ -0,0 +1,748 @@
+#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.
+ *
+ */
+
+/*! \addtogroup oml
+ * @{
+ */
+
+/*! \file gsm_12_21.h */
+
+#include <stdint.h>
+#include <osmocom/gsm/tlv.h>
+
+/*! \brief generic header in front of every OML message according to TS 08.59 */
+struct abis_om_hdr {
+ /*! \brief Message Discriminator \ref abis_oml_mdisc */
+ uint8_t mdisc;
+ /*! \brief Placement (like \ref ABIS_OM_PLACEMENT_ONLY) */
+ uint8_t placement;
+ /*! \brief Sequence Number (if \ref ABIS_OM_PLACEMENT_MIDDLE) */
+ uint8_t sequence;
+ /*! \brief Length in octets */
+ uint8_t length;
+ /*! \brief actual payload data */
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/*! \brief Message Discriminator for Formatted Object Messages */
+#define ABIS_OM_MDISC_FOM 0x80
+/*! \brief Message Discriminator for Man Machine Interface */
+#define ABIS_OM_MDISC_MMI 0x40
+/*! \brief Message Discriminator for TRAU management */
+#define ABIS_OM_MDISC_TRAU 0x20
+/*! \brief Message Discriminator for Manufacturer Specific Messages */
+#define ABIS_OM_MDISC_MANUF 0x10
+
+/*! \brief Entire OML message is in the L2 frame */
+#define ABIS_OM_PLACEMENT_ONLY 0x80
+/*! \brief First fragment of OML message is in this L2 frame */
+#define ABIS_OM_PLACEMENT_FIRST 0x40
+/*! \brief Middle fragment of OML message is in this L2 frame */
+#define ABIS_OM_PLACEMENT_MIDDLE 0x20
+/*! \brief Last fragment of OML message is in this L2 frame */
+#define ABIS_OM_PLACEMENT_LAST 0x10
+
+/*! \brief OML Object Instance */
+struct abis_om_obj_inst {
+ uint8_t bts_nr; /*!< \brief BTS Number */
+ uint8_t trx_nr; /*!< \brief TRX Number */
+ uint8_t ts_nr; /*!< \brief Timeslot Number */
+} __attribute__ ((packed));
+
+/*! \brief OML Object Instance */
+struct abis_om_fom_hdr {
+ uint8_t msg_type; /*!< \brief Message Type (\ref abis_nm_msgtype) */
+ uint8_t obj_class; /*!< \brief Object Class (\ref abis_nm_obj_class) */
+ struct abis_om_obj_inst obj_inst; /*!< \brief Object Instance */
+ uint8_t data[0]; /*!< \brief Data */
+} __attribute__ ((packed));
+
+/*! \brief Size of the OML FOM header in octets */
+#define ABIS_OM_FOM_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr))
+
+/*! \brief OML Message Type (Section 9.1) */
+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,
+};
+
+/*! \brief Siemens specific OML Message Types */
+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,
+};
+
+/*! \brief ip.access specific OML Message Types */
+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,
+};
+
+/*! \brief OML Object Class (Section 9.2) */
+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,
+};
+
+/*! \brief OML Attributes / IEs (Section 9.4) */
+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
+
+/*! \brief OML Administrative State (Section 9.4.4) */
+enum abis_nm_adm_state {
+ NM_STATE_LOCKED = 0x01,
+ NM_STATE_UNLOCKED = 0x02,
+ NM_STATE_SHUTDOWN = 0x03,
+ NM_STATE_NULL = 0xff,
+};
+
+/*! \brief OML Availability State (Section 9.4.7) */
+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,
+};
+
+/*! \brief OML Operational State */
+enum abis_nm_op_state {
+ NM_OPSTATE_DISABLED = 1,
+ NM_OPSTATE_ENABLED = 2,
+ NM_OPSTATE_NULL = 0xff,
+};
+
+/* \brief Channel Combination (Section 9.4.13) */
+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,
+};
+
+/*! \brief Event Type (Section 9.4.16) */
+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,
+};
+
+/*! \brief Perceived Severity (Section: 9.4.63) */
+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,
+};
+
+/*! \brief Probable Cause Type (Section 9.4.43) */
+enum abis_nm_pcause_type {
+ NM_PCAUSE_T_X721 = 0x01,
+ NM_PCAUSE_T_GSM = 0x02,
+ NM_PCAUSE_T_MANUF = 0x03,
+};
+
+/*! \brief NACK causes (Section 9.4.36) */
+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,
+};
+
+/*! \brief Abis OML Channel (Section 9.4.1) */
+struct abis_nm_channel {
+ uint8_t attrib;
+ uint8_t bts_port; /*!< \brief BTS port number */
+ uint8_t timeslot; /*!< \brief E1 timeslot */
+ uint8_t subslot; /*!< \brief E1 sub-slot */
+} __attribute__ ((packed));
+
+/*! \brief 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*/
+};
+
+/*! \brief Siemens BS11 TRX power */
+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,
+};
+
+/*! \brief Siemens BS11 PLL mode */
+enum abis_bs11_li_pll_mode {
+ BS11_LI_PLL_LOCKED = 2,
+ BS11_LI_PLL_STANDALONE = 3,
+};
+
+/*! \brief Siemens BS11 E1 line configuration */
+enum abis_bs11_line_cfg {
+ BS11_LINE_CFG_STAR = 0x00,
+ BS11_LINE_CFG_MULTIDROP = 0x01,
+ BS11_LINE_CFG_LOOP = 0x02,
+};
+
+/*! \brief Siemens BS11 boot phase */
+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,
+};
+
+/*! \brief ip.access test number */
+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,
+};
+
+/*! \brief 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,
+};
+
+/*! \brief 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,
+};
+
+/*! \brief ip.access IEI */
+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,
+};
+
+/*! \brief ip.access NWL BCCH information type */
+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/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_44_318.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_44_318.h
new file mode 100644
index 00000000..31c0ea7c
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_44_318.h
@@ -0,0 +1,153 @@
+#ifndef PROTO_GSM_44_318_H
+#define PROTO_GSM_44_318_H
+
+#include <stdint.h>
+
+/* Definitions according to 3GPP TS 44.318 6.8.0 Release 6 */
+
+/* Table 11.1.1.4.1: Message types for URR */
+
+enum gan_msg_type {
+ GA_MT_RC_DISCOVERY_REQUEST = 0x01,
+ GA_MT_RC_DISCOVERY_ACCEPT = 0x02,
+ GA_MT_RC_DISCOVERY_REJECT = 0x03,
+
+ GA_MT_RC_REGISTER_REQUEST = 0x10,
+ GA_MT_RC_REGISTER_ACCEPT = 0x11,
+ GA_MT_RC_REGISTER_REDIRECT = 0x12,
+ GA_MT_RC_REGISTER_REJECT = 0x13,
+ GA_MT_RC_DEREGISTER = 0x14,
+ GA_MT_RC_REGISTER_UPDATE_UL = 0x15,
+ GA_MT_RC_REGISTER_UPDATE_DL = 0x16,
+ GA_MT_RC_CELL_BCAST_INFO = 0x17,
+
+ GA_MT_CSR_CIPH_MODE_CMD = 0x20,
+ GA_MT_CSR_CIPH_MODE_COMPL = 0x21,
+
+ GA_MT_CSR_ACT_CHAN = 0x30,
+ GA_MT_CSR_ACT_CHAN_ACK = 0x31,
+ GA_MT_CSR_ACT_CHAN_COMPL = 0x32,
+ GA_MT_CSR_ACT_CHAN_FAIL = 0x33,
+ GA_MT_CSR_CHAN_MODE_MOD = 0x34,
+ GA_MT_CSR_CHAN_MODE_MOD_ACK = 0x35,
+
+ GA_MT_CSR_RELEASE = 0x40,
+ GA_MT_CSR_RELEASE_COMPL = 0x41,
+ GA_MT_CSR_CLEAR_REQ = 0x42,
+
+ GA_MT_CSR_HO_ACCESS = 0x50,
+ GA_MT_CSR_HO_COMPL = 0x51,
+ GA_MT_CSR_UL_QUAL_IND = 0x52,
+ GA_MT_CSR_HO_INFO = 0x53,
+ GA_MT_CSR_HO_CMD = 0x54,
+ GA_MT_CSR_HO_FAIL = 0x55,
+
+ GA_MT_CSR_PAGING_REQ = 0x60,
+ GA_MT_CSR_PAGING_RESP = 0x61,
+
+ GA_MT_CSR_UL_DIRECT_XFER = 0x70,
+ GA_MT_CSR_DL_DIRECT_XFER = 0x72,
+ GA_MT_CSR_STATUS = 0x73,
+ GA_MT_RC_KEEPALIVE = 0x74,
+ GA_MT_CSR_CM_ENQ = 0x75,
+ GA_MT_CSR_CM_CHANGE = 0x76,
+
+ GA_MT_CSR_REQUEST = 0x80,
+ GA_MT_CSR_REQUEST_ACCEPT = 0x81,
+ GA_MT_CSR_REQUEST_REJECT = 0x82,
+};
+
+/* All tables in 10.1.x and 10.2.x / Table 11.2.1 */
+enum gan_iei {
+ GA_IE_MI = 1,
+ GA_IE_GAN_RELEASE_IND = 2,
+ GA_IE_RADIO_IE = 3,
+ GA_IE_GERAN_CELL_ID = 4,
+ GA_IE_LAC = 5,
+ GA_IE_GERAN_COV_IND = 6,
+ GA_IE_GAN_CM = 7,
+ GA_IE_GEO_LOC = 8,
+ GA_IE_DEF_SEGW_IP = 9,
+ GA_IE_DEF_SEGW_FQDN = 10,
+ GA_IE_REDIR_CTR = 11,
+ GA_IE_DISCOV_REJ_CAUSE = 12,
+ GA_IE_GANC_CELL_DESC = 13,
+ GA_IE_GANC_CTRL_CH_DESC = 14,
+ GA_IE_GERAN_CELL_ID_LIST= 15,
+ GA_IE_TU3907_TIMER = 16,
+ GA_IE_RR_STATE = 17,
+ GA_IE_RAI = 18,
+ GA_IE_GAN_BAND = 19,
+ GA_IE_GARC_GACSR_STATE = 20,
+ GA_IE_REG_REJ_CAUSE = 21,
+ GA_IE_TU3906_TIMER = 22,
+ GA_IE_TU3910_TIMER = 23,
+ GA_IE_TU3902_TIMER = 24,
+ GA_IE_L3_MSG = 26,
+ GA_IE_CHAN_MODE = 27,
+ GA_IE_MS_CLASSMARK2 = 28,
+ GA_IE_RR_CAUSE = 29,
+ GA_EI_CIPH_MODE_SET = 30,
+ GA_IE_GPRS_RESUMPTION = 31,
+ GA_IE_HO_FROM_GAN_CMD = 32,
+ GA_IE_UL_QUAL_IND = 33,
+ GA_IE_TLLI = 34,
+ GA_IE_PFI = 35,
+ GA_IE_SUSP_CAUSE = 36,
+ GA_IE_TU3820_TIMER = 37,
+ GA_IE_REQD_QOS = 38,
+ GA_IE_P_DEACT_CAUSE = 39
+ GA_IE_REQD_UL_RATE = 40,
+ GA_IE_RAC = 41,
+ GA_IE_AP_LOCATION = 42,
+ GA_IE_TU4001_TIMER = 43,
+ GA_IE_LOC_STATUS = 44,
+ GA_IE_CIPH_RESP = 45,
+ GA_IE_CIPH_RAND = 46,
+ GA_IE_CIPH_MAC = 47,
+ GA_IE_CKSN = 48,
+ GA_IE_SAPI_ID = 49,
+ GA_IE_EST_CAUSE = 50,
+ GA_IE_CHAN_NEEDED = 51,
+ GA_IE_PDU_IN_ERROR = 52,
+ GA_IE_SAMPLE_SIZE = 53,
+ GA_IE_PAYLOAD_TYPE = 54,
+ GA_IE_MULTIRATE_CONF = 55,
+ GA_IE_MS_CLASSMARK3 = 56,
+ GA_IE_LLC_PDU = 57,
+ GA_IE_LOC_BLACKL_IND = 58,
+ GA_IE_RESET_IND = 59,
+ GA_IE_TU4003_TIMER = 60,
+ GA_IE_AP_SERV_NAME = 61,
+ GA_IE_SERV_ZONE_INFO = 62,
+ GA_IE_RTP_RED_CONF = 63,
+ GA_IE_UTRAN_CLASSMARK = 64,
+ GA_IE_CM_ENQ_MASK = 65,
+ GA_IE_UTRAN_CELLID_LIST = 66,
+ GA_IE_SERV_GANC_TBL_IND = 67,
+ GA_IE_AP_REG_IND = 68,
+ GA_IE_GAN_PLMN_LIST = 69,
+ GA_IE_REQD_GAN_SERV = 71,
+ GA_IE_BCAST_CONTAINER = 72,
+ GA_IE_3G_CELL_ID = 73,
+ GA_IE_MS_RADIO_ID = 96,
+ GA_IE_DEF_GANC_IP = 97,
+ GA_IE_DEF_GANC_FQDN = 98,
+ GA_IE_GPRS_IP_ADDR = 99,
+ GA_IE_GPRS_UDP_PORT = 100
+ GA_IE_GANC_TCP_PORT = 103,
+ GA_IE_RTP_UDP_PORT = 104,
+ GA_IE_RTCP_UDP_PORT = 105,
+ GA_IE_GERAN_RCV_SIGL_LIST = 106,
+ GA_IE_UTRAN_RCV_SIGL_LIST = 107,
+};
+
+/* 11.1.1 GA-RC and GA-CSR Message header IE */
+struct gan_rc_csr_hdr {
+ uint16_t len;
+ uint8_t pdisc:4,
+ skip_ind:4;
+ uint8_t msg_type;
+} __attribute__((packed));
+
+#endif /* PROTO_GSM_44_318_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h
new file mode 100644
index 00000000..5d98a21f
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h
@@ -0,0 +1,94 @@
+#ifndef _OSMO_PROTO_IPACCESS_H
+#define _OSMO_PROTO_IPACCESS_H
+
+#include <stdint.h>
+
+#define IPA_TCP_PORT_OML 3002
+#define IPA_TCP_PORT_RSL 3003
+
+struct ipaccess_head {
+ uint16_t len; /* network byte order */
+ uint8_t proto;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct ipaccess_head_ext {
+ uint8_t proto;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+enum ipaccess_proto {
+ IPAC_PROTO_RSL = 0x00,
+ IPAC_PROTO_IPACCESS = 0xfe,
+ IPAC_PROTO_SCCP = 0xfd,
+ IPAC_PROTO_OML = 0xff,
+
+
+ /* OpenBSC extensions */
+ IPAC_PROTO_OSMO = 0xee,
+ IPAC_PROTO_MGCP_OLD = 0xfc,
+};
+
+enum ipaccess_proto_ext {
+ IPAC_PROTO_EXT_CTRL = 0x00,
+ IPAC_PROTO_EXT_MGCP = 0x01,
+ IPAC_PROTO_EXT_LAC = 0x02,
+ IPAC_PROTO_EXT_SMSC = 0x03,
+};
+
+enum ipaccess_msgtype {
+ IPAC_MSGT_PING = 0x00,
+ IPAC_MSGT_PONG = 0x01,
+ IPAC_MSGT_ID_GET = 0x04,
+ IPAC_MSGT_ID_RESP = 0x05,
+ IPAC_MSGT_ID_ACK = 0x06,
+
+ /* OpenBSC extension */
+ IPAC_MSGT_SCCP_OLD = 0xff,
+};
+
+enum ipaccess_id_tags {
+ IPAC_IDTAG_SERNR = 0x00,
+ IPAC_IDTAG_UNITNAME = 0x01,
+ IPAC_IDTAG_LOCATION1 = 0x02,
+ IPAC_IDTAG_LOCATION2 = 0x03,
+ IPAC_IDTAG_EQUIPVERS = 0x04,
+ IPAC_IDTAG_SWVERSION = 0x05,
+ IPAC_IDTAG_IPADDR = 0x06,
+ IPAC_IDTAG_MACADDR = 0x07,
+ IPAC_IDTAG_UNIT = 0x08,
+};
+
+/*
+ * Firmware specific header
+ */
+struct sdp_firmware {
+ char magic[4];
+ char more_magic[2];
+ uint16_t more_more_magic;
+ uint32_t header_length;
+ uint32_t file_length;
+ char sw_part[20];
+ char text1[64];
+ char time[12];
+ char date[14];
+ char text2[10];
+ char version[20];
+ uint16_t table_offset;
+ /* stuff i don't know */
+} __attribute__((packed));
+
+struct sdp_header_entry {
+ uint16_t something1;
+ char text1[64];
+ char time[12];
+ char date[14];
+ char text2[10];
+ char version[20];
+ uint32_t length;
+ uint32_t addr1;
+ uint32_t addr2;
+ uint32_t start;
+} __attribute__((packed));
+
+#endif /* _OSMO_PROTO_IPACCESS_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/rsl.h b/src/shared/libosmocore/include/osmocom/gsm/rsl.h
new file mode 100644
index 00000000..712d17b1
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/rsl.h
@@ -0,0 +1,55 @@
+#ifndef _OSMOCORE_RSL_H
+#define _OSMOCORE_RSL_H
+
+#include <stdint.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+/*! \defgroup rsl RSL
+ * @{
+ */
+
+/*! \file rsl.h */
+
+void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type);
+
+void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type);
+
+extern const struct tlv_definition rsl_att_tlvdef;
+
+/*! \brief Parse RSL TLV structure using \ref tlv_parse */
+#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);
+/* Turns channel number into a string */
+const char *rsl_chan_nr_str(uint8_t chan_nr);
+
+
+const char *rsl_err_name(uint8_t err);
+const char *rsl_rlm_cause_name(uint8_t err);
+const char *rsl_msg_name(uint8_t err);
+const char *rsl_ipac_msg_name(uint8_t msg_type);
+
+/* 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 */
+void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
+ uint8_t link_id, int transparent);
+
+/* 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/src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h b/src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h
new file mode 100644
index 00000000..415509dc
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/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/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h b/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h
new file mode 100644
index 00000000..06feb1de
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h
@@ -0,0 +1,43 @@
+#ifndef _OSMO_GSM_SYSINFO_H
+#define _OSMO_GSM_SYSINFO_H
+
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+enum osmo_sysinfo_type {
+ SYSINFO_TYPE_NONE,
+ SYSINFO_TYPE_1,
+ SYSINFO_TYPE_2,
+ SYSINFO_TYPE_3,
+ SYSINFO_TYPE_4,
+ SYSINFO_TYPE_5,
+ SYSINFO_TYPE_6,
+ SYSINFO_TYPE_7,
+ SYSINFO_TYPE_8,
+ SYSINFO_TYPE_9,
+ SYSINFO_TYPE_10,
+ SYSINFO_TYPE_13,
+ SYSINFO_TYPE_16,
+ SYSINFO_TYPE_17,
+ SYSINFO_TYPE_18,
+ SYSINFO_TYPE_19,
+ SYSINFO_TYPE_20,
+ SYSINFO_TYPE_2bis,
+ SYSINFO_TYPE_2ter,
+ SYSINFO_TYPE_2quater,
+ SYSINFO_TYPE_5bis,
+ SYSINFO_TYPE_5ter,
+ SYSINFO_TYPE_EMO,
+ SYSINFO_TYPE_MEAS_INFO,
+ /* FIXME all the various bis and ter */
+ _MAX_SYSINFO_TYPE
+};
+
+typedef uint8_t sysinfo_buf_t[GSM_MACBLOCK_LEN];
+
+extern const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE];
+
+uint8_t osmo_sitype2rsl(enum osmo_sysinfo_type si_type);
+enum osmo_sysinfo_type osmo_rsl2sitype(uint8_t rsl_si);
+
+#endif /* _OSMO_GSM_SYSINFO_H */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/tlv.h b/src/shared/libosmocore/include/osmocom/gsm/tlv.h
new file mode 100644
index 00000000..d2936d6d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/gsm/tlv.h
@@ -0,0 +1,319 @@
+#ifndef _TLV_H
+#define _TLV_H
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/msgb.h>
+
+/*! \defgroup tlv GSM L3 compatible TLV parser
+ * @{
+ */
+/*! \file tlv.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
+
+*/
+
+/*! \brief gross length of a LV type field */
+#define LV_GROSS_LEN(x) (x+1)
+/*! \brief gross length of a TLV type field */
+#define TLV_GROSS_LEN(x) (x+2)
+/*! \brief gross length of a TLV16 type field */
+#define TLV16_GROSS_LEN(x) ((2*x)+2)
+/*! \brief gross length of a TL16V type field */
+#define TL16V_GROSS_LEN(x) (x+3)
+/*! \brief gross length of a L16TV type field */
+#define L16TV_GROSS_LEN(x) (x+3)
+
+/*! \brief maximum length of TLV of one byte length */
+#define TVLV_MAX_ONEBYTE 0x7f
+
+/*! \brief gross length of a TVLV type field */
+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 */
+
+/*! \brief put (append) a LV field */
+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;
+}
+
+/*! \brief put (append) a TLV field */
+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;
+}
+
+/*! \brief put (append) a TLV16 field */
+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;
+}
+
+/*! \brief put (append) a TL16V field */
+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;
+}
+
+/*! \brief put (append) a TvLV field */
+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;
+}
+
+/*! \brief put (append) a TLV16 field to \ref msgb */
+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);
+}
+
+/*! \brief put (append) a TL16V field to \ref msgb */
+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);
+}
+
+/*! \brief put (append) a TvLV field to \ref msgb */
+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);
+}
+
+/*! \brief put (append) a L16TV field to \ref msgb */
+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;
+}
+
+/*! \brief put (append) a V field */
+static inline uint8_t *v_put(uint8_t *buf, uint8_t val)
+{
+ *buf++ = val;
+ return buf;
+}
+
+/*! \brief put (append) a TV field */
+static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag,
+ uint8_t val)
+{
+ *buf++ = tag;
+ *buf++ = val;
+ return buf;
+}
+
+/*! \brief put (append) a TVfixed field */
+static inline uint8_t *tv_fixed_put(uint8_t *buf, uint8_t tag,
+ unsigned int len, const uint8_t *val)
+{
+ *buf++ = tag;
+ memcpy(buf, val, len);
+ return buf + len;
+}
+
+/*! \brief put (append) a TV16 field
+ * \param[in,out] buf data buffer
+ * \param[in] tag Tag value
+ * \param[in] val Value (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;
+}
+
+/*! \brief put (append) a LV field to a \ref msgb
+ * \returns pointer to first byte after newly-put information */
+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);
+}
+
+/*! \brief put (append) a TLV field to a \ref msgb
+ * \returns pointer to first byte after newly-put information */
+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);
+}
+
+/*! \brief put (append) a TV field to a \ref msgb
+ * \returns pointer to first byte after newly-put information */
+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);
+}
+
+/*! \brief put (append) a TVfixed field to a \ref msgb
+ * \returns pointer to first byte after newly-put information */
+static inline uint8_t *msgb_tv_fixed_put(struct msgb *msg, uint8_t tag,
+ unsigned int len, const uint8_t *val)
+{
+ uint8_t *buf = msgb_put(msg, 1+len);
+ return tv_fixed_put(buf, tag, len, val);
+}
+
+/*! \brief put (append) a V field to a \ref msgb
+ * \returns pointer to first byte after newly-put information */
+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);
+}
+
+/*! \brief put (append) a TV16 field to a \ref msgb
+ * \returns pointer to first byte after newly-put information */
+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);
+}
+
+/*! \brief push (prepend) a TLV field to a \ref msgb
+ * \returns pointer to first byte of newly-pushed information */
+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));
+ tlv_put(buf, tag, len, val);
+ return buf;
+}
+
+/*! \brief push (prepend) a TV field to a \ref msgb
+ * \returns pointer to first byte of newly-pushed information */
+static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val)
+{
+ uint8_t *buf = msgb_push(msg, 2);
+ tv_put(buf, tag, val);
+ return buf;
+}
+
+/*! \brief push (prepend) a TV16 field to a \ref msgb
+ * \returns pointer to first byte of newly-pushed information */
+static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val)
+{
+ uint8_t *buf = msgb_push(msg, 3);
+ tv16_put(buf, tag, val);
+ return buf;
+}
+
+/*! \brief push (prepend) a TvLV field to a \ref msgb
+ * \returns pointer to first byte of newly-pushed information */
+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));
+ tvlv_put(buf, tag, len, val);
+ return buf;
+}
+
+/* TLV parsing */
+
+/*! \brief Entry in a TLV parser array */
+struct tlv_p_entry {
+ uint16_t len; /*!< \brief length */
+ const uint8_t *val; /*!< \brief pointer to value */
+};
+
+/*! \brief TLV type */
+enum tlv_type {
+ TLV_TYPE_NONE, /*!< \brief no type */
+ TLV_TYPE_FIXED, /*!< \brief fixed-length value-only */
+ TLV_TYPE_T, /*!< \brief tag-only */
+ TLV_TYPE_TV, /*!< \brief tag-value (8bit) */
+ TLV_TYPE_TLV, /*!< \brief tag-length-value */
+ TLV_TYPE_TL16V, /*!< \brief tag, 16 bit length, value */
+ TLV_TYPE_TvLV, /*!< \brief tag, variable length, value */
+ TLV_TYPE_SINGLE_TV /*!< \brief tag and value (both 4 bit) in 1 byte */
+};
+
+/*! \brief Definition of a single IE (Information Element) */
+struct tlv_def {
+ enum tlv_type type; /*!< \brief TLV type */
+ uint8_t fixed_len; /*!< \brief length in case of \ref TLV_TYPE_FIXED */
+};
+
+/*! \brief Definition of All 256 IE / TLV */
+struct tlv_definition {
+ struct tlv_def def[256];
+};
+
+/*! \brief result of the TLV parser */
+struct tlv_parsed {
+ struct tlv_p_entry lv[256];
+};
+
+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/src/shared/libosmocore/include/osmocom/vty/Makefile.am b/src/shared/libosmocore/include/osmocom/vty/Makefile.am
new file mode 100644
index 00000000..83d00107
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/Makefile.am
@@ -0,0 +1,4 @@
+osmovty_HEADERS = buffer.h command.h vector.h vty.h \
+ telnet_interface.h logging.h misc.h
+
+osmovtydir = $(includedir)/osmocom/vty
diff --git a/src/shared/libosmocore/include/osmocom/vty/buffer.h b/src/shared/libosmocore/include/osmocom/vty/buffer.h
new file mode 100644
index 00000000..c9467a91
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/buffer.h
@@ -0,0 +1,102 @@
+/*
+ * Buffering to output and input.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_BUFFER_H
+#define _ZEBRA_BUFFER_H
+
+#include <sys/types.h>
+
+/* Create a new buffer. Memory will be allocated in chunks of the given
+ size. If the argument is 0, the library will supply a reasonable
+ default size suitable for buffering socket I/O. */
+struct buffer *buffer_new(void *ctx, size_t);
+
+/* Free all data in the buffer. */
+void buffer_reset(struct buffer *);
+
+/* This function first calls buffer_reset to release all buffered data.
+ Then it frees the struct buffer itself. */
+void buffer_free(struct buffer *);
+
+/* Add the given data to the end of the buffer. */
+extern void buffer_put(struct buffer *, const void *, size_t);
+/* Add a single character to the end of the buffer. */
+extern void buffer_putc(struct buffer *, u_char);
+/* Add a NUL-terminated string to the end of the buffer. */
+extern void buffer_putstr(struct buffer *, const char *);
+
+/* Combine all accumulated (and unflushed) data inside the buffer into a
+ single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note
+ that this function does not alter the state of the buffer, so the data
+ is still inside waiting to be flushed. */
+char *buffer_getstr(struct buffer *);
+
+/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */
+int buffer_empty(struct buffer *);
+
+typedef enum {
+ /* An I/O error occurred. The buffer should be destroyed and the
+ file descriptor should be closed. */
+ BUFFER_ERROR = -1,
+
+ /* The data was written successfully, and the buffer is now empty
+ (there is no pending data waiting to be flushed). */
+ BUFFER_EMPTY = 0,
+
+ /* There is pending data in the buffer waiting to be flushed. Please
+ try flushing the buffer when select indicates that the file descriptor
+ is writeable. */
+ BUFFER_PENDING = 1
+} buffer_status_t;
+
+/* Try to write this data to the file descriptor. Any data that cannot
+ be written immediately is added to the buffer queue. */
+extern buffer_status_t buffer_write(struct buffer *, int fd,
+ const void *, size_t);
+
+/* This function attempts to flush some (but perhaps not all) of
+ the queued data to the given file descriptor. */
+extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
+
+/* The following 2 functions (buffer_flush_all and buffer_flush_window)
+ are for use in lib/vty.c only. They should not be used elsewhere. */
+
+/* Call buffer_flush_available repeatedly until either all data has been
+ flushed, or an I/O error has been encountered, or the operation would
+ block. */
+extern buffer_status_t buffer_flush_all(struct buffer *, int fd);
+
+/* Attempt to write enough data to the given fd to fill a window of the
+ given width and height (and remove the data written from the buffer).
+
+ If !no_more, then a message saying " --More-- " is appended.
+ If erase is true, then first overwrite the previous " --More-- " message
+ with spaces.
+
+ Any write error (including EAGAIN or EINTR) will cause this function
+ to return -1 (because the logic for handling the erase and more features
+ is too complicated to retry the write later).
+*/
+extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width,
+ int height, int erase, int no_more);
+
+#endif /* _ZEBRA_BUFFER_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/command.h b/src/shared/libosmocore/include/osmocom/vty/command.h
new file mode 100644
index 00000000..79d0aecf
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/command.h
@@ -0,0 +1,370 @@
+/*
+ * Zebra configuration command interface routine
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_COMMAND_H
+#define _ZEBRA_COMMAND_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "vector.h"
+#include "vty.h"
+
+/*! \defgroup command VTY Command
+ * @{
+ */
+/*! \file command.h */
+
+/*! \brief Host configuration variable */
+struct host {
+ /*! \brief Host name of this router. */
+ char *name;
+
+ /*! \brief Password for vty interface. */
+ char *password;
+ char *password_encrypt;
+
+ /*! \brief Enable password */
+ char *enable;
+ char *enable_encrypt;
+
+ /*! \brief System wide terminal lines. */
+ int lines;
+
+ /*! \brief Log filename. */
+ char *logfile;
+
+ /*! \brief config file name of this host */
+ char *config;
+
+ /*! \brief Flags for services */
+ int advanced;
+ int encrypt;
+
+ /*! \brief Banner configuration. */
+ const char *motd;
+ char *motdfile;
+
+ /*! \brief VTY application information */
+ const struct vty_app_info *app_info;
+};
+
+/*! \brief There are some command levels which called from command node. */
+enum node_type {
+ AUTH_NODE, /*!< \brief Authentication mode of vty interface. */
+ VIEW_NODE, /*!< \brief View node. Default mode of vty interface. */
+ AUTH_ENABLE_NODE, /*!< \brief Authentication mode for change enable. */
+ ENABLE_NODE, /*!< \brief Enable node. */
+ CONFIG_NODE, /*!< \brief Config node. Default mode of config file. */
+ SERVICE_NODE, /*!< \brief Service node. */
+ DEBUG_NODE, /*!< \brief Debug node. */
+ CFG_LOG_NODE, /*!< \brief Configure the logging */
+
+ VTY_NODE, /*!< \brief Vty node. */
+
+ L_E1INP_NODE, /*!< \brief E1 line in libosmo-abis. */
+ L_IPA_NODE, /*!< \brief IPA proxying commands in libosmo-abis. */
+
+ _LAST_OSMOVTY_NODE
+};
+
+/*! \brief Node which has some commands and prompt string and
+ * configuration function pointer . */
+struct cmd_node {
+ /*! \brief Node index */
+ enum node_type node;
+
+ /*! \brief Prompt character at vty interface. */
+ const char *prompt;
+
+ /*! \brief Is this node's configuration goes to vtysh ? */
+ int vtysh;
+
+ /*! \brief Node's configuration write function */
+ int (*func) (struct vty *);
+
+ /*! \brief Vector of this node's command list. */
+ vector cmd_vector;
+};
+
+enum {
+ CMD_ATTR_DEPRECATED = 1,
+ CMD_ATTR_HIDDEN,
+};
+
+/*! \brief Structure of a command element */
+struct cmd_element {
+ const char *string; /*!< \brief Command specification by string. */
+ int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
+ const char *doc; /*!< \brief Documentation of this command. */
+ int daemon; /*!< \brief Daemon to which this command belong. */
+ vector strvec; /*!< \brief Pointing out each description vector. */
+ unsigned int cmdsize; /*!< \brief Command index count. */
+ char *config; /*!< \brief Configuration string */
+ vector subconfig; /*!< \brief Sub configuration string */
+ u_char attr; /*!< \brief Command attributes */
+};
+
+/*! \brief Command description structure. */
+struct desc {
+ const char *cmd; /*!< \brief Command string. */
+ const char *str; /*!< \brief Command's description. */
+};
+
+/*! \brief Return value of the commands. */
+#define CMD_SUCCESS 0
+#define CMD_WARNING 1
+#define CMD_ERR_NO_MATCH 2
+#define CMD_ERR_AMBIGUOUS 3
+#define CMD_ERR_INCOMPLETE 4
+#define CMD_ERR_EXEED_ARGC_MAX 5
+#define CMD_ERR_NOTHING_TODO 6
+#define CMD_COMPLETE_FULL_MATCH 7
+#define CMD_COMPLETE_MATCH 8
+#define CMD_COMPLETE_LIST_MATCH 9
+#define CMD_SUCCESS_DAEMON 10
+
+/* Argc max counts. */
+#define CMD_ARGC_MAX 256
+
+/* Turn off these macros when uisng cpp with extract.pl */
+#ifndef VTYSH_EXTRACT_PL
+
+/* helper defines for end-user DEFUN* macros */
+#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+ static struct cmd_element cmdname = \
+ { \
+ .string = cmdstr, \
+ .func = funcname, \
+ .doc = helpstr, \
+ .attr = attrs, \
+ .daemon = dnum, \
+ };
+
+/* global (non static) cmd_element */
+#define gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+ struct cmd_element cmdname = \
+ { \
+ .string = cmdstr, \
+ .func = funcname, \
+ .doc = helpstr, \
+ .attr = attrs, \
+ .daemon = dnum, \
+ };
+
+#define DEFUN_CMD_FUNC_DECL(funcname) \
+ static int funcname (struct cmd_element *, struct vty *, int, const char *[]); \
+
+#define DEFUN_CMD_FUNC_TEXT(funcname) \
+ static int funcname \
+ (struct cmd_element *self, struct vty *vty, int argc, const char *argv[])
+
+/*! \brief Macro for defining a VTY node and function
+ * \param[in] funcname Name of the function implementing the node
+ * \param[in] cmdname Name of the command node
+ * \param[in] cmdstr String with syntax of node
+ * \param[in] helpstr String with help message of node
+ */
+#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+/*! \brief Macro for defining a non-static (global) VTY node and function
+ * \param[in] funcname Name of the function implementing the node
+ * \param[in] cmdname Name of the command node
+ * \param[in] cmdstr String with syntax of node
+ * \param[in] helpstr String with help message of node
+ */
+#define gDEFUN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \
+
+/* DEFUN_NOSH for commands that vtysh should ignore */
+#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN(funcname, cmdname, cmdstr, helpstr)
+
+/* DEFSH for vtysh. */
+#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \
+
+/* DEFUN + DEFSH */
+#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+/* DEFUN + DEFSH with attributes */
+#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
+
+/* ALIAS macro which define existing command's alias. */
+#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+
+/* global (non static) cmd_element */
+#define gALIAS(funcname, cmdname, cmdstr, helpstr) \
+ gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+
+#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
+
+#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
+
+#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
+
+#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
+
+#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
+
+#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon)
+
+#endif /* VTYSH_EXTRACT_PL */
+
+/* Some macroes */
+#define CMD_OPTION(S) ((S[0]) == '[')
+#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<'))
+#define CMD_VARARG(S) ((S[0]) == '.')
+#define CMD_RANGE(S) ((S[0] == '<'))
+
+#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
+#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
+#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
+#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
+
+/* Common descriptions. */
+#define SHOW_STR "Show running system information\n"
+#define IP_STR "IP information\n"
+#define IPV6_STR "IPv6 information\n"
+#define NO_STR "Negate a command or set its defaults\n"
+#define CLEAR_STR "Reset functions\n"
+#define RIP_STR "RIP information\n"
+#define BGP_STR "BGP information\n"
+#define OSPF_STR "OSPF information\n"
+#define NEIGHBOR_STR "Specify neighbor router\n"
+#define DEBUG_STR "Debugging functions (see also 'undebug')\n"
+#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n"
+#define ROUTER_STR "Enable a routing process\n"
+#define AS_STR "AS number\n"
+#define MBGP_STR "MBGP information\n"
+#define MATCH_STR "Match values from routing table\n"
+#define SET_STR "Set values in destination routing protocol\n"
+#define OUT_STR "Filter outgoing routing updates\n"
+#define IN_STR "Filter incoming routing updates\n"
+#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n"
+#define OSPF6_NUMBER_STR "Specify by number\n"
+#define INTERFACE_STR "Interface infomation\n"
+#define IFNAME_STR "Interface name(e.g. ep0)\n"
+#define IP6_STR "IPv6 Information\n"
+#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n"
+#define OSPF6_ROUTER_STR "Enable a routing process\n"
+#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n"
+#define SECONDS_STR "<1-65535> Seconds\n"
+#define ROUTE_STR "Routing Table\n"
+#define PREFIX_LIST_STR "Build a prefix list\n"
+#define OSPF6_DUMP_TYPE_LIST \
+"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)"
+#define ISIS_STR "IS-IS information\n"
+#define AREA_TAG_STR "[area tag]\n"
+
+#define CONF_BACKUP_EXT ".sav"
+
+/* IPv4 only machine should not accept IPv6 address for peer's IP
+ address. So we replace VTY command string like below. */
+#ifdef HAVE_IPV6
+#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) "
+#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) "
+#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n"
+#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n"
+#else
+#define NEIGHBOR_CMD "neighbor A.B.C.D "
+#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D "
+#define NEIGHBOR_ADDR_STR "Neighbor address\n"
+#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) "
+#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n"
+#endif /* HAVE_IPV6 */
+
+/* Prototypes. */
+void install_node(struct cmd_node *, int (*)(struct vty *));
+void install_default(enum node_type);
+void install_element(enum node_type, struct cmd_element *);
+void install_element_ve(struct cmd_element *cmd);
+void sort_node(void);
+
+/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
+ string with a space between each element (allocated using
+ XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */
+char *argv_concat(const char **argv, int argc, int shift);
+
+vector cmd_make_strvec(const char *);
+void cmd_free_strvec(vector);
+vector cmd_describe_command();
+char **cmd_complete_command();
+const char *cmd_prompt(enum node_type);
+int config_from_file(struct vty *, FILE *);
+enum node_type node_parent(enum node_type);
+int cmd_execute_command(vector, struct vty *, struct cmd_element **, int);
+int cmd_execute_command_strict(vector, struct vty *, struct cmd_element **);
+void config_replace_string(struct cmd_element *, char *, ...);
+void cmd_init(int);
+
+/* Export typical functions. */
+extern struct cmd_element config_exit_cmd;
+extern struct cmd_element config_help_cmd;
+extern struct cmd_element config_list_cmd;
+char *host_config_file();
+void host_config_set(const char *);
+
+/* This is called from main when a daemon is invoked with -v or --version. */
+void print_version(int print_copyright);
+
+extern void *tall_vty_cmd_ctx;
+
+/*! }@ */
+#endif /* _ZEBRA_COMMAND_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/logging.h b/src/shared/libosmocore/include/osmocom/vty/logging.h
new file mode 100644
index 00000000..e0011bf9
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/logging.h
@@ -0,0 +1,12 @@
+#ifndef _VTY_LOGGING_H
+#define _VTY_LOGGING_H
+
+#define LOGGING_STR "Configure log message to this terminal\n"
+#define FILTER_STR "Filter log messages\n"
+
+struct log_info;
+void logging_vty_add_cmds(const struct log_info *cat);
+struct vty;
+struct log_target *osmo_log_vty2tgt(struct vty *vty);
+
+#endif /* _VTY_LOGGING_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/misc.h b/src/shared/libosmocore/include/osmocom/vty/misc.h
new file mode 100644
index 00000000..707f82fa
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/misc.h
@@ -0,0 +1,10 @@
+#ifndef OSMO_VTY_MISC_H
+#define OSMO_VTY_MISC_H
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/core/rate_ctr.h>
+
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
new file mode 100644
index 00000000..2de4f192
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
@@ -0,0 +1,55 @@
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (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.
+ *
+ */
+
+#ifndef TELNET_INTERFACE_H
+#define TELNET_INTERFACE_H
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+
+#include <osmocom/vty/vty.h>
+
+/*! \defgroup telnet_interface Telnet Interface
+ * @{
+ */
+
+/*! \file telnet_interface.h */
+
+/*! \brief A telnet connection */
+struct telnet_connection {
+ /*! \brief linked list header for internal management */
+ struct llist_head entry;
+ /*! \brief private data pointer passed through */
+ void *priv;
+ /*! \brief filedsecriptor (socket ) */
+ struct osmo_fd fd;
+ /*! \brief VTY instance associated with telnet connection */
+ struct vty *vty;
+ /*! \brief logging target associated with this telnet connection */
+ struct log_target *dbg;
+};
+
+int telnet_init(void *tall_ctx, void *priv, int port);
+
+void telnet_exit(void);
+
+/*! }@ */
+
+#endif /* TELNET_INTERFACE_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/vector.h b/src/shared/libosmocore/include/osmocom/vty/vector.h
new file mode 100644
index 00000000..22a184d6
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/vector.h
@@ -0,0 +1,64 @@
+/*
+ * Generic vector interface header.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VECTOR_H
+#define _ZEBRA_VECTOR_H
+
+/* struct for vector */
+struct _vector {
+ unsigned int active; /* number of active slots */
+ unsigned int alloced; /* number of allocated slot */
+ void **index; /* index to data */
+};
+typedef struct _vector *vector;
+
+#define VECTOR_MIN_SIZE 1
+
+/* (Sometimes) usefull macros. This macro convert index expression to
+ array expression. */
+/* Reference slot at given index, caller must ensure slot is active */
+#define vector_slot(V,I) ((V)->index[(I)])
+/* Number of active slots.
+ * Note that this differs from vector_count() as it the count returned
+ * will include any empty slots
+ */
+#define vector_active(V) ((V)->active)
+
+/* Prototypes. */
+vector vector_init(unsigned int size);
+void vector_ensure(vector v, unsigned int num);
+int vector_empty_slot(vector v);
+int vector_set(vector v, void *val);
+int vector_set_index(vector v, unsigned int i, void *val);
+void vector_unset(vector v, unsigned int i);
+unsigned int vector_count(vector v);
+void vector_only_wrapper_free(vector v);
+void vector_only_index_free(void *index);
+void vector_free(vector v);
+vector vector_copy(vector v);
+
+void *vector_lookup(vector, unsigned int);
+void *vector_lookup_ensure(vector, unsigned int);
+
+extern void *tall_vty_vec_ctx;
+
+#endif /* _ZEBRA_VECTOR_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/vty.h b/src/shared/libosmocore/include/osmocom/vty/vty.h
new file mode 100644
index 00000000..83506c5e
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/vty.h
@@ -0,0 +1,188 @@
+#ifndef _VTY_H
+#define _VTY_H
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/*! \defgroup vty VTY (Virtual TTY) interface
+ * @{
+ */
+/*! \file vty.h */
+
+/* GCC have printf type attribute check. */
+#ifdef __GNUC__
+#define VTY_PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define VTY_PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+/* Does the I/O error indicate that the operation should be retried later? */
+#define ERRNO_IO_RETRY(EN) \
+ (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
+
+/* Vty read buffer size. */
+#define VTY_READ_BUFSIZ 512
+
+#define VTY_BUFSIZ 512
+#define VTY_MAXHIST 20
+
+/*! \brief VTY events */
+enum event {
+ VTY_SERV,
+ VTY_READ,
+ VTY_WRITE,
+ VTY_CLOSED,
+ VTY_TIMEOUT_RESET,
+#ifdef VTYSH
+ VTYSH_SERV,
+ VTYSH_READ,
+ VTYSH_WRITE
+#endif /* VTYSH */
+};
+
+/*! Internal representation of a single VTY */
+struct vty {
+ /*! \brief underlying file (if any) */
+ FILE *file;
+
+ /*! \brief private data, specified by creator */
+ void *priv;
+
+ /*! \brief File descripter of this vty. */
+ int fd;
+
+ /*! \brief Is this vty connect to file or not */
+ enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type;
+
+ /*! \brief Node status of this vty */
+ int node;
+
+ /*! \brief Failure count */
+ int fail;
+
+ /*! \brief Output buffer. */
+ struct buffer *obuf;
+
+ /*! \brief Command input buffer */
+ char *buf;
+
+ /*! \brief Command cursor point */
+ int cp;
+
+ /*! \brief Command length */
+ int length;
+
+ /*! \brief Command max length. */
+ int max;
+
+ /*! \brief Histry of command */
+ char *hist[VTY_MAXHIST];
+
+ /*! \brief History lookup current point */
+ int hp;
+
+ /*! \brief History insert end point */
+ int hindex;
+
+ /*! \brief For current referencing point of interface, route-map,
+ access-list etc... */
+ void *index;
+
+ /*! \brief For multiple level index treatment such as key chain and key. */
+ void *index_sub;
+
+ /*! \brief For escape character. */
+ unsigned char escape;
+
+ /*! \brief Current vty status. */
+ enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status;
+
+ /*! \brief IAC handling
+ *
+ * IAC handling: was the last character received the IAC
+ * (interpret-as-command) escape character (and therefore the next
+ * character will be the command code)? Refer to Telnet RFC 854. */
+ unsigned char iac;
+
+ /*! \brief IAC SB (option subnegotiation) handling */
+ unsigned char iac_sb_in_progress;
+ /* At the moment, we care only about the NAWS (window size) negotiation,
+ * and that requires just a 5-character buffer (RFC 1073):
+ * <NAWS char> <16-bit width> <16-bit height> */
+#define TELNET_NAWS_SB_LEN 5
+ /*! \brief sub-negotiation buffer */
+ unsigned char sb_buf[TELNET_NAWS_SB_LEN];
+ /*! \brief How many subnegotiation characters have we received?
+ *
+ * We just drop those that do not fit in the buffer. */
+ size_t sb_len;
+
+ /*! \brief Window width */
+ int width;
+ /*! \brief Widnow height */
+ int height;
+
+ /*! \brief Configure lines. */
+ int lines;
+
+ int monitor;
+
+ /*! \brief In configure mode. */
+ int config;
+};
+
+/* Small macro to determine newline is newline only or linefeed needed. */
+#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n")
+
+static inline char *vty_newline(struct vty *vty)
+{
+ return VTY_NEWLINE;
+}
+
+/*! Information an application registers with the VTY */
+struct vty_app_info {
+ /*! \brief name of the application */
+ const char *name;
+ /*! \brief version string of the application */
+ const char *version;
+ /*! \brief copyright string of the application */
+ const char *copyright;
+ /*! \brief \ref talloc context */
+ void *tall_ctx;
+ /*! \brief call-back for returning to parent n ode */
+ enum node_type (*go_parent_cb)(struct vty *vty);
+ /*! \brief call-back to determine if node is config node */
+ int (*is_config_node)(struct vty *vty, int node);
+};
+
+/* Prototypes. */
+void vty_init(struct vty_app_info *app_info);
+int vty_read_config_file(const char *file_name, void *priv);
+void vty_init_vtysh (void);
+void vty_reset (void);
+struct vty *vty_new (void);
+struct vty *vty_create (int vty_sock, void *priv);
+int vty_out (struct vty *, const char *, ...) VTY_PRINTF_ATTRIBUTE(2, 3);
+int vty_out_newline(struct vty *);
+int vty_read(struct vty *vty);
+//void vty_time_print (struct vty *, int);
+void vty_close (struct vty *);
+char *vty_get_cwd (void);
+void vty_log (const char *level, const char *proto, const char *fmt, va_list);
+int vty_config_lock (struct vty *);
+int vty_config_unlock (struct vty *);
+int vty_shell (struct vty *);
+int vty_shell_serv (struct vty *);
+void vty_hello (struct vty *);
+void *vty_current_index(struct vty *);
+int vty_current_node(struct vty *vty);
+enum node_type vty_go_parent(struct vty *vty);
+
+extern void *tall_vty_ctx;
+
+extern struct cmd_element cfg_description_cmd;
+extern struct cmd_element cfg_no_description_cmd;
+
+/*! }@ */
+
+#endif
diff --git a/src/shared/libosmocore/libosmocodec.pc.in b/src/shared/libosmocore/libosmocodec.pc.in
new file mode 100644
index 00000000..3030230b
--- /dev/null
+++ b/src/shared/libosmocore/libosmocodec.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom Codec related utilities Library
+Description: C Utility Library
+Version: @VERSION@
+Libs: -L${libdir} -losmocodec
+Cflags: -I${includedir}/
+
diff --git a/src/shared/libosmocore/libosmocore.pc.in b/src/shared/libosmocore/libosmocore.pc.in
new file mode 100644
index 00000000..7c298693
--- /dev/null
+++ b/src/shared/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/src/shared/libosmocore/libosmogsm.pc.in b/src/shared/libosmocore/libosmogsm.pc.in
new file mode 100644
index 00000000..753bb3a1
--- /dev/null
+++ b/src/shared/libosmocore/libosmogsm.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom GSM Core Library
+Description: GSM Core Library
+Version: @VERSION@
+Libs: -L${libdir} -losmogsm
+Cflags: -I${includedir}/
+
diff --git a/src/shared/libosmocore/libosmovty.pc.in b/src/shared/libosmocore/libosmovty.pc.in
new file mode 100644
index 00000000..2cc0b5f8
--- /dev/null
+++ b/src/shared/libosmocore/libosmovty.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom VTY Interface Library
+Description: C Utility Library
+Version: @VERSION@
+Libs: -L${libdir} -losmovty
+Cflags: -I${includedir}/
+
diff --git a/src/shared/libosmocore/m4/DUMMY b/src/shared/libosmocore/m4/DUMMY
new file mode 100644
index 00000000..fda557ad
--- /dev/null
+++ b/src/shared/libosmocore/m4/DUMMY
@@ -0,0 +1 @@
+Dummply placeholder.
diff --git a/src/shared/libosmocore/src/Makefile.am b/src/shared/libosmocore/src/Makefile.am
new file mode 100644
index 00000000..079d0b4c
--- /dev/null
+++ b/src/shared/libosmocore/src/Makefile.am
@@ -0,0 +1,43 @@
+SUBDIRS=. vty codec gsm
+
+# 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=4:0:0
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -fPIC -Wall
+
+lib_LTLIBRARIES = libosmocore.la
+
+libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \
+ bitvec.c statistics.c \
+ write_queue.c utils.c socket.c \
+ logging.c logging_syslog.c rate_ctr.c \
+ gsmtap_util.c crc16.c panic.c backtrace.c \
+ conv.c application.c rbtree.c \
+ crc8gen.c crc16gen.c crc32gen.c crc64gen.c
+
+if ENABLE_PLUGIN
+libosmocore_la_SOURCES += plugin.c
+libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) $(LIBRARY_DL)
+else
+libosmocore_la_LDFLAGS = -version-info $(LIBVERSION)
+endif
+
+if ENABLE_TALLOC
+libosmocore_la_SOURCES += talloc.c
+else
+libosmocore_la_LIBADD = -ltalloc
+endif
+
+if ENABLE_MSGFILE
+libosmocore_la_SOURCES += msgfile.c
+endif
+
+if ENABLE_SERIAL
+libosmocore_la_SOURCES += serial.c
+endif
+
+crc%gen.c: crcXXgen.c.tpl
+ @echo " SED $< -> $@"
+ @sed -e's/XX/$*/g' $< > $@
diff --git a/src/shared/libosmocore/src/application.c b/src/shared/libosmocore/src/application.c
new file mode 100644
index 00000000..e0d989e5
--- /dev/null
+++ b/src/shared/libosmocore/src/application.c
@@ -0,0 +1,154 @@
+/* Utility functions to setup applications */
+/*
+ * (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2011 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.
+ *
+ */
+
+/*! \file application.c
+ * \brief Routines for helping with the osmocom application setup.
+ */
+
+/*! \mainpage libosmocore Documentation
+ * \section sec_intro Introduction
+ * This library is a collection of common code used in various
+ * sub-projects inside the Osmocom family of projects. It includes a
+ * logging framework, select() loop abstraction, timers with callbacks,
+ * bit vectors, bit packing/unpacking, convolutional decoding, GSMTAP, a
+ * generic plugin interface, statistics counters, memory allocator,
+ * socket abstraction, message buffers, etc.
+ * \n\n
+ * Please note that C language projects inside Osmocom are typically
+ * single-threaded event-loop state machine designs. As such,
+ * routines in libosmocore are not thread-safe. If you must use them in
+ * a multi-threaded context, you have to add your own locking.
+ *
+ * \section sec_copyright Copyright and License
+ * Copyright © 2008-2011 - Harald Welte, Holger Freyther and contributors\n
+ * All rights reserved. \n\n
+ * The source code of libosmocore is licensed 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.\n
+ * See <http://www.gnu.org/licenses/> or COPYING included in the source
+ * code package istelf.\n
+ * The information detailed here is provided AS IS with NO WARRANTY OF
+ * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * \n\n
+ *
+ * \section sec_contact Contact and Support
+ * Community-based support is available at the OpenBSC mailing list
+ * <http://lists.osmocom.org/mailman/listinfo/openbsc>\n
+ * Commercial support options available upon request from
+ * <http://sysmocom.de/>
+ */
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+struct log_target *osmo_stderr_target;
+
+/*! \brief Ignore \ref SIGPIPE, \ref SIGALRM, \ref SIGHUP and \ref SIGIO */
+void osmo_init_ignore_signals(void)
+{
+ /* Signals that by default would terminate */
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGIO, SIG_IGN);
+}
+
+/*! \brief Initialize the osmocom logging framework
+ * \param[in] log_info Array of available logging sub-systems
+ * \returns 0 on success, -1 in case of error
+ *
+ * This function initializes the osmocom logging systems. It also
+ * creates the default (stderr) logging target.
+ */
+int osmo_init_logging(const struct log_info *log_info)
+{
+ log_init(log_info, NULL);
+ osmo_stderr_target = log_target_create_stderr();
+ if (!osmo_stderr_target)
+ return -1;
+
+ log_add_target(osmo_stderr_target);
+ log_set_all_filter(osmo_stderr_target, 1);
+ return 0;
+}
+
+/*! \brief Turn the current process into a background daemon
+ *
+ * This function will fork the process, exit the parent and set umask,
+ * create a new session, close stdin/stdout/stderr and chdir to /tmp
+ */
+int osmo_daemonize(void)
+{
+ int rc;
+ pid_t pid, sid;
+
+ /* Check if parent PID == init, in which case we are already a daemon */
+ if (getppid() == 1)
+ return -EEXIST;
+
+ /* Fork from the parent process */
+ pid = fork();
+ if (pid < 0) {
+ /* some error happened */
+ return pid;
+ }
+
+ if (pid > 0) {
+ /* if we have received a positive PID, then we are the parent
+ * and can exit */
+ exit(0);
+ }
+
+ /* FIXME: do we really want this? */
+ umask(0);
+
+ /* Create a new session and set process group ID */
+ sid = setsid();
+ if (sid < 0)
+ return sid;
+
+ /* Change to the /tmp directory, which prevents the CWD from being locked
+ * and unable to remove it */
+ rc = chdir("/tmp");
+ if (rc < 0)
+ return rc;
+
+ /* Redirect stdio to /dev/null */
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
+ freopen("/dev/null", "r", stdin);
+ freopen("/dev/null", "w", stdout);
+ freopen("/dev/null", "w", stderr);
+#endif
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/backtrace.c b/src/shared/libosmocore/src/backtrace.c
new file mode 100644
index 00000000..023671c2
--- /dev/null
+++ b/src/shared/libosmocore/src/backtrace.c
@@ -0,0 +1,64 @@
+/*
+ * (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>
+ * (C) 2010 by Nico Golde <nico@ngolde.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.
+ *
+ */
+
+/*! \file backtrace.c
+ * \brief Routines realted to generating call back traces
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <osmocom/core/utils.h>
+#include "config.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+
+/*! \brief Generate and print a call back-trace
+ *
+ * This function will generate a function call back-trace of the
+ * current process and print it to stdout
+ */
+void osmo_generate_backtrace(void)
+{
+ 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);
+}
+#else
+void osmo_generate_backtrace(void)
+{
+}
+#endif
diff --git a/src/shared/libosmocore/src/bits.c b/src/shared/libosmocore/src/bits.c
new file mode 100644
index 00000000..9eb2d690
--- /dev/null
+++ b/src/shared/libosmocore/src/bits.c
@@ -0,0 +1,188 @@
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+
+/*! \addtogroup bits
+ * @{
+ */
+
+/*! \file bits.c
+ * \brief Osmocom bit level support code
+ */
+
+
+/*! \brief convert unpacked bits to packed bits, return length in bytes
+ * \param[out] out output buffer of packed bits
+ * \param[in] in input buffer of unpacked bits
+ * \param[in] num_bits number of bits
+ */
+int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits)
+{
+ unsigned int i;
+ uint8_t curbyte = 0;
+ pbit_t *outptr = out;
+
+ for (i = 0; i < num_bits; i++) {
+ uint8_t bitnum = 7 - (i % 8);
+
+ curbyte |= (in[i] << bitnum);
+
+ if(i % 8 == 7){
+ *outptr++ = curbyte;
+ curbyte = 0;
+ }
+ }
+ /* we have a non-modulo-8 bitcount */
+ if (i % 8)
+ *outptr++ = curbyte;
+
+ return outptr - out;
+}
+
+/*! \brief convert packed bits to unpacked bits, return length in bytes
+ * \param[out] out output buffer of unpacked bits
+ * \param[in] in input buffer of packed bits
+ * \param[in] num_bits number of bits
+ */
+int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits)
+{
+ unsigned int i;
+ ubit_t *cur = out;
+ ubit_t *limit = out + num_bits;
+
+ for (i = 0; i < (num_bits/8)+1; i++) {
+ pbit_t byte = in[i];
+ *cur++ = (byte >> 7) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 6) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 5) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 4) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 3) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 2) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 1) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 0) & 1;
+ if (cur >= limit)
+ break;
+ }
+ return cur - out;
+}
+
+/*! \brief convert unpacked bits to packed bits (extended options)
+ * \param[out] out output buffer of packed bits
+ * \param[in] out_ofs offset into output buffer
+ * \param[in] in input buffer of unpacked bits
+ * \param[in] in_ofs offset into input buffer
+ * \param[in] num_bits number of bits
+ * \param[in] lsb_mode Encode bits in LSB orde instead of MSB
+ * \returns length in bytes (max written offset of output buffer + 1)
+ */
+int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
+ const ubit_t *in, unsigned int in_ofs,
+ unsigned int num_bits, int lsb_mode)
+{
+ int i, op, bn;
+ for (i=0; i<num_bits; i++) {
+ op = out_ofs + i;
+ bn = lsb_mode ? (op&7) : (7-(op&7));
+ if (in[in_ofs+i])
+ out[op>>3] |= 1 << bn;
+ else
+ out[op>>3] &= ~(1 << bn);
+ }
+ return ((out_ofs + num_bits - 1) >> 3) + 1;
+}
+
+/*! \brief convert packed bits to unpacked bits (extended options)
+ * \param[out] out output buffer of unpacked bits
+ * \param[in] out_ofs offset into output buffer
+ * \param[in] in input buffer of packed bits
+ * \param[in] in_ofs offset into input buffer
+ * \param[in] num_bits number of bits
+ * \param[in] lsb_mode Encode bits in LSB orde instead of MSB
+ * \returns length in bytes (max written offset of output buffer + 1)
+ */
+int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
+ const pbit_t *in, unsigned int in_ofs,
+ unsigned int num_bits, int lsb_mode)
+{
+ int i, ip, bn;
+ for (i=0; i<num_bits; i++) {
+ ip = in_ofs + i;
+ bn = lsb_mode ? (ip&7) : (7-(ip&7));
+ out[out_ofs+i] = !!(in[ip>>3] & (1<<bn));
+ }
+ return out_ofs + num_bits;
+}
+
+/* generalized bit reversal function, Chapter 7 "Hackers Delight" */
+uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k)
+{
+ if (k & 1) x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
+ if (k & 2) x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
+ if (k & 4) x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
+ if (k & 8) x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
+ if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
+
+ return x;
+}
+
+/* generalized bit reversal function, Chapter 7 "Hackers Delight" */
+uint32_t osmo_revbytebits_32(uint32_t x)
+{
+ x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
+ x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
+ x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
+
+ return x;
+}
+
+uint32_t osmo_revbytebits_8(uint8_t x)
+{
+ x = (x & 0x55) << 1 | (x & 0xAA) >> 1;
+ x = (x & 0x33) << 2 | (x & 0xCC) >> 2;
+ x = (x & 0x0F) << 4 | (x & 0xF0) >> 4;
+
+ return x;
+}
+
+void osmo_revbytebits_buf(uint8_t *buf, int len)
+{
+ unsigned int i;
+ unsigned int unaligned_cnt;
+ int len_remain = len;
+
+ unaligned_cnt = ((unsigned long)buf & 3);
+ for (i = 0; i < unaligned_cnt; i++) {
+ buf[i] = osmo_revbytebits_8(buf[i]);
+ len_remain--;
+ if (len_remain <= 0)
+ return;
+ }
+
+ for (i = unaligned_cnt; i < len; i += 4) {
+ uint32_t *cur = (uint32_t *) (buf + i);
+ *cur = osmo_revbytebits_32(*cur);
+ len_remain -= 4;
+ }
+
+ for (i = len - len_remain; i < len; i++) {
+ buf[i] = osmo_revbytebits_8(buf[i]);
+ len_remain--;
+ }
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/bitvec.c b/src/shared/libosmocore/src/bitvec.c
new file mode 100644
index 00000000..8a086b81
--- /dev/null
+++ b/src/shared/libosmocore/src/bitvec.c
@@ -0,0 +1,264 @@
+/* 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.
+ *
+ */
+
+/*! \addtogroup bitvec
+ * @{
+ */
+
+/*! \file bitvec.c
+ * \brief Osmocom bit vector abstraction
+ */
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <osmocom/core/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;
+}
+
+/*! \brief check if the bit is 0 or 1 for a given position inside a bitvec
+ * \param[in] bv the bit vector on which to check
+ * \param[in] bitnr the bit number inside the bit vector to check
+ * \returns
+ */
+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;
+}
+
+/*! \brief check if the bit is L or H for a given position inside a bitvec
+ * \param[in] bv the bit vector on which to check
+ * \param[in] bitnr the bit number inside the bit vector to check
+ */
+enum bit_value bitvec_get_bit_pos_high(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(H, bitnum);
+
+ if ((bv->data[bytenum] & (1 << bitnum)) == bitval)
+ return H;
+
+ return L;
+}
+
+/*! \brief get the Nth set bit inside the bit vector
+ * \param[in] bv the bit vector to use
+ * \param[in] n the bit number to get
+ * \returns the bit number (offset) of the Nth set bit in \a bv
+ */
+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;
+}
+
+/*! \brief set a bit at given position in a bit vector
+ * \param[in] bv bit vector on which to operate
+ * \param[in] bitnum number of bit to be set
+ * \param[in] bit value to which the bit is to be set
+ */
+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;
+}
+
+/*! \brief set the next bit inside a bitvec
+ * \param[in] bv bit vector to be used
+ * \param[in] bit value of the bit to be set
+ */
+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;
+}
+
+/*! \brief get the next bit (low/high) inside a bitvec */
+int bitvec_get_bit_high(struct bitvec *bv)
+{
+ int rc;
+
+ rc = bitvec_get_bit_pos_high(bv, bv->cur_bit);
+ if (rc >= 0)
+ bv->cur_bit++;
+
+ return rc;
+}
+
+/*! \brief set multiple bits (based on array of bitvals) at current pos
+ * \param[in] bv bit vector
+ * \param[in] bits array of \ref bit_value
+ * \param[in] count number of bits to set
+ */
+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;
+}
+
+/*! \brief 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;
+}
+
+/*! \brief get multiple bits (based on numeric value) from current pos */
+int bitvec_get_uint(struct bitvec *bv, int num_bits)
+{
+ int i;
+ unsigned int ui = 0;
+
+ for (i = 0; i < num_bits; i++) {
+ int bit = bitvec_get_bit_pos(bv, bv->cur_bit);
+ if (bit < 0)
+ return bit;
+ if (bit)
+ ui |= (1 << (num_bits - i - 1));
+ bv->cur_bit++;
+ }
+
+ return ui;
+}
+
+/*! \brief 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;
+}
+
+/*! \brief find first bit set in bit vector */
+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;
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/codec/Makefile.am b/src/shared/libosmocore/src/codec/Makefile.am
new file mode 100644
index 00000000..5a54c42d
--- /dev/null
+++ b/src/shared/libosmocore/src/codec/Makefile.am
@@ -0,0 +1,11 @@
+# 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 = libosmocodec.la
+
+libosmocodec_la_SOURCES = gsm610.c gsm620.c gsm660.c gsm690.c
+libosmocodec_la_LDFALGS = -version-info $(LIBVERSION)
diff --git a/src/shared/libosmocore/src/codec/gsm610.c b/src/shared/libosmocore/src/codec/gsm610.c
new file mode 100644
index 00000000..09fbeb5a
--- /dev/null
+++ b/src/shared/libosmocore/src/codec/gsm610.c
@@ -0,0 +1,294 @@
+/* GSM 06.10 - GSM FR codec */
+
+/*
+ * (C) 2010 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.
+ *
+ */
+
+#include <stdint.h>
+
+/* GSM FR - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 2.
+ * It's also GSM 06.10 Table A.2.1a
+ *
+ * It converts between serial parameter output by the encoder and the
+ * order needed before channel encoding.
+ */
+uint16_t gsm610_bitorder[260] = {
+ 0, /* LARc0:5 */
+ 47, /* Xmaxc0:5 */
+ 103, /* Xmaxc1:5 */
+ 159, /* Xmaxc2:5 */
+ 215, /* Xmaxc3:5 */
+ 1, /* LARc0:4 */
+ 6, /* LARc1:5 */
+ 12, /* LARc2:4 */
+ 2, /* LARc0:3 */
+ 7, /* LARc1:4 */
+ 13, /* LARc2:3 */
+ 17, /* LARc3:4 */
+ 36, /* Nc0:6 */
+ 92, /* Nc1:6 */
+ 148, /* Nc2:6 */
+ 204, /* Nc3:6 */
+ 48, /* Xmaxc0:4 */
+ 104, /* Xmaxc1:4 */
+ 160, /* Xmaxc2:4 */
+ 216, /* Xmaxc3:4 */
+ 8, /* LARc1:3 */
+ 22, /* LARc4:3 */
+ 26, /* LARc5:3 */
+ 37, /* Nc0:5 */
+ 93, /* Nc1:5 */
+ 149, /* Nc2:5 */
+ 205, /* Nc3:5 */
+ 38, /* Nc0:4 */
+ 94, /* Nc1:4 */
+ 150, /* Nc2:4 */
+ 206, /* Nc3:4 */
+ 39, /* Nc0:3 */
+ 95, /* Nc1:3 */
+ 151, /* Nc2:3 */
+ 207, /* Nc3:3 */
+ 40, /* Nc0:2 */
+ 96, /* Nc1:2 */
+ 152, /* Nc2:2 */
+ 208, /* Nc3:2 */
+ 49, /* Xmaxc0:3 */
+ 105, /* Xmaxc1:3 */
+ 161, /* Xmaxc2:3 */
+ 217, /* Xmaxc3:3 */
+ 3, /* LARc0:2 */
+ 18, /* LARc3:3 */
+ 30, /* LARc6:2 */
+ 41, /* Nc0:1 */
+ 97, /* Nc1:1 */
+ 153, /* Nc2:1 */
+ 209, /* Nc3:1 */
+ 23, /* LARc4:2 */
+ 27, /* LARc5:2 */
+ 43, /* bc0:1 */
+ 99, /* bc1:1 */
+ 155, /* bc2:1 */
+ 211, /* bc3:1 */
+ 42, /* Nc0:0 */
+ 98, /* Nc1:0 */
+ 154, /* Nc2:0 */
+ 210, /* Nc3:0 */
+ 45, /* Mc0:1 */
+ 101, /* Mc1:1 */
+ 157, /* Mc2:1 */
+ 213, /* Mc3:1 */
+ 4, /* LARc0:1 */
+ 9, /* LARc1:2 */
+ 14, /* LARc2:2 */
+ 33, /* LARc7:2 */
+ 19, /* LARc3:2 */
+ 24, /* LARc4:1 */
+ 31, /* LARc6:1 */
+ 44, /* bc0:0 */
+ 100, /* bc1:0 */
+ 156, /* bc2:0 */
+ 212, /* bc3:0 */
+ 50, /* Xmaxc0:2 */
+ 106, /* Xmaxc1:2 */
+ 162, /* Xmaxc2:2 */
+ 218, /* Xmaxc3:2 */
+ 53, /* xmc0_0:2 */
+ 56, /* xmc0_1:2 */
+ 59, /* xmc0_2:2 */
+ 62, /* xmc0_3:2 */
+ 65, /* xmc0_4:2 */
+ 68, /* xmc0_5:2 */
+ 71, /* xmc0_6:2 */
+ 74, /* xmc0_7:2 */
+ 77, /* xmc0_8:2 */
+ 80, /* xmc0_9:2 */
+ 83, /* xmc0_10:2 */
+ 86, /* xmc0_11:2 */
+ 89, /* xmc0_12:2 */
+ 109, /* xmc1_0:2 */
+ 112, /* xmc1_1:2 */
+ 115, /* xmc1_2:2 */
+ 118, /* xmc1_3:2 */
+ 121, /* xmc1_4:2 */
+ 124, /* xmc1_5:2 */
+ 127, /* xmc1_6:2 */
+ 130, /* xmc1_7:2 */
+ 133, /* xmc1_8:2 */
+ 136, /* xmc1_9:2 */
+ 139, /* xmc1_10:2 */
+ 142, /* xmc1_11:2 */
+ 145, /* xmc1_12:2 */
+ 165, /* xmc2_0:2 */
+ 168, /* xmc2_1:2 */
+ 171, /* xmc2_2:2 */
+ 174, /* xmc2_3:2 */
+ 177, /* xmc2_4:2 */
+ 180, /* xmc2_5:2 */
+ 183, /* xmc2_6:2 */
+ 186, /* xmc2_7:2 */
+ 189, /* xmc2_8:2 */
+ 192, /* xmc2_9:2 */
+ 195, /* xmc2_10:2 */
+ 198, /* xmc2_11:2 */
+ 201, /* xmc2_12:2 */
+ 221, /* xmc3_0:2 */
+ 224, /* xmc3_1:2 */
+ 227, /* xmc3_2:2 */
+ 230, /* xmc3_3:2 */
+ 233, /* xmc3_4:2 */
+ 236, /* xmc3_5:2 */
+ 239, /* xmc3_6:2 */
+ 242, /* xmc3_7:2 */
+ 245, /* xmc3_8:2 */
+ 248, /* xmc3_9:2 */
+ 251, /* xmc3_10:2 */
+ 254, /* xmc3_11:2 */
+ 257, /* xmc3_12:2 */
+ 46, /* Mc0:0 */
+ 102, /* Mc1:0 */
+ 158, /* Mc2:0 */
+ 214, /* Mc3:0 */
+ 51, /* Xmaxc0:1 */
+ 107, /* Xmaxc1:1 */
+ 163, /* Xmaxc2:1 */
+ 219, /* Xmaxc3:1 */
+ 54, /* xmc0_0:1 */
+ 57, /* xmc0_1:1 */
+ 60, /* xmc0_2:1 */
+ 63, /* xmc0_3:1 */
+ 66, /* xmc0_4:1 */
+ 69, /* xmc0_5:1 */
+ 72, /* xmc0_6:1 */
+ 75, /* xmc0_7:1 */
+ 78, /* xmc0_8:1 */
+ 81, /* xmc0_9:1 */
+ 84, /* xmc0_10:1 */
+ 87, /* xmc0_11:1 */
+ 90, /* xmc0_12:1 */
+ 110, /* xmc1_0:1 */
+ 113, /* xmc1_1:1 */
+ 116, /* xmc1_2:1 */
+ 119, /* xmc1_3:1 */
+ 122, /* xmc1_4:1 */
+ 125, /* xmc1_5:1 */
+ 128, /* xmc1_6:1 */
+ 131, /* xmc1_7:1 */
+ 134, /* xmc1_8:1 */
+ 137, /* xmc1_9:1 */
+ 140, /* xmc1_10:1 */
+ 143, /* xmc1_11:1 */
+ 146, /* xmc1_12:1 */
+ 166, /* xmc2_0:1 */
+ 169, /* xmc2_1:1 */
+ 172, /* xmc2_2:1 */
+ 175, /* xmc2_3:1 */
+ 178, /* xmc2_4:1 */
+ 181, /* xmc2_5:1 */
+ 184, /* xmc2_6:1 */
+ 187, /* xmc2_7:1 */
+ 190, /* xmc2_8:1 */
+ 193, /* xmc2_9:1 */
+ 196, /* xmc2_10:1 */
+ 199, /* xmc2_11:1 */
+ 202, /* xmc2_12:1 */
+ 222, /* xmc3_0:1 */
+ 225, /* xmc3_1:1 */
+ 228, /* xmc3_2:1 */
+ 231, /* xmc3_3:1 */
+ 234, /* xmc3_4:1 */
+ 237, /* xmc3_5:1 */
+ 240, /* xmc3_6:1 */
+ 243, /* xmc3_7:1 */
+ 246, /* xmc3_8:1 */
+ 249, /* xmc3_9:1 */
+ 252, /* xmc3_10:1 */
+ 255, /* xmc3_11:1 */
+ 258, /* xmc3_12:1 */
+ 5, /* LARc0:0 */
+ 10, /* LARc1:1 */
+ 15, /* LARc2:1 */
+ 28, /* LARc5:1 */
+ 32, /* LARc6:0 */
+ 34, /* LARc7:1 */
+ 35, /* LARc7:0 */
+ 16, /* LARc2:0 */
+ 20, /* LARc3:1 */
+ 21, /* LARc3:0 */
+ 25, /* LARc4:0 */
+ 52, /* Xmaxc0:0 */
+ 108, /* Xmaxc1:0 */
+ 164, /* Xmaxc2:0 */
+ 220, /* Xmaxc3:0 */
+ 55, /* xmc0_0:0 */
+ 58, /* xmc0_1:0 */
+ 61, /* xmc0_2:0 */
+ 64, /* xmc0_3:0 */
+ 67, /* xmc0_4:0 */
+ 70, /* xmc0_5:0 */
+ 73, /* xmc0_6:0 */
+ 76, /* xmc0_7:0 */
+ 79, /* xmc0_8:0 */
+ 82, /* xmc0_9:0 */
+ 85, /* xmc0_10:0 */
+ 88, /* xmc0_11:0 */
+ 91, /* xmc0_12:0 */
+ 111, /* xmc1_0:0 */
+ 114, /* xmc1_1:0 */
+ 117, /* xmc1_2:0 */
+ 120, /* xmc1_3:0 */
+ 123, /* xmc1_4:0 */
+ 126, /* xmc1_5:0 */
+ 129, /* xmc1_6:0 */
+ 132, /* xmc1_7:0 */
+ 135, /* xmc1_8:0 */
+ 138, /* xmc1_9:0 */
+ 141, /* xmc1_10:0 */
+ 144, /* xmc1_11:0 */
+ 147, /* xmc1_12:0 */
+ 167, /* xmc2_0:0 */
+ 170, /* xmc2_1:0 */
+ 173, /* xmc2_2:0 */
+ 176, /* xmc2_3:0 */
+ 179, /* xmc2_4:0 */
+ 182, /* xmc2_5:0 */
+ 185, /* xmc2_6:0 */
+ 188, /* xmc2_7:0 */
+ 191, /* xmc2_8:0 */
+ 194, /* xmc2_9:0 */
+ 197, /* xmc2_10:0 */
+ 200, /* xmc2_11:0 */
+ 203, /* xmc2_12:0 */
+ 223, /* xmc3_0:0 */
+ 226, /* xmc3_1:0 */
+ 229, /* xmc3_2:0 */
+ 232, /* xmc3_3:0 */
+ 235, /* xmc3_4:0 */
+ 238, /* xmc3_5:0 */
+ 241, /* xmc3_6:0 */
+ 244, /* xmc3_7:0 */
+ 247, /* xmc3_8:0 */
+ 250, /* xmc3_9:0 */
+ 253, /* xmc3_10:0 */
+ 256, /* xmc3_11:0 */
+ 259, /* xmc3_12:0 */
+ 11, /* LARc1:0 */
+ 29, /* LARc5:0 */
+};
diff --git a/src/shared/libosmocore/src/codec/gsm620.c b/src/shared/libosmocore/src/codec/gsm620.c
new file mode 100644
index 00000000..09aca502
--- /dev/null
+++ b/src/shared/libosmocore/src/codec/gsm620.c
@@ -0,0 +1,262 @@
+/* GSM 06.20 - GSM HR codec */
+
+/*
+ * (C) 2010 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.
+ *
+ */
+
+#include <stdint.h>
+
+/* GSM HR unvoiced (mode=0) frames - subjective importance bit ordering */
+ /* This array encode mapping between GSM 05.03 Table 3a (bits
+ * ordering before channel coding on TCH) and GSM 06.20 Table B.1
+ * (bit ordering on A-bis */
+uint16_t gsm620_unvoiced_bitorder[112] = {
+ 3, /* R0:1 */
+ 25, /* LPC 3:7 */
+ 52, /* GSP 0-1:2 */
+ 71, /* GSP 0-2:2 */
+ 90, /* GSP 0-3:2 */
+ 109, /* GSP 0-4:2 */
+ 15, /* LPC 1:0 */
+ 23, /* LPC 2:1 */
+ 22, /* LPC 2:2 */
+ 21, /* LPC 2:3 */
+ 20, /* LPC 2:4 */
+ 19, /* LPC 2:5 */
+ 31, /* LPC 3:1 */
+ 30, /* LPC 3:2 */
+ 29, /* LPC 3:3 */
+ 28, /* LPC 3:4 */
+ 27, /* LPC 3:5 */
+ 26, /* LPC 3:6 */
+ 61, /* Code 1-2:0 */
+ 68, /* Code 2-2:0 */
+ 67, /* Code 2-2:1 */
+ 66, /* Code 2-2:2 */
+ 65, /* Code 2-2:3 */
+ 64, /* Code 2-2:4 */
+ 63, /* Code 2-2:5 */
+ 62, /* Code 2-2:6 */
+ 80, /* Code 1-3:0 */
+ 79, /* Code 1-3:1 */
+ 78, /* Code 1-3:2 */
+ 77, /* Code 1-3:3 */
+ 76, /* Code 1-3:4 */
+ 75, /* Code 1-3:5 */
+ 74, /* Code 1-3:6 */
+ 84, /* Code 2-3:3 */
+ 83, /* Code 2-3:4 */
+ 82, /* Code 2-3:5 */
+ 81, /* Code 2-3:6 */
+ 32, /* LPC 3:0 */
+ 4, /* R0:0 */
+ 33, /* INT-LPC:0 */
+ 60, /* Code 1-2:1 */
+ 59, /* Code 1-2:2 */
+ 58, /* Code 1-2:3 */
+ 57, /* Code 1-2:4 */
+ 56, /* Code 1-2:5 */
+ 55, /* Code 1-2:6 */
+ 49, /* Code 2-1:0 */
+ 48, /* Code 2-1:1 */
+ 47, /* Code 2-1:2 */
+ 46, /* Code 2-1:3 */
+ 45, /* Code 2-1:4 */
+ 44, /* Code 2-1:5 */
+ 43, /* Code 2-1:6 */
+ 42, /* Code 1-1:0 */
+ 41, /* Code 1-1:1 */
+ 40, /* Code 1-1:2 */
+ 39, /* Code 1-1:3 */
+ 38, /* Code 1-1:4 */
+ 37, /* Code 1-1:5 */
+ 36, /* Code 1-1:6 */
+ 111, /* GSP 0-4:0 */
+ 92, /* GSP 0-3:0 */
+ 73, /* GSP 0-2:0 */
+ 54, /* GSP 0-1:0 */
+ 24, /* LPC 2:0 */
+ 110, /* GSP 0-4:1 */
+ 91, /* GSP 0-3:1 */
+ 72, /* GSP 0-2:1 */
+ 53, /* GSP 0-1:1 */
+ 14, /* LPC 1:1 */
+ 13, /* LPC 1:2 */
+ 12, /* LPC 1:3 */
+ 11, /* LPC 1:4 */
+ 10, /* LPC 1:5 */
+ 108, /* GSP 0-4:3 */
+ 89, /* GSP 0-3:3 */
+ 70, /* GSP 0-2:3 */
+ 51, /* GSP 0-1:3 */
+ 18, /* LPC 2:6 */
+ 17, /* LPC 2:7 */
+ 16, /* LPC 2:8 */
+ 107, /* GSP 0-4:4 */
+ 88, /* GSP 0-3:4 */
+ 69, /* GSP 0-2:4 */
+ 50, /* GSP 0-1:4 */
+ 9, /* LPC 1:6 */
+ 8, /* LPC 1:7 */
+ 7, /* LPC 1:8 */
+ 6, /* LPC 1:9 */
+ 2, /* R0:2 */
+ 5, /* LPC 1:10 */
+ 1, /* R0:3 */
+ 0, /* R0:4 */
+ 35, /* Mode:0 */
+ 34, /* Mode:1 */
+ 106, /* Code 2-4:0 */
+ 105, /* Code 2-4:1 */
+ 104, /* Code 2-4:2 */
+ 103, /* Code 2-4:3 */
+ 102, /* Code 2-4:4 */
+ 101, /* Code 2-4:5 */
+ 100, /* Code 2-4:6 */
+ 99, /* Code 1-4:0 */
+ 98, /* Code 1-4:1 */
+ 97, /* Code 1-4:2 */
+ 96, /* Code 1-4:3 */
+ 95, /* Code 1-4:4 */
+ 94, /* Code 1-4:5 */
+ 93, /* Code 1-4:6 */
+ 87, /* Code 2-3:0 */
+ 86, /* Code 2-3:1 */
+ 85, /* Code 2-3:2 */
+};
+
+/* GSM HR voiced (mode=1,2,3) frames - subjective importance bit ordering */
+ /* This array encode mapping between GSM 05.03 Table 3b (bits
+ * ordering before channel coding on TCH) and GSM 06.20 Table B.2
+ * (bit ordering on A-bis */
+uint16_t gsm620_voiced_bitorder[112] = {
+ 13, /* LPC 1:2 */
+ 14, /* LPC 1:1 */
+ 20, /* LPC 2:4 */
+ 19, /* LPC 2:5 */
+ 18, /* LPC 2:6 */
+ 53, /* GSP 0-1:4 */
+ 71, /* GSP 0-2:4 */
+ 89, /* GSP 0-3:4 */
+ 107, /* GSP 0-4:4 */
+ 54, /* GSP 0-1:3 */
+ 72, /* GSP 0-2:3 */
+ 90, /* GSP 0-3:3 */
+ 108, /* GSP 0-4:3 */
+ 55, /* GSP 0-1:2 */
+ 73, /* GSP 0-2:2 */
+ 91, /* GSP 0-3:2 */
+ 109, /* GSP 0-4:2 */
+ 52, /* Code 1:0 */
+ 51, /* Code 1:1 */
+ 50, /* Code 1:2 */
+ 49, /* Code 1:3 */
+ 48, /* Code 1:4 */
+ 47, /* Code 1:5 */
+ 46, /* Code 1:6 */
+ 45, /* Code 1:7 */
+ 44, /* Code 1:8 */
+ 65, /* Code 2:5 */
+ 64, /* Code 2:6 */
+ 63, /* Code 2:7 */
+ 62, /* Code 2:8 */
+ 70, /* Code 2:0 */
+ 69, /* Code 2:1 */
+ 68, /* Code 2:2 */
+ 80, /* Code 3:8 */
+ 66, /* Code 2:4 */
+ 67, /* Code 2:3 */
+ 56, /* GSP 0-1:1 */
+ 74, /* GSP 0-2:1 */
+ 92, /* GSP 0-3:1 */
+ 110, /* GSP 0-4:1 */
+ 57, /* GSP 0-1:0 */
+ 75, /* GSP 0-2:0 */
+ 93, /* GSP 0-3:0 */
+ 111, /* GSP 0-4:0 */
+ 33, /* INT-LPC:0 */
+ 24, /* LPC 2:0 */
+ 32, /* LPC 3:0 */
+ 97, /* LAG 4:0 */
+ 31, /* LPC 3:1 */
+ 23, /* LPC 2:1 */
+ 96, /* LAG 4:1 */
+ 79, /* LAG 3:0 */
+ 61, /* LAG 2:0 */
+ 43, /* LAG 1:0 */
+ 95, /* LAG 4:2 */
+ 78, /* LAG 3:1 */
+ 60, /* LAG 2:1 */
+ 42, /* LAG 1:1 */
+ 30, /* LPC 3:2 */
+ 29, /* LPC 3:3 */
+ 28, /* LPC 3:4 */
+ 22, /* LPC 2:2 */
+ 27, /* LPC 3:5 */
+ 26, /* LPC 3:6 */
+ 21, /* LPC 2:3 */
+ 4, /* R0:0 */
+ 25, /* LPC 3:7 */
+ 15, /* LPC 1:0 */
+ 94, /* LAG 4:3 */
+ 77, /* LAG 3:2 */
+ 59, /* LAG 2:2 */
+ 41, /* LAG 1:2 */
+ 3, /* R0:1 */
+ 76, /* LAG 3:3 */
+ 58, /* LAG 2:3 */
+ 40, /* LAG 1:3 */
+ 39, /* LAG 1:4 */
+ 17, /* LPC 2:7 */
+ 16, /* LPC 2:8 */
+ 12, /* LPC 1:3 */
+ 11, /* LPC 1:4 */
+ 10, /* LPC 1:5 */
+ 9, /* LPC 1:6 */
+ 2, /* R0:2 */
+ 38, /* LAG 1:5 */
+ 37, /* LAG 1:6 */
+ 36, /* LAG 1:7 */
+ 8, /* LPC 1:7 */
+ 7, /* LPC 1:8 */
+ 6, /* LPC 1:9 */
+ 5, /* LPC 1:10 */
+ 1, /* R0:3 */
+ 0, /* R0:4 */
+ 35, /* Mode:0 */
+ 34, /* Mode:1 */
+ 106, /* Code 4:0 */
+ 105, /* Code 4:1 */
+ 104, /* Code 4:2 */
+ 103, /* Code 4:3 */
+ 102, /* Code 4:4 */
+ 101, /* Code 4:5 */
+ 100, /* Code 4:6 */
+ 99, /* Code 4:7 */
+ 98, /* Code 4:8 */
+ 88, /* Code 3:0 */
+ 87, /* Code 3:1 */
+ 86, /* Code 3:2 */
+ 85, /* Code 3:3 */
+ 84, /* Code 3:4 */
+ 83, /* Code 3:5 */
+ 82, /* Code 3:6 */
+ 81, /* Code 3:7 */
+};
diff --git a/src/shared/libosmocore/src/codec/gsm660.c b/src/shared/libosmocore/src/codec/gsm660.c
new file mode 100644
index 00000000..4fff5ffc
--- /dev/null
+++ b/src/shared/libosmocore/src/codec/gsm660.c
@@ -0,0 +1,256 @@
+/* GSM 06.60 - GSM EFR Codec */
+
+/*
+ * (C) 2010 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.
+ *
+ */
+
+#include <stdint.h>
+
+/* GSM EFR - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 6.
+ *
+ * It converts between serial parameter output (as described in
+ * GSM 06.60 Table 6 and GSM 05.03 Table 5) and the order needed
+ * before channel encoding. CRC poly and bit repetition must be
+ * applied prior to this table, as in GSM 05.03 3.1.1, to get 260
+ * bits from a 244 bits raw EFR frame.
+ */
+uint16_t gsm660_bitorder[260] = {
+ 38, 39, 40, 41, 42, 43, /* 0 -> LTP-LAG 1: b8..b3 */
+ 145, 146, 147, 148, 149, 150, /* 6 -> LTP-LAG 3: b8..b3 */
+ 93, 94, /* 12 -> LTP-LAG 2: b5..b4 */
+ 200, 201, /* 14 -> LTP-LAG 4: b5..b4 */
+ 47, /* 16 -> LTP-GAIN 1: b3 */
+ 88, /* 17 -> FCB-GAIN 1: b4 */
+ 99, /* 18 -> LTP-GAIN 2: b3 */
+ 140, /* 19 -> FCB-GAIN 2: b4 */
+ 44, /* 20 -> LTP-LAG 1: b2 */
+ 151, /* 21 -> LTP-LAG 3: b2 */
+ 95, /* 22 -> LTP-LAG 2: b3 */
+ 202, /* 23 -> LTP-LAG 4: b3 */
+ 1, 2, /* 24 -> LPC 1: b5..b4 */
+ 7, /* 26 -> LPC 2: b7 */
+ 9, /* 27 -> LPC 2: b5 */
+ 17, 18, /* 28 -> LPC 3: b6..b5 */
+ 23, /* 30 -> LPC 3: b0 */
+ 45, 46, /* 31 -> LTP-LAG 1: b1..b0 */
+ 152, 153, /* 33 -> LTP-LAG 3: b1..b0 */
+ 96, /* 35 -> LTP-LAG 2: b2 */
+ 203, /* 36 -> LTP-LAG 4: b2 */
+ 3, 4, /* 37 -> LPC 1: b3..b2 */
+ 10, 11, /* 39 -> LPC 2: b4..b3 */
+ 15, /* 41 -> LPC 3: b8 */
+ 8, /* 42 -> LPC 2: b6 */
+ 5, 6, /* 43 -> LPC 1: b1..b0 */
+ 12, /* 45 -> LPC 2: b2 */
+ 16, /* 46 -> LPC 3: b7 */
+ 19, /* 47 -> LPC 3: b4 */
+ 97, /* 48 -> LTP-LAG 2: b1 */
+ 204, /* 49 -> LTP-LAG 4: b1 */
+ 0, /* 50 -> LPC 1: b6 */
+ 13, 14, /* 51 -> LPC 2: b1..b0 */
+ 20, /* 53 -> LPC 3: b3 */
+ 24, 25, /* 54 -> LPC 4: b7..b6 */
+ 27, /* 56 -> LPC 4: b4 */
+ 154, /* 57 -> LTP-GAIN 3: b3 */
+ 206, /* 58 -> LTP-GAIN 4: b3 */
+ 195, /* 59 -> FCB-GAIN 3: b4 */
+ 247, /* 60 -> FCB-GAIN 4: b4 */
+ 89, /* 61 -> FCB-GAIN 1: b3 */
+ 141, /* 62 -> FCB-GAIN 2: b3 */
+ 196, /* 63 -> FCB-GAIN 3: b3 */
+ 248, /* 64 -> FCB-GAIN 4: b3 */
+ 252, 253, 254, 255, 256, 257, 258, 259, /* 65 -> CRC-POLY: b7..b0 */
+ 48, /* 73 -> LTP-GAIN 1: b2 */
+ 100, /* 74 -> LTP-GAIN 2: b2 */
+ 155, /* 75 -> LTP-GAIN 3: b2 */
+ 207, /* 76 -> LTP-GAIN 4: b2 */
+ 21, 22, /* 77 -> LPC 3: b2..b1 */
+ 26, /* 79 -> LPC 4: b5 */
+ 28, /* 80 -> LPC 4: b3 */
+ 51, /* 81 -> PULSE 1_1: b3 */
+ 55, /* 82 -> PULSE 1_2: b3 */
+ 59, /* 83 -> PULSE 1_3: b3 */
+ 63, /* 84 -> PULSE 1_4: b3 */
+ 67, /* 85 -> PULSE 1_5: b3 */
+ 103, /* 86 -> PULSE 2_1: b3 */
+ 107, /* 87 -> PULSE 2_2: b3 */
+ 111, /* 88 -> PULSE 2_3: b3 */
+ 115, /* 89 -> PULSE 2_4: b3 */
+ 119, /* 90 -> PULSE 2_5: b3 */
+ 158, /* 91 -> PULSE 3_1: b3 */
+ 162, /* 92 -> PULSE 3_2: b3 */
+ 166, /* 93 -> PULSE 3_3: b3 */
+ 170, /* 94 -> PULSE 3_4: b3 */
+ 174, /* 95 -> PULSE 3_5: b3 */
+ 210, /* 96 -> PULSE 4_1: b3 */
+ 214, /* 97 -> PULSE 4_2: b3 */
+ 218, /* 98 -> PULSE 4_3: b3 */
+ 222, /* 99 -> PULSE 4_4: b3 */
+ 226, /* 100 -> PULSE 4_5: b3 */
+ 90, /* 101 -> FCB-GAIN 1: b2 */
+ 142, /* 102 -> FCB-GAIN 2: b2 */
+ 197, /* 103 -> FCB-GAIN 3: b2 */
+ 249, /* 104 -> FCB-GAIN 4: b2 */
+ 49, /* 105 -> LTP-GAIN 1: b1 */
+ 101, /* 106 -> LTP-GAIN 2: b1 */
+ 156, /* 107 -> LTP-GAIN 3: b1 */
+ 208, /* 108 -> LTP-GAIN 4: b1 */
+ 29, 30, 31, /* 109 -> LPC 4: b2..b0 */
+ 32, 33, 34, 35, /* 112 -> LPC 5: b5..b2 */
+ 98, /* 116 -> LTP-LAG 2: b0 */
+ 205, /* 117 -> LTP-LAG 4: b0 */
+ 52, /* 118 -> PULSE 1_1: b2 */
+ 56, /* 119 -> PULSE 1_2: b2 */
+ 60, /* 120 -> PULSE 1_3: b2 */
+ 64, /* 121 -> PULSE 1_4: b2 */
+ 68, /* 122 -> PULSE 1_5: b2 */
+ 104, /* 123 -> PULSE 2_1: b2 */
+ 108, /* 124 -> PULSE 2_2: b2 */
+ 112, /* 125 -> PULSE 2_3: b2 */
+ 116, /* 126 -> PULSE 2_4: b2 */
+ 120, /* 127 -> PULSE 2_5: b2 */
+ 159, /* 128 -> PULSE 3_1: b2 */
+ 163, /* 129 -> PULSE 3_2: b2 */
+ 167, /* 130 -> PULSE 3_3: b2 */
+ 171, /* 131 -> PULSE 3_4: b2 */
+ 175, /* 132 -> PULSE 3_5: b2 */
+ 211, /* 133 -> PULSE 4_1: b2 */
+ 215, /* 134 -> PULSE 4_2: b2 */
+ 219, /* 135 -> PULSE 4_3: b2 */
+ 223, /* 136 -> PULSE 4_4: b2 */
+ 227, /* 137 -> PULSE 4_5: b2 */
+ 53, /* 138 -> PULSE 1_1: b1 */
+ 57, /* 139 -> PULSE 1_2: b1 */
+ 61, /* 140 -> PULSE 1_3: b1 */
+ 65, /* 141 -> PULSE 1_4: b1 */
+ 105, /* 142 -> PULSE 2_1: b1 */
+ 109, /* 143 -> PULSE 2_2: b1 */
+ 113, /* 144 -> PULSE 2_3: b1 */
+ 117, /* 145 -> PULSE 2_4: b1 */
+ 160, /* 146 -> PULSE 3_1: b1 */
+ 164, /* 147 -> PULSE 3_2: b1 */
+ 168, /* 148 -> PULSE 3_3: b1 */
+ 172, /* 149 -> PULSE 3_4: b1 */
+ 212, /* 150 -> PULSE 4_1: b1 */
+ 220, /* 151 -> PULSE 4_3: b1 */
+ 224, /* 152 -> PULSE 4_4: b1 */
+ 91, /* 153 -> FCB-GAIN 1: b1 */
+ 143, /* 154 -> FCB-GAIN 2: b1 */
+ 198, /* 155 -> FCB-GAIN 3: b1 */
+ 250, /* 156 -> FCB-GAIN 4: b1 */
+ 50, /* 157 -> LTP-GAIN 1: b0 */
+ 102, /* 158 -> LTP-GAIN 2: b0 */
+ 157, /* 159 -> LTP-GAIN 3: b0 */
+ 209, /* 160 -> LTP-GAIN 4: b0 */
+ 92, /* 161 -> FCB-GAIN 1: b0 */
+ 144, /* 162 -> FCB-GAIN 2: b0 */
+ 199, /* 163 -> FCB-GAIN 3: b0 */
+ 251, /* 164 -> FCB-GAIN 4: b0 */
+ 54, /* 165 -> PULSE 1_1: b0 */
+ 58, /* 166 -> PULSE 1_2: b0 */
+ 62, /* 167 -> PULSE 1_3: b0 */
+ 66, /* 168 -> PULSE 1_4: b0 */
+ 106, /* 169 -> PULSE 2_1: b0 */
+ 110, /* 170 -> PULSE 2_2: b0 */
+ 114, /* 171 -> PULSE 2_3: b0 */
+ 118, /* 172 -> PULSE 2_4: b0 */
+ 161, /* 173 -> PULSE 3_1: b0 */
+ 165, /* 174 -> PULSE 3_2: b0 */
+ 169, /* 175 -> PULSE 3_3: b0 */
+ 173, /* 176 -> PULSE 3_4: b0 */
+ 213, /* 177 -> PULSE 4_1: b0 */
+ 221, /* 178 -> PULSE 4_3: b0 */
+ 225, /* 179 -> PULSE 4_4: b0 */
+ 36, 37, /* 180 -> LPC 5: b1..b0 */
+ 69, /* 182 -> PULSE 1_5: b1 */
+ 71, 72, /* 183 -> PULSE 1_5: b1..b1 */
+ 121, /* 185 -> PULSE 2_5: b1 */
+ 123, 124, /* 186 -> PULSE 2_5: b1..b1 */
+ 176, /* 188 -> PULSE 3_5: b1 */
+ 178, 179, /* 189 -> PULSE 3_5: b1..b1 */
+ 228, /* 191 -> PULSE 4_5: b1 */
+ 230, 231, /* 192 -> PULSE 4_5: b1..b1 */
+ 216, 217, /* 194 -> PULSE 4_2: b1..b0 */
+ 70, /* 196 -> PULSE 1_5: b0 */
+ 122, /* 197 -> PULSE 2_5: b0 */
+ 177, /* 198 -> PULSE 3_5: b0 */
+ 229, /* 199 -> PULSE 4_5: b0 */
+ 73, /* 200 -> PULSE 1_6: b2 */
+ 76, /* 201 -> PULSE 1_7: b2 */
+ 79, /* 202 -> PULSE 1_8: b2 */
+ 82, /* 203 -> PULSE 1_9: b2 */
+ 85, /* 204 -> PULSE 1_10: b2 */
+ 125, /* 205 -> PULSE 2_6: b2 */
+ 128, /* 206 -> PULSE 2_7: b2 */
+ 131, /* 207 -> PULSE 2_8: b2 */
+ 134, /* 208 -> PULSE 2_9: b2 */
+ 137, /* 209 -> PULSE 2_10: b2 */
+ 180, /* 210 -> PULSE 3_6: b2 */
+ 183, /* 211 -> PULSE 3_7: b2 */
+ 186, /* 212 -> PULSE 3_8: b2 */
+ 189, /* 213 -> PULSE 3_9: b2 */
+ 192, /* 214 -> PULSE 3_10: b2 */
+ 232, /* 215 -> PULSE 4_6: b2 */
+ 235, /* 216 -> PULSE 4_7: b2 */
+ 238, /* 217 -> PULSE 4_8: b2 */
+ 241, /* 218 -> PULSE 4_9: b2 */
+ 244, /* 219 -> PULSE 4_10: b2 */
+ 74, /* 220 -> PULSE 1_6: b1 */
+ 77, /* 221 -> PULSE 1_7: b1 */
+ 80, /* 222 -> PULSE 1_8: b1 */
+ 83, /* 223 -> PULSE 1_9: b1 */
+ 86, /* 224 -> PULSE 1_10: b1 */
+ 126, /* 225 -> PULSE 2_6: b1 */
+ 129, /* 226 -> PULSE 2_7: b1 */
+ 132, /* 227 -> PULSE 2_8: b1 */
+ 135, /* 228 -> PULSE 2_9: b1 */
+ 138, /* 229 -> PULSE 2_10: b1 */
+ 181, /* 230 -> PULSE 3_6: b1 */
+ 184, /* 231 -> PULSE 3_7: b1 */
+ 187, /* 232 -> PULSE 3_8: b1 */
+ 190, /* 233 -> PULSE 3_9: b1 */
+ 193, /* 234 -> PULSE 3_10: b1 */
+ 233, /* 235 -> PULSE 4_6: b1 */
+ 236, /* 236 -> PULSE 4_7: b1 */
+ 239, /* 237 -> PULSE 4_8: b1 */
+ 242, /* 238 -> PULSE 4_9: b1 */
+ 245, /* 239 -> PULSE 4_10: b1 */
+ 75, /* 240 -> PULSE 1_6: b0 */
+ 78, /* 241 -> PULSE 1_7: b0 */
+ 81, /* 242 -> PULSE 1_8: b0 */
+ 84, /* 243 -> PULSE 1_9: b0 */
+ 87, /* 244 -> PULSE 1_10: b0 */
+ 127, /* 245 -> PULSE 2_6: b0 */
+ 130, /* 246 -> PULSE 2_7: b0 */
+ 133, /* 247 -> PULSE 2_8: b0 */
+ 136, /* 248 -> PULSE 2_9: b0 */
+ 139, /* 249 -> PULSE 2_10: b0 */
+ 182, /* 250 -> PULSE 3_6: b0 */
+ 185, /* 251 -> PULSE 3_7: b0 */
+ 188, /* 252 -> PULSE 3_8: b0 */
+ 191, /* 253 -> PULSE 3_9: b0 */
+ 194, /* 254 -> PULSE 3_10: b0 */
+ 234, /* 255 -> PULSE 4_6: b0 */
+ 237, /* 256 -> PULSE 4_7: b0 */
+ 240, /* 257 -> PULSE 4_8: b0 */
+ 243, /* 258 -> PULSE 4_9: b0 */
+ 246, /* 259 -> PULSE 4_10: b0 */
+};
diff --git a/src/shared/libosmocore/src/codec/gsm690.c b/src/shared/libosmocore/src/codec/gsm690.c
new file mode 100644
index 00000000..e5b9bd49
--- /dev/null
+++ b/src/shared/libosmocore/src/codec/gsm690.c
@@ -0,0 +1,210 @@
+/* GSM 06.90 - GSM AMR Codec */
+
+/*
+ * (C) 2010 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.
+ *
+ */
+
+#include <stdint.h>
+
+/*
+ * These table map between the raw encoder parameter output and
+ * the format used before channel coding. Both in GSM and in various
+ * file/network format (same tables used in several specs).
+ */
+
+/* AMR 12.2 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 7
+ * It's also TS 26.101 Table B.8
+ */
+uint16_t gsm690_12_2_bitorder[244] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 23, 15, 16, 17, 18,
+ 19, 20, 21, 22, 24, 25, 26, 27, 28, 38,
+ 141, 39, 142, 40, 143, 41, 144, 42, 145, 43,
+ 146, 44, 147, 45, 148, 46, 149, 47, 97, 150,
+ 200, 48, 98, 151, 201, 49, 99, 152, 202, 86,
+ 136, 189, 239, 87, 137, 190, 240, 88, 138, 191,
+ 241, 91, 194, 92, 195, 93, 196, 94, 197, 95,
+ 198, 29, 30, 31, 32, 33, 34, 35, 50, 100,
+ 153, 203, 89, 139, 192, 242, 51, 101, 154, 204,
+ 55, 105, 158, 208, 90, 140, 193, 243, 59, 109,
+ 162, 212, 63, 113, 166, 216, 67, 117, 170, 220,
+ 36, 37, 54, 53, 52, 58, 57, 56, 62, 61,
+ 60, 66, 65, 64, 70, 69, 68, 104, 103, 102,
+ 108, 107, 106, 112, 111, 110, 116, 115, 114, 120,
+ 119, 118, 157, 156, 155, 161, 160, 159, 165, 164,
+ 163, 169, 168, 167, 173, 172, 171, 207, 206, 205,
+ 211, 210, 209, 215, 214, 213, 219, 218, 217, 223,
+ 222, 221, 73, 72, 71, 76, 75, 74, 79, 78,
+ 77, 82, 81, 80, 85, 84, 83, 123, 122, 121,
+ 126, 125, 124, 129, 128, 127, 132, 131, 130, 135,
+ 134, 133, 176, 175, 174, 179, 178, 177, 182, 181,
+ 180, 185, 184, 183, 188, 187, 186, 226, 225, 224,
+ 229, 228, 227, 232, 231, 230, 235, 234, 233, 238,
+ 237, 236, 96, 199,
+};
+
+/* AMR 10.2 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 8
+ * It's also TS 26.101 Table B.7
+ */
+uint16_t gsm690_10_2_bitorder[204] = {
+ 7, 6, 5, 4, 3, 2, 1, 0, 16, 15,
+ 14, 13, 12, 11, 10, 9, 8, 26, 27, 28,
+ 29, 30, 31, 115, 116, 117, 118, 119, 120, 72,
+ 73, 161, 162, 65, 68, 69, 108, 111, 112, 154,
+ 157, 158, 197, 200, 201, 32, 33, 121, 122, 74,
+ 75, 163, 164, 66, 109, 155, 198, 19, 23, 21,
+ 22, 18, 17, 20, 24, 25, 37, 36, 35, 34,
+ 80, 79, 78, 77, 126, 125, 124, 123, 169, 168,
+ 167, 166, 70, 67, 71, 113, 110, 114, 159, 156,
+ 160, 202, 199, 203, 76, 165, 81, 82, 92, 91,
+ 93, 83, 95, 85, 84, 94, 101, 102, 96, 104,
+ 86, 103, 87, 97, 127, 128, 138, 137, 139, 129,
+ 141, 131, 130, 140, 147, 148, 142, 150, 132, 149,
+ 133, 143, 170, 171, 181, 180, 182, 172, 184, 174,
+ 173, 183, 190, 191, 185, 193, 175, 192, 176, 186,
+ 38, 39, 49, 48, 50, 40, 52, 42, 41, 51,
+ 58, 59, 53, 61, 43, 60, 44, 54, 194, 179,
+ 189, 196, 177, 195, 178, 187, 188, 151, 136, 146,
+ 153, 134, 152, 135, 144, 145, 105, 90, 100, 107,
+ 88, 106, 89, 98, 99, 62, 47, 57, 64, 45,
+ 63, 46, 55, 56,
+};
+
+/* AMR 7.95 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 9
+ * It's also TS 26.101 Table B.6
+ */
+uint16_t gsm690_7_95_bitorder[159] = {
+ 8, 7, 6, 5, 4, 3, 2, 14, 16, 9,
+ 10, 12, 13, 15, 11, 17, 20, 22, 24, 23,
+ 19, 18, 21, 56, 88, 122, 154, 57, 89, 123,
+ 155, 58, 90, 124, 156, 52, 84, 118, 150, 53,
+ 85, 119, 151, 27, 93, 28, 94, 29, 95, 30,
+ 96, 31, 97, 61, 127, 62, 128, 63, 129, 59,
+ 91, 125, 157, 32, 98, 64, 130, 1, 0, 25,
+ 26, 33, 99, 34, 100, 65, 131, 66, 132, 54,
+ 86, 120, 152, 60, 92, 126, 158, 55, 87, 121,
+ 153, 117, 116, 115, 46, 78, 112, 144, 43, 75,
+ 109, 141, 40, 72, 106, 138, 36, 68, 102, 134,
+ 114, 149, 148, 147, 146, 83, 82, 81, 80, 51,
+ 50, 49, 48, 47, 45, 44, 42, 39, 35, 79,
+ 77, 76, 74, 71, 67, 113, 111, 110, 108, 105,
+ 101, 145, 143, 142, 140, 137, 133, 41, 73, 107,
+ 139, 37, 69, 103, 135, 38, 70, 104, 136,
+};
+
+/* AMR 7.4 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 10
+ * It's also TS 26.101 Table B.5
+ */
+uint16_t gsm690_7_4_bitorder[148] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 26, 87, 27,
+ 88, 28, 89, 29, 90, 30, 91, 51, 80, 112,
+ 141, 52, 81, 113, 142, 54, 83, 115, 144, 55,
+ 84, 116, 145, 58, 119, 59, 120, 21, 22, 23,
+ 17, 18, 19, 31, 60, 92, 121, 56, 85, 117,
+ 146, 20, 24, 25, 50, 79, 111, 140, 57, 86,
+ 118, 147, 49, 78, 110, 139, 48, 77, 53, 82,
+ 114, 143, 109, 138, 47, 76, 108, 137, 32, 33,
+ 61, 62, 93, 94, 122, 123, 41, 42, 43, 44,
+ 45, 46, 70, 71, 72, 73, 74, 75, 102, 103,
+ 104, 105, 106, 107, 131, 132, 133, 134, 135, 136,
+ 34, 63, 95, 124, 35, 64, 96, 125, 36, 65,
+ 97, 126, 37, 66, 98, 127, 38, 67, 99, 128,
+ 39, 68, 100, 129, 40, 69, 101, 130,
+};
+
+/* AMR 6.7 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 11
+ * It's also TS 26.101 Table B.4
+ */
+uint16_t gsm690_6_7_bitorder[134] = {
+ 0, 1, 4, 3, 5, 6, 13, 7, 2, 8,
+ 9, 11, 15, 12, 14, 10, 28, 82, 29, 83,
+ 27, 81, 26, 80, 30, 84, 16, 55, 109, 56,
+ 110, 31, 85, 57, 111, 48, 73, 102, 127, 32,
+ 86, 51, 76, 105, 130, 52, 77, 106, 131, 58,
+ 112, 33, 87, 19, 23, 53, 78, 107, 132, 21,
+ 22, 18, 17, 20, 24, 25, 50, 75, 104, 129,
+ 47, 72, 101, 126, 54, 79, 108, 133, 46, 71,
+ 100, 125, 128, 103, 74, 49, 45, 70, 99, 124,
+ 42, 67, 96, 121, 39, 64, 93, 118, 38, 63,
+ 92, 117, 35, 60, 89, 114, 34, 59, 88, 113,
+ 44, 69, 98, 123, 43, 68, 97, 122, 41, 66,
+ 95, 120, 40, 65, 94, 119, 37, 62, 91, 116,
+ 36, 61, 90, 115,
+};
+
+/* AMR 5.9 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 12
+ * It's also TS 26.101 Table B.3
+ */
+uint16_t gsm690_5_9_bitorder[118] = {
+ 0, 1, 4, 5, 3, 6, 7, 2, 13, 15,
+ 8, 9, 11, 12, 14, 10, 16, 28, 74, 29,
+ 75, 27, 73, 26, 72, 30, 76, 51, 97, 50,
+ 71, 96, 117, 31, 77, 52, 98, 49, 70, 95,
+ 116, 53, 99, 32, 78, 33, 79, 48, 69, 94,
+ 115, 47, 68, 93, 114, 46, 67, 92, 113, 19,
+ 21, 23, 22, 18, 17, 20, 24, 111, 43, 89,
+ 110, 64, 65, 44, 90, 25, 45, 66, 91, 112,
+ 54, 100, 40, 61, 86, 107, 39, 60, 85, 106,
+ 36, 57, 82, 103, 35, 56, 81, 102, 34, 55,
+ 80, 101, 42, 63, 88, 109, 41, 62, 87, 108,
+ 38, 59, 84, 105, 37, 58, 83, 104,
+};
+
+/* AMR 5.15 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 13
+ * It's also TS 26.101 Table B.2
+ */
+uint16_t gsm690_5_15_bitorder[103] = {
+ 7, 6, 5, 4, 3, 2, 1, 0, 15, 14,
+ 13, 12, 11, 10, 9, 8, 23, 24, 25, 26,
+ 27, 46, 65, 84, 45, 44, 43, 64, 63, 62,
+ 83, 82, 81, 102, 101, 100, 42, 61, 80, 99,
+ 28, 47, 66, 85, 18, 41, 60, 79, 98, 29,
+ 48, 67, 17, 20, 22, 40, 59, 78, 97, 21,
+ 30, 49, 68, 86, 19, 16, 87, 39, 38, 58,
+ 57, 77, 35, 54, 73, 92, 76, 96, 95, 36,
+ 55, 74, 93, 32, 51, 33, 52, 70, 71, 89,
+ 90, 31, 50, 69, 88, 37, 56, 75, 94, 34,
+ 53, 72, 91,
+};
+
+/* AMR 4.75 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 14
+ * It's also TS 26.101 Table B.1
+ */
+uint16_t gsm690_4_75_bitorder[95] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 23, 24, 25, 26,
+ 27, 28, 48, 49, 61, 62, 82, 83, 47, 46,
+ 45, 44, 81, 80, 79, 78, 17, 18, 20, 22,
+ 77, 76, 75, 74, 29, 30, 43, 42, 41, 40,
+ 38, 39, 16, 19, 21, 50, 51, 59, 60, 63,
+ 64, 72, 73, 84, 85, 93, 94, 32, 33, 35,
+ 36, 53, 54, 56, 57, 66, 67, 69, 70, 87,
+ 88, 90, 91, 34, 55, 68, 89, 37, 58, 71,
+ 92, 31, 52, 65, 86,
+};
diff --git a/src/shared/libosmocore/src/conv.c b/src/shared/libosmocore/src/conv.c
new file mode 100644
index 00000000..ac39e29f
--- /dev/null
+++ b/src/shared/libosmocore/src/conv.c
@@ -0,0 +1,631 @@
+/*
+ * conv.c
+ *
+ * Generic convolutional encoding / decoding
+ *
+ * Copyright (C) 2011 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.
+ */
+
+/*! \addtogroup conv
+ * @{
+ */
+
+/*! \file conv.c
+ * \file Osmocom convolutional encoder and decoder
+ */
+#include "config.h"
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/conv.h>
+
+
+/* ------------------------------------------------------------------------ */
+/* Common */
+/* ------------------------------------------------------------------------ */
+
+int
+osmo_conv_get_input_length(const struct osmo_conv_code *code, int len)
+{
+ return len <= 0 ? code->len : len;
+}
+
+int
+osmo_conv_get_output_length(const struct osmo_conv_code *code, int len)
+{
+ int pbits, in_len, out_len;
+
+ /* Input length */
+ in_len = osmo_conv_get_input_length(code, len);
+
+ /* Output length */
+ out_len = in_len * code->N;
+
+ if (code->term == CONV_TERM_FLUSH)
+ out_len += code->N * (code->K - 1);
+
+ /* Count punctured bits */
+ if (code->puncture) {
+ for (pbits=0; code->puncture[pbits] >= 0; pbits++);
+ out_len -= pbits;
+ }
+
+ return out_len;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* Encoding */
+/* ------------------------------------------------------------------------ */
+
+/*! \brief Initialize a convolutional encoder
+ * \param[in,out] encoder Encoder state to initialize
+ * \param[in] code Description of convolutional code
+ */
+void
+osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
+ const struct osmo_conv_code *code)
+{
+ memset(encoder, 0x00, sizeof(struct osmo_conv_encoder));
+ encoder->code = code;
+}
+
+void
+osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder,
+ const ubit_t *input)
+{
+ int i;
+ uint8_t state = 0;
+
+ for (i=0; i<(encoder->code->K-1); i++)
+ state = (state << 1) | input[i];
+
+ encoder->state = state;
+}
+
+static inline int
+_conv_encode_do_output(struct osmo_conv_encoder *encoder,
+ uint8_t out, ubit_t *output)
+{
+ const struct osmo_conv_code *code = encoder->code;
+ int o_idx = 0;
+ int j;
+
+ if (code->puncture) {
+ for (j=0; j<code->N; j++)
+ {
+ int bit_no = code->N - j - 1;
+ int r_idx = encoder->i_idx * code->N + j;
+
+ if (code->puncture[encoder->p_idx] == r_idx)
+ encoder->p_idx++;
+ else
+ output[o_idx++] = (out >> bit_no) & 1;
+ }
+ } else {
+ for (j=0; j<code->N; j++)
+ {
+ int bit_no = code->N - j - 1;
+ output[o_idx++] = (out >> bit_no) & 1;
+ }
+ }
+
+ return o_idx;
+}
+
+int
+osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
+ const ubit_t *input, ubit_t *output, int n)
+{
+ const struct osmo_conv_code *code = encoder->code;
+ uint8_t state;
+ int i;
+ int o_idx;
+
+ o_idx = 0;
+ state = encoder->state;
+
+ for (i=0; i<n; i++) {
+ int bit = input[i];
+ uint8_t out;
+
+ out = code->next_output[state][bit];
+ state = code->next_state[state][bit];
+
+ o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
+
+ encoder->i_idx++;
+ }
+
+ encoder->state = state;
+
+ return o_idx;
+}
+
+int
+osmo_conv_encode_flush(struct osmo_conv_encoder *encoder,
+ ubit_t *output)
+{
+ const struct osmo_conv_code *code = encoder->code;
+ uint8_t state;
+ int n;
+ int i;
+ int o_idx;
+
+ n = code->K - 1;
+
+ o_idx = 0;
+ state = encoder->state;
+
+ for (i=0; i<n; i++) {
+ uint8_t out;
+
+ if (code->next_term_output) {
+ out = code->next_term_output[state];
+ state = code->next_term_state[state];
+ } else {
+ out = code->next_output[state][0];
+ state = code->next_state[state][0];
+ }
+
+ o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
+
+ encoder->i_idx++;
+ }
+
+ encoder->state = state;
+
+ return o_idx;
+}
+
+/*! \brief All-in-one convolutional encoding function
+ * \param[in] code description of convolutional code to be used
+ * \param[in] input array of unpacked bits (uncoded)
+ * \param[out] output array of unpacked bits (encoded)
+ * \return Number of produced output bits
+ *
+ * This is an all-in-one function, taking care of
+ * \ref osmo_conv_init, \ref osmo_conv_encode_load_state,
+ * \ref osmo_conv_encode_raw and \ref osmo_conv_encode_flush as needed.
+ */
+int
+osmo_conv_encode(const struct osmo_conv_code *code,
+ const ubit_t *input, ubit_t *output)
+{
+ struct osmo_conv_encoder encoder;
+ int l;
+
+ osmo_conv_encode_init(&encoder, code);
+
+ if (code->term == CONV_TERM_TAIL_BITING) {
+ int eidx = code->len - code->K + 1;
+ osmo_conv_encode_load_state(&encoder, &input[eidx]);
+ }
+
+ l = osmo_conv_encode_raw(&encoder, input, output, code->len);
+
+ if (code->term == CONV_TERM_FLUSH)
+ l += osmo_conv_encode_flush(&encoder, &output[l]);
+
+ return l;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* Decoding (viterbi) */
+/* ------------------------------------------------------------------------ */
+
+#define MAX_AE 0x00ffffff
+
+void
+osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
+ const struct osmo_conv_code *code, int len, int start_state)
+{
+ int n_states;
+
+ /* Init */
+ if (len <= 0)
+ len = code->len;
+
+ n_states = 1 << (code->K - 1);
+
+ memset(decoder, 0x00, sizeof(struct osmo_conv_decoder));
+
+ decoder->code = code;
+ decoder->n_states = n_states;
+ decoder->len = len;
+
+ /* Allocate arrays */
+ decoder->ae = malloc(sizeof(unsigned int) * n_states);
+ decoder->ae_next = malloc(sizeof(unsigned int) * n_states);
+
+ decoder->state_history = malloc(sizeof(uint8_t) * n_states * (len + decoder->code->K - 1));
+
+ /* Classic reset */
+ osmo_conv_decode_reset(decoder, start_state);
+}
+
+void
+osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state)
+{
+ int i;
+
+ /* Reset indexes */
+ decoder->o_idx = 0;
+ decoder->p_idx = 0;
+
+ /* Initial error */
+ if (start_state < 0) {
+ /* All states possible */
+ memset(decoder->ae, 0x00, sizeof(unsigned int) * decoder->n_states);
+ } else {
+ /* Fixed start state */
+ for (i=0; i<decoder->n_states; i++) {
+ decoder->ae[i] = (i == start_state) ? 0 : MAX_AE;
+ }
+ }
+}
+
+void
+osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder)
+{
+ int i;
+ unsigned int min_ae = MAX_AE;
+
+ /* Reset indexes */
+ decoder->o_idx = 0;
+ decoder->p_idx = 0;
+
+ /* Initial error normalize (remove constant) */
+ for (i=0; i<decoder->n_states; i++) {
+ if (decoder->ae[i] < min_ae)
+ min_ae = decoder->ae[i];
+ }
+
+ for (i=0; i<decoder->n_states; i++)
+ decoder->ae[i] -= min_ae;
+}
+
+void
+osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder)
+{
+ free(decoder->ae);
+ free(decoder->ae_next);
+ free(decoder->state_history);
+
+ memset(decoder, 0x00, sizeof(struct osmo_conv_decoder));
+}
+
+int
+osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
+ const sbit_t *input, int n)
+{
+ const struct osmo_conv_code *code = decoder->code;
+
+ int i, s, b, j;
+
+ int n_states;
+ unsigned int *ae;
+ unsigned int *ae_next;
+ uint8_t *state_history;
+ sbit_t *in_sym;
+
+ int i_idx, p_idx;
+
+ /* Prepare */
+ n_states = decoder->n_states;
+
+ ae = decoder->ae;
+ ae_next = decoder->ae_next;
+ state_history = &decoder->state_history[n_states * decoder->o_idx];
+
+ in_sym = alloca(sizeof(sbit_t) * code->N);
+
+ i_idx = 0;
+ p_idx = decoder->p_idx;
+
+ /* Scan the treillis */
+ for (i=0; i<n; i++)
+ {
+ /* Reset next accumulated error */
+ for (s=0; s<n_states; s++) {
+ ae_next[s] = MAX_AE;
+ }
+
+ /* Get input */
+ if (code->puncture) {
+ /* Hard way ... */
+ for (j=0; j<code->N; j++) {
+ int idx = ((decoder->o_idx + i) * code->N) + j;
+ if (idx == code->puncture[p_idx]) {
+ in_sym[j] = 0; /* Undefined */
+ p_idx++;
+ } else {
+ in_sym[j] = input[i_idx];
+ i_idx++;
+ }
+ }
+ } else {
+ /* Easy, just copy N bits */
+ memcpy(in_sym, &input[i_idx], code->N);
+ i_idx += code->N;
+ }
+
+ /* Scan all state */
+ for (s=0; s<n_states; s++)
+ {
+ /* Scan possible input bits */
+ for (b=0; b<2; b++)
+ {
+ int nae, ov, e;
+ uint8_t m;
+
+ /* Next output and state */
+ uint8_t out = code->next_output[s][b];
+ uint8_t state = code->next_state[s][b];
+
+ /* New error for this path */
+ nae = ae[s]; /* start from last error */
+ m = 1 << (code->N - 1); /* mask for 'out' bit selection */
+
+ for (j=0; j<code->N; j++) {
+ int is = (int)in_sym[j];
+ if (is) {
+ ov = (out & m) ? -127 : 127; /* sbit_t value for it */
+ e = is - ov; /* raw error for this bit */
+ nae += (e * e) >> 9; /* acc the squared/scaled value */
+ }
+ m >>= 1; /* next mask bit */
+ }
+
+ /* Is it survivor ? */
+ if (ae_next[state] > nae) {
+ ae_next[state] = nae;
+ state_history[(n_states * i) + state] = s;
+ }
+ }
+ }
+
+ /* Copy accumulated error */
+ memcpy(ae, ae_next, sizeof(unsigned int) * n_states);
+ }
+
+ /* Update decoder state */
+ decoder->p_idx = p_idx;
+ decoder->o_idx += n;
+
+ return i_idx;
+}
+
+int
+osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
+ const sbit_t *input)
+{
+ const struct osmo_conv_code *code = decoder->code;
+
+ int i, s, j;
+
+ int n_states;
+ unsigned int *ae;
+ unsigned int *ae_next;
+ uint8_t *state_history;
+ sbit_t *in_sym;
+
+ int i_idx, p_idx;
+
+ /* Prepare */
+ n_states = decoder->n_states;
+
+ ae = decoder->ae;
+ ae_next = decoder->ae_next;
+ state_history = &decoder->state_history[n_states * decoder->o_idx];
+
+ in_sym = alloca(sizeof(sbit_t) * code->N);
+
+ i_idx = 0;
+ p_idx = decoder->p_idx;
+
+ /* Scan the treillis */
+ for (i=0; i<code->K-1; i++)
+ {
+ /* Reset next accumulated error */
+ for (s=0; s<n_states; s++) {
+ ae_next[s] = MAX_AE;
+ }
+
+ /* Get input */
+ if (code->puncture) {
+ /* Hard way ... */
+ for (j=0; j<code->N; j++) {
+ int idx = ((decoder->o_idx + i) * code->N) + j;
+ if (idx == code->puncture[p_idx]) {
+ in_sym[j] = 0; /* Undefined */
+ p_idx++;
+ } else {
+ in_sym[j] = input[i_idx];
+ i_idx++;
+ }
+ }
+ } else {
+ /* Easy, just copy N bits */
+ memcpy(in_sym, &input[i_idx], code->N);
+ i_idx += code->N;
+ }
+
+ /* Scan all state */
+ for (s=0; s<n_states; s++)
+ {
+ int nae, ov, e;
+ uint8_t m;
+
+ /* Next output and state */
+ uint8_t out;
+ uint8_t state;
+
+ if (code->next_term_output) {
+ out = code->next_term_output[s];
+ state = code->next_term_state[s];
+ } else {
+ out = code->next_output[s][0];
+ state = code->next_state[s][0];
+ }
+
+ /* New error for this path */
+ nae = ae[s]; /* start from last error */
+ m = 1 << (code->N - 1); /* mask for 'out' bit selection */
+
+ for (j=0; j<code->N; j++) {
+ int is = (int)in_sym[j];
+ if (is) {
+ ov = (out & m) ? -127 : 127; /* sbit_t value for it */
+ e = is - ov; /* raw error for this bit */
+ nae += (e * e) >> 9; /* acc the squared/scaled value */
+ }
+ m >>= 1; /* next mask bit */
+ }
+
+ /* Is it survivor ? */
+ if (ae_next[state] > nae) {
+ ae_next[state] = nae;
+ state_history[(n_states * i) + state] = s;
+ }
+ }
+
+ /* Copy accumulated error */
+ memcpy(ae, ae_next, sizeof(unsigned int) * n_states);
+ }
+
+ /* Update decoder state */
+ decoder->p_idx = p_idx;
+ decoder->o_idx += code->K - 1;
+
+ return i_idx;
+}
+
+int
+osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
+ ubit_t *output, int has_flush, int end_state)
+{
+ const struct osmo_conv_code *code = decoder->code;
+
+ int min_ae;
+ uint8_t min_state, cur_state;
+ int i, s, n;
+
+ uint8_t *sh_ptr;
+
+ /* End state ? */
+ if (end_state < 0) {
+ /* Find state with least error */
+ min_ae = MAX_AE;
+ min_state = 0xff;
+
+ for (s=0; s<decoder->n_states; s++)
+ {
+ if (decoder->ae[s] < min_ae) {
+ min_ae = decoder->ae[s];
+ min_state = s;
+ }
+ }
+
+ if (min_state == 0xff)
+ return -1;
+ } else {
+ min_state = (uint8_t) end_state;
+ min_ae = decoder->ae[end_state];
+ }
+
+ /* Traceback */
+ cur_state = min_state;
+
+ n = decoder->o_idx;
+
+ sh_ptr = &decoder->state_history[decoder->n_states * (n-1)];
+
+ /* No output for the K-1 termination input bits */
+ if (has_flush) {
+ for (i=0; i<code->K-1; i++) {
+ cur_state = sh_ptr[cur_state];
+ sh_ptr -= decoder->n_states;
+ }
+ n -= code->K - 1;
+ }
+
+ /* Generate output backward */
+ for (i=n-1; i>=0; i--)
+ {
+ min_state = cur_state;
+ cur_state = sh_ptr[cur_state];
+
+ sh_ptr -= decoder->n_states;
+
+ if (code->next_state[cur_state][0] == min_state)
+ output[i] = 0;
+ else
+ output[i] = 1;
+ }
+
+ return min_ae;
+}
+
+/*! \brief All-in-one convolutional decoding function
+ * \param[in] code description of convolutional code to be used
+ * \param[in] input array of soft bits (coded)
+ * \param[out] output array of unpacked bits (decoded)
+ *
+ * This is an all-in-one function, taking care of
+ * \ref osmo_conv_decode_init, \ref osmo_conv_decode_scan,
+ * \ref osmo_conv_decode_flush, \ref osmo_conv_decode_get_output and
+ * \ref osmo_conv_decode_deinit.
+ */
+int
+osmo_conv_decode(const struct osmo_conv_code *code,
+ const sbit_t *input, ubit_t *output)
+{
+ struct osmo_conv_decoder decoder;
+ int rv, l;
+
+ osmo_conv_decode_init(&decoder, code, 0, 0);
+
+ if (code->term == CONV_TERM_TAIL_BITING) {
+ osmo_conv_decode_scan(&decoder, input, code->len);
+ osmo_conv_decode_rewind(&decoder);
+ }
+
+ l = osmo_conv_decode_scan(&decoder, input, code->len);
+
+ if (code->term == CONV_TERM_FLUSH)
+ l = osmo_conv_decode_flush(&decoder, &input[l]);
+
+ rv = osmo_conv_decode_get_output(&decoder, output,
+ code->term == CONV_TERM_FLUSH, /* has_flush */
+ code->term == CONV_TERM_FLUSH ? 0 : -1 /* end_state */
+ );
+
+ osmo_conv_decode_deinit(&decoder);
+
+ return rv;
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/crc16.c b/src/shared/libosmocore/src/crc16.c
new file mode 100644
index 00000000..2741cf53
--- /dev/null
+++ b/src/shared/libosmocore/src/crc16.c
@@ -0,0 +1,62 @@
+/*
+ * This was copied from the linux kernel and adjusted for our types.
+ */
+/*
+ * crc16.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <osmocom/core/crc16.h>
+
+/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
+uint16_t const osmo_crc16_table[256] = {
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+};
+
+/**
+ * crc16 - compute the CRC-16 for the data buffer
+ * @crc: previous CRC value
+ * @buffer: data pointer
+ * @len: number of bytes in the buffer
+ *
+ * Returns the updated CRC value.
+ */
+uint16_t osmo_crc16(uint16_t crc, uint8_t const *buffer, size_t len)
+{
+ while (len--)
+ crc = osmo_crc16_byte(crc, *buffer++);
+ return crc;
+}
diff --git a/src/shared/libosmocore/src/crcXXgen.c.tpl b/src/shared/libosmocore/src/crcXXgen.c.tpl
new file mode 100644
index 00000000..5d70753a
--- /dev/null
+++ b/src/shared/libosmocore/src/crcXXgen.c.tpl
@@ -0,0 +1,120 @@
+/*
+ * crcXXgen.c
+ *
+ * Generic CRC routines (for max XX bits poly)
+ *
+ * Copyright (C) 2011 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.
+ */
+
+/*! \addtogroup crcgen
+ * @{
+ */
+
+/*! \file crcXXgen.c
+ * \file Osmocom generic CRC routines (for max XX bits poly)
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/crcXXgen.h>
+
+
+/*! \brief Compute the CRC value of a given array of hard-bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \returns The CRC value
+ */
+uintXX_t
+osmo_crcXXgen_compute_bits(const struct osmo_crcXXgen_code *code,
+ const ubit_t *in, int len)
+{
+ const uintXX_t poly = code->poly;
+ uintXX_t crc = code->init;
+ int i, n = code->bits-1;
+
+ for (i=0; i<len; i++) {
+ uintXX_t bit = in[i] & 1;
+ crc ^= (bit << n);
+ if (crc & (1 << n)) {
+ crc <<= 1;
+ crc ^= poly;
+ } else {
+ crc <<= 1;
+ }
+ crc &= (1ULL << code->bits) - 1;
+ }
+
+ crc ^= code->remainder;
+
+ return crc;
+}
+
+
+/*! \brief Checks the CRC value of a given array of hard-bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \param[in] crc_bits Array of hard bits with the alleged CRC
+ * \returns 0 if CRC matches. 1 in case of error.
+ *
+ * The crc_bits array must have a length of code->len
+ */
+int
+osmo_crcXXgen_check_bits(const struct osmo_crcXXgen_code *code,
+ const ubit_t *in, int len, const ubit_t *crc_bits)
+{
+ uintXX_t crc;
+ int i;
+
+ crc = osmo_crcXXgen_compute_bits(code, in, len);
+
+ for (i=0; i<code->bits; i++)
+ if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1))
+ return 1;
+
+ return 0;
+}
+
+
+/*! \brief Computes and writes the CRC value of a given array of bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \param[in] crc_bits Array of hard bits to write the computed CRC to
+ *
+ * The crc_bits array must have a length of code->len
+ */
+void
+osmo_crcXXgen_set_bits(const struct osmo_crcXXgen_code *code,
+ const ubit_t *in, int len, ubit_t *crc_bits)
+{
+ uintXX_t crc;
+ int i;
+
+ crc = osmo_crcXXgen_compute_bits(code, in, len);
+
+ for (i=0; i<code->bits; i++)
+ crc_bits[i] = ((crc >> (code->bits-i-1)) & 1);
+}
+
+/*! }@ */
+
+/* vim: set syntax=c: */
diff --git a/src/shared/libosmocore/src/gsm/Makefile.am b/src/shared/libosmocore/src/gsm/Makefile.am
new file mode 100644
index 00000000..46d40a11
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/Makefile.am
@@ -0,0 +1,25 @@
+# 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=2:0:1
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS = -fPIC -Wall
+
+# FIXME: this should eventually go into a milenage/Makefile.am
+noinst_HEADERS = milenage/aes.h milenage/aes_i.h milenage/aes_wrap.h \
+ milenage/common.h milenage/crypto.h milenage/includes.h \
+ milenage/milenage.h
+
+lib_LTLIBRARIES = libosmogsm.la
+
+libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \
+ rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \
+ gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \
+ gsm0411_utils.c gsm0411_smc.c gsm0411_smr.c \
+ lapd_core.c lapdm.c \
+ auth_core.c auth_comp128v1.c auth_milenage.c \
+ milenage/aes-encblock.c milenage/aes-internal.c \
+ milenage/aes-internal-enc.c milenage/milenage.c
+
+libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION)
+libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la
diff --git a/src/shared/libosmocore/src/gsm/a5.c b/src/shared/libosmocore/src/gsm/a5.c
new file mode 100644
index 00000000..e330c759
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/a5.c
@@ -0,0 +1,367 @@
+/*
+ * a5.c
+ *
+ * Full reimplementation of A5/1,2 (split and threadsafe)
+ *
+ * The logic behind the algorithm is taken from "A pedagogical implementation
+ * of the GSM A5/1 and A5/2 "voice privacy" encryption algorithms." by
+ * Marc Briceno, Ian Goldberg, and David Wagner.
+ *
+ * Copyright (C) 2011 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.
+ */
+
+/*! \addtogroup a5
+ * @{
+ */
+
+/*! \file gsm/a5.c
+ * \brief Osmocom GSM A5 ciphering algorithm implementation
+ */
+
+#include <string.h>
+
+#include <osmocom/gsm/a5.h>
+
+/*! \brief Main method to generate a A5/x cipher stream
+ * \param[in] n Which A5/x method to use
+ * \param[in] key 8 byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ *
+ * Currently A5/[0-2] are supported.
+ * Either (or both) of dl/ul can be NULL if not needed.
+ */
+void
+osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ switch (n)
+ {
+ case 0:
+ if (dl)
+ memset(dl, 0x00, 114);
+ if (ul)
+ memset(ul, 0x00, 114);
+ break;
+
+ case 1:
+ osmo_a5_1(key, fn, dl, ul);
+ break;
+
+ case 2:
+ osmo_a5_2(key, fn, dl, ul);
+ break;
+
+ default:
+ /* a5/[3..7] not supported here/yet */
+ break;
+ }
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* A5/1&2 common stuff */
+/* ------------------------------------------------------------------------ */
+
+#define A5_R1_LEN 19
+#define A5_R2_LEN 22
+#define A5_R3_LEN 23
+#define A5_R4_LEN 17 /* A5/2 only */
+
+#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
+#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
+#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
+#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
+
+#define A5_R1_TAPS 0x072000 /* x^19 + x^5 + x^2 + x + 1 */
+#define A5_R2_TAPS 0x300000 /* x^22 + x + 1 */
+#define A5_R3_TAPS 0x700080 /* x^23 + x^15 + x^2 + x + 1 */
+#define A5_R4_TAPS 0x010800 /* x^17 + x^5 + 1 */
+
+/*! \brief Computes parity of a 32-bit word
+ * \param[in] x 32 bit word
+ * \return Parity bit (xor of all bits) as 0 or 1
+ */
+static inline uint32_t
+_a5_12_parity(uint32_t x)
+{
+ x ^= x >> 16;
+ x ^= x >> 8;
+ x ^= x >> 4;
+ x &= 0xf;
+ return (0x6996 >> x) & 1;
+}
+
+/*! \brief Compute majority bit from 3 taps
+ * \param[in] v1 LFSR state ANDed with tap-bit
+ * \param[in] v2 LFSR state ANDed with tap-bit
+ * \param[in] v3 LFSR state ANDed with tap-bit
+ * \return The majority bit (0 or 1)
+ */
+static inline uint32_t
+_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
+{
+ return (!!v1 + !!v2 + !!v3) >= 2;
+}
+
+/*! \brief Compute the next LFSR state
+ * \param[in] r Current state
+ * \param[in] mask LFSR mask
+ * \param[in] taps LFSR taps
+ * \return Next state
+ */
+static inline uint32_t
+_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
+{
+ return ((r << 1) & mask) | _a5_12_parity(r & taps);
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* A5/1 */
+/* ------------------------------------------------------------------------ */
+
+#define A51_R1_CLKBIT 0x000100
+#define A51_R2_CLKBIT 0x000400
+#define A51_R3_CLKBIT 0x000400
+
+/*! \brief GSM A5/1 Clocking function
+ * \param[in] r Register state
+ * \param[in] force Non-zero value disable conditional clocking
+ */
+static inline void
+_a5_1_clock(uint32_t r[], int force)
+{
+ int cb[3], maj;
+
+ cb[0] = !!(r[0] & A51_R1_CLKBIT);
+ cb[1] = !!(r[1] & A51_R2_CLKBIT);
+ cb[2] = !!(r[2] & A51_R3_CLKBIT);
+
+ maj = _a5_12_majority(cb[0], cb[1], cb[2]);
+
+ if (force || (maj == cb[0]))
+ r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
+
+ if (force || (maj == cb[1]))
+ r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
+
+ if (force || (maj == cb[2]))
+ r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
+}
+
+/*! \brief GSM A5/1 Output function
+ * \param[in] r Register state
+ * \return The A5/1 output function bit
+ */
+static inline uint8_t
+_a5_1_get_output(uint32_t r[])
+{
+ return (r[0] >> (A5_R1_LEN-1)) ^
+ (r[1] >> (A5_R2_LEN-1)) ^
+ (r[2] >> (A5_R3_LEN-1));
+}
+
+/*! \brief Generate a GSM A5/1 cipher stream
+ * \param[in] key 8 byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ *
+ * Either (or both) of dl/ul can be NULL if not needed.
+ */
+void
+osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ uint32_t r[3] = {0, 0, 0};
+ uint32_t fn_count;
+ uint32_t b;
+ int i;
+
+ /* Key load */
+ for (i=0; i<64; i++)
+ {
+ b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
+
+ _a5_1_clock(r, 1);
+
+ r[0] ^= b;
+ r[1] ^= b;
+ r[2] ^= b;
+ }
+
+ /* Frame count load */
+ fn_count = osmo_a5_fn_count(fn);
+
+ for (i=0; i<22; i++)
+ {
+ b = (fn_count >> i) & 1;
+
+ _a5_1_clock(r, 1);
+
+ r[0] ^= b;
+ r[1] ^= b;
+ r[2] ^= b;
+ }
+
+ /* Mix */
+ for (i=0; i<100; i++)
+ {
+ _a5_1_clock(r, 0);
+ }
+
+ /* Output */
+ for (i=0; i<114; i++) {
+ _a5_1_clock(r, 0);
+ if (dl)
+ dl[i] = _a5_1_get_output(r);
+ }
+
+ for (i=0; i<114; i++) {
+ _a5_1_clock(r, 0);
+ if (ul)
+ ul[i] = _a5_1_get_output(r);
+ }
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* A5/2 */
+/* ------------------------------------------------------------------------ */
+
+#define A52_R4_CLKBIT0 0x000400
+#define A52_R4_CLKBIT1 0x000008
+#define A52_R4_CLKBIT2 0x000080
+
+/*! \brief GSM A5/2 Clocking function
+ * \param[in] r Register state
+ * \param[in] force Non-zero value disable conditional clocking
+ */
+static inline void
+_a5_2_clock(uint32_t r[], int force)
+{
+ int cb[3], maj;
+
+ cb[0] = !!(r[3] & A52_R4_CLKBIT0);
+ cb[1] = !!(r[3] & A52_R4_CLKBIT1);
+ cb[2] = !!(r[3] & A52_R4_CLKBIT2);
+
+ maj = (cb[0] + cb[1] + cb[2]) >= 2;
+
+ if (force || (maj == cb[0]))
+ r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
+
+ if (force || (maj == cb[1]))
+ r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
+
+ if (force || (maj == cb[2]))
+ r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
+
+ r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
+}
+
+/*! \brief GSM A5/2 Output function
+ * \param[in] r Register state
+ * \return The A5/2 output function bit
+ */
+static inline uint8_t
+_a5_2_get_output(uint32_t r[])
+{
+ uint8_t b;
+
+ b = (r[0] >> (A5_R1_LEN-1)) ^
+ (r[1] >> (A5_R2_LEN-1)) ^
+ (r[2] >> (A5_R3_LEN-1)) ^
+ _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
+ _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
+ _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000);
+
+ return b;
+}
+
+/*! \brief Generate a GSM A5/1 cipher stream
+ * \param[in] key 8 byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ *
+ * Either (or both) of dl/ul can be NULL if not needed.
+ */
+void
+osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ uint32_t r[4] = {0, 0, 0, 0};
+ uint32_t fn_count;
+ uint32_t b;
+ int i;
+
+ /* Key load */
+ for (i=0; i<64; i++)
+ {
+ b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
+
+ _a5_2_clock(r, 1);
+
+ r[0] ^= b;
+ r[1] ^= b;
+ r[2] ^= b;
+ r[3] ^= b;
+ }
+
+ /* Frame count load */
+ fn_count = osmo_a5_fn_count(fn);
+
+ for (i=0; i<22; i++)
+ {
+ b = (fn_count >> i) & 1;
+
+ _a5_2_clock(r, 1);
+
+ r[0] ^= b;
+ r[1] ^= b;
+ r[2] ^= b;
+ r[3] ^= b;
+ }
+
+ r[0] |= 1 << 15;
+ r[1] |= 1 << 16;
+ r[2] |= 1 << 18;
+ r[3] |= 1 << 10;
+
+ /* Mix */
+ for (i=0; i<99; i++)
+ {
+ _a5_2_clock(r, 0);
+ }
+
+ /* Output */
+ for (i=0; i<114; i++) {
+ _a5_2_clock(r, 0);
+ if (dl)
+ dl[i] = _a5_2_get_output(r);
+ }
+
+ for (i=0; i<114; i++) {
+ _a5_2_clock(r, 0);
+ if (ul)
+ ul[i] = _a5_2_get_output(r);
+ }
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/gsm/abis_nm.c b/src/shared/libosmocore/src/gsm/abis_nm.c
new file mode 100644
index 00000000..5c3647ca
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/abis_nm.c
@@ -0,0 +1,455 @@
+/* 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-2011 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*! \addtogroup oml
+ * @{
+ */
+
+/*! \file abis_nm.c */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+#include <osmocom/gsm/abis_nm.h>
+
+/*! \brief unidirectional messages from BTS to BSC */
+const enum abis_nm_msgtype abis_nm_reports[4] = {
+ NM_MT_SW_ACTIVATED_REP,
+ NM_MT_TEST_REP,
+ NM_MT_STATECHG_EVENT_REP,
+ NM_MT_FAILURE_EVENT_REP,
+};
+
+/*! \brief messages without ACK/NACK */
+const enum abis_nm_msgtype abis_nm_no_ack_nack[3] = {
+ NM_MT_MEAS_RES_REQ,
+ NM_MT_STOP_MEAS,
+ NM_MT_START_MEAS,
+};
+
+/*! \brief messages related to software load */
+const enum abis_nm_msgtype abis_nm_sw_load_msgs[9] = {
+ NM_MT_LOAD_INIT_ACK,
+ NM_MT_LOAD_INIT_NACK,
+ NM_MT_LOAD_SEG_ACK,
+ NM_MT_LOAD_ABORT,
+ NM_MT_LOAD_END_ACK,
+ NM_MT_LOAD_END_NACK,
+ //NM_MT_SW_ACT_REQ,
+ NM_MT_ACTIVATE_SW_ACK,
+ NM_MT_ACTIVATE_SW_NACK,
+ NM_MT_SW_ACTIVATED_REP,
+};
+
+/*! \brief All NACKs (negative acknowledgements */
+const enum abis_nm_msgtype abis_nm_nacks[33] = {
+ NM_MT_LOAD_INIT_NACK,
+ NM_MT_LOAD_END_NACK,
+ NM_MT_SW_ACT_REQ_NACK,
+ NM_MT_ACTIVATE_SW_NACK,
+ NM_MT_ESTABLISH_TEI_NACK,
+ NM_MT_CONN_TERR_SIGN_NACK,
+ NM_MT_DISC_TERR_SIGN_NACK,
+ NM_MT_CONN_TERR_TRAF_NACK,
+ NM_MT_DISC_TERR_TRAF_NACK,
+ NM_MT_CONN_MDROP_LINK_NACK,
+ NM_MT_DISC_MDROP_LINK_NACK,
+ NM_MT_SET_BTS_ATTR_NACK,
+ NM_MT_SET_RADIO_ATTR_NACK,
+ NM_MT_SET_CHAN_ATTR_NACK,
+ NM_MT_PERF_TEST_NACK,
+ NM_MT_SEND_TEST_REP_NACK,
+ NM_MT_STOP_TEST_NACK,
+ NM_MT_STOP_EVENT_REP_NACK,
+ NM_MT_REST_EVENT_REP_NACK,
+ NM_MT_CHG_ADM_STATE_NACK,
+ NM_MT_CHG_ADM_STATE_REQ_NACK,
+ NM_MT_REP_OUTST_ALARMS_NACK,
+ NM_MT_CHANGEOVER_NACK,
+ NM_MT_OPSTART_NACK,
+ NM_MT_REINIT_NACK,
+ NM_MT_SET_SITE_OUT_NACK,
+ NM_MT_CHG_HW_CONF_NACK,
+ NM_MT_GET_ATTR_NACK,
+ NM_MT_SET_ALARM_THRES_NACK,
+ NM_MT_BS11_BEGIN_DB_TX_NACK,
+ NM_MT_BS11_END_DB_TX_NACK,
+ NM_MT_BS11_CREATE_OBJ_NACK,
+ NM_MT_BS11_DELETE_OBJ_NACK,
+};
+
+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 }
+};
+
+/*! \brief Get human-readable string for OML NACK message type */
+const char *abis_nm_nack_name(uint8_t nack)
+{
+ return get_value_string(nack_names, nack);
+}
+
+/* Chapter 9.4.36 */
+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" },
+ /* 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" },
+ { 0, NULL }
+};
+
+/*! \brief Get human-readable string for NACK cause */
+const char *abis_nm_nack_cause_name(uint8_t cause)
+{
+ return get_value_string(nack_cause_names, cause);
+}
+
+/* Chapter 9.4.16: Event Type */
+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 }
+};
+
+/*! \brief Get human-readable string for OML event type */
+const char *abis_nm_event_type_name(uint8_t cause)
+{
+ return get_value_string(event_type_names, cause);
+}
+
+/* Chapter 9.4.63: Perceived Severity */
+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 }
+};
+
+/*! \brief Get human-readable string for perceived OML severity */
+const char *abis_nm_severity_name(uint8_t cause)
+{
+ return get_value_string(severity_names, cause);
+}
+
+/*! \brief Attributes that the BSC can set, not only get, according to Section 9.4 */
+const enum abis_nm_attr abis_nm_att_settable[] = {
+ NM_ATT_ADD_INFO,
+ NM_ATT_ADD_TEXT,
+ NM_ATT_DEST,
+ NM_ATT_EVENT_TYPE,
+ NM_ATT_FILE_DATA,
+ NM_ATT_GET_ARI,
+ NM_ATT_HW_CONF_CHG,
+ NM_ATT_LIST_REQ_ATTR,
+ NM_ATT_MDROP_LINK,
+ NM_ATT_MDROP_NEXT,
+ NM_ATT_NACK_CAUSES,
+ NM_ATT_OUTST_ALARM,
+ NM_ATT_PHYS_CONF,
+ NM_ATT_PROB_CAUSE,
+ NM_ATT_RAD_SUBC,
+ NM_ATT_SOURCE,
+ NM_ATT_SPEC_PROB,
+ NM_ATT_START_TIME,
+ NM_ATT_TEST_DUR,
+ NM_ATT_TEST_NO,
+ NM_ATT_TEST_REPORT,
+ NM_ATT_WINDOW_SIZE,
+ NM_ATT_SEVERITY,
+ NM_ATT_MEAS_RES,
+ NM_ATT_MEAS_TYPE,
+};
+
+/*! \brief GSM A-bis OML TLV parser definition */
+const struct tlv_definition abis_nm_att_tlvdef = {
+ .def = {
+ [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V },
+ [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V },
+ [NM_ATT_ADM_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V },
+ [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV },
+ [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V },
+ [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_BSIC] = { TLV_TYPE_TV },
+ [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV },
+ [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV },
+ [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV },
+ [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV },
+ [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V },
+ [NM_ATT_DEST] = { TLV_TYPE_TL16V },
+ [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV },
+ [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V },
+ [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V },
+ [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_HSN] = { TLV_TYPE_TV },
+ [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V },
+ [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V },
+ [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV },
+ [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 },
+ [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V },
+ [NM_ATT_MAIO] = { TLV_TYPE_TV },
+ [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V },
+ [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_MAX_TA] = { TLV_TYPE_TV },
+ [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV },
+ [NM_ATT_NY1] = { TLV_TYPE_TV },
+ [NM_ATT_OPER_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V },
+ [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V },
+ [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV },
+ [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV },
+ [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV },
+ [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV },
+ [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V },
+ [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V },
+ [NM_ATT_SOURCE] = { TLV_TYPE_TL16V },
+ [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV },
+ [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 },
+ [NM_ATT_TEI] = { TLV_TYPE_TV },
+ [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_TEST_NO] = { TLV_TYPE_TV },
+ [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V },
+ [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV },
+ [NM_ATT_TSC] = { TLV_TYPE_TV },
+ [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V },
+ [NM_ATT_SEVERITY] = { TLV_TYPE_TV },
+ [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V },
+ [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V },
+ [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV },
+ [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
+ },
+};
+
+/*! \brief Human-readable strings for A-bis OML Object Class */
+const struct value_string abis_nm_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 }
+};
+
+/*! \brief Get human-readable string for OML Operational State */
+const char *abis_nm_opstate_name(uint8_t os)
+{
+ switch (os) {
+ case NM_OPSTATE_DISABLED:
+ return "Disabled";
+ case NM_OPSTATE_ENABLED:
+ return "Enabled";
+ case NM_OPSTATE_NULL:
+ return "NULL";
+ default:
+ return "RFU";
+ }
+}
+
+/* Chapter 9.4.7 */
+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 }
+};
+
+/*! \brief Get human-readable string for OML Availability State */
+const char *abis_nm_avail_name(uint8_t avail)
+{
+ return get_value_string(avail_names, avail);
+}
+
+static struct value_string test_names[] = {
+ /* FIXME: standard test names */
+ { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" },
+ { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" },
+ { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" },
+ { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Info" },
+ { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" },
+ { NM_IPACC_TESTNO_SYSINFO_MONITOR, "System Info Monitor" },
+ { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH Monitor" },
+ { 0, NULL }
+};
+
+/*! \brief Get human-readable string for OML test */
+const char *abis_nm_test_name(uint8_t test)
+{
+ return get_value_string(test_names, test);
+}
+
+/*! \brief Human-readable names for OML administrative state */
+const struct value_string abis_nm_adm_state_names[] = {
+ { NM_STATE_LOCKED, "Locked" },
+ { NM_STATE_UNLOCKED, "Unlocked" },
+ { NM_STATE_SHUTDOWN, "Shutdown" },
+ { NM_STATE_NULL, "NULL" },
+ { 0, NULL }
+};
+
+/*! \brief write a human-readable OML header to the debug log
+ * \param[in] ss Logging sub-system
+ * \param[in] foh A-bis OML FOM header
+ */
+void abis_nm_debugp_foh(int ss, struct abis_om_fom_hdr *foh)
+{
+ DEBUGP(ss, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
+ get_value_string(abis_nm_obj_class_names, foh->obj_class),
+ foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
+ foh->obj_inst.ts_nr);
+}
+
+static const enum abis_nm_chan_comb chcomb4pchan[] = {
+ [GSM_PCHAN_NONE] = 0xff,
+ [GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH,
+ [GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCHComb,
+ [GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull,
+ [GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf,
+ [GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH,
+ [GSM_PCHAN_PDCH] = NM_CHANC_IPAC_PDCH,
+ [GSM_PCHAN_TCH_F_PDCH] = NM_CHANC_IPAC_TCHFull_PDCH,
+ [GSM_PCHAN_UNKNOWN] = 0xff,
+ /* FIXME: bounds check */
+};
+
+/*! \brief Obtain OML Channel Combination for phnsical channel config */
+int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan)
+{
+ if (pchan < ARRAY_SIZE(chcomb4pchan))
+ return chcomb4pchan[pchan];
+
+ return -EINVAL;
+}
+
+/*! \brief Obtain physical channel config for OML Channel Combination */
+enum abis_nm_chan_comb abis_nm_pchan4chcomb(uint8_t chcomb)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(chcomb4pchan); i++) {
+ if (chcomb4pchan[i] == chcomb)
+ return i;
+ }
+ return GSM_PCHAN_NONE;
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/gsm/auth_comp128v1.c b/src/shared/libosmocore/src/gsm/auth_comp128v1.c
new file mode 100644
index 00000000..41aef71c
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/auth_comp128v1.c
@@ -0,0 +1,47 @@
+
+/* GSM/GPRS/3G authentication core infrastructure */
+
+/* (C) 2010-2011 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 <osmocom/crypt/auth.h>
+#include <osmocom/gsm/comp128.h>
+
+static int c128v1_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *_rand)
+{
+ comp128(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
+ vec->auth_types = OSMO_AUTH_TYPE_GSM;
+
+ return 0;
+}
+
+static struct osmo_auth_impl c128v1_alg = {
+ .algo = OSMO_AUTH_ALG_COMP128v1,
+ .name = "COMP128v1 (libosmogsm built-in)",
+ .priority = 1000,
+ .gen_vec = &c128v1_gen_vec,
+};
+
+static __attribute__((constructor)) void on_dso_load_c128(void)
+{
+ osmo_auth_register(&c128v1_alg);
+}
diff --git a/src/shared/libosmocore/src/gsm/auth_core.c b/src/shared/libosmocore/src/gsm/auth_core.c
new file mode 100644
index 00000000..f790ff5b
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/auth_core.c
@@ -0,0 +1,121 @@
+/* GSM/GPRS/3G authentication core infrastructure */
+
+/* (C) 2010-2011 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 <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/plugin.h>
+
+#include <osmocom/crypt/auth.h>
+
+static LLIST_HEAD(osmo_auths);
+
+static struct osmo_auth_impl *selected_auths[_OSMO_AUTH_ALG_NUM];
+
+/* register a cipher with the core */
+int osmo_auth_register(struct osmo_auth_impl *impl)
+{
+ if (impl->algo >= ARRAY_SIZE(selected_auths))
+ return -ERANGE;
+
+ llist_add_tail(&impl->list, &osmo_auths);
+
+ /* check if we want to select this implementation over others */
+ if (!selected_auths[impl->algo] ||
+ (selected_auths[impl->algo]->priority > impl->priority))
+ selected_auths[impl->algo] = impl;
+
+ return 0;
+}
+
+/* load all available GPRS cipher plugins */
+int osmo_auth_load(const char *path)
+{
+ /* load all plugins available from path */
+ return osmo_plugin_load_all(path);
+}
+
+int osmo_auth_supported(enum osmo_auth_algo algo)
+{
+ if (algo >= ARRAY_SIZE(selected_auths))
+ return -ERANGE;
+
+ if (selected_auths[algo])
+ return 1;
+
+ return 0;
+}
+
+int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *_rand)
+{
+ struct osmo_auth_impl *impl = selected_auths[aud->algo];
+ int rc;
+
+ if (!impl)
+ return -ENOENT;
+
+ rc = impl->gen_vec(vec, aud, _rand);
+ if (rc < 0)
+ return rc;
+
+ memcpy(vec->rand, _rand, sizeof(vec->rand));
+
+ return 0;
+}
+
+int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *rand_auts, const uint8_t *auts,
+ const uint8_t *_rand)
+{
+ struct osmo_auth_impl *impl = selected_auths[aud->algo];
+
+ if (!impl || !impl->gen_vec_auts)
+ return -ENOENT;
+
+ return impl->gen_vec_auts(vec, aud, rand_auts, auts, _rand);
+}
+
+const struct value_string auth_alg_vals[] = {
+ { OSMO_AUTH_ALG_NONE, "None" },
+ { OSMO_AUTH_ALG_COMP128v1, "COMP128v1" },
+ { OSMO_AUTH_ALG_COMP128v2, "COMP128v2" },
+ { OSMO_AUTH_ALG_COMP128v3, "COMP128v3" },
+ { OSMO_AUTH_ALG_XOR, "XOR" },
+ { OSMO_AUTH_ALG_MILENAGE, "MILENAGE" },
+ { 0, NULL }
+};
+
+const char *osmo_auth_alg_name(enum osmo_auth_algo alg)
+{
+ return get_value_string(auth_alg_vals, alg);
+}
+
+enum osmo_auth_algo osmo_auth_alg_parse(const char *name)
+{
+ return get_string_value(auth_alg_vals, name);
+}
diff --git a/src/shared/libosmocore/src/gsm/auth_milenage.c b/src/shared/libosmocore/src/gsm/auth_milenage.c
new file mode 100644
index 00000000..2a9ba334
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/auth_milenage.c
@@ -0,0 +1,109 @@
+/* GSM/GPRS/3G authentication core infrastructure */
+
+/* (C) 2011 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 <osmocom/crypt/auth.h>
+#include "milenage/common.h"
+#include "milenage/milenage.h"
+
+static void sqn_u64_to_48bit(uint8_t *sqn, const uint64_t sqn64)
+{
+ sqn[5] = (sqn64 >> 0) & 0xff;
+ sqn[4] = (sqn64 >> 8) & 0xff;
+ sqn[3] = (sqn64 >> 16) & 0xff;
+ sqn[2] = (sqn64 >> 24) & 0xff;
+ sqn[1] = (sqn64 >> 32) & 0xff;
+ sqn[0] = (sqn64 >> 40) & 0xff;
+}
+
+static uint64_t sqn_48bit_to_u64(const uint8_t *sqn)
+{
+ uint64_t sqn64;
+
+ sqn64 = sqn[0];
+ sqn64 <<= 8;
+ sqn64 |= sqn[1];
+ sqn64 <<= 8;
+ sqn64 |= sqn[2];
+ sqn64 <<= 8;
+ sqn64 |= sqn[3];
+ sqn64 <<= 8;
+ sqn64 |= sqn[4];
+ sqn64 <<= 8;
+ sqn64 |= sqn[5];
+
+ return sqn64;
+}
+
+
+static int milenage_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *_rand)
+{
+ size_t res_len = sizeof(vec->res);
+ uint8_t sqn[6];
+ int rc;
+
+ sqn_u64_to_48bit(sqn, aud->u.umts.sqn);
+ milenage_generate(aud->u.umts.opc, aud->u.umts.amf, aud->u.umts.k,
+ sqn, _rand,
+ vec->autn, vec->ik, vec->ck, vec->res, &res_len);
+ vec->res_len = res_len;
+ rc = gsm_milenage(aud->u.umts.opc, aud->u.umts.k, _rand, vec->sres, vec->kc);
+ if (rc < 0)
+ return rc;
+
+ vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
+ aud->u.umts.sqn++;
+
+ return 0;
+}
+
+static int milenage_gen_vec_auts(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand)
+{
+ uint8_t sqn_out[6];
+ int rc;
+
+ rc = milenage_auts(aud->u.umts.opc, aud->u.umts.k,
+ rand_auts, auts, sqn_out);
+ if (rc < 0)
+ return rc;
+
+ aud->u.umts.sqn = sqn_48bit_to_u64(sqn_out) + 1;
+
+ return milenage_gen_vec(vec, aud, _rand);
+}
+
+static struct osmo_auth_impl milenage_alg = {
+ .algo = OSMO_AUTH_ALG_MILENAGE,
+ .name = "MILENAGE (libosmogsm built-in)",
+ .priority = 1000,
+ .gen_vec = &milenage_gen_vec,
+ .gen_vec_auts = &milenage_gen_vec_auts,
+};
+
+static __attribute__((constructor)) void on_dso_load_milenage(void)
+{
+ osmo_auth_register(&milenage_alg);
+}
diff --git a/src/shared/libosmocore/src/gsm/comp128.c b/src/shared/libosmocore/src/gsm/comp128.c
new file mode 100644
index 00000000..b7a23820
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/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(const uint8_t *ki, const 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/src/shared/libosmocore/src/gsm/gprs_cipher_core.c b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c
new file mode 100644
index 00000000..b9a22a10
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c
@@ -0,0 +1,99 @@
+/* GPRS LLC cipher core infrastructure */
+
+/* (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 <errno.h>
+#include <stdint.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/plugin.h>
+
+#include <osmocom/crypt/gprs_cipher.h>
+
+static LLIST_HEAD(gprs_ciphers);
+
+static struct gprs_cipher_impl *selected_ciphers[_GPRS_ALGO_NUM];
+
+/* register a cipher with the core */
+int gprs_cipher_register(struct gprs_cipher_impl *ciph)
+{
+ if (ciph->algo >= ARRAY_SIZE(selected_ciphers))
+ return -ERANGE;
+
+ llist_add_tail(&ciph->list, &gprs_ciphers);
+
+ /* check if we want to select this implementation over others */
+ if (!selected_ciphers[ciph->algo] ||
+ (selected_ciphers[ciph->algo]->priority > ciph->priority))
+ selected_ciphers[ciph->algo] = ciph;
+
+ return 0;
+}
+
+/* load all available GPRS cipher plugins */
+int gprs_cipher_load(const char *path)
+{
+ /* load all plugins available from path */
+ return osmo_plugin_load_all(path);
+}
+
+/* function to be called by core code */
+int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo,
+ uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir)
+{
+ if (algo >= ARRAY_SIZE(selected_ciphers))
+ return -ERANGE;
+
+ if (!selected_ciphers[algo])
+ return -EINVAL;
+
+ if (len > GSM0464_CIPH_MAX_BLOCK)
+ return -ERANGE;
+
+ /* run the actual cipher from the plugin */
+ return selected_ciphers[algo]->run(out, len, kc, iv, dir);
+}
+
+int gprs_cipher_supported(enum gprs_ciph_algo algo)
+{
+ if (algo >= ARRAY_SIZE(selected_ciphers))
+ return -ERANGE;
+
+ if (selected_ciphers[algo])
+ return 1;
+
+ return 0;
+}
+
+/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
+uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc)
+{
+ uint32_t sx = ((1<<27) * sapi) + (1<<31);
+
+ return (iov_ui ^ sx) + lfn + oc;
+}
+
+/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
+uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc)
+{
+ return iov_i + lfn + oc;
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm0411_smc.c b/src/shared/libosmocore/src/gsm/gsm0411_smc.c
new file mode 100644
index 00000000..54e6129c
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm0411_smc.c
@@ -0,0 +1,539 @@
+/* Point-to-Point (PP) Short Message Service (SMS)
+ * Support on Mobile Radio Interface
+ * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */
+
+/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by On-Waves
+ * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Notes on msg:
+ *
+ * Messages from lower layer are freed by lower layer.
+ *
+ * Messages to upper layer are freed after upper layer call returns, so upper
+ * layer cannot use data after returning. Upper layer must not free the msg.
+ *
+ * This implies: Lower layer messages can be forwarded to upper layer.
+ *
+ * Upper layer messages are freed by lower layer, so they must not be freed
+ * after calling lower layer.
+ *
+ *
+ * Notes on release:
+ *
+ * Whenever the process returns to IDLE, the MM connection is released using
+ * MMSMS-REL-REQ. It is allowed to destroy this process while processing
+ * this message.
+ *
+ * There is expeption, if MMSMS-REL-IND is received from lower layer, the
+ * process returns to IDLE without sending MMSMS-REL-REQ.
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
+
+#include <osmocom/gsm/gsm0411_utils.h>
+#include <osmocom/gsm/gsm0411_smc.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+static void cp_timer_expired(void *data);
+
+#define MAX_SMS_RETRY 2
+
+/* init a new instance */
+void gsm411_smc_init(struct gsm411_smc_inst *inst, int network,
+ int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg),
+ int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg, int cp_msg_type))
+{
+ memset(inst, 0, sizeof(*inst));
+ inst->network = network;
+ inst->cp_max_retr = MAX_SMS_RETRY;
+ inst->cp_tc1 = GSM411_TMR_TC1A_SEC / (inst->cp_max_retr + 1);
+ inst->cp_state = GSM411_CPS_IDLE;
+ inst->mn_recv = mn_recv;
+ inst->mm_send = mm_send;
+
+ LOGP(DLSMS, LOGL_INFO, "New SMC instance created\n");
+}
+
+/* clear instance */
+void gsm411_smc_clear(struct gsm411_smc_inst *inst)
+{
+ LOGP(DLSMS, LOGL_INFO, "Clear SMC instance\n");
+
+ osmo_timer_del(&inst->cp_timer);
+
+ /* free stored msg */
+ if (inst->cp_msg) {
+ LOGP(DLSMS, LOGL_INFO, "Dropping pending message\n");
+ msgb_free(inst->cp_msg);
+ inst->cp_msg = NULL;
+ }
+}
+
+const char *smc_state_names[] = {
+ "IDLE",
+ "MM_CONN_PENDING",
+ "WAIT_CP_ACK",
+ "MM_ESTABLISHED",
+};
+
+const struct value_string gsm411_cp_cause_strs[] = {
+ { GSM411_CP_CAUSE_NET_FAIL, "Network Failure" },
+ { GSM411_CP_CAUSE_CONGESTION, "Congestion" },
+ { GSM411_CP_CAUSE_INV_TRANS_ID, "Invalid Transaction ID" },
+ { 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,
+ "Message incompatible with protocol state" },
+ { GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" },
+ { GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" },
+ { 0, 0 }
+};
+
+static void new_cp_state(struct gsm411_smc_inst *inst,
+ enum gsm411_cp_state state)
+{
+ LOGP(DLSMS, LOGL_INFO, "New CP state %s -> %s\n",
+ smc_state_names[inst->cp_state], smc_state_names[state]);
+ inst->cp_state = state;
+}
+
+static int gsm411_tx_cp_error(struct gsm411_smc_inst *inst, uint8_t cause)
+{
+ struct msgb *nmsg = gsm411_msgb_alloc();
+ uint8_t *causep;
+
+ LOGP(DLSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause,
+ get_value_string(gsm411_cp_cause_strs, cause));
+
+ causep = msgb_put(nmsg, 1);
+ *causep = cause;
+
+ return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg,
+ GSM411_MT_CP_ERROR);
+}
+
+/* etablish SMC connection */
+static int gsm411_mnsms_est_req(struct gsm411_smc_inst *inst, struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ if (inst->cp_msg) {
+ LOGP(DLSMS, LOGL_FATAL, "EST REQ, but we already have an "
+ "cp_msg. This should never happen, please fix!\n");
+ msgb_free(inst->cp_msg);
+ }
+
+ inst->cp_msg = msg;
+ new_cp_state(inst, GSM411_CPS_MM_CONN_PENDING);
+ /* clear stored release flag */
+ inst->cp_rel = 0;
+ /* send MMSMS_EST_REQ */
+ nmsg = gsm411_msgb_alloc();
+ return inst->mm_send(inst, GSM411_MMSMS_EST_REQ, nmsg, 0);
+}
+
+static int gsm411_mmsms_send_msg(struct gsm411_smc_inst *inst)
+{
+ struct msgb *nmsg;
+
+ LOGP(DLSMS, LOGL_INFO, "Send CP data\n");
+ /* reset retry counter */
+ if (inst->cp_state != GSM411_CPS_WAIT_CP_ACK)
+ inst->cp_retx = 0;
+ /* 5.2.3.1.2: enter MO-wait for CP-ACK */
+ /* 5.2.3.2.3: enter MT-wait for CP-ACK */
+ new_cp_state(inst, GSM411_CPS_WAIT_CP_ACK);
+ inst->cp_timer.data = inst;
+ inst->cp_timer.cb = cp_timer_expired;
+ /* 5.3.2.1: Set Timer TC1A */
+ osmo_timer_schedule(&inst->cp_timer, inst->cp_tc1, 0);
+ /* clone cp_msg */
+ nmsg = gsm411_msgb_alloc();
+ memcpy(msgb_put(nmsg, inst->cp_msg->len), inst->cp_msg->data,
+ inst->cp_msg->len);
+ /* send MMSMS_DATA_REQ with CP-DATA */
+ return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg,
+ GSM411_MT_CP_DATA);
+}
+
+static int gsm411_mmsms_est_cnf(struct gsm411_smc_inst *inst, struct msgb *msg)
+{
+ if (!inst->cp_msg) {
+ LOGP(DLSMS, LOGL_FATAL, "EST CNF, but we have no cp_msg. This "
+ "should never happen, please fix!\n");
+ return -EINVAL;
+ }
+
+ return gsm411_mmsms_send_msg(inst);
+}
+
+/* SMC TC1* is expired */
+static void cp_timer_expired(void *data)
+{
+ struct gsm411_smc_inst *inst = data;
+ struct msgb *nmsg;
+
+ if (inst->cp_retx == inst->cp_max_retr) {
+
+ LOGP(DLSMS, LOGL_INFO, "TC1* timeout, no more retries.\n");
+ /* 5.3.2.1: enter idle state */
+ new_cp_state(inst, GSM411_CPS_IDLE);
+ /* indicate error */
+ nmsg = gsm411_msgb_alloc();
+ inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg);
+ msgb_free(nmsg);
+ /* free pending stored msg */
+ if (inst->cp_msg) {
+ msgb_free(inst->cp_msg);
+ inst->cp_msg = NULL;
+ }
+ /* release MM connection */
+ nmsg = gsm411_msgb_alloc();
+ inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
+ return;
+ }
+
+ LOGP(DLSMS, LOGL_INFO, "TC1* timeout, retrying...\n");
+ inst->cp_retx++;
+ gsm411_mmsms_est_cnf(inst, NULL);
+}
+
+static int gsm411_mmsms_cp_ack(struct gsm411_smc_inst *inst, struct msgb *msg)
+{
+ /* free stored msg */
+ if (inst->cp_msg) {
+ msgb_free(inst->cp_msg);
+ inst->cp_msg = NULL;
+ }
+
+ LOGP(DLSMS, LOGL_INFO, "Received CP-ACK\n");
+ /* 5.3.2.1 enter MM Connection established */
+ new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED);
+ /* 5.3.2.1: Reset Timer TC1* */
+ osmo_timer_del(&inst->cp_timer);
+
+ /* pending release? */
+ if (inst->cp_rel) {
+ struct msgb *nmsg;
+
+ LOGP(DLSMS, LOGL_INFO, "We have pending release.\n");
+ new_cp_state(inst, GSM411_CPS_IDLE);
+ /* release MM connection */
+ nmsg = gsm411_msgb_alloc();
+ return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
+ }
+
+ return 0;
+}
+
+static int gsm411_mmsms_cp_data(struct gsm411_smc_inst *inst, struct msgb *msg)
+{
+ struct msgb *nmsg;
+ int mt = GSM411_MNSMS_DATA_IND;
+
+ LOGP(DLSMS, LOGL_INFO, "Received CP-DATA\n");
+ /* 5.3.1 enter MM Connection established (if idle) */
+ if (inst->cp_state == GSM411_CPS_IDLE) {
+ new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED);
+ mt = GSM411_MNSMS_EST_IND;
+ /* clear stored release flag */
+ inst->cp_rel = 0;
+ }
+ /* send MMSMS_DATA_REQ (CP ACK) */
+ nmsg = gsm411_msgb_alloc();
+ inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, GSM411_MT_CP_ACK);
+ /* indicate data */
+ inst->mn_recv(inst, mt, msg);
+
+ return 0;
+}
+
+/* send CP DATA */
+static int gsm411_mnsms_data_req(struct gsm411_smc_inst *inst, struct msgb *msg)
+{
+ if (inst->cp_msg) {
+ LOGP(DLSMS, LOGL_FATAL, "DATA REQ, but we already have an "
+ "cp_msg. This should never happen, please fix!\n");
+ msgb_free(inst->cp_msg);
+ }
+
+ /* store and send */
+ inst->cp_msg = msg;
+ return gsm411_mmsms_send_msg(inst);
+}
+
+/* release SMC connection */
+static int gsm411_mnsms_rel_req(struct gsm411_smc_inst *inst, struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ msgb_free(msg);
+
+ /* discard silently */
+ if (inst->cp_state == GSM411_CPS_IDLE)
+ return 0;
+
+ /* store release, until established or released */
+ if (inst->cp_state != GSM411_CPS_MM_ESTABLISHED) {
+ LOGP(DLSMS, LOGL_NOTICE, "Cannot release yet.\n");
+ inst->cp_rel = 1;
+ return 0;
+ }
+
+ /* free stored msg */
+ if (inst->cp_msg) {
+ msgb_free(inst->cp_msg);
+ inst->cp_msg = NULL;
+ }
+
+ new_cp_state(inst, GSM411_CPS_IDLE);
+ /* release MM connection */
+ nmsg = gsm411_msgb_alloc();
+ return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
+}
+
+static int gsm411_mmsms_cp_error(struct gsm411_smc_inst *inst, struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ /* free stored msg */
+ if (inst->cp_msg) {
+ msgb_free(inst->cp_msg);
+ inst->cp_msg = NULL;
+ }
+
+ LOGP(DLSMS, LOGL_INFO, "Received CP-ERROR\n");
+ /* 5.3.4 enter idle */
+ new_cp_state(inst, GSM411_CPS_IDLE);
+ /* indicate error */
+ inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, msg);
+ /* release MM connection */
+ nmsg = gsm411_msgb_alloc();
+ return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
+}
+
+static int gsm411_mmsms_rel_ind(struct gsm411_smc_inst *inst, struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ /* free stored msg */
+ if (inst->cp_msg) {
+ msgb_free(inst->cp_msg);
+ inst->cp_msg = NULL;
+ }
+
+ LOGP(DLSMS, LOGL_INFO, "MM layer is released\n");
+ /* 5.3.4 enter idle */
+ new_cp_state(inst, GSM411_CPS_IDLE);
+ /* indicate error */
+ nmsg = gsm411_msgb_alloc();
+ inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg);
+ msgb_free(nmsg);
+
+ return 0;
+}
+
+/* abort SMC connection */
+static int gsm411_mnsms_abort_req(struct gsm411_smc_inst *inst,
+ struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ /* free stored msg */
+ if (inst->cp_msg) {
+ msgb_free(inst->cp_msg);
+ inst->cp_msg = NULL;
+ }
+
+ /* 5.3.4 go idle */
+ new_cp_state(inst, GSM411_CPS_IDLE);
+ /* send MMSMS_DATA_REQ with CP-ERROR */
+ inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, msg, GSM411_MT_CP_ERROR);
+ /* release MM connection */
+ nmsg = gsm411_msgb_alloc();
+ return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
+}
+
+/* statefull handling for MNSMS SAP messages */
+static struct smcdownstate {
+ uint32_t states;
+ int type;
+ const char *name;
+ int (*rout) (struct gsm411_smc_inst *inst,
+ struct msgb *msg);
+} smcdownstatelist[] = {
+ /* establish request */
+ {SBIT(GSM411_CPS_IDLE),
+ GSM411_MNSMS_EST_REQ,
+ "MNSMS-EST-REQ", gsm411_mnsms_est_req},
+
+ /* release request */
+ {ALL_STATES,
+ GSM411_MNSMS_REL_REQ,
+ "MNSMS-REL-REQ", gsm411_mnsms_rel_req},
+
+ /* data request */
+ {SBIT(GSM411_CPS_MM_ESTABLISHED),
+ GSM411_MNSMS_DATA_REQ,
+ "MNSMS-DATA-REQ", gsm411_mnsms_data_req},
+
+ /* abort request */
+ {ALL_STATES - SBIT(GSM411_CPS_IDLE),
+ GSM411_MNSMS_ABORT_REQ,
+ "MNSMS-ABORT-REQ", gsm411_mnsms_abort_req},
+};
+
+#define SMCDOWNSLLEN \
+ (sizeof(smcdownstatelist) / sizeof(struct smcdownstate))
+
+/* message from upper layer */
+int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg)
+{
+ int i, rc;
+
+ /* find function for current state and message */
+ for (i = 0; i < SMCDOWNSLLEN; i++) {
+ if ((msg_type == smcdownstatelist[i].type)
+ && (SBIT(inst->cp_state) & smcdownstatelist[i].states))
+ break;
+ }
+ if (i == SMCDOWNSLLEN) {
+ LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state "
+ "%s.\n", msg_type, smc_state_names[inst->cp_state]);
+ msgb_free(msg);
+ return 0;
+ }
+
+ LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
+ smcdownstatelist[i].name, smc_state_names[inst->cp_state]);
+
+ rc = smcdownstatelist[i].rout(inst, msg);
+
+ return rc;
+}
+
+/* statefull handling for MMSMS SAP messages */
+static struct smcdatastate {
+ uint32_t states;
+ int type, cp_type;
+ const char *name;
+ int (*rout) (struct gsm411_smc_inst *inst,
+ struct msgb *msg);
+} smcdatastatelist[] = {
+ /* establish confirm */
+ {SBIT(GSM411_CPS_MM_CONN_PENDING),
+ GSM411_MMSMS_EST_CNF, 0,
+ "MMSMS-EST-CNF", gsm411_mmsms_est_cnf},
+
+ /* establish indication (CP DATA) */
+ {SBIT(GSM411_CPS_IDLE),
+ GSM411_MMSMS_EST_IND, GSM411_MT_CP_DATA,
+ "MMSMS-EST-IND (CP DATA)", gsm411_mmsms_cp_data},
+
+ /* data indication (CP DATA) */
+ {SBIT(GSM411_CPS_MM_ESTABLISHED),
+ GSM411_MMSMS_DATA_IND, GSM411_MT_CP_DATA,
+ "MMSMS-DATA-IND (CP DATA)", gsm411_mmsms_cp_data},
+
+ /* data indication (CP ACK) */
+ {SBIT(GSM411_CPS_WAIT_CP_ACK),
+ GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ACK,
+ "MMSMS-DATA-IND (CP ACK)", gsm411_mmsms_cp_ack},
+
+ /* data indication (CP ERROR) */
+ {ALL_STATES,
+ GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ERROR,
+ "MMSMS-DATA-IND (CP_ERROR)", gsm411_mmsms_cp_error},
+
+ /* release indication */
+ {ALL_STATES - SBIT(GSM411_CPS_IDLE),
+ GSM411_MMSMS_REL_IND, 0,
+ "MMSMS-REL-IND", gsm411_mmsms_rel_ind},
+
+};
+
+#define SMCDATASLLEN \
+ (sizeof(smcdatastatelist) / sizeof(struct smcdatastate))
+
+/* message from lower layer
+ * WARNING: We must not free msg, since it will be performed by the
+ * lower layer. */
+int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type,
+ struct msgb *msg, int cp_msg_type)
+{
+ int i, rc;
+
+ /* find function for current state and message */
+ for (i = 0; i < SMCDATASLLEN; i++) {
+ /* state must machtch, MM message must match
+ * CP msg must match only in case of MMSMS_DATA_IND
+ */
+ if ((msg_type == smcdatastatelist[i].type)
+ && (SBIT(inst->cp_state) & smcdatastatelist[i].states)
+ && (msg_type != GSM411_MMSMS_DATA_IND
+ || cp_msg_type == smcdatastatelist[i].cp_type))
+ break;
+ }
+ if (i == SMCDATASLLEN) {
+ LOGP(DLSMS, LOGL_NOTICE, "Message 0x%x/%u unhandled at this "
+ "state %s.\n", msg_type, cp_msg_type,
+ smc_state_names[inst->cp_state]);
+ if (msg_type == GSM411_MMSMS_EST_IND
+ || msg_type == GSM411_MMSMS_DATA_IND) {
+ struct msgb *nmsg;
+
+ LOGP(DLSMS, LOGL_NOTICE, "RX Unimplemented CP "
+ "msg_type: 0x%02x\n", msg_type);
+ /* 5.3.4 enter idle */
+ new_cp_state(inst, GSM411_CPS_IDLE);
+ /* indicate error */
+ gsm411_tx_cp_error(inst,
+ GSM411_CP_CAUSE_MSGTYPE_NOTEXIST);
+ /* send error indication to upper layer */
+ nmsg = gsm411_msgb_alloc();
+ inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg);
+ msgb_free(nmsg);
+ /* release MM connection */
+ nmsg = gsm411_msgb_alloc();
+ return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg,
+ 0);
+ }
+ return 0;
+ }
+
+ LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
+ smcdatastatelist[i].name, smc_state_names[inst->cp_state]);
+
+ rc = smcdatastatelist[i].rout(inst, msg);
+
+ return rc;
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm0411_smr.c b/src/shared/libosmocore/src/gsm/gsm0411_smr.c
new file mode 100644
index 00000000..d5ca9238
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm0411_smr.c
@@ -0,0 +1,451 @@
+/* Point-to-Point (PP) Short Message Service (SMS)
+ * Support on Mobile Radio Interface
+ * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */
+
+/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by On-Waves
+ * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Notes on msg:
+ *
+ * Messages from lower layer are freed by lower layer.
+ *
+ * Messages to upper layer are freed after upper layer call returns, so upper
+ * layer cannot use data after returning. Upper layer must not free the msg.
+ *
+ * This implies: Lower layer messages can be forwarded to upper layer.
+ *
+ * Upper layer messages are freed by lower layer, so they must not be freed
+ * after calling lower layer.
+ *
+ *
+ * Notes on release:
+ *
+ * Sending Abort/Release (MNSMS-ABORT-REQ or MNSMS-REL-REQ) may cause the
+ * lower layer to become IDLE. Then it is allowed to destroy this instance,
+ * so sending this this MUST be the last thing that is done.
+ *
+ */
+
+
+#include <string.h>
+#include <errno.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/gsm/tlv.h>
+
+#include <osmocom/gsm/gsm0411_utils.h>
+#include <osmocom/gsm/gsm0411_smc.h>
+#include <osmocom/gsm/gsm0411_smr.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+static void rp_timer_expired(void *data);
+
+/* init a new instance */
+void gsm411_smr_init(struct gsm411_smr_inst *inst, int network,
+ int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg),
+ int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg))
+{
+ memset(inst, 0, sizeof(*inst));
+ inst->network = network;
+ inst->rp_state = GSM411_RPS_IDLE;
+ inst->rl_recv = rl_recv;
+ inst->mn_send = mn_send;
+ inst->rp_timer.data = inst;
+ inst->rp_timer.cb = rp_timer_expired;
+
+ LOGP(DLSMS, LOGL_INFO, "New SMR instance created\n");
+}
+
+/* clear instance */
+void gsm411_smr_clear(struct gsm411_smr_inst *inst)
+{
+ LOGP(DLSMS, LOGL_INFO, "Clear SMR instance\n");
+
+ osmo_timer_del(&inst->rp_timer);
+}
+
+const char *smr_state_names[] = {
+ "IDLE",
+ "WAIT_FOR_RP_ACK",
+ "illegal state 2"
+ "WAIT_TO_TX_RP_ACK",
+ "WAIT_FOR_RETRANS_T",
+};
+
+const struct value_string gsm411_rp_cause_strs[] = {
+ { GSM411_RP_CAUSE_MO_NUM_UNASSIGNED, "(MO) Number not assigned" },
+ { GSM411_RP_CAUSE_MO_OP_DET_BARR, "(MO) Operator determined barring" },
+ { GSM411_RP_CAUSE_MO_CALL_BARRED, "(MO) Call barred" },
+ { GSM411_RP_CAUSE_MO_SMS_REJECTED, "(MO) SMS rejected" },
+ { GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER, "(MO) Destination out of order" },
+ { GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR, "(MO) Unidentified subscriber" },
+ { GSM411_RP_CAUSE_MO_FACILITY_REJ, "(MO) Facility reject" },
+ { GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR, "(MO) Unknown subscriber" },
+ { GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER, "(MO) Network out of order" },
+ { GSM411_RP_CAUSE_MO_TEMP_FAIL, "(MO) Temporary failure" },
+ { GSM411_RP_CAUSE_MO_CONGESTION, "(MO) Congestion" },
+ { GSM411_RP_CAUSE_MO_RES_UNAVAIL, "(MO) Resource unavailable" },
+ { GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR, "(MO) Requested facility not subscribed" },
+ { GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL, "(MO) Requested facility not implemented" },
+ { GSM411_RP_CAUSE_MO_INTERWORKING, "(MO) Interworking" },
+ /* valid only for MT */
+ { GSM411_RP_CAUSE_MT_MEM_EXCEEDED, "(MT) Memory Exceeded" },
+ /* valid for both directions */
+ { GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" },
+ { GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" },
+ { GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" },
+ { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" },
+ { GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" },
+ { GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" },
+ { GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" },
+ { 0, NULL }
+};
+
+static void new_rp_state(struct gsm411_smr_inst *inst,
+ enum gsm411_rp_state state)
+{
+ LOGP(DLSMS, LOGL_INFO, "New RP state %s -> %s\n",
+ smr_state_names[inst->rp_state], smr_state_names[state]);
+ inst->rp_state = state;
+
+ /* stop timer when going idle */
+ if (state == GSM411_RPS_IDLE)
+ osmo_timer_del(&inst->rp_timer);
+}
+
+/* Prefix msg with a RP-DATA header and send as CP-DATA */
+static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg,
+ uint8_t rp_msg_type, uint8_t rp_msg_ref,
+ int mnsms_msg_type)
+{
+ struct gsm411_rp_hdr *rp;
+ uint8_t len = msg->len;
+
+ /* GSM 04.11 RP-DATA header */
+ rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
+ rp->len = len + 2;
+ rp->msg_type = rp_msg_type;
+ rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
+
+ return inst->mn_send(inst, mnsms_msg_type, msg);
+}
+
+static int gsm411_send_rp_error(struct gsm411_smr_inst *inst,
+ uint8_t msg_ref, uint8_t cause)
+{
+ struct msgb *msg = gsm411_msgb_alloc();
+
+ msgb_tv_put(msg, 1, cause);
+
+ LOGP(DLSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause,
+ get_value_string(gsm411_rp_cause_strs, cause));
+
+ return gsm411_rp_sendmsg(inst, msg,
+ (inst->network) ? GSM411_MT_RP_ERROR_MT : GSM411_MT_RP_ERROR_MO,
+ msg_ref, GSM411_MNSMS_DATA_REQ);
+}
+
+static int gsm411_send_release(struct gsm411_smr_inst *inst)
+{
+ struct msgb *msg = gsm411_msgb_alloc();
+
+ LOGP(DLSMS, LOGL_NOTICE, "TX: MNSMS-REL-REQ\n");
+
+ return inst->mn_send(inst, GSM411_MNSMS_REL_REQ, msg);
+}
+
+static int gsm411_send_abort(struct gsm411_smr_inst *inst)
+{
+ struct msgb *msg = gsm411_msgb_alloc();
+
+ msgb_tv_put(msg, 1, 111); //FIXME: better idea ? */
+ LOGP(DLSMS, LOGL_NOTICE, "TX: MNSMS-ABORT-REQ\n");
+
+ return inst->mn_send(inst, GSM411_MNSMS_ABORT_REQ, msg);
+}
+
+static int gsm411_send_report(struct gsm411_smr_inst *inst)
+{
+ struct msgb *msg = gsm411_msgb_alloc();
+
+ LOGP(DLSMS, LOGL_NOTICE, "send empty SM_RL_REPORT_IND\n");
+
+ return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
+}
+
+static int gsm411_rl_data_req(struct gsm411_smr_inst *inst, struct msgb *msg)
+{
+ LOGP(DLSMS, LOGL_DEBUG, "TX SMS RP-DATA\n");
+ /* start TR1N and enter 'wait for RP-ACK state' */
+ osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR1M);
+ new_rp_state(inst, GSM411_RPS_WAIT_FOR_RP_ACK);
+
+ return inst->mn_send(inst, GSM411_MNSMS_EST_REQ, msg);
+}
+
+static int gsm411_rl_report_req(struct gsm411_smr_inst *inst, struct msgb *msg)
+{
+ LOGP(DLSMS, LOGL_DEBUG, "TX SMS REPORT\n");
+ new_rp_state(inst, GSM411_RPS_IDLE);
+
+ inst->mn_send(inst, GSM411_MNSMS_DATA_REQ, msg);
+ gsm411_send_release(inst);
+ return 0;
+}
+
+static int gsm411_mnsms_est_ind(struct gsm411_smr_inst *inst, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr*)msg->l3h;
+ struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data;
+ uint8_t msg_type = rp_data->msg_type & 0x07;
+ int rc;
+
+ /* check direction */
+ if (inst->network == (msg_type & 1)) {
+ LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
+ gsm411_send_rp_error(inst, rp_data->msg_ref,
+ GSM411_RP_CAUSE_MSG_INCOMP_STATE);
+ new_rp_state(inst, GSM411_RPS_IDLE);
+ gsm411_send_release(inst);
+ return -EINVAL;
+ }
+
+ switch (msg_type) {
+ case GSM411_MT_RP_DATA_MT:
+ case GSM411_MT_RP_DATA_MO:
+ LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-DATA\n");
+ /* start TR2N and enter 'wait to send RP-ACK state' */
+ osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M);
+ new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK);
+ rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg);
+ break;
+ case GSM411_MT_RP_SMMA_MO:
+ LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-SMMA\n");
+ /* start TR2N and enter 'wait to send RP-ACK state' */
+ osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M);
+ new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK);
+ rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg);
+ break;
+ default:
+ LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
+ gsm411_send_rp_error(inst, rp_data->msg_ref,
+ GSM411_RP_CAUSE_MSGTYPE_NOTEXIST);
+ new_rp_state(inst, GSM411_RPS_IDLE);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int gsm411_mnsms_data_ind_tx(struct gsm411_smr_inst *inst,
+ struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr*)msg->l3h;
+ struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data;
+ uint8_t msg_type = rp_data->msg_type & 0x07;
+ int rc;
+
+ /* check direction */
+ if (inst->network == (msg_type & 1)) {
+ LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
+ gsm411_send_rp_error(inst, rp_data->msg_ref,
+ GSM411_RP_CAUSE_MSG_INCOMP_STATE);
+ new_rp_state(inst, GSM411_RPS_IDLE);
+ gsm411_send_release(inst);
+ return -EINVAL;
+ }
+
+ switch (msg_type) {
+ case GSM411_MT_RP_ACK_MO:
+ case GSM411_MT_RP_ACK_MT:
+ LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-ACK\n");
+ new_rp_state(inst, GSM411_RPS_IDLE);
+ inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
+ gsm411_send_release(inst);
+ return 0;
+ case GSM411_MT_RP_ERROR_MO:
+ case GSM411_MT_RP_ERROR_MT:
+ LOGP(DLSMS, LOGL_DEBUG, "RX SMS RP-ERROR\n");
+ new_rp_state(inst, GSM411_RPS_IDLE);
+ inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
+ gsm411_send_release(inst);
+ return 0;
+ default:
+ LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
+ gsm411_send_rp_error(inst, rp_data->msg_ref,
+ GSM411_RP_CAUSE_MSGTYPE_NOTEXIST);
+ new_rp_state(inst, GSM411_RPS_IDLE);
+ gsm411_send_release(inst);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int gsm411_mnsms_error_ind_tx(struct gsm411_smr_inst *inst,
+ struct msgb *msg)
+{
+ LOGP(DLSMS, LOGL_DEBUG, "RX SMS MNSMS-ERROR-IND\n");
+ new_rp_state(inst, GSM411_RPS_IDLE);
+ inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
+ gsm411_send_release(inst);
+ return 0;
+}
+
+static int gsm411_mnsms_error_ind_rx(struct gsm411_smr_inst *inst,
+ struct msgb *msg)
+{
+ LOGP(DLSMS, LOGL_DEBUG, "RX SMS MNSMS-ERROR-IND\n");
+ new_rp_state(inst, GSM411_RPS_IDLE);
+ return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg);
+}
+
+/* SMR TR1* is expired */
+static void rp_timer_expired(void *data)
+{
+ struct gsm411_smr_inst *inst = data;
+
+ if (inst->rp_state == GSM411_RPS_WAIT_TO_TX_RP_ACK)
+ LOGP(DLSMS, LOGL_DEBUG, "TR2N\n");
+ else
+ LOGP(DLSMS, LOGL_DEBUG, "TR1N\n");
+ gsm411_send_report(inst);
+ gsm411_send_abort(inst);
+}
+
+/* statefull handling for SM-RL SAP messages */
+static struct smrdownstate {
+ uint32_t states;
+ int type;
+ const char *name;
+ int (*rout) (struct gsm411_smr_inst *inst,
+ struct msgb *msg);
+} smrdownstatelist[] = {
+ /* data request */
+ {SBIT(GSM411_RPS_IDLE),
+ GSM411_SM_RL_DATA_REQ,
+ "SM-RL-DATA_REQ", gsm411_rl_data_req},
+
+ /* report request */
+ {SBIT(GSM411_RPS_WAIT_TO_TX_RP_ACK),
+ GSM411_SM_RL_REPORT_REQ,
+ "SM-RL-REPORT_REQ", gsm411_rl_report_req},
+};
+
+#define SMRDOWNSLLEN \
+ (sizeof(smrdownstatelist) / sizeof(struct smrdownstate))
+
+/* message from upper layer */
+int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg)
+{
+ int i, rc;
+
+ /* find function for current state and message */
+ for (i = 0; i < SMRDOWNSLLEN; i++) {
+ if ((msg_type == smrdownstatelist[i].type)
+ && (SBIT(inst->rp_state) & smrdownstatelist[i].states))
+ break;
+ }
+ if (i == SMRDOWNSLLEN) {
+ LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state "
+ "%s.\n", msg_type, smr_state_names[inst->rp_state]);
+ msgb_free(msg);
+ return 0;
+ }
+
+ LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
+ smrdownstatelist[i].name, smr_state_names[inst->rp_state]);
+
+ rc = smrdownstatelist[i].rout(inst, msg);
+
+ return rc;
+}
+
+/* statefull handling for MMSMS SAP messages */
+static struct smrdatastate {
+ uint32_t states;
+ int type;
+ const char *name;
+ int (*rout) (struct gsm411_smr_inst *inst,
+ struct msgb *msg);
+} smrdatastatelist[] = {
+ /* establish indication */
+ {SBIT(GSM411_RPS_IDLE),
+ GSM411_MNSMS_EST_IND,
+ "MNSMS-EST-IND", gsm411_mnsms_est_ind},
+
+ /* data indication */
+ {SBIT(GSM411_RPS_WAIT_FOR_RP_ACK),
+ GSM411_MNSMS_DATA_IND,
+ "MNSMS-DATA-IND", gsm411_mnsms_data_ind_tx},
+
+ /* error indication */
+ {SBIT(GSM411_RPS_WAIT_FOR_RP_ACK),
+ GSM411_MNSMS_ERROR_IND,
+ "MNSMS-ERROR-IND", gsm411_mnsms_error_ind_tx},
+
+ /* error indication */
+ {SBIT(GSM411_RPS_WAIT_TO_TX_RP_ACK),
+ GSM411_MNSMS_ERROR_IND,
+ "MNSMS-ERROR-IND", gsm411_mnsms_error_ind_rx},
+
+};
+
+#define SMRDATASLLEN \
+ (sizeof(smrdatastatelist) / sizeof(struct smrdatastate))
+
+/* message from lower layer
+ * WARNING: We must not free msg, since it will be performed by the
+ * lower layer. */
+int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type,
+ struct msgb *msg)
+{
+ int i, rc;
+
+ /* find function for current state and message */
+ for (i = 0; i < SMRDATASLLEN; i++) {
+ /* state must machtch, MM message must match
+ * CP msg must match only in case of MMSMS_DATA_IND
+ */
+ if ((msg_type == smrdatastatelist[i].type)
+ && (SBIT(inst->rp_state) & smrdatastatelist[i].states))
+ break;
+ }
+ if (i == SMRDATASLLEN) {
+ LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state "
+ "%s.\n", msg_type, smr_state_names[inst->rp_state]);
+ return 0;
+ }
+
+ LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
+ smrdatastatelist[i].name, smr_state_names[inst->rp_state]);
+
+ rc = smrdatastatelist[i].rout(inst, msg);
+
+ return rc;
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm0411_utils.c b/src/shared/libosmocore/src/gsm/gsm0411_utils.c
new file mode 100644
index 00000000..5076ec82
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm0411_utils.c
@@ -0,0 +1,306 @@
+/* Point-to-Point (PP) Short Message Service (SMS)
+ * Support on Mobile Radio Interface
+ * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */
+
+/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by On-Waves
+ * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <time.h>
+#include <string.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/protocol/gsm_04_11.h>
+
+#define GSM411_ALLOC_SIZE 1024
+#define GSM411_ALLOC_HEADROOM 128
+
+struct msgb *gsm411_msgb_alloc(void)
+{
+ return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
+ "GSM 04.11");
+}
+
+/* Turn int into semi-octet representation: 98 => 0x89 */
+uint8_t gsm411_bcdify(uint8_t value)
+{
+ uint8_t ret;
+
+ ret = value / 10;
+ ret |= (value % 10) << 4;
+
+ return ret;
+}
+
+/* Turn semi-octet representation into int: 0x89 => 98 */
+uint8_t gsm411_unbcdify(uint8_t value)
+{
+ uint8_t ret;
+
+ if ((value & 0x0F) > 9 || (value >> 4) > 9)
+ LOGP(DLSMS, LOGL_ERROR,
+ "gsm411_unbcdify got too big nibble: 0x%02X\n", value);
+
+ ret = (value&0x0F)*10;
+ ret += value>>4;
+
+ return ret;
+}
+
+/* Generate 03.40 TP-SCTS */
+void gsm340_gen_scts(uint8_t *scts, time_t time)
+{
+ struct tm *tm = gmtime(&time);
+
+ *scts++ = gsm411_bcdify(tm->tm_year % 100);
+ *scts++ = gsm411_bcdify(tm->tm_mon + 1);
+ *scts++ = gsm411_bcdify(tm->tm_mday);
+ *scts++ = gsm411_bcdify(tm->tm_hour);
+ *scts++ = gsm411_bcdify(tm->tm_min);
+ *scts++ = gsm411_bcdify(tm->tm_sec);
+ *scts++ = gsm411_bcdify(0); /* GMT */
+}
+
+/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
+time_t gsm340_scts(uint8_t *scts)
+{
+ struct tm tm;
+ uint8_t yr = gsm411_unbcdify(*scts++);
+ int ofs;
+
+ memset(&tm, 0x00, sizeof(struct tm));
+
+ if (yr <= 80)
+ tm.tm_year = 100 + yr;
+ else
+ tm.tm_year = yr;
+ tm.tm_mon = gsm411_unbcdify(*scts++) - 1;
+ tm.tm_mday = gsm411_unbcdify(*scts++);
+ tm.tm_hour = gsm411_unbcdify(*scts++);
+ tm.tm_min = gsm411_unbcdify(*scts++);
+ tm.tm_sec = gsm411_unbcdify(*scts++);
+
+ /* according to gsm 03.40 time zone is
+ "expressed in quarters of an hour" */
+ ofs = gsm411_unbcdify(*scts++) * 15*60;
+
+ return mktime(&tm) - ofs;
+}
+
+/* Return the default validity period in minutes */
+static unsigned long gsm340_vp_default(void)
+{
+ unsigned long minutes;
+ /* Default validity: two days */
+ minutes = 24 * 60 * 2;
+ return minutes;
+}
+
+/* Decode validity period format 'relative' */
+static unsigned long gsm340_vp_relative(uint8_t *sms_vp)
+{
+ /* Chapter 9.2.3.12.1 */
+ uint8_t vp;
+ unsigned long minutes;
+
+ vp = *(sms_vp);
+ if (vp <= 143)
+ minutes = vp + 1 * 5;
+ else if (vp <= 167)
+ minutes = 12*60 + (vp-143) * 30;
+ else if (vp <= 196)
+ minutes = vp-166 * 60 * 24;
+ else
+ minutes = vp-192 * 60 * 24 * 7;
+ return minutes;
+}
+
+/* Decode validity period format 'absolute' */
+static unsigned long gsm340_vp_absolute(uint8_t *sms_vp)
+{
+ /* Chapter 9.2.3.12.2 */
+ time_t expires, now;
+ unsigned long minutes;
+
+ expires = gsm340_scts(sms_vp);
+ now = time(NULL);
+ if (expires <= now)
+ minutes = 0;
+ else
+ minutes = (expires-now)/60;
+ return minutes;
+}
+
+/* Decode validity period format 'relative in integer representation' */
+static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp)
+{
+ uint8_t vp;
+ unsigned long minutes;
+ vp = *(sms_vp);
+ if (vp == 0) {
+ LOGP(DLSMS, LOGL_ERROR,
+ "reserved relative_integer validity period\n");
+ return gsm340_vp_default();
+ }
+ minutes = vp/60;
+ return minutes;
+}
+
+/* Decode validity period format 'relative in semi-octet representation' */
+static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp)
+{
+ unsigned long minutes;
+ minutes = gsm411_unbcdify(*sms_vp++)*60; /* hours */
+ minutes += gsm411_unbcdify(*sms_vp++); /* minutes */
+ minutes += gsm411_unbcdify(*sms_vp++)/60; /* seconds */
+ return minutes;
+}
+
+/* decode validity period. return minutes */
+unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp)
+{
+ uint8_t fi; /* functionality indicator */
+
+ switch (sms_vpf) {
+ case GSM340_TP_VPF_RELATIVE:
+ return gsm340_vp_relative(sms_vp);
+ case GSM340_TP_VPF_ABSOLUTE:
+ return gsm340_vp_absolute(sms_vp);
+ case GSM340_TP_VPF_ENHANCED:
+ /* Chapter 9.2.3.12.3 */
+ fi = *sms_vp++;
+ /* ignore additional fi */
+ if (fi & (1<<7)) sms_vp++;
+ /* read validity period format */
+ switch (fi & 0x7) {
+ case 0x0:
+ return gsm340_vp_default(); /* no vpf specified */
+ case 0x1:
+ return gsm340_vp_relative(sms_vp);
+ case 0x2:
+ return gsm340_vp_relative_integer(sms_vp);
+ case 0x3:
+ return gsm340_vp_relative_semioctet(sms_vp);
+ default:
+ /* The GSM spec says that the SC should reject any
+ unsupported and/or undefined values. FIXME */
+ LOGP(DLSMS, LOGL_ERROR,
+ "Reserved enhanced validity period format\n");
+ return gsm340_vp_default();
+ }
+ case GSM340_TP_VPF_NONE:
+ default:
+ return gsm340_vp_default();
+ }
+}
+
+/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
+enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
+{
+ uint8_t cgbits = dcs >> 4;
+ enum sms_alphabet alpha = DCS_NONE;
+
+ if ((cgbits & 0xc) == 0) {
+ if (cgbits & 2) {
+ LOGP(DLSMS, LOGL_NOTICE,
+ "Compressed SMS not supported yet\n");
+ return 0xffffffff;
+ }
+
+ switch ((dcs >> 2)&0x03) {
+ case 0:
+ alpha = DCS_7BIT_DEFAULT;
+ break;
+ case 1:
+ alpha = DCS_8BIT_DATA;
+ break;
+ case 2:
+ alpha = DCS_UCS2;
+ break;
+ }
+ } else if (cgbits == 0xc || cgbits == 0xd)
+ alpha = DCS_7BIT_DEFAULT;
+ else if (cgbits == 0xe)
+ alpha = DCS_UCS2;
+ else if (cgbits == 0xf) {
+ if (dcs & 4)
+ alpha = DCS_8BIT_DATA;
+ else
+ alpha = DCS_7BIT_DEFAULT;
+ }
+
+ return alpha;
+}
+
+/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
+int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
+ uint8_t plan, const char *number)
+{
+ int len_in_bytes;
+
+ /* prevent buffer overflows */
+ if (strlen(number) > 20)
+ number = "";
+
+// oa[1] = 0xb9; /* networks-specific number, private numbering plan */
+ oa[1] = 0x80 | (type << 4) | plan;
+
+ len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
+
+ /* GSM 03.40 tells us the length is in 'useful semi-octets' */
+ oa[0] = strlen(number) & 0xff;
+
+ return len_in_bytes;
+}
+
+/* Prefix msg with a RP header */
+int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
+ uint8_t rp_msg_ref)
+{
+ struct gsm411_rp_hdr *rp;
+ uint8_t len = msg->len;
+
+ /* GSM 04.11 RP-DATA header */
+ rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
+ rp->len = len + 2;
+ rp->msg_type = rp_msg_type;
+ rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
+
+ return 0;
+}
+
+/* Prefix msg with a 04.08/04.11 CP header */
+int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
+ uint8_t msg_type)
+{
+ struct gsm48_hdr *gh;
+
+ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
+ /* Outgoing needs the highest bit set */
+ gh->proto_discr = proto | (trans << 4);
+ gh->msg_type = msg_type;
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm0480.c b/src/shared/libosmocore/src/gsm/gsm0480.c
new file mode 100644
index 00000000..b9b3ed97
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm0480.c
@@ -0,0 +1,461 @@
+/* Format functions for GSM 04.80 */
+
+/*
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Mike Haben <michael.haben@btinternet.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.
+ *
+ */
+
+#include <osmocom/gsm/gsm0480.h>
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_04_80.h>
+
+#include <string.h>
+
+static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag)
+{
+ uint8_t *data = msgb_push(msgb, 2);
+
+ data[0] = tag;
+ data[1] = msgb->len - 2;
+ return data;
+}
+
+static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag,
+ uint8_t value)
+{
+ uint8_t *data = msgb_push(msgb, 3);
+
+ data[0] = tag;
+ data[1] = 1;
+ data[2] = value;
+ return data;
+}
+
+/* wrap an invoke around it... the other way around
+ *
+ * 1.) Invoke Component tag
+ * 2.) Invoke ID Tag
+ * 3.) Operation
+ * 4.) Data
+ */
+int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id)
+{
+ /* 3. operation */
+ msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, op);
+
+ /* 2. invoke id tag */
+ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, link_id);
+
+ /* 1. component tag */
+ msgb_wrap_with_TL(msg, GSM0480_CTYPE_INVOKE);
+
+ return 0;
+}
+
+/* wrap the GSM 04.08 Facility IE around it */
+int gsm0480_wrap_facility(struct msgb *msg)
+{
+ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+
+ return 0;
+}
+
+struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text)
+{
+ struct msgb *msg;
+ uint8_t *seq_len_ptr, *ussd_len_ptr, *data;
+ int len;
+
+ msg = msgb_alloc_headroom(1024, 128, "GSM 04.80");
+ if (!msg)
+ return NULL;
+
+ /* SEQUENCE { */
+ msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG);
+ seq_len_ptr = msgb_put(msg, 1);
+
+ /* DCS { */
+ msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, 0x0F);
+ /* } DCS */
+
+ /* USSD-String { */
+ msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
+ ussd_len_ptr = msgb_put(msg, 1);
+ data = msgb_put(msg, 0);
+ len = gsm_7bit_encode(data, text);
+ msgb_put(msg, len);
+ ussd_len_ptr[0] = len;
+ /* USSD-String } */
+
+ /* alertingPattern { */
+ msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, alertPattern);
+ /* } alertingPattern */
+
+ seq_len_ptr[0] = 3 + 2 + ussd_len_ptr[0] + 3;
+ /* } SEQUENCE */
+
+ return msg;
+}
+
+struct msgb *gsm0480_create_notifySS(const char *text)
+{
+ struct msgb *msg;
+ uint8_t *data, *tmp_len;
+ uint8_t *seq_len_ptr, *cal_len_ptr, *opt_len_ptr, *nam_len_ptr;
+ int len;
+
+ len = strlen(text);
+ if (len < 1 || len > 160)
+ return NULL;
+
+ msg = msgb_alloc_headroom(1024, 128, "GSM 04.80");
+ if (!msg)
+ return NULL;
+
+ msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG);
+ seq_len_ptr = msgb_put(msg, 1);
+
+ /* ss_code for CNAP { */
+ msgb_put_u8(msg, 0x81);
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, 0x19);
+ /* } ss_code */
+
+
+ /* nameIndicator { */
+ msgb_put_u8(msg, 0xB4);
+ nam_len_ptr = msgb_put(msg, 1);
+
+ /* callingName { */
+ msgb_put_u8(msg, 0xA0);
+ opt_len_ptr = msgb_put(msg, 1);
+ msgb_put_u8(msg, 0xA0);
+ cal_len_ptr = msgb_put(msg, 1);
+
+ /* namePresentationAllowed { */
+ /* add the DCS value */
+ msgb_put_u8(msg, 0x80);
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, 0x0F);
+
+ /* add the lengthInCharacters */
+ msgb_put_u8(msg, 0x81);
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, strlen(text));
+
+ /* add the actual string */
+ msgb_put_u8(msg, 0x82);
+ tmp_len = msgb_put(msg, 1);
+ data = msgb_put(msg, 0);
+ len = gsm_7bit_encode(data, text);
+ tmp_len[0] = len;
+ msgb_put(msg, len);
+
+ /* }; namePresentationAllowed */
+
+ cal_len_ptr[0] = 3 + 3 + 2 + len;
+ opt_len_ptr[0] = cal_len_ptr[0] + 2;
+ /* }; callingName */
+
+ nam_len_ptr[0] = opt_len_ptr[0] + 2;
+ /* ); nameIndicator */
+
+ /* write the lengths... */
+ seq_len_ptr[0] = 3 + nam_len_ptr[0] + 2;
+
+ return msg;
+}
+
+/* Forward declarations */
+static int parse_ussd(const struct gsm48_hdr *hdr,
+ uint16_t len, struct ussd_request *req);
+static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len,
+ struct ussd_request *req);
+static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length,
+ struct ussd_request *req);
+static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
+ struct ussd_request *req);
+static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
+ struct ussd_request *req);
+
+/* Decode a mobile-originated USSD-request message */
+int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len,
+ struct ussd_request *req)
+{
+ int rc = 0;
+
+ if (len < sizeof(*hdr) + 2) {
+ LOGP(0, LOGL_DEBUG, "USSD Request is too short.\n");
+ return 0;
+ }
+
+ if ((hdr->proto_discr & 0x0f) == GSM48_PDISC_NC_SS) {
+ req->transaction_id = hdr->proto_discr & 0x70;
+ rc = parse_ussd(hdr, len, req);
+ }
+
+ if (!rc)
+ LOGP(0, LOGL_DEBUG, "Error occurred while parsing received USSD!\n");
+
+ return rc;
+}
+
+static int parse_ussd(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_request *req)
+{
+ int rc = 1;
+ uint8_t msg_type = hdr->msg_type & 0xBF; /* message-type - section 3.4 */
+
+ switch (msg_type) {
+ case GSM0480_MTYPE_RELEASE_COMPLETE:
+ LOGP(0, LOGL_DEBUG, "USS Release Complete\n");
+ /* could also parse out the optional Cause/Facility data */
+ req->text[0] = 0xFF;
+ break;
+ case GSM0480_MTYPE_REGISTER:
+ case GSM0480_MTYPE_FACILITY:
+ rc &= parse_ussd_info_elements(&hdr->data[0], len - sizeof(*hdr), req);
+ break;
+ default:
+ LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 message-type field 0x%02x\n",
+ hdr->msg_type);
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len,
+ struct ussd_request *req)
+{
+ int rc = -1;
+ /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */
+ uint8_t iei;
+ uint8_t iei_length;
+
+ iei = ussd_ie[0];
+ iei_length = ussd_ie[1];
+
+ /* If the data does not fit, report an error */
+ if (len - 2 < iei_length)
+ return 0;
+
+ switch (iei) {
+ case GSM48_IE_CAUSE:
+ break;
+ case GSM0480_IE_FACILITY:
+ rc = parse_facility_ie(ussd_ie+2, iei_length, req);
+ break;
+ case GSM0480_IE_SS_VERSION:
+ break;
+ default:
+ LOGP(0, LOGL_DEBUG, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n",
+ iei);
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length,
+ struct ussd_request *req)
+{
+ int rc = 1;
+ uint8_t offset = 0;
+
+ while (offset + 2 <= length) {
+ /* Component Type tag - table 3.7 */
+ uint8_t component_type = facility_ie[offset];
+ uint8_t component_length = facility_ie[offset+1];
+
+ /* size check */
+ if (offset + 2 + component_length > length) {
+ LOGP(0, LOGL_ERROR, "Component does not fit.\n");
+ return 0;
+ }
+
+ switch (component_type) {
+ case GSM0480_CTYPE_INVOKE:
+ rc &= parse_ss_invoke(facility_ie+2,
+ component_length,
+ req);
+ break;
+ case GSM0480_CTYPE_RETURN_RESULT:
+ break;
+ case GSM0480_CTYPE_RETURN_ERROR:
+ break;
+ case GSM0480_CTYPE_REJECT:
+ break;
+ default:
+ LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 Facility "
+ "Component Type 0x%02x\n", component_type);
+ rc = 0;
+ break;
+ }
+ offset += (component_length+2);
+ };
+
+ return rc;
+}
+
+/* Parse an Invoke component - see table 3.3 */
+static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
+ struct ussd_request *req)
+{
+ int rc = 1;
+ uint8_t offset;
+
+ if (length < 3)
+ return 0;
+
+ /* mandatory part */
+ if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) {
+ LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag "
+ "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
+ }
+
+ offset = invoke_data[1] + 2;
+ req->invoke_id = invoke_data[2];
+
+ /* look ahead once */
+ if (offset + 1 > length)
+ return 0;
+
+ /* optional part */
+ if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID)
+ offset += invoke_data[offset+1] + 2; /* skip over it */
+
+ /* mandatory part */
+ if (invoke_data[offset] == GSM0480_OPERATION_CODE) {
+ if (offset + 2 > length)
+ return 0;
+ uint8_t operation_code = invoke_data[offset+2];
+ switch (operation_code) {
+ case GSM0480_OP_CODE_PROCESS_USS_REQ:
+ rc = parse_process_uss_req(invoke_data + offset + 3,
+ length - offset - 3,
+ req);
+ break;
+ default:
+ LOGP(0, LOGL_DEBUG, "GSM 04.80 operation code 0x%02x "
+ "is not yet handled\n", operation_code);
+ rc = 0;
+ break;
+ }
+ } else {
+ LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag 0x%02x "
+ "(expecting Operation Code tag)\n",
+ invoke_data[0]);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/* Parse the parameters of a Process UnstructuredSS Request */
+static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
+ struct ussd_request *req)
+{
+ int rc = 0;
+ int num_chars;
+ uint8_t dcs;
+
+
+ /* we need at least that much */
+ if (length < 8)
+ return 0;
+
+
+ if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) {
+ if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) {
+ dcs = uss_req_data[4];
+ if ((dcs == 0x0F) &&
+ (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) {
+ num_chars = (uss_req_data[6] * 8) / 7;
+ /* Prevent a mobile-originated buffer-overrun! */
+ if (num_chars > MAX_LEN_USSD_STRING)
+ num_chars = MAX_LEN_USSD_STRING;
+ gsm_7bit_decode(req->text,
+ &(uss_req_data[7]), num_chars);
+ rc = 1;
+ }
+ }
+ }
+ return rc;
+}
+
+struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text)
+{
+ struct msgb *msg;
+ struct gsm48_hdr *gh;
+ uint8_t *ptr8;
+ int response_len;
+
+ msg = msgb_alloc_headroom(1024, 128, "GSM 04.80");
+ if (!msg)
+ return NULL;
+
+ /* First put the payload text into the message */
+ ptr8 = msgb_put(msg, 0);
+ response_len = gsm_7bit_encode(ptr8, text);
+ msgb_put(msg, response_len);
+
+ /* Then wrap it as an Octet String */
+ msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG);
+
+ /* Pre-pend the DCS octet string */
+ msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F);
+
+ /* Then wrap these as a Sequence */
+ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
+
+ /* Pre-pend the operation code */
+ msgb_push_TLV1(msg, GSM0480_OPERATION_CODE,
+ GSM0480_OP_CODE_PROCESS_USS_REQ);
+
+ /* Wrap the operation code and IA5 string as a sequence */
+ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
+
+ /* Pre-pend the invoke ID */
+ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id);
+
+ /* Wrap this up as a Return Result component */
+ msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT);
+
+ /* Wrap the component in a Facility message */
+ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+
+ /* And finally pre-pend the L3 header */
+ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_NC_SS | trans_id
+ | (1<<7); /* TI direction = 1 */
+ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+
+ return msg;
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm0502.c b/src/shared/libosmocore/src/gsm/gsm0502.c
new file mode 100644
index 00000000..df1d8e9e
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm0502.c
@@ -0,0 +1,43 @@
+/* Paging helper code */
+
+/* (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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/rsl.h>
+
+unsigned int
+gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi)
+{
+ int ccch_conf;
+ int bs_cc_chans;
+ int blocks;
+ unsigned int group;
+
+ ccch_conf = chan_desc->ccch_conf;
+ bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf);
+ /* code word + 2, as 2 channels equals 0x0 */
+ blocks = gsm48_number_of_paging_subchannels(chan_desc);
+ group = gsm0502_get_paging_group(imsi, bs_cc_chans, blocks);
+
+ return group;
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm0808.c b/src/shared/libosmocore/src/gsm/gsm0808.c
new file mode 100644
index 00000000..30098278
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm0808.c
@@ -0,0 +1,402 @@
+/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009,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 <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/protocol/gsm_08_08.h>
+#include <osmocom/gsm/gsm48.h>
+
+#include <arpa/inet.h>
+
+#define BSSMAP_MSG_SIZE 512
+#define BSSMAP_MSG_HEADROOM 128
+
+struct msgb *gsm0808_create_layer3(struct msgb *msg_l3, uint16_t nc, uint16_t cc, int lac, uint16_t _ci)
+{
+ struct msgb* msg;
+ struct {
+ uint8_t ident;
+ struct gsm48_loc_area_id lai;
+ uint16_t ci;
+ } __attribute__ ((packed)) lai_ci;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap cmpl l3");
+ if (!msg)
+ return NULL;
+
+ /* create layer 3 header */
+ msgb_v_put(msg, BSS_MAP_MSG_COMPLETE_LAYER_3);
+
+ /* create the cell header */
+ lai_ci.ident = CELL_IDENT_WHOLE_GLOBAL;
+ gsm48_generate_lai(&lai_ci.lai, cc, nc, lac);
+ lai_ci.ci = htons(_ci);
+ msgb_tlv_put(msg, GSM0808_IE_CELL_IDENTIFIER, sizeof(lai_ci),
+ (uint8_t *) &lai_ci);
+
+ /* copy the layer3 data */
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION,
+ msgb_l3len(msg_l3), msg_l3->l3h);
+
+ /* push the bssmap header */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_reset(void)
+{
+ uint8_t cause = GSM0808_CAUSE_EQUIPMENT_FAILURE;
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: reset");
+ if (!msg)
+ return NULL;
+
+ msgb_v_put(msg, BSS_MAP_MSG_RESET);
+ msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &cause);
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_clear_complete(void)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: clear complete");
+ uint8_t val = BSS_MAP_MSG_CLEAR_COMPLETE;
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msg->data;
+ msgb_tlv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 1, &val);
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_clear_command(uint8_t reason)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: clear command");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msgb_tv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 4);
+ msgb_v_put(msg, BSS_MAP_MSG_CLEAR_CMD);
+ msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &reason);
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "cipher-complete");
+ if (!msg)
+ return NULL;
+
+ /* send response with BSS override for A5/1... cheating */
+ msgb_v_put(msg, BSS_MAP_MSG_CIPHER_MODE_COMPLETE);
+
+ /* include layer3 in case we have at least two octets */
+ if (layer3 && msgb_l3len(layer3) > 2) {
+ msg->l4h = msgb_tlv_put(msg, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS,
+ msgb_l3len(layer3), layer3->l3h);
+ }
+
+ /* and the optional BSS message */
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, alg_id);
+
+ /* pre-pend the header */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_cipher_reject(uint8_t cause)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: clear complete");
+ if (!msg)
+ return NULL;
+
+ msgb_tv_put(msg, BSS_MAP_MSG_CIPHER_MODE_REJECT, cause);
+
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len,
+ const uint8_t *cm3, uint8_t cm3_len)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "classmark-update");
+ if (!msg)
+ return NULL;
+
+ msgb_v_put(msg, BSS_MAP_MSG_CLASSMARK_UPDATE);
+ msgb_tlv_put(msg, GSM0808_IE_CLASSMARK_INFORMATION_T2, cm2_len, cm2);
+ if (cm3)
+ msgb_tlv_put(msg, GSM0808_IE_CLASSMARK_INFORMATION_T3,
+ cm3_len, cm3);
+
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: sapi 'n' reject");
+ if (!msg)
+ return NULL;
+
+ msgb_v_put(msg, BSS_MAP_MSG_SAPI_N_REJECT);
+ msgb_v_put(msg, link_id);
+ msgb_v_put(msg, GSM0808_CAUSE_BSS_NOT_EQUIPPED);
+
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_assignment_completed(uint8_t rr_cause,
+ uint8_t chosen_channel, uint8_t encr_alg_id,
+ uint8_t speech_mode)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: ass compl");
+ if (!msg)
+ return NULL;
+
+ msgb_v_put(msg, BSS_MAP_MSG_ASSIGMENT_COMPLETE);
+
+ /* write 3.2.2.22 */
+ msgb_tv_put(msg, GSM0808_IE_RR_CAUSE, rr_cause);
+
+ /* write cirtcuit identity code 3.2.2.2 */
+ /* write cell identifier 3.2.2.17 */
+ /* write chosen channel 3.2.2.33 when BTS picked it */
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, chosen_channel);
+
+ /* write chosen encryption algorithm 3.2.2.44 */
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, encr_alg_id);
+
+ /* write circuit pool 3.2.2.45 */
+ /* write speech version chosen: 3.2.2.51 when BTS picked it */
+ if (speech_mode != 0)
+ msgb_tv_put(msg, GSM0808_IE_SPEECH_VERSION, speech_mode);
+
+ /* write LSA identifier 3.2.2.15 */
+
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause)
+{
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: ass fail");
+ if (!msg)
+ return NULL;
+
+ msgb_v_put(msg, BSS_MAP_MSG_ASSIGMENT_FAILURE);
+ msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &cause);
+
+ /* RR cause 3.2.2.22 */
+ if (rr_cause)
+ msgb_tv_put(msg, GSM0808_IE_RR_CAUSE, *rr_cause);
+
+ /* Circuit pool 3.22.45 */
+ /* Circuit pool list 3.2.2.46 */
+
+ /* update the size */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_clear_rqst(uint8_t cause)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "bssmap: clear rqst");
+ if (!msg)
+ return NULL;
+
+ msgb_v_put(msg, BSS_MAP_MSG_CLEAR_RQST);
+ msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &cause);
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id)
+{
+ uint8_t *hh = msgb_push(msg, 3);
+ hh[0] = BSSAP_MSG_DTAP;
+ hh[1] = link_id;
+ hh[2] = msg->len - 3;
+}
+
+struct msgb *gsm0808_create_dtap(struct msgb *msg_l3, uint8_t link_id)
+{
+ struct dtap_header *header;
+ uint8_t *data;
+ struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+ "dtap");
+ if (!msg)
+ return NULL;
+
+ /* DTAP header */
+ msg->l3h = msgb_put(msg, sizeof(*header));
+ header = (struct dtap_header *) &msg->l3h[0];
+ header->type = BSSAP_MSG_DTAP;
+ header->link_id = link_id;
+ header->length = msgb_l3len(msg_l3);
+
+ /* Payload */
+ data = msgb_put(msg, header->length);
+ memcpy(data, msg_l3->l3h, header->length);
+
+ return msg;
+}
+
+static const struct tlv_definition bss_att_tlvdef = {
+ .def = {
+ [GSM0808_IE_IMSI] = { TLV_TYPE_TLV },
+ [GSM0808_IE_TMSI] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV },
+ [GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV },
+ [GSM0808_IE_CHANNEL_TYPE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_PRIORITY] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_FIXED, 2 },
+ [GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV },
+ [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV },
+ [GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV },
+ [GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_TALKER_FLAG] = { TLV_TYPE_T },
+ [GSM0808_IE_CONFIG_EVO_INDI] = { TLV_TYPE_TV },
+ [GSM0808_IE_LSA_ACCESS_CTRL_SUPPR] = { TLV_TYPE_TV },
+ [GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TLV },
+ [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV },
+ [GSM0808_IE_CELL_IDENTIFIER] = { TLV_TYPE_TLV },
+ [GSM0808_IE_CHOSEN_CHANNEL] = { TLV_TYPE_TV },
+ [GSM0808_IE_LAYER_3_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_IE_SPEECH_VERSION] = { TLV_TYPE_TV },
+ [GSM0808_IE_CHOSEN_ENCR_ALG] = { TLV_TYPE_TV },
+ },
+};
+
+const struct tlv_definition *gsm0808_att_tlvdef(void)
+{
+ return &bss_att_tlvdef;
+}
+
+static const struct value_string gsm0808_msgt_names[] = {
+ { BSS_MAP_MSG_ASSIGMENT_RQST, "ASSIGNMENT REQ" },
+ { BSS_MAP_MSG_ASSIGMENT_COMPLETE, "ASSIGNMENT COMPL" },
+ { BSS_MAP_MSG_ASSIGMENT_FAILURE, "ASSIGNMENT FAIL" },
+
+ { BSS_MAP_MSG_HANDOVER_RQST, "HANDOVER REQ" },
+ { BSS_MAP_MSG_HANDOVER_REQUIRED, "HANDOVER REQUIRED" },
+ { BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE,"HANDOVER REQ ACK" },
+ { BSS_MAP_MSG_HANDOVER_CMD, "HANDOVER CMD" },
+ { BSS_MAP_MSG_HANDOVER_COMPLETE, "HANDOVER COMPLETE" },
+ { BSS_MAP_MSG_HANDOVER_SUCCEEDED, "HANDOVER SUCCESS" },
+ { BSS_MAP_MSG_HANDOVER_FAILURE, "HANDOVER FAILURE" },
+ { BSS_MAP_MSG_HANDOVER_PERFORMED, "HANDOVER PERFORMED" },
+ { BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE, "HANDOVER CAND ENQ" },
+ { BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE, "HANDOVER CAND RESP" },
+ { BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT, "HANDOVER REQ REJ" },
+ { BSS_MAP_MSG_HANDOVER_DETECT, "HANDOVER DETECT" },
+
+ { BSS_MAP_MSG_CLEAR_CMD, "CLEAR COMMAND" },
+ { BSS_MAP_MSG_CLEAR_COMPLETE, "CLEAR COMPLETE" },
+ { BSS_MAP_MSG_CLEAR_RQST, "CLEAR REQUEST" },
+ { BSS_MAP_MSG_SAPI_N_REJECT, "SAPI N REJECT" },
+ { BSS_MAP_MSG_CONFUSION, "CONFUSION" },
+
+ { BSS_MAP_MSG_SUSPEND, "SUSPEND" },
+ { BSS_MAP_MSG_RESUME, "RESUME" },
+ { BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION, "CONN ORIENT INFO" },
+ { BSS_MAP_MSG_PERFORM_LOCATION_RQST, "PERFORM LOC REQ" },
+ { BSS_MAP_MSG_LSA_INFORMATION, "LSA INFORMATION" },
+ { BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE, "PERFORM LOC RESP" },
+ { BSS_MAP_MSG_PERFORM_LOCATION_ABORT, "PERFORM LOC ABORT" },
+ { BSS_MAP_MSG_COMMON_ID, "COMMON ID" },
+
+ { BSS_MAP_MSG_RESET, "RESET" },
+ { BSS_MAP_MSG_RESET_ACKNOWLEDGE, "RESET ACK" },
+ { BSS_MAP_MSG_OVERLOAD, "OVERLOAD" },
+ { BSS_MAP_MSG_RESET_CIRCUIT, "RESET CIRCUIT" },
+ { BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE, "RESET CIRCUIT ACK" },
+ { BSS_MAP_MSG_MSC_INVOKE_TRACE, "MSC INVOKE TRACE" },
+ { BSS_MAP_MSG_BSS_INVOKE_TRACE, "BSS INVOKE TRACE" },
+ { BSS_MAP_MSG_CONNECTIONLESS_INFORMATION, "CONNLESS INFO" },
+
+ { BSS_MAP_MSG_BLOCK, "BLOCK" },
+ { BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE, "BLOCK ACK" },
+ { BSS_MAP_MSG_UNBLOCK, "UNBLOCK" },
+ { BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE, "UNBLOCK ACK" },
+ { BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK, "CIRC GROUP BLOCK" },
+ { BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE, "CIRC GORUP BLOCK ACK" },
+ { BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK, "CIRC GROUP UNBLOCK" },
+ { BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE, "CIRC GROUP UNBLOCK ACK" },
+ { BSS_MAP_MSG_UNEQUIPPED_CIRCUIT, "UNEQUIPPED CIRCUIT" },
+ { BSS_MAP_MSG_CHANGE_CIRCUIT, "CHANGE CIRCUIT" },
+ { BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE, "CHANGE CIRCUIT ACK" },
+
+ { BSS_MAP_MSG_RESOURCE_RQST, "RESOURCE REQ" },
+ { BSS_MAP_MSG_RESOURCE_INDICATION, "RESOURCE IND" },
+ { BSS_MAP_MSG_PAGING, "PAGING" },
+ { BSS_MAP_MSG_CIPHER_MODE_CMD, "CIPHER MODE CMD" },
+ { BSS_MAP_MSG_CLASSMARK_UPDATE, "CLASSMARK UPDATE" },
+ { BSS_MAP_MSG_CIPHER_MODE_COMPLETE, "CIPHER MODE COMPLETE" },
+ { BSS_MAP_MSG_QUEUING_INDICATION, "QUEUING INDICATION" },
+ { BSS_MAP_MSG_COMPLETE_LAYER_3, "COMPLETE LAYER 3" },
+ { BSS_MAP_MSG_CLASSMARK_RQST, "CLASSMARK REQ" },
+ { BSS_MAP_MSG_CIPHER_MODE_REJECT, "CIPHER MODE REJECT" },
+ { BSS_MAP_MSG_LOAD_INDICATION, "LOAD IND" },
+
+ /* FIXME: VGCS/VBS */
+
+ { 0, NULL }
+};
+
+const char *gsm0808_bssmap_name(uint8_t msg_type)
+{
+ return get_value_string(gsm0808_msgt_names, msg_type);
+}
+
+static const struct value_string gsm0808_bssap_names[] = {
+ { BSSAP_MSG_BSS_MANAGEMENT, "MANAGEMENT" },
+ { BSSAP_MSG_DTAP, "DTAP" },
+};
+
+const char *gsm0808_bssap_name(uint8_t msg_type)
+{
+ return get_value_string(gsm0808_bssap_names, msg_type);
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm48.c b/src/shared/libosmocore/src/gsm/gsm48.c
new file mode 100644
index 00000000..379ed65c
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm48.c
@@ -0,0 +1,428 @@
+/* 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 <osmocom/core/utils.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.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 */
+ },
+};
+
+/* RR elements */
+const struct tlv_definition gsm48_rr_att_tlvdef = {
+ .def = {
+ /* NOTE: Don't add IE 17 = MOBILE_ID here, it already used. */
+ [GSM48_IE_VGCS_TARGET] = { TLV_TYPE_TLV },
+ [GSM48_IE_FRQSHORT_AFTER] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_MUL_RATE_CFG] = { TLV_TYPE_TLV },
+ [GSM48_IE_FREQ_L_AFTER] = { TLV_TYPE_TLV },
+ [GSM48_IE_MSLOT_DESC] = { TLV_TYPE_TLV },
+ [GSM48_IE_CHANMODE_2] = { TLV_TYPE_TV },
+ [GSM48_IE_FRQSHORT_BEFORE] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_CHANMODE_3] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_4] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_5] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_6] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_7] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_8] = { TLV_TYPE_TV },
+ [GSM48_IE_FREQ_L_BEFORE] = { TLV_TYPE_TLV },
+ [GSM48_IE_CH_DESC_1_BEFORE] = { TLV_TYPE_FIXED, 3 },
+ [GSM48_IE_CH_DESC_2_BEFORE] = { TLV_TYPE_FIXED, 3 },
+ [GSM48_IE_F_CH_SEQ_BEFORE] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_CLASSMARK3] = { TLV_TYPE_TLV },
+ [GSM48_IE_MA_BEFORE] = { TLV_TYPE_TLV },
+ [GSM48_IE_RR_PACKET_UL] = { TLV_TYPE_TLV },
+ [GSM48_IE_RR_PACKET_DL] = { TLV_TYPE_TLV },
+ [GSM48_IE_CELL_CH_DESC] = { TLV_TYPE_FIXED, 16 },
+ [GSM48_IE_CHANMODE_1] = { TLV_TYPE_TV },
+ [GSM48_IE_CHDES_2_AFTER] = { TLV_TYPE_FIXED, 3 },
+ [GSM48_IE_MODE_SEC_CH] = { TLV_TYPE_TV },
+ [GSM48_IE_F_CH_SEQ_AFTER] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_MA_AFTER] = { TLV_TYPE_TLV },
+ [GSM48_IE_BA_RANGE] = { TLV_TYPE_TLV },
+ [GSM48_IE_GROUP_CHDES] = { TLV_TYPE_TLV },
+ [GSM48_IE_BA_LIST_PREF] = { TLV_TYPE_TLV },
+ [GSM48_IE_MOB_OVSERV_DIF] = { TLV_TYPE_TLV },
+ [GSM48_IE_REALTIME_DIFF] = { TLV_TYPE_TLV },
+ [GSM48_IE_START_TIME] = { TLV_TYPE_FIXED, 2 },
+ [GSM48_IE_TIMING_ADVANCE] = { TLV_TYPE_TV },
+ [GSM48_IE_GROUP_CIP_SEQ] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_CIP_MODE_SET] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_GPRS_RESUMPT] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_SYNC_IND] = { TLV_TYPE_SINGLE_TV },
+ },
+};
+
+/* MM elements */
+const struct tlv_definition gsm48_mm_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_LOCATION_AREA] = { TLV_TYPE_FIXED, 5 },
+ [GSM48_IE_PRIORITY_LEV] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_FOLLOW_ON_PROC] = { TLV_TYPE_T },
+ [GSM48_IE_CTS_PERMISSION] = { TLV_TYPE_T },
+ },
+};
+
+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",
+ "MM_CONNECTION_PEND",
+ "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 (mnc > 99) {
+ 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);
+ }
+
+ 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] = osmo_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 = osmo_char2bcd(imsi[++off]);
+ if (!odd && off + 1 == length)
+ upper = 0x0f;
+ else
+ upper = osmo_char2bcd(imsi[++off]) & 0x0f;
+
+ buf[2 + i] = (upper << 4) | lower;
+ }
+
+ return 2 + buf[1];
+}
+
+/* Convert Mobile Identity (10.5.1.4) to string */
+int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi,
+ const int mi_len)
+{
+ int i;
+ uint8_t mi_type;
+ char *str_cur = string;
+ uint32_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++ = osmo_bcd2char(mi[0] >> 4);
+
+ for (i = 1; i < mi_len; i++) {
+ if (str_cur + 2 >= string + str_len)
+ return str_cur - string;
+ *str_cur++ = osmo_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++ = osmo_bcd2char(mi[i] >> 4);
+ }
+ break;
+ default:
+ break;
+ }
+ *str_cur++ = '\0';
+
+ return str_cur - string;
+}
+
+void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf)
+{
+ raid->mcc = (buf[0] & 0xf) * 100;
+ raid->mcc += (buf[0] >> 4) * 10;
+ raid->mcc += (buf[1] & 0xf) * 1;
+
+ /* I wonder who came up with the stupidity of encoding the MNC
+ * differently depending on how many digits its decimal number has! */
+ if ((buf[1] >> 4) == 0xf) {
+ raid->mnc = (buf[2] & 0xf) * 10;
+ raid->mnc += (buf[2] >> 4) * 1;
+ } else {
+ raid->mnc = (buf[2] & 0xf) * 100;
+ raid->mnc += (buf[2] >> 4) * 10;
+ raid->mnc += (buf[1] >> 4) * 1;
+ }
+
+ raid->lac = ntohs(*(uint16_t *)(buf + 3));
+ raid->rac = buf[5];
+}
+
+int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid)
+{
+ uint16_t mcc = raid->mcc;
+ uint16_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) | (((mnc / 10) % 10) << 4);
+ }
+
+ *(uint16_t *)(buf+3) = htons(raid->lac);
+
+ buf[5] = raid->rac;
+
+ return 6;
+}
+
+/* From Table 10.5.33 of GSM 04.08 */
+int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc)
+{
+ unsigned int n_pag_blocks = gsm0502_get_n_pag_blocks(chan_desc);
+
+ if (chan_desc->ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
+ return OSMO_MAX(1, n_pag_blocks) * (chan_desc->bs_pa_mfrms + 2);
+ else
+ return n_pag_blocks * (chan_desc->bs_pa_mfrms + 2);
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm48_ie.c b/src/shared/libosmocore/src/gsm/gsm48_ie.c
new file mode 100644
index 00000000..c10d0ed7
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm48_ie.c
@@ -0,0 +1,1107 @@
+/* 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) 2009-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 <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/mncc.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm48_ie.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] = 0x80; /* no extension */
+ 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;
+}
+
+static int32_t smod(int32_t n, int32_t m)
+{
+ int32_t res;
+
+ res = n % m;
+
+ if (res <= 0)
+ res += m;
+
+ return res;
+}
+
+/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
+int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
+ uint8_t len, uint8_t mask, uint8_t frqt)
+{
+ int i;
+
+ /* NOTES:
+ *
+ * The Range format uses "SMOD" computation.
+ * e.g. "n SMOD m" equals "((n - 1) % m) + 1"
+ * A cascade of multiple SMOD computations is simpified:
+ * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1"
+ *
+ * The Range format uses 16 octets of data in SYSTEM INFORMATION.
+ * When used in dedicated messages, the length can be less.
+ * In this case the ranges are decoded for all frequencies that
+ * fit in the block of given length.
+ */
+
+ /* tabula rasa */
+ for (i = 0; i < 1024; i++)
+ f[i].mask &= ~frqt;
+
+ /* 00..XXX. */
+ if ((cd[0] & 0xc0 & mask) == 0x00) {
+ /* Bit map 0 format */
+ if (len < 16)
+ return -EINVAL;
+ for (i = 1; i <= 124; i++)
+ if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7))))
+ f[i].mask |= frqt;
+
+ return 0;
+ }
+
+ /* 10..0XX. */
+ if ((cd[0] & 0xc8 & mask) == 0x80) {
+ /* Range 1024 format */
+ uint16_t w[17]; /* 1..16 */
+ struct gsm48_range_1024 *r = (struct gsm48_range_1024 *)cd;
+
+ if (len < 2)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ if (r->f0)
+ f[0].mask |= frqt;
+ w[1] = (r->w1_hi << 8) | r->w1_lo;
+ if (len >= 4)
+ w[2] = (r->w2_hi << 1) | r->w2_lo;
+ if (len >= 5)
+ w[3] = (r->w3_hi << 2) | r->w3_lo;
+ if (len >= 6)
+ w[4] = (r->w4_hi << 2) | r->w4_lo;
+ if (len >= 7)
+ w[5] = (r->w5_hi << 2) | r->w5_lo;
+ if (len >= 8)
+ w[6] = (r->w6_hi << 2) | r->w6_lo;
+ if (len >= 9)
+ w[7] = (r->w7_hi << 2) | r->w7_lo;
+ if (len >= 10)
+ w[8] = (r->w8_hi << 1) | r->w8_lo;
+ if (len >= 10)
+ w[9] = r->w9;
+ if (len >= 11)
+ w[10] = r->w10;
+ if (len >= 12)
+ w[11] = (r->w11_hi << 6) | r->w11_lo;
+ if (len >= 13)
+ w[12] = (r->w12_hi << 5) | r->w12_lo;
+ if (len >= 14)
+ w[13] = (r->w13_hi << 4) | r->w13_lo;
+ if (len >= 15)
+ w[14] = (r->w14_hi << 3) | r->w14_lo;
+ if (len >= 16)
+ w[15] = (r->w15_hi << 2) | r->w15_lo;
+ if (len >= 16)
+ w[16] = r->w16;
+ if (w[1])
+ f[w[1]].mask |= frqt;
+ if (w[2])
+ f[smod(w[1] - 512 + w[2], 1023)].mask |= frqt;
+ if (w[3])
+ f[smod(w[1] + w[3], 1023)].mask |= frqt;
+ if (w[4])
+ f[smod(w[1] - 512 + smod(w[2] - 256 + w[4], 511), 1023)].mask |= frqt;
+ if (w[5])
+ f[smod(w[1] + smod(w[3] - 256 + w[5], 511), 1023)].mask |= frqt;
+ if (w[6])
+ f[smod(w[1] - 512 + smod(w[2] + w[6], 511), 1023)].mask |= frqt;
+ if (w[7])
+ f[smod(w[1] + smod(w[3] + w[7], 511), 1023)].mask |= frqt;
+ if (w[8])
+ f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] - 128 + w[8] , 255), 511), 1023)].mask |= frqt;
+ if (w[9])
+ f[smod(w[1] + smod(w[3] - 256 + smod(w[5] - 128 + w[9] , 255), 511), 1023)].mask |= frqt;
+ if (w[10])
+ f[smod(w[1] - 512 + smod(w[2] + smod(w[6] - 128 + w[10], 255), 511), 1023)].mask |= frqt;
+ if (w[11])
+ f[smod(w[1] + smod(w[3] + smod(w[7] - 128 + w[11], 255), 511), 1023)].mask |= frqt;
+ if (w[12])
+ f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] + w[12], 255), 511), 1023)].mask |= frqt;
+ if (w[13])
+ f[smod(w[1] + smod(w[3] - 256 + smod(w[5] + w[13], 255), 511), 1023)].mask |= frqt;
+ if (w[14])
+ f[smod(w[1] - 512 + smod(w[2] + smod(w[6] + w[14], 255), 511), 1023)].mask |= frqt;
+ if (w[15])
+ f[smod(w[1] + smod(w[3] + smod(w[7] + w[15], 255), 511), 1023)].mask |= frqt;
+ if (w[16])
+ f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] - 128 + smod(w[8] - 64 + w[16], 127), 255), 511), 1023)].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..100. */
+ if ((cd[0] & 0xce & mask) == 0x88) {
+ /* Range 512 format */
+ uint16_t w[18]; /* 1..17 */
+ struct gsm48_range_512 *r = (struct gsm48_range_512 *)cd;
+
+ if (len < 4)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ w[1] = (r->w1_hi << 2) | r->w1_lo;
+ if (len >= 5)
+ w[2] = (r->w2_hi << 2) | r->w2_lo;
+ if (len >= 6)
+ w[3] = (r->w3_hi << 2) | r->w3_lo;
+ if (len >= 7)
+ w[4] = (r->w4_hi << 1) | r->w4_lo;
+ if (len >= 7)
+ w[5] = r->w5;
+ if (len >= 8)
+ w[6] = r->w6;
+ if (len >= 9)
+ w[7] = (r->w7_hi << 6) | r->w7_lo;
+ if (len >= 10)
+ w[8] = (r->w8_hi << 4) | r->w8_lo;
+ if (len >= 11)
+ w[9] = (r->w9_hi << 2) | r->w9_lo;
+ if (len >= 11)
+ w[10] = r->w10;
+ if (len >= 12)
+ w[11] = r->w11;
+ if (len >= 13)
+ w[12] = (r->w12_hi << 4) | r->w12_lo;
+ if (len >= 14)
+ w[13] = (r->w13_hi << 2) | r->w13_lo;
+ if (len >= 14)
+ w[14] = r->w14;
+ if (len >= 15)
+ w[15] = r->w15;
+ if (len >= 16)
+ w[16] = (r->w16_hi << 3) | r->w16_lo;
+ if (len >= 16)
+ w[17] = r->w17;
+ f[w[0]].mask |= frqt;
+ if (w[1])
+ f[(w[0] + w[1]) % 1024].mask |= frqt;
+ if (w[2])
+ f[(w[0] + smod(w[1] - 256 + w[2], 511)) % 1024].mask |= frqt;
+ if (w[3])
+ f[(w[0] + smod(w[1] + w[3], 511)) % 1024].mask |= frqt;
+ if (w[4])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + w[4], 255), 511)) % 1024].mask |= frqt;
+ if (w[5])
+ f[(w[0] + smod(w[1] + smod(w[3] - 128 + w[5], 255), 511)) % 1024].mask |= frqt;
+ if (w[6])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] + w[6], 255), 511)) % 1024].mask |= frqt;
+ if (w[7])
+ f[(w[0] + smod(w[1] + smod(w[3] + w[7], 255), 511)) % 1024].mask |= frqt;
+ if (w[8])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] - 64 + w[8] , 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[9])
+ f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] - 64 + w[9] , 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[10])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] + smod(w[6] - 64 + w[10], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[11])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 64 + w[11], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[12])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] + w[12], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[13])
+ f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] + w[13], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[14])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] + smod(w[6] + w[14], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[15])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[16])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] - 64 + smod(w[8] - 32 + w[16], 63), 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[17])
+ f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] - 64 + smod(w[9] - 32 + w[17], 63), 127), 255), 511)) % 1024].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..101. */
+ if ((cd[0] & 0xce & mask) == 0x8a) {
+ /* Range 256 format */
+ uint16_t w[22]; /* 1..21 */
+ struct gsm48_range_256 *r = (struct gsm48_range_256 *)cd;
+
+ if (len < 4)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ w[1] = (r->w1_hi << 1) | r->w1_lo;
+ if (len >= 4)
+ w[2] = r->w2;
+ if (len >= 5)
+ w[3] = r->w3;
+ if (len >= 6)
+ w[4] = (r->w4_hi << 5) | r->w4_lo;
+ if (len >= 7)
+ w[5] = (r->w5_hi << 3) | r->w5_lo;
+ if (len >= 8)
+ w[6] = (r->w6_hi << 1) | r->w6_lo;
+ if (len >= 8)
+ w[7] = r->w7;
+ if (len >= 9)
+ w[8] = (r->w8_hi << 4) | r->w8_lo;
+ if (len >= 10)
+ w[9] = (r->w9_hi << 1) | r->w9_lo;
+ if (len >= 10)
+ w[10] = r->w10;
+ if (len >= 11)
+ w[11] = (r->w11_hi << 3) | r->w11_lo;
+ if (len >= 11)
+ w[12] = r->w12;
+ if (len >= 12)
+ w[13] = r->w13;
+ if (len >= 13)
+ w[14] = r->w15;
+ if (len >= 13)
+ w[15] = (r->w14_hi << 2) | r->w14_lo;
+ if (len >= 14)
+ w[16] = (r->w16_hi << 3) | r->w16_lo;
+ if (len >= 14)
+ w[17] = r->w17;
+ if (len >= 15)
+ w[18] = r->w19;
+ if (len >= 15)
+ w[19] = (r->w18_hi << 3) | r->w18_lo;
+ if (len >= 16)
+ w[20] = (r->w20_hi << 3) | r->w20_lo;
+ if (len >= 16)
+ w[21] = r->w21;
+ f[w[0]].mask |= frqt;
+ if (w[1])
+ f[(w[0] + w[1]) % 1024].mask |= frqt;
+ if (w[2])
+ f[(w[0] + smod(w[1] - 128 + w[2], 255)) % 1024].mask |= frqt;
+ if (w[3])
+ f[(w[0] + smod(w[1] + w[3], 255)) % 1024].mask |= frqt;
+ if (w[4])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + w[4], 127), 255)) % 1024].mask |= frqt;
+ if (w[5])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + w[5], 127), 255)) % 1024].mask |= frqt;
+ if (w[6])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] + w[6], 127), 255)) % 1024].mask |= frqt;
+ if (w[7])
+ f[(w[0] + smod(w[1] + smod(w[3] + w[7], 127), 255)) % 1024].mask |= frqt;
+ if (w[8])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] - 32 + w[8] , 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[9])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] - 32 + w[9] , 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[10])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] - 32 + w[10], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[11])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 32 + w[11], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[12])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] + w[12], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[13])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] + w[13], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[14])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] + w[14], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[15])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[16])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] - 32 + smod(w[8] - 16 + w[16], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[17])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] - 32 + smod(w[9] - 16 + w[17], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[18])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] - 32 + smod(w[10] - 16 + w[18], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[19])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 32 + smod(w[11] - 16 + w[19], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[20])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] + smod(w[12] - 16 + w[20], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[21])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] + smod(w[13] - 16 + w[21], 31), 63), 127), 255)) % 1024].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..110. */
+ if ((cd[0] & 0xce & mask) == 0x8c) {
+ /* Range 128 format */
+ uint16_t w[29]; /* 1..28 */
+ struct gsm48_range_128 *r = (struct gsm48_range_128 *)cd;
+
+ if (len < 3)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ w[1] = r->w1;
+ if (len >= 4)
+ w[2] = r->w2;
+ if (len >= 5)
+ w[3] = (r->w3_hi << 4) | r->w3_lo;
+ if (len >= 6)
+ w[4] = (r->w4_hi << 1) | r->w4_lo;
+ if (len >= 6)
+ w[5] = r->w5;
+ if (len >= 7)
+ w[6] = (r->w6_hi << 3) | r->w6_lo;
+ if (len >= 7)
+ w[7] = r->w7;
+ if (len >= 8)
+ w[8] = r->w8;
+ if (len >= 8)
+ w[9] = r->w9;
+ if (len >= 9)
+ w[10] = r->w10;
+ if (len >= 9)
+ w[11] = r->w11;
+ if (len >= 10)
+ w[12] = r->w12;
+ if (len >= 10)
+ w[13] = r->w13;
+ if (len >= 11)
+ w[14] = r->w14;
+ if (len >= 11)
+ w[15] = r->w15;
+ if (len >= 12)
+ w[16] = r->w16;
+ if (len >= 12)
+ w[17] = r->w17;
+ if (len >= 13)
+ w[18] = (r->w18_hi << 1) | r->w18_lo;
+ if (len >= 13)
+ w[19] = r->w19;
+ if (len >= 13)
+ w[20] = r->w20;
+ if (len >= 14)
+ w[21] = (r->w21_hi << 2) | r->w21_lo;
+ if (len >= 14)
+ w[22] = r->w22;
+ if (len >= 14)
+ w[23] = r->w23;
+ if (len >= 15)
+ w[24] = r->w24;
+ if (len >= 15)
+ w[25] = r->w25;
+ if (len >= 16)
+ w[26] = (r->w26_hi << 1) | r->w26_lo;
+ if (len >= 16)
+ w[27] = r->w27;
+ if (len >= 16)
+ w[28] = r->w28;
+ f[w[0]].mask |= frqt;
+ if (w[1])
+ f[(w[0] + w[1]) % 1024].mask |= frqt;
+ if (w[2])
+ f[(w[0] + smod(w[1] - 64 + w[2], 127)) % 1024].mask |= frqt;
+ if (w[3])
+ f[(w[0] + smod(w[1] + w[3], 127)) % 1024].mask |= frqt;
+ if (w[4])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + w[4], 63), 127)) % 1024].mask |= frqt;
+ if (w[5])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + w[5], 63), 127)) % 1024].mask |= frqt;
+ if (w[6])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + w[6], 63), 127)) % 1024].mask |= frqt;
+ if (w[7])
+ f[(w[0] + smod(w[1] + smod(w[3] + w[7], 63), 127)) % 1024].mask |= frqt;
+ if (w[8])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + w[8] , 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[9])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + w[9] , 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[10])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + w[10], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[11])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + w[11], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[12])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + w[12], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[13])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] + w[13], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[14])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] + w[14], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[15])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[16])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + smod(w[8] - 8 + w[16], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[17])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + smod(w[9] - 8 + w[17], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[18])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + smod(w[10] - 8 + w[18], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[19])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + smod(w[11] - 8 + w[19], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[20])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + smod(w[12] - 8 + w[20], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[21])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] + smod(w[13] - 8 + w[21], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[22])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] + smod(w[14] - 8 + w[22], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[23])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + smod(w[15] - 8 + w[23], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[24])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + smod(w[8] + w[24], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[25])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + smod(w[9] + w[25], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[26])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + smod(w[10] + w[26], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[27])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + smod(w[11] + w[27], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[28])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + smod(w[12] + w[28], 15), 31), 63), 127)) % 1024].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..111. */
+ if ((cd[0] & 0xce & mask) == 0x8e) {
+ /* Variable bitmap format (can be any length >= 3) */
+ uint16_t orig = 0;
+ struct gsm48_var_bit *r = (struct gsm48_var_bit *)cd;
+
+ if (len < 3)
+ return -EINVAL;
+ orig = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ f[orig].mask |= frqt;
+ for (i = 1; 2 + (i >> 3) < len; i++)
+ if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7))))
+ f[(orig + i) % 1024].mask |= frqt;
+
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/gsm/gsm_utils.c b/src/shared/libosmocore/src/gsm/gsm_utils.c
new file mode 100644
index 00000000..8d072a1f
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/gsm_utils.c
@@ -0,0 +1,606 @@
+/*
+ * (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>
+ * (C) 2010 by Nico Golde <nico@ngolde.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.
+ *
+ */
+
+/*! \mainpage libosmogsm Documentation
+ *
+ * \section sec_intro Introduction
+ * This library is a collection of common code used in various
+ * GSM related sub-projects inside the Osmocom family of projects. It
+ * includes A5/1 and A5/2 ciphers, COMP128v1, a LAPDm implementation,
+ * a GSM TLV parser, SMS utility routines as well as
+ * protocol definitions for a series of protocols:
+ * * Um L2 (04.06)
+ * * Um L3 (04.08)
+ * * A-bis RSL (08.58)
+ * * A-bis OML (08.59, 12.21)
+ * * A (08.08)
+ * \n\n
+ * Please note that C language projects inside Osmocom are typically
+ * single-threaded event-loop state machine designs. As such,
+ * routines in libosmogsm are not thread-safe. If you must use them in
+ * a multi-threaded context, you have to add your own locking.
+ *
+ * \section sec_copyright Copyright and License
+ * Copyright © 2008-2011 - Harald Welte, Holger Freyther and contributors\n
+ * All rights reserved. \n\n
+ * The source code of libosmogsm is licensed 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.\n
+ * See <http://www.gnu.org/licenses/> or COPYING included in the source
+ * code package istelf.\n
+ * The information detailed here is provided AS IS with NO WARRANTY OF
+ * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * \n\n
+ *
+ * \section sec_contact Contact and Support
+ * Community-based support is available at the OpenBSC mailing list
+ * <http://lists.osmocom.org/mailman/listinfo/openbsc>\n
+ * Commercial support options available upon request from
+ * <http://sysmocom.de/>
+ */
+
+//#include <openbsc/gsm_data.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "../../config.h"
+
+/* ETSI GSM 03.38 6.2.1 and 6.2.1.1 default alphabet
+ * Greek symbols at hex positions 0x10 and 0x12-0x1a
+ * left out as they can't be handled with a char and
+ * since most phones don't display or write these
+ * characters this would only needlessly make the code
+ * more complex
+*/
+static unsigned char gsm_7bit_alphabet[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0xff, 0xff, 0x0d, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+ 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0x3e, 0x3f, 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x3c, 0x2f, 0x3e, 0x14, 0x11, 0xff, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x28, 0x40, 0x29, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0xff, 0x01, 0xff,
+ 0x03, 0xff, 0x7b, 0x7d, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5c, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x7e, 0x5d, 0xff, 0x7c, 0xff, 0xff, 0xff,
+ 0xff, 0x5b, 0x0e, 0x1c, 0x09, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5d,
+ 0xff, 0xff, 0xff, 0xff, 0x5c, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff, 0x1e, 0x7f,
+ 0xff, 0xff, 0xff, 0x7b, 0x0f, 0x1d, 0xff, 0x04, 0x05, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff,
+ 0xff, 0x7d, 0x08, 0xff, 0xff, 0xff, 0x7c, 0xff, 0x0c, 0x06, 0xff, 0xff, 0x7e, 0xff, 0xff
+};
+
+/* GSM 03.38 6.2.1 Character lookup for decoding */
+static int gsm_septet_lookup(uint8_t ch)
+{
+ int i = 0;
+ for (; i < sizeof(gsm_7bit_alphabet); i++) {
+ if (gsm_7bit_alphabet[i] == ch)
+ return i;
+ }
+ return -1;
+}
+
+/* Compute the number of octets from the number of septets, for instance: 47 septets needs 41,125 = 42 octets */
+uint8_t gsm_get_octet_len(const uint8_t sept_len){
+ int octet_len = (sept_len * 7) / 8;
+ if ((sept_len * 7) % 8 != 0)
+ octet_len++;
+
+ return octet_len;
+}
+
+/* GSM 03.38 6.2.1 Character unpacking */
+int gsm_7bit_decode_hdr(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind)
+{
+ int i = 0;
+ int shift = 0;
+
+ uint8_t *rtext = calloc(septet_l, sizeof(uint8_t));
+ uint8_t tmp;
+
+ /* skip the user data header */
+ if (ud_hdr_ind) {
+ /* get user data header length + 1 (for the 'user data header length'-field) */
+ shift = ((user_data[0] + 1) * 8) / 7;
+ if ((((user_data[0] + 1) * 8) % 7) != 0)
+ shift++;
+ septet_l = septet_l - shift;
+ }
+
+ for (i = 0; i < septet_l; i++) {
+ rtext[i] =
+ ((user_data[((i + shift) * 7 + 7) >> 3] <<
+ (7 - (((i + shift) * 7 + 7) & 7))) |
+ (user_data[((i + shift) * 7) >> 3] >>
+ (((i + shift) * 7) & 7))) & 0x7f;
+ }
+
+ for (i = 0; i < septet_l; i++) {
+ /* this is an extension character */
+ if(rtext[i] == 0x1b && i + 1 < septet_l){
+ tmp = rtext[i+1];
+ *(text++) = gsm_7bit_alphabet[0x7f + tmp];
+ i++;
+ continue;
+ }
+
+ *(text++) = gsm_septet_lookup(rtext[i]);
+ }
+
+ if (ud_hdr_ind)
+ i += shift;
+ *text = '\0';
+ free(rtext);
+
+ return i;
+}
+
+int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t septet_l)
+{
+ return gsm_7bit_decode_hdr(text, user_data, septet_l, 0);
+}
+
+/* GSM 03.38 6.2.1 Prepare character packing */
+int gsm_septet_encode(uint8_t *result, const char *data)
+{
+ int i, y = 0;
+ uint8_t ch;
+ for (i = 0; i < strlen(data); i++) {
+ ch = data[i];
+ switch(ch){
+ /* fall-through for extension characters */
+ case 0x0c:
+ case 0x5e:
+ case 0x7b:
+ case 0x7d:
+ case 0x5c:
+ case 0x5b:
+ case 0x7e:
+ case 0x5d:
+ case 0x7c:
+ result[y++] = 0x1b;
+ default:
+ result[y] = gsm_7bit_alphabet[ch];
+ break;
+ }
+ y++;
+ }
+
+ return y;
+}
+
+/* 7bit to octet packing */
+int gsm_septets2octets(uint8_t *result, uint8_t *rdata, uint8_t septet_len, uint8_t padding){
+ int i = 0, z = 0;
+ uint8_t cb, nb;
+ int shift = 0;
+ uint8_t *data = calloc(septet_len + 1, sizeof(uint8_t));
+
+ if (padding) {
+ shift = 7 - padding;
+ /* the first zero is needed for padding */
+ memcpy(data + 1, rdata, septet_len);
+ septet_len++;
+ } else
+ memcpy(data, rdata, septet_len);
+
+ for (i = 0; i < septet_len; i++) {
+ if (shift == 7) {
+ /*
+ * special end case with the. This is necessary if the
+ * last septet fits into the previous octet. E.g. 48
+ * non-extension characters:
+ * ....ag ( a = 1100001, g = 1100111)
+ * result[40] = 100001 XX, result[41] = 1100111 1 */
+ if (i + 1 < septet_len) {
+ shift = 0;
+ continue;
+ } else if (i + 1 == septet_len)
+ break;
+ }
+
+ cb = (data[i] & 0x7f) >> shift;
+ if (i + 1 < septet_len) {
+ nb = (data[i + 1] & 0x7f) << (7 - shift);
+ cb = cb | nb;
+ }
+
+ result[z++] = cb;
+ shift++;
+ }
+
+ free(data);
+
+ return z;
+}
+
+/* GSM 03.38 6.2.1 Character packing */
+int gsm_7bit_encode(uint8_t *result, const char *data)
+{
+ int y = 0, z = 0;
+ /* prepare for the worst case, every character expanding to two bytes */
+ uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t));
+ y = gsm_septet_encode(rdata, data);
+ z = gsm_septets2octets(result, rdata, y, 0);
+
+ free(rdata);
+
+ /*
+ * We don't care about the number of octets (z), because they are not
+ * unique. E.g.:
+ * 1.) 46 non-extension characters + 1 extension character
+ * => (46 * 7 bit + (1 * (2 * 7 bit))) / 8 bit = 42 octets
+ * 2.) 47 non-extension characters
+ * => (47 * 7 bit) / 8 bit = 41,125 = 42 octets
+ * 3.) 48 non-extension characters
+ * => (48 * 7 bit) / 8 bit = 42 octects
+ */
+ return y;
+}
+
+/* convert power class to dBm according to GSM TS 05.05 */
+unsigned int ms_class_gmsk_dbm(enum gsm_band band, int class)
+{
+ 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 (class == 1)
+ return 43; /* 20W */
+ if (class == 2)
+ return 39; /* 8W */
+ if (class == 3)
+ return 37; /* 5W */
+ if (class == 4)
+ return 33; /* 2W */
+ if (class == 5)
+ return 29; /* 0.8W */
+ break;
+ case GSM_BAND_1800:
+ if (class == 1)
+ return 30; /* 1W */
+ if (class == 2)
+ return 24; /* 0.25W */
+ if (class == 3)
+ return 36; /* 4W */
+ break;
+ case GSM_BAND_1900:
+ if (class == 1)
+ return 30; /* 1W */
+ if (class == 2)
+ return 24; /* 0.25W */
+ if (class == 3)
+ return 33; /* 2W */
+ break;
+ }
+ return -EINVAL;
+}
+
+/* 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 "GSM480";
+ 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;
+ }
+}
+
+enum gsm_band gsm_arfcn2band(uint16_t arfcn)
+{
+ int is_pcs = arfcn & ARFCN_PCS;
+
+ arfcn &= ~ARFCN_FLAG_MASK;
+
+ if (is_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;
+ int is_pcs = arfcn & ARFCN_PCS;
+
+ arfcn &= ~ARFCN_FLAG_MASK;
+
+ if (is_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));
+}
+
+/* TS 03.03 Chapter 2.6 */
+int gprs_tlli_type(uint32_t tlli)
+{
+ if ((tlli & 0xc0000000) == 0xc0000000)
+ return TLLI_LOCAL;
+ else if ((tlli & 0xc0000000) == 0x80000000)
+ return TLLI_FOREIGN;
+ else if ((tlli & 0xf8000000) == 0x78000000)
+ return TLLI_RANDOM;
+ else if ((tlli & 0xf8000000) == 0x70000000)
+ return TLLI_AUXILIARY;
+
+ return TLLI_RESERVED;
+}
+
+uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type)
+{
+ uint32_t tlli;
+ switch (type) {
+ case TLLI_LOCAL:
+ tlli = p_tmsi | 0xc0000000;
+ break;
+ case TLLI_FOREIGN:
+ tlli = (p_tmsi & 0x3fffffff) | 0x80000000;
+ break;
+ default:
+ tlli = 0;
+ break;
+ }
+ return tlli;
+}
diff --git a/src/shared/libosmocore/src/gsm/lapd_core.c b/src/shared/libosmocore/src/gsm/lapd_core.c
new file mode 100644
index 00000000..fb79a2f0
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/lapd_core.c
@@ -0,0 +1,2170 @@
+/* LAPD core implementation */
+
+/* (C) 2010-2011 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010-2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ */
+
+/*! \addtogroup lapd
+ * @{
+ */
+
+/*! \file lapd.c */
+
+/*!
+ * Notes on Buffering: rcv_buffer, tx_queue, tx_hist, send_buffer, send_queue
+ *
+ * RX data is stored in the rcv_buffer (pointer). If the message is complete, it
+ * is removed from rcv_buffer pointer and forwarded to L3. If the RX data is
+ * received while there is an incomplete rcv_buffer, it is appended to it.
+ *
+ * TX data is stored in the send_queue first. When transmitting a frame,
+ * the first message in the send_queue is moved to the send_buffer. There it
+ * resides until all fragments are acknowledged. Fragments to be sent by I
+ * frames are stored in the tx_hist buffer for resend, if required. Also the
+ * current fragment is copied into the tx_queue. There it resides until it is
+ * forwarded to layer 1.
+ *
+ * In case we have SAPI 0, we only have a window size of 1, so the unack-
+ * nowledged message resides always in the send_buffer. In case of a suspend,
+ * it can be written back to the first position of the send_queue.
+ *
+ * The layer 1 normally sends a PH-READY-TO-SEND. But because we use
+ * asynchronous transfer between layer 1 and layer 2 (serial link), we must
+ * send a frame before layer 1 reaches the right timeslot to send it. So we
+ * move the tx_queue to layer 1 when there is not already a pending frame, and
+ * wait until acknowledge after the frame has been sent. If we receive an
+ * acknowledge, we can send the next frame from the buffer, if any.
+ *
+ * The moving of tx_queue to layer 1 may also trigger T200, if desired. Also it
+ * will trigger next I frame, if possible.
+ *
+ * T203 is optional. It will be stated when entering MF EST state. It will also
+ * be started when I or S frame is received in that state . It will be
+ * restarted in the lapd_acknowledge() function, in case outstanding frames
+ * will not trigger T200. It will be stoped, when T200 is started in MF EST
+ * state. It will also be stoped when leaving MF EST state.
+ *
+ */
+
+/* Enable this to test content resolution on network side:
+ * - The first SABM is received, UA is dropped.
+ * - The phone repeats SABM, but it's content is wrong, so it is ignored
+ * - The phone repeats SABM again, content is right, so UA is sent.
+ */
+//#define TEST_CONTENT_RESOLUTION_NETWORK
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/lapd_core.h>
+
+/* TS 04.06 Table 4 / Section 3.8.1 */
+#define LAPD_U_SABM 0x7
+#define LAPD_U_SABME 0xf
+#define LAPD_U_DM 0x3
+#define LAPD_U_UI 0x0
+#define LAPD_U_DISC 0x8
+#define LAPD_U_UA 0xC
+#define LAPD_U_FRMR 0x11
+
+#define LAPD_S_RR 0x0
+#define LAPD_S_RNR 0x1
+#define LAPD_S_REJ 0x2
+
+#define CR_USER2NET_CMD 0
+#define CR_USER2NET_RESP 1
+#define CR_NET2USER_CMD 1
+#define CR_NET2USER_RESP 0
+
+#define LAPD_HEADROOM 56
+
+#define SBIT(a) (1 << a)
+#define ALL_STATES 0xffffffff
+
+static void lapd_t200_cb(void *data);
+static void lapd_t203_cb(void *data);
+static int lapd_send_i(struct lapd_msg_ctx *lctx, int line);
+static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
+
+/* UTILITY FUNCTIONS */
+
+struct msgb *lapd_msgb_alloc(int length, const char *name)
+{
+ /* adding space for padding, FIXME: add as an option */
+ if (length < 21)
+ length = 21;
+ return msgb_alloc_headroom(length + LAPD_HEADROOM, LAPD_HEADROOM, name);
+}
+
+static inline uint8_t do_mod(uint8_t x, uint8_t m)
+{
+ return x & (m - 1);
+}
+
+static inline uint8_t inc_mod(uint8_t x, uint8_t m)
+{
+ return (x + 1) & (m - 1);
+}
+
+static inline uint8_t add_mod(uint8_t x, uint8_t y, uint8_t m)
+{
+ return (x + y) & (m - 1);
+}
+
+static inline uint8_t sub_mod(uint8_t x, uint8_t y, uint8_t m)
+{
+ return (x - y) & (m - 1); /* handle negative results correctly */
+}
+
+static void lapd_dl_flush_send(struct lapd_datalink *dl)
+{
+ struct msgb *msg;
+
+ /* Flush send-queue */
+ while ((msg = msgb_dequeue(&dl->send_queue)))
+ msgb_free(msg);
+
+ /* Clear send-buffer */
+ if (dl->send_buffer) {
+ msgb_free(dl->send_buffer);
+ dl->send_buffer = NULL;
+ }
+}
+
+static void lapd_dl_flush_hist(struct lapd_datalink *dl)
+{
+ unsigned int i;
+
+ for (i = 0; i < dl->range_hist; i++) {
+ if (dl->tx_hist[i].msg) {
+ msgb_free(dl->tx_hist[i].msg);
+ dl->tx_hist[i].msg = NULL;
+ }
+ }
+}
+
+static void lapd_dl_flush_tx(struct lapd_datalink *dl)
+{
+ struct msgb *msg;
+
+ while ((msg = msgb_dequeue(&dl->tx_queue)))
+ msgb_free(msg);
+ lapd_dl_flush_hist(dl);
+}
+
+/* Figure B.2/Q.921 */
+const char *lapd_state_names[] = {
+ "LAPD_STATE_NULL",
+ "LAPD_STATE_TEI_UNASS",
+ "LAPD_STATE_ASS_TEI_WAIT",
+ "LAPD_STATE_EST_TEI_WAIT",
+ "LAPD_STATE_IDLE",
+ "LAPD_STATE_SABM_SENT",
+ "LAPD_STATE_DISC_SENT",
+ "LAPD_STATE_MF_EST",
+ "LAPD_STATE_TIMER_RECOV",
+
+};
+
+static void lapd_start_t200(struct lapd_datalink *dl)
+{
+ if (osmo_timer_pending(&dl->t200))
+ return;
+ LOGP(DLLAPD, LOGL_INFO, "start T200\n");
+ osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec);
+}
+
+static void lapd_start_t203(struct lapd_datalink *dl)
+{
+ if (osmo_timer_pending(&dl->t203))
+ return;
+ LOGP(DLLAPD, LOGL_INFO, "start T203\n");
+ osmo_timer_schedule(&dl->t203, dl->t203_sec, dl->t203_usec);
+}
+
+static void lapd_stop_t200(struct lapd_datalink *dl)
+{
+ if (!osmo_timer_pending(&dl->t200))
+ return;
+ LOGP(DLLAPD, LOGL_INFO, "stop T200\n");
+ osmo_timer_del(&dl->t200);
+}
+
+static void lapd_stop_t203(struct lapd_datalink *dl)
+{
+ if (!osmo_timer_pending(&dl->t203))
+ return;
+ LOGP(DLLAPD, LOGL_INFO, "stop T203\n");
+ osmo_timer_del(&dl->t203);
+}
+
+static void lapd_dl_newstate(struct lapd_datalink *dl, uint32_t state)
+{
+ LOGP(DLLAPD, LOGL_INFO, "new state %s -> %s\n",
+ lapd_state_names[dl->state], lapd_state_names[state]);
+
+ if (state != LAPD_STATE_MF_EST && dl->state == LAPD_STATE_MF_EST) {
+ /* stop T203 on leaving MF EST state, if running */
+ lapd_stop_t203(dl);
+ /* remove content res. (network side) on leaving MF EST state */
+ if (dl->cont_res) {
+ msgb_free(dl->cont_res);
+ dl->cont_res = NULL;
+ }
+ }
+
+ /* start T203 on entering MF EST state, if enabled */
+ if ((dl->t203_sec || dl->t203_usec)
+ && state == LAPD_STATE_MF_EST && dl->state != LAPD_STATE_MF_EST)
+ lapd_start_t203(dl);
+
+ dl->state = state;
+}
+
+static void *tall_lapd_ctx = NULL;
+
+/* init datalink instance and allocate history */
+void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range,
+ int maxf)
+{
+ int m;
+
+ memset(dl, 0, sizeof(*dl));
+ INIT_LLIST_HEAD(&dl->send_queue);
+ INIT_LLIST_HEAD(&dl->tx_queue);
+ dl->reestablish = 1;
+ dl->n200_est_rel = 3;
+ dl->n200 = 3;
+ dl->t200_sec = 1;
+ dl->t200_usec = 0;
+ dl->t200.data = dl;
+ dl->t200.cb = &lapd_t200_cb;
+ dl->t203_sec = 10;
+ dl->t203_usec = 0;
+ dl->t203.data = dl;
+ dl->t203.cb = &lapd_t203_cb;
+ dl->maxf = maxf;
+ if (k > v_range - 1)
+ k = v_range - 1;
+ dl->k = k;
+ dl->v_range = v_range;
+
+ /* Calculate modulo for history array:
+ * - The history range must be at least k+1.
+ * - The history range must be 2^x, where x is as low as possible.
+ */
+ k++;
+ for (m = 0x80; m; m >>= 1) {
+ if ((m & k)) {
+ if (k > m)
+ m <<= 1;
+ dl->range_hist = m;
+ break;
+ }
+ }
+
+ LOGP(DLLAPD, LOGL_INFO, "Init DL layer: sequence range = %d, k = %d, "
+ "history range = %d\n", dl->v_range, dl->k, dl->range_hist);
+
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+
+ if (!tall_lapd_ctx)
+ tall_lapd_ctx = talloc_named_const(NULL, 1, "lapd context");
+ dl->tx_hist = (struct lapd_history *) talloc_zero_array(tall_lapd_ctx,
+ struct log_info, dl->range_hist);
+}
+
+/* reset to IDLE state */
+void lapd_dl_reset(struct lapd_datalink *dl)
+{
+ if (dl->state == LAPD_STATE_IDLE)
+ return;
+ LOGP(DLLAPD, LOGL_INFO, "Resetting LAPDm instance\n");
+ /* enter idle state (and remove eventual cont_res) */
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ /* flush buffer */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ /* Discard partly received L3 message */
+ if (dl->rcv_buffer) {
+ msgb_free(dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
+ }
+ /* stop Timers */
+ lapd_stop_t200(dl);
+ lapd_stop_t203(dl);
+}
+
+/* reset and de-allocate history buffer */
+void lapd_dl_exit(struct lapd_datalink *dl)
+{
+ /* free all ressources except history buffer */
+ lapd_dl_reset(dl);
+ /* free history buffer list */
+ talloc_free(dl->tx_hist);
+}
+
+/*! \brief Set the \ref lapdm_mode of a LAPDm entity */
+int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode)
+{
+ switch (mode) {
+ case LAPD_MODE_USER:
+ dl->cr.loc2rem.cmd = CR_USER2NET_CMD;
+ dl->cr.loc2rem.resp = CR_USER2NET_RESP;
+ dl->cr.rem2loc.cmd = CR_NET2USER_CMD;
+ dl->cr.rem2loc.resp = CR_NET2USER_RESP;
+ break;
+ case LAPD_MODE_NETWORK:
+ dl->cr.loc2rem.cmd = CR_NET2USER_CMD;
+ dl->cr.loc2rem.resp = CR_NET2USER_RESP;
+ dl->cr.rem2loc.cmd = CR_USER2NET_CMD;
+ dl->cr.rem2loc.resp = CR_USER2NET_RESP;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dl->mode = mode;
+
+ return 0;
+}
+
+/* send DL message with optional msgb */
+static int send_dl_l3(uint8_t prim, uint8_t op, struct lapd_msg_ctx *lctx,
+ struct msgb *msg)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct osmo_dlsap_prim dp;
+
+ osmo_prim_init(&dp.oph, 0, prim, op, msg);
+ return dl->send_dlsap(&dp, lctx);
+}
+
+/* send simple DL message */
+static inline int send_dl_simple(uint8_t prim, uint8_t op,
+ struct lapd_msg_ctx *lctx)
+{
+ struct msgb *msg = lapd_msgb_alloc(0, "DUMMY");
+
+ return send_dl_l3(prim, op, lctx, msg);
+}
+
+/* send MDL-ERROR INDICATION */
+static int mdl_error(uint8_t cause, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct osmo_dlsap_prim dp;
+
+ LOGP(DLLAPD, LOGL_NOTICE, "sending MDL-ERROR-IND cause %d\n",
+ cause);
+ osmo_prim_init(&dp.oph, 0, PRIM_MDL_ERROR, PRIM_OP_INDICATION, NULL);
+ dp.u.error_ind.cause = cause;
+ return dl->send_dlsap(&dp, lctx);
+}
+
+/* send UA response */
+static int lapd_send_ua(struct lapd_msg_ctx *lctx, uint8_t len, uint8_t *data)
+{
+ struct msgb *msg = lapd_msgb_alloc(len, "LAPD UA");
+ struct lapd_msg_ctx nctx;
+ struct lapd_datalink *dl = lctx->dl;
+
+ memcpy(&nctx, lctx, sizeof(nctx));
+ msg->l3h = msgb_put(msg, len);
+ if (len)
+ memcpy(msg->l3h, data, len);
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.resp;
+ nctx.format = LAPD_FORM_U;
+ nctx.s_u = LAPD_U_UA;
+ /* keep nctx.p_f */
+ nctx.length = len;
+ nctx.more = 0;
+
+ return dl->send_ph_data_req(&nctx, msg);
+}
+
+/* send DM response */
+static int lapd_send_dm(struct lapd_msg_ctx *lctx)
+{
+ struct msgb *msg = lapd_msgb_alloc(0, "LAPD DM");
+ struct lapd_msg_ctx nctx;
+ struct lapd_datalink *dl = lctx->dl;
+
+ memcpy(&nctx, lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.resp;
+ nctx.format = LAPD_FORM_U;
+ nctx.s_u = LAPD_U_DM;
+ /* keep nctx.p_f */
+ nctx.length = 0;
+ nctx.more = 0;
+
+ return dl->send_ph_data_req(&nctx, msg);
+}
+
+/* send RR response / command */
+static int lapd_send_rr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd)
+{
+ struct msgb *msg = lapd_msgb_alloc(0, "LAPD RR");
+ struct lapd_msg_ctx nctx;
+ struct lapd_datalink *dl = lctx->dl;
+
+ memcpy(&nctx, lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp;
+ nctx.format = LAPD_FORM_S;
+ nctx.s_u = LAPD_S_RR;
+ nctx.p_f = f_bit;
+ nctx.n_recv = dl->v_recv;
+ nctx.length = 0;
+ nctx.more = 0;
+
+ return dl->send_ph_data_req(&nctx, msg);
+}
+
+/* send RNR response / command */
+static int lapd_send_rnr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd)
+{
+ struct msgb *msg = lapd_msgb_alloc(0, "LAPD RNR");
+ struct lapd_msg_ctx nctx;
+ struct lapd_datalink *dl = lctx->dl;
+
+ memcpy(&nctx, lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp;
+ nctx.format = LAPD_FORM_S;
+ nctx.s_u = LAPD_S_RNR;
+ nctx.p_f = f_bit;
+ nctx.n_recv = dl->v_recv;
+ nctx.length = 0;
+ nctx.more = 0;
+
+ return dl->send_ph_data_req(&nctx, msg);
+}
+
+/* send REJ response */
+static int lapd_send_rej(struct lapd_msg_ctx *lctx, uint8_t f_bit)
+{
+ struct msgb *msg = lapd_msgb_alloc(0, "LAPD REJ");
+ struct lapd_msg_ctx nctx;
+ struct lapd_datalink *dl = lctx->dl;
+
+ memcpy(&nctx, lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.resp;
+ nctx.format = LAPD_FORM_S;
+ nctx.s_u = LAPD_S_REJ;
+ nctx.p_f = f_bit;
+ nctx.n_recv = dl->v_recv;
+ nctx.length = 0;
+ nctx.more = 0;
+
+ return dl->send_ph_data_req(&nctx, msg);
+}
+
+/* resend SABM or DISC message */
+static int lapd_send_resend(struct lapd_datalink *dl)
+{
+ struct msgb *msg;
+ uint8_t h = do_mod(dl->v_send, dl->range_hist);
+ int length = dl->tx_hist[h].msg->len;
+ struct lapd_msg_ctx nctx;
+
+ /* assemble message */
+ memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.cmd;
+ nctx.format = LAPD_FORM_U;
+ if (dl->state == LAPD_STATE_SABM_SENT)
+ nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM;
+ else
+ nctx.s_u = LAPD_U_DISC;
+ nctx.p_f = 1;
+ nctx.length = length;
+ nctx.more = 0;
+
+ /* Resend SABM/DISC from tx_hist */
+ msg = lapd_msgb_alloc(length, "LAPD resend");
+ msg->l3h = msgb_put(msg, length);
+ if (length)
+ memcpy(msg->l3h, dl->tx_hist[h].msg->data, length);
+
+ return dl->send_ph_data_req(&nctx, msg);
+}
+
+/* reestablish link */
+static int lapd_reestablish(struct lapd_datalink *dl)
+{
+ struct osmo_dlsap_prim dp;
+ struct msgb *msg;
+
+ msg = lapd_msgb_alloc(0, "DUMMY");
+ osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
+
+ return lapd_est_req(&dp, &dl->lctx);
+}
+
+/* Timer callback on T200 expiry */
+static void lapd_t200_cb(void *data)
+{
+ struct lapd_datalink *dl = data;
+
+ LOGP(DLLAPD, LOGL_INFO, "Timeout T200 (%p) state=%d\n", dl,
+ (int) dl->state);
+
+ switch (dl->state) {
+ case LAPD_STATE_SABM_SENT:
+ /* 5.4.1.3 */
+ if (dl->retrans_ctr + 1 >= dl->n200_est_rel + 1) {
+ /* send RELEASE INDICATION to L3 */
+ send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION,
+ &dl->lctx);
+ /* send MDL ERROR INIDCATION to L3 */
+ mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx);
+ /* flush tx and send buffers */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ /* go back to idle state */
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ /* NOTE: we must not change any other states or buffers
+ * and queues, since we may reconnect after handover
+ * failure. the buffered messages is replaced there */
+ break;
+ }
+ /* retransmit SABM command */
+ lapd_send_resend(dl);
+ /* increment re-transmission counter */
+ dl->retrans_ctr++;
+ /* restart T200 (PH-READY-TO-SEND) */
+ lapd_start_t200(dl);
+ break;
+ case LAPD_STATE_DISC_SENT:
+ /* 5.4.4.3 */
+ if (dl->retrans_ctr + 1 >= dl->n200_est_rel + 1) {
+ /* send RELEASE INDICATION to L3 */
+ send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx);
+ /* send MDL ERROR INIDCATION to L3 */
+ mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx);
+ /* flush tx and send buffers */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ /* go back to idle state */
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ /* NOTE: we must not change any other states or buffers
+ * and queues, since we may reconnect after handover
+ * failure. the buffered messages is replaced there */
+ break;
+ }
+ /* retransmit DISC command */
+ lapd_send_resend(dl);
+ /* increment re-transmission counter */
+ dl->retrans_ctr++;
+ /* restart T200 (PH-READY-TO-SEND) */
+ lapd_start_t200(dl);
+ break;
+ case LAPD_STATE_MF_EST:
+ /* 5.5.7 */
+ dl->retrans_ctr = 0;
+ lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV);
+ /* fall through */
+ case LAPD_STATE_TIMER_RECOV:
+ dl->retrans_ctr++;
+ if (dl->retrans_ctr < dl->n200) {
+ uint8_t vs = sub_mod(dl->v_send, 1, dl->v_range);
+ uint8_t h = do_mod(vs, dl->range_hist);
+ /* retransmit I frame (V_s-1) with P=1, if any */
+ if (dl->tx_hist[h].msg) {
+ struct msgb *msg;
+ int length = dl->tx_hist[h].msg->len;
+ struct lapd_msg_ctx nctx;
+
+ LOGP(DLLAPD, LOGL_INFO, "retransmit last frame"
+ " V(S)=%d\n", vs);
+ /* Create I frame (segment) from tx_hist */
+ memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.cmd;
+ nctx.format = LAPD_FORM_I;
+ nctx.p_f = 1;
+ nctx.n_send = vs;
+ nctx.n_recv = dl->v_recv;
+ nctx.length = length;
+ nctx.more = dl->tx_hist[h].more;
+ msg = lapd_msgb_alloc(length, "LAPD I resend");
+ msg->l3h = msgb_put(msg, length);
+ memcpy(msg->l3h, dl->tx_hist[h].msg->data,
+ length);
+ dl->send_ph_data_req(&nctx, msg);
+ } else {
+ /* OR send appropriate supervision frame with P=1 */
+ if (!dl->own_busy && !dl->seq_err_cond) {
+ lapd_send_rr(&dl->lctx, 1, 1);
+ /* NOTE: In case of sequence error
+ * condition, the REJ frame has been
+ * transmitted when entering the
+ * condition, so it has not be done
+ * here
+ */
+ } else if (dl->own_busy) {
+ lapd_send_rnr(&dl->lctx, 1, 1);
+ } else {
+ LOGP(DLLAPD, LOGL_INFO, "unhandled, "
+ "pls. fix\n");
+ }
+ }
+ /* restart T200 (PH-READY-TO-SEND) */
+ lapd_start_t200(dl);
+ } else {
+ /* send MDL ERROR INIDCATION to L3 */
+ mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx);
+ /* reestablish */
+ if (!dl->reestablish)
+ break;
+ LOGP(DLLAPD, LOGL_NOTICE, "N200 reached, performing "
+ "reestablishment.\n");
+ lapd_reestablish(dl);
+ }
+ break;
+ default:
+ LOGP(DLLAPD, LOGL_INFO, "T200 expired in unexpected "
+ "dl->state %d\n", (int) dl->state);
+ }
+}
+
+/* Timer callback on T203 expiry */
+static void lapd_t203_cb(void *data)
+{
+ struct lapd_datalink *dl = data;
+
+ LOGP(DLLAPD, LOGL_INFO, "Timeout T203 (%p) state=%d\n", dl,
+ (int) dl->state);
+
+ if (dl->state != LAPD_STATE_MF_EST) {
+ LOGP(DLLAPD, LOGL_ERROR, "T203 fired outside MF EST state, "
+ "please fix!\n");
+ return;
+ }
+
+ /* set retransmission counter to 0 */
+ dl->retrans_ctr = 0;
+ /* enter timer recovery state */
+ lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV);
+ /* transmit a supervisory command with P bit set to 1 as follows: */
+ if (!dl->own_busy) {
+ LOGP(DLLAPD, LOGL_INFO, "transmit an RR poll command\n");
+ /* Send RR with P=1 */
+ lapd_send_rr(&dl->lctx, 1, 1);
+ } else {
+ LOGP(DLLAPD, LOGL_INFO, "transmit an RNR poll command\n");
+ /* Send RNR with P=1 */
+ lapd_send_rnr(&dl->lctx, 1, 1);
+ }
+ /* start T200 */
+ lapd_start_t200(dl);
+}
+
+/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */
+static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ uint8_t nr = lctx->n_recv;
+ int s = 0, rej = 0, t200_reset = 0, t200_start = 0;
+ int i, h;
+
+ /* supervisory frame ? */
+ if (lctx->format == LAPD_FORM_S)
+ s = 1;
+ /* REJ frame ? */
+ if (s && lctx->s_u == LAPD_S_REJ)
+ rej = 1;
+
+ /* Flush all transmit buffers of acknowledged frames */
+ for (i = dl->v_ack; i != nr; i = inc_mod(i, dl->v_range)) {
+ h = do_mod(i, dl->range_hist);
+ if (dl->tx_hist[h].msg) {
+ msgb_free(dl->tx_hist[h].msg);
+ dl->tx_hist[h].msg = NULL;
+ LOGP(DLLAPD, LOGL_INFO, "ack frame %d\n", i);
+ }
+ }
+
+ if (dl->state != LAPD_STATE_TIMER_RECOV) {
+ /* When not in the timer recovery condition, the data
+ * link layer entity shall reset the timer T200 on
+ * receipt of a valid I frame with N(R) higher than V(A),
+ * or an REJ with an N(R) equal to V(A). */
+ if ((!rej && nr != dl->v_ack)
+ || (rej && nr == dl->v_ack)) {
+ t200_reset = 1;
+ lapd_stop_t200(dl);
+ /* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */
+ }
+ /* 5.7.4: N(R) sequence error
+ * N(R) is called valid, if and only if
+ * (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8.
+ */
+ if (sub_mod(nr, dl->v_ack, dl->v_range)
+ > sub_mod(dl->v_send, dl->v_ack, dl->v_range)) {
+ LOGP(DLLAPD, LOGL_NOTICE, "N(R) sequence error\n");
+ mdl_error(MDL_CAUSE_SEQ_ERR, lctx);
+ }
+ }
+
+ /* V(A) shall be set to the value of N(R) */
+ dl->v_ack = nr;
+
+ /* If T200 has been stopped by the receipt of an I, RR or RNR frame,
+ * and if there are outstanding I frames, restart T200 */
+ if (t200_reset && !rej) {
+ if (dl->tx_hist[sub_mod(dl->v_send, 1, dl->range_hist)].msg) {
+ LOGP(DLLAPD, LOGL_INFO, "start T200, due to unacked I "
+ "frame(s)\n");
+ t200_start = 1;
+ lapd_start_t200(dl);
+ }
+ }
+
+ /* This also does a restart, when I or S frame is received */
+
+ /* Stop T203, if running */
+ lapd_stop_t203(dl);
+ /* Start T203, if T200 is not running in MF EST state, if enabled */
+ if (!osmo_timer_pending(&dl->t200)
+ && (dl->t203_sec || dl->t203_usec)
+ && (dl->state == LAPD_STATE_MF_EST)) {
+ lapd_start_t203(dl);
+ }
+}
+
+/* L1 -> L2 */
+
+/* Receive a LAPD U (Unnumbered) message from L1 */
+static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ int length = lctx->length;
+ int rc = 0;
+ uint8_t prim, op;
+
+ switch (lctx->s_u) {
+ case LAPD_U_SABM:
+ case LAPD_U_SABME:
+ prim = PRIM_DL_EST;
+ op = PRIM_OP_INDICATION;
+
+ LOGP(DLLAPD, LOGL_INFO, "SABM(E) received in state %s\n",
+ lapd_state_names[dl->state]);
+ /* 5.7.1 */
+ dl->seq_err_cond = 0;
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.resp) {
+ LOGP(DLLAPD, LOGL_NOTICE, "SABM response error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+
+ /* G.4.5 If SABM is received with L>N201 or with M bit
+ * set, AN MDL-ERROR-INDICATION is sent to MM.
+ */
+ if (lctx->more || length > lctx->n201) {
+ LOGP(DLLAPD, LOGL_NOTICE, "SABM too large error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+ return -EIO;
+ }
+
+ switch (dl->state) {
+ case LAPD_STATE_IDLE:
+ break;
+ case LAPD_STATE_MF_EST:
+ LOGP(DLLAPD, LOGL_INFO, "SABM command, multiple "
+ "frame established state\n");
+ /* If link is lost on the remote side, we start over
+ * and send DL-ESTABLISH indication again. */
+ if (dl->v_send != dl->v_recv) {
+ LOGP(DLLAPD, LOGL_INFO, "Remote reestablish\n");
+ mdl_error(MDL_CAUSE_SABM_MF, lctx);
+ break;
+ }
+ /* Ignore SABM if content differs from first SABM. */
+ if (dl->mode == LAPD_MODE_NETWORK && length
+ && dl->cont_res) {
+#ifdef TEST_CONTENT_RESOLUTION_NETWORK
+ dl->cont_res->data[0] ^= 0x01;
+#endif
+ if (memcmp(dl->cont_res, msg->data, length)) {
+ LOGP(DLLAPD, LOGL_INFO, "Another SABM "
+ "with diffrent content - "
+ "ignoring!\n");
+ msgb_free(msg);
+ return 0;
+ }
+ }
+ /* send UA again */
+ lapd_send_ua(lctx, length, msg->l3h);
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_DISC_SENT:
+ /* 5.4.6.2 send DM with F=P */
+ lapd_send_dm(lctx);
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ msgb_free(msg);
+ return send_dl_simple(prim, op, lctx);
+ default:
+ /* collision: Send UA, but still wait for rx UA, then
+ * change to MF_EST state.
+ */
+ /* check for contention resoultion */
+ if (dl->tx_hist[0].msg && dl->tx_hist[0].msg->len) {
+ LOGP(DLLAPD, LOGL_NOTICE, "SABM not allowed "
+ "during contention resolution\n");
+ mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx);
+ }
+ lapd_send_ua(lctx, length, msg->l3h);
+ msgb_free(msg);
+ return 0;
+ }
+ /* save message context for further use */
+ memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
+#ifndef TEST_CONTENT_RESOLUTION_NETWORK
+ /* send UA response */
+ lapd_send_ua(lctx, length, msg->l3h);
+#endif
+ /* set Vs, Vr and Va to 0 */
+ dl->v_send = dl->v_recv = dl->v_ack = 0;
+ /* clear tx_hist */
+ lapd_dl_flush_hist(dl);
+ /* enter multiple-frame-established state */
+ lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
+ /* store content resolution data on network side
+ * Note: cont_res will be removed when changing state again,
+ * so it must be allocated AFTER lapd_dl_newstate(). */
+ if (dl->mode == LAPD_MODE_NETWORK && length) {
+ dl->cont_res = lapd_msgb_alloc(length, "CONT RES");
+ memcpy(msgb_put(dl->cont_res, length), msg->l3h,
+ length);
+ LOGP(DLLAPD, LOGL_NOTICE, "Store content res.\n");
+ }
+ /* send notification to L3 */
+ if (length == 0) {
+ /* 5.4.1.2 Normal establishment procedures */
+ rc = send_dl_simple(prim, op, lctx);
+ msgb_free(msg);
+ } else {
+ /* 5.4.1.4 Contention resolution establishment */
+ rc = send_dl_l3(prim, op, lctx, msg);
+ }
+ break;
+ case LAPD_U_DM:
+ LOGP(DLLAPD, LOGL_INFO, "DM received in state %s\n",
+ lapd_state_names[dl->state]);
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.cmd) {
+ LOGP(DLLAPD, LOGL_NOTICE, "DM command error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+ if (!lctx->p_f) {
+ /* 5.4.1.2 DM responses with the F bit set to "0"
+ * shall be ignored.
+ */
+ msgb_free(msg);
+ return 0;
+ }
+ switch (dl->state) {
+ case LAPD_STATE_SABM_SENT:
+ break;
+ case LAPD_STATE_MF_EST:
+ if (lctx->p_f) {
+ LOGP(DLLAPD, LOGL_INFO, "unsolicited DM "
+ "response\n");
+ mdl_error(MDL_CAUSE_UNSOL_DM_RESP, lctx);
+ } else {
+ LOGP(DLLAPD, LOGL_INFO, "unsolicited DM "
+ "response, multiple frame established "
+ "state\n");
+ mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
+ /* reestablish */
+ if (!dl->reestablish) {
+ msgb_free(msg);
+ return 0;
+ }
+ LOGP(DLLAPD, LOGL_NOTICE, "Performing "
+ "reestablishment.\n");
+ lapd_reestablish(dl);
+ }
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_TIMER_RECOV:
+ /* FP = 0 (DM is normal in case PF = 1) */
+ if (!lctx->p_f) {
+ LOGP(DLLAPD, LOGL_INFO, "unsolicited DM "
+ "response, multiple frame established "
+ "state\n");
+ mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
+ msgb_free(msg);
+ /* reestablish */
+ if (!dl->reestablish)
+ return 0;
+ LOGP(DLLAPD, LOGL_NOTICE, "Performing "
+ "reestablishment.\n");
+ return lapd_reestablish(dl);
+ }
+ break;
+ case LAPD_STATE_DISC_SENT:
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* go to idle state */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_IDLE:
+ /* 5.4.5 all other frame types shall be discarded */
+ default:
+ LOGP(DLLAPD, LOGL_INFO, "unsolicited DM response! "
+ "(discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ /* stop timer T200 */
+ lapd_stop_t200(dl);
+ /* go to idle state */
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx);
+ msgb_free(msg);
+ break;
+ case LAPD_U_UI:
+ LOGP(DLLAPD, LOGL_INFO, "UI received\n");
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.resp) {
+ LOGP(DLLAPD, LOGL_NOTICE, "UI indicates response "
+ "error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+
+ /* G.4.5 If UI is received with L>N201 or with M bit
+ * set, AN MDL-ERROR-INDICATION is sent to MM.
+ */
+ if (length > lctx->n201 || lctx->more) {
+ LOGP(DLLAPD, LOGL_NOTICE, "UI too large error "
+ "(%d > N201(%d) or M=%d)\n", length,
+ lctx->n201, lctx->more);
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+ return -EIO;
+ }
+
+ /* do some length checks */
+ if (length == 0) {
+ /* 5.3.3 UI frames received with the length indicator
+ * set to "0" shall be ignored
+ */
+ LOGP(DLLAPD, LOGL_INFO, "length=0 (discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ rc = send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx,
+ msg);
+ break;
+ case LAPD_U_DISC:
+ prim = PRIM_DL_REL;
+ op = PRIM_OP_INDICATION;
+
+ LOGP(DLLAPD, LOGL_INFO, "DISC received in state %s\n",
+ lapd_state_names[dl->state]);
+ /* flush tx and send buffers */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ /* 5.7.1 */
+ dl->seq_err_cond = 0;
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.resp) {
+ LOGP(DLLAPD, LOGL_NOTICE, "DISC response error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+ if (length > 0 || lctx->more) {
+ /* G.4.4 If a DISC or DM frame is received with L>0 or
+ * with the M bit set to "1", an MDL-ERROR-INDICATION
+ * primitive with cause "U frame with incorrect
+ * parameters" is sent to the mobile management entity.
+ */
+ LOGP(DLLAPD, LOGL_NOTICE,
+ "U frame iwth incorrect parameters ");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+ return -EIO;
+ }
+ switch (dl->state) {
+ case LAPD_STATE_IDLE:
+ LOGP(DLLAPD, LOGL_INFO, "DISC in idle state\n");
+ /* send DM with F=P */
+ msgb_free(msg);
+ return lapd_send_dm(lctx);
+ case LAPD_STATE_SABM_SENT:
+ LOGP(DLLAPD, LOGL_INFO, "DISC in SABM state\n");
+ /* 5.4.6.2 send DM with F=P */
+ lapd_send_dm(lctx);
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* go to idle state */
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ msgb_free(msg);
+ return send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION,
+ lctx);
+ case LAPD_STATE_MF_EST:
+ case LAPD_STATE_TIMER_RECOV:
+ LOGP(DLLAPD, LOGL_INFO, "DISC in est state\n");
+ break;
+ case LAPD_STATE_DISC_SENT:
+ LOGP(DLLAPD, LOGL_INFO, "DISC in disc state\n");
+ prim = PRIM_DL_REL;
+ op = PRIM_OP_CONFIRM;
+ break;
+ default:
+ lapd_send_ua(lctx, length, msg->l3h);
+ msgb_free(msg);
+ return 0;
+ }
+ /* send UA response */
+ lapd_send_ua(lctx, length, msg->l3h);
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* enter idle state, keep tx-buffer with UA response */
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ /* send notification to L3 */
+ rc = send_dl_simple(prim, op, lctx);
+ msgb_free(msg);
+ break;
+ case LAPD_U_UA:
+ LOGP(DLLAPD, LOGL_INFO, "UA received in state %s\n",
+ lapd_state_names[dl->state]);
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.cmd) {
+ LOGP(DLLAPD, LOGL_NOTICE, "UA indicates command "
+ "error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+
+ /* G.4.5 If UA is received with L>N201 or with M bit
+ * set, AN MDL-ERROR-INDICATION is sent to MM.
+ */
+ if (lctx->more || length > lctx->n201) {
+ LOGP(DLLAPD, LOGL_NOTICE, "UA too large error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+ return -EIO;
+ }
+
+ if (!lctx->p_f) {
+ /* 5.4.1.2 A UA response with the F bit set to "0"
+ * shall be ignored.
+ */
+ LOGP(DLLAPD, LOGL_INFO, "F=0 (discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ switch (dl->state) {
+ case LAPD_STATE_SABM_SENT:
+ break;
+ case LAPD_STATE_MF_EST:
+ case LAPD_STATE_TIMER_RECOV:
+ LOGP(DLLAPD, LOGL_INFO, "unsolicited UA response! "
+ "(discarding)\n");
+ mdl_error(MDL_CAUSE_UNSOL_UA_RESP, lctx);
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_DISC_SENT:
+ LOGP(DLLAPD, LOGL_INFO, "UA in disconnect state\n");
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* go to idle state */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_IDLE:
+ /* 5.4.5 all other frame types shall be discarded */
+ default:
+ LOGP(DLLAPD, LOGL_INFO, "unsolicited UA response! "
+ "(discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ LOGP(DLLAPD, LOGL_INFO, "UA in SABM state\n");
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* compare UA with SABME if contention resolution is applied */
+ if (dl->tx_hist[0].msg->len) {
+ if (length != (dl->tx_hist[0].msg->len)
+ || !!memcmp(dl->tx_hist[0].msg->data, msg->l3h,
+ length)) {
+ LOGP(DLLAPD, LOGL_INFO, "**** UA response "
+ "mismatches ****\n");
+ rc = send_dl_simple(PRIM_DL_REL,
+ PRIM_OP_INDICATION, lctx);
+ msgb_free(msg);
+ /* go to idle state */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ return 0;
+ }
+ }
+ /* set Vs, Vr and Va to 0 */
+ dl->v_send = dl->v_recv = dl->v_ack = 0;
+ /* clear tx_hist */
+ lapd_dl_flush_hist(dl);
+ /* enter multiple-frame-established state */
+ lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
+ /* send outstanding frames, if any (resume / reconnect) */
+ lapd_send_i(lctx, __LINE__);
+ /* send notification to L3 */
+ rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx);
+ msgb_free(msg);
+ break;
+ case LAPD_U_FRMR:
+ LOGP(DLLAPD, LOGL_NOTICE, "Frame reject received\n");
+ /* send MDL ERROR INIDCATION to L3 */
+ mdl_error(MDL_CAUSE_FRMR, lctx);
+ msgb_free(msg);
+ /* reestablish */
+ if (!dl->reestablish)
+ break;
+ LOGP(DLLAPD, LOGL_NOTICE, "Performing reestablishment.\n");
+ rc = lapd_reestablish(dl);
+ break;
+ default:
+ /* G.3.1 */
+ LOGP(DLLAPD, LOGL_NOTICE, "Unnumbered frame not allowed.\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+/* Receive a LAPD S (Supervisory) message from L1 */
+static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ int length = lctx->length;
+
+ if (length > 0 || lctx->more) {
+ /* G.4.3 If a supervisory frame is received with L>0 or
+ * with the M bit set to "1", an MDL-ERROR-INDICATION
+ * primitive with cause "S frame with incorrect
+ * parameters" is sent to the mobile management entity. */
+ LOGP(DLLAPD, LOGL_NOTICE,
+ "S frame with incorrect parameters\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_SFRM_INC_PARAM, lctx);
+ return -EIO;
+ }
+
+ if (lctx->cr == dl->cr.rem2loc.resp
+ && lctx->p_f
+ && dl->state != LAPD_STATE_TIMER_RECOV) {
+ /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */
+ LOGP(DLLAPD, LOGL_NOTICE, "S frame response with F=1 error\n");
+ mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx);
+ }
+
+ switch (dl->state) {
+ case LAPD_STATE_IDLE:
+ /* if P=1, respond DM with F=1 (5.2.2) */
+ /* 5.4.5 all other frame types shall be discarded */
+ if (lctx->p_f)
+ lapd_send_dm(lctx); /* F=P */
+ /* fall though */
+ case LAPD_STATE_SABM_SENT:
+ case LAPD_STATE_DISC_SENT:
+ LOGP(DLLAPD, LOGL_NOTICE, "S frame ignored in this state\n");
+ msgb_free(msg);
+ return 0;
+ }
+ switch (lctx->s_u) {
+ case LAPD_S_RR:
+ LOGP(DLLAPD, LOGL_INFO, "RR received in state %s\n",
+ lapd_state_names[dl->state]);
+ /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
+ lapd_acknowledge(lctx);
+
+ /* 5.5.3.2 */
+ if (lctx->cr == dl->cr.rem2loc.cmd
+ && lctx->p_f) {
+ if (!dl->own_busy && !dl->seq_err_cond) {
+ LOGP(DLLAPD, LOGL_INFO, "RR frame command "
+ "with polling bit set and we are not "
+ "busy, so we reply with RR frame "
+ "response\n");
+ lapd_send_rr(lctx, 1, 0);
+ /* NOTE: In case of sequence error condition,
+ * the REJ frame has been transmitted when
+ * entering the condition, so it has not be
+ * done here
+ */
+ } else if (dl->own_busy) {
+ LOGP(DLLAPD, LOGL_INFO, "RR frame command "
+ "with polling bit set and we are busy, "
+ "so we reply with RR frame response\n");
+ lapd_send_rnr(lctx, 1, 0);
+ }
+ } else if (lctx->cr == dl->cr.rem2loc.resp
+ && lctx->p_f
+ && dl->state == LAPD_STATE_TIMER_RECOV) {
+ LOGP(DLLAPD, LOGL_INFO, "RR response with F==1, "
+ "and we are in timer recovery state, so "
+ "we leave that state\n");
+ /* V(S) to the N(R) in the RR frame */
+ dl->v_send = lctx->n_recv;
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* 5.5.7 Clear timer recovery condition */
+ lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
+ }
+ /* Send message, if possible due to acknowledged data */
+ lapd_send_i(lctx, __LINE__);
+
+ break;
+ case LAPD_S_RNR:
+ LOGP(DLLAPD, LOGL_INFO, "RNR received in state %s\n",
+ lapd_state_names[dl->state]);
+ /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
+ lapd_acknowledge(lctx);
+
+ /* 5.5.5 */
+ /* Set peer receiver busy condition */
+ dl->peer_busy = 1;
+
+ if (lctx->p_f) {
+ if (lctx->cr == dl->cr.rem2loc.cmd) {
+ if (!dl->own_busy) {
+ LOGP(DLLAPD, LOGL_INFO, "RNR poll "
+ "command and we are not busy, "
+ "so we reply with RR final "
+ "response\n");
+ /* Send RR with F=1 */
+ lapd_send_rr(lctx, 1, 0);
+ } else {
+ LOGP(DLLAPD, LOGL_INFO, "RNR poll "
+ "command and we are busy, so "
+ "we reply with RNR final "
+ "response\n");
+ /* Send RNR with F=1 */
+ lapd_send_rnr(lctx, 1, 0);
+ }
+ } else if (dl->state == LAPD_STATE_TIMER_RECOV) {
+ LOGP(DLLAPD, LOGL_INFO, "RNR poll response "
+ "and we in timer recovery state, so "
+ "we leave that state\n");
+ /* 5.5.7 Clear timer recovery condition */
+ lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
+ /* V(S) to the N(R) in the RNR frame */
+ dl->v_send = lctx->n_recv;
+ }
+ } else
+ LOGP(DLLAPD, LOGL_INFO, "RNR not polling/final state "
+ "received\n");
+
+ /* Send message, if possible due to acknowledged data */
+ lapd_send_i(lctx, __LINE__);
+
+ break;
+ case LAPD_S_REJ:
+ LOGP(DLLAPD, LOGL_INFO, "REJ received in state %s\n",
+ lapd_state_names[dl->state]);
+ /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
+ lapd_acknowledge(lctx);
+
+ /* 5.5.4.1 */
+ if (dl->state != LAPD_STATE_TIMER_RECOV) {
+ /* Clear an existing peer receiver busy condition */
+ dl->peer_busy = 0;
+ /* V(S) and V(A) to the N(R) in the REJ frame */
+ dl->v_send = dl->v_ack = lctx->n_recv;
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* 5.5.3.2 */
+ if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) {
+ if (!dl->own_busy && !dl->seq_err_cond) {
+ LOGP(DLLAPD, LOGL_INFO, "REJ poll "
+ "command not in timer recovery "
+ "state and not in own busy "
+ "condition received, so we "
+ "respond with RR final "
+ "response\n");
+ lapd_send_rr(lctx, 1, 0);
+ /* NOTE: In case of sequence error
+ * condition, the REJ frame has been
+ * transmitted when entering the
+ * condition, so it has not be done
+ * here
+ */
+ } else if (dl->own_busy) {
+ LOGP(DLLAPD, LOGL_INFO, "REJ poll "
+ "command not in timer recovery "
+ "state and in own busy "
+ "condition received, so we "
+ "respond with RNR final "
+ "response\n");
+ lapd_send_rnr(lctx, 1, 0);
+ }
+ } else
+ LOGP(DLLAPD, LOGL_INFO, "REJ response or not "
+ "polling command not in timer recovery "
+ "state received\n");
+ /* send MDL ERROR INIDCATION to L3 */
+ if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) {
+ mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx);
+ }
+
+ } else if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) {
+ LOGP(DLLAPD, LOGL_INFO, "REJ poll response in timer "
+ "recovery state received\n");
+ /* Clear an existing peer receiver busy condition */
+ dl->peer_busy = 0;
+ /* V(S) and V(A) to the N(R) in the REJ frame */
+ dl->v_send = dl->v_ack = lctx->n_recv;
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* 5.5.7 Clear timer recovery condition */
+ lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
+ } else {
+ /* Clear an existing peer receiver busy condition */
+ dl->peer_busy = 0;
+ /* V(S) and V(A) to the N(R) in the REJ frame */
+ dl->v_send = dl->v_ack = lctx->n_recv;
+ /* 5.5.3.2 */
+ if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) {
+ if (!dl->own_busy && !dl->seq_err_cond) {
+ LOGP(DLLAPD, LOGL_INFO, "REJ poll "
+ "command in timer recovery "
+ "state and not in own busy "
+ "condition received, so we "
+ "respond with RR final "
+ "response\n");
+ lapd_send_rr(lctx, 1, 0);
+ /* NOTE: In case of sequence error
+ * condition, the REJ frame has been
+ * transmitted when entering the
+ * condition, so it has not be done
+ * here
+ */
+ } else if (dl->own_busy) {
+ LOGP(DLLAPD, LOGL_INFO, "REJ poll "
+ "command in timer recovery "
+ "state and in own busy "
+ "condition received, so we "
+ "respond with RNR final "
+ "response\n");
+ lapd_send_rnr(lctx, 1, 0);
+ }
+ } else
+ LOGP(DLLAPD, LOGL_INFO, "REJ response or not "
+ "polling command in timer recovery "
+ "state received\n");
+ }
+
+ /* FIXME: 5.5.4.2 2) */
+
+ /* Send message, if possible due to acknowledged data */
+ lapd_send_i(lctx, __LINE__);
+
+ break;
+ default:
+ /* G.3.1 */
+ LOGP(DLLAPD, LOGL_NOTICE, "Supervisory frame not allowed.\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+ msgb_free(msg);
+ return 0;
+}
+
+/* Receive a LAPD I (Information) message from L1 */
+static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ //uint8_t nr = lctx->n_recv;
+ uint8_t ns = lctx->n_send;
+ int length = lctx->length;
+ int rc;
+
+ LOGP(DLLAPD, LOGL_INFO, "I received in state %s\n",
+ lapd_state_names[dl->state]);
+
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.resp) {
+ LOGP(DLLAPD, LOGL_NOTICE, "I frame response not allowed\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+
+ if (length == 0 || length > lctx->n201) {
+ /* G.4.2 If the length indicator of an I frame is set
+ * to a numerical value L>N201 or L=0, an MDL-ERROR-INDICATION
+ * primitive with cause "I frame with incorrect length"
+ * is sent to the mobile management entity. */
+ LOGP(DLLAPD, LOGL_NOTICE, "I frame length not allowed\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_IFRM_INC_LEN, lctx);
+ return -EIO;
+ }
+
+ /* G.4.2 If the numerical value of L is L<N201 and the M
+ * bit is set to "1", then an MDL-ERROR-INDICATION primitive with
+ * cause "I frame with incorrect use of M bit" is sent to the
+ * mobile management entity. */
+ if (lctx->more && length < lctx->n201) {
+ LOGP(DLLAPD, LOGL_NOTICE, "I frame with M bit too short\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_IFRM_INC_MBITS, lctx);
+ return -EIO;
+ }
+
+ switch (dl->state) {
+ case LAPD_STATE_IDLE:
+ /* if P=1, respond DM with F=1 (5.2.2) */
+ /* 5.4.5 all other frame types shall be discarded */
+ if (lctx->p_f)
+ lapd_send_dm(lctx); /* F=P */
+ /* fall though */
+ case LAPD_STATE_SABM_SENT:
+ case LAPD_STATE_DISC_SENT:
+ LOGP(DLLAPD, LOGL_NOTICE, "I frame ignored in this state\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ /* 5.7.1: N(s) sequence error */
+ if (ns != dl->v_recv) {
+ LOGP(DLLAPD, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, "
+ "V(R)=%u\n", ns, dl->v_recv);
+ /* discard data */
+ msgb_free(msg);
+ if (dl->seq_err_cond != 1) {
+ /* FIXME: help me understand what exactly todo here
+ */
+ dl->seq_err_cond = 1;
+ lapd_send_rej(lctx, lctx->p_f);
+ } else {
+ /* If there are two subsequent sequence errors received,
+ * ignore it. (Ignore every second subsequent error.)
+ * This happens if our reply with the REJ is too slow,
+ * so the remote gets a T200 timeout and sends another
+ * frame with a sequence error.
+ * Test showed that replying with two subsequent REJ
+ * messages could the remote L2 process to abort.
+ * Replying too slow shouldn't happen, but may happen
+ * over serial link between BB and LAPD.
+ */
+ dl->seq_err_cond = 2;
+ }
+ /* Even if N(s) sequence error, acknowledge to N(R)-1 */
+ /* 5.5.3.1: Acknowlege all transmitted frames up the N(R)-1 */
+ lapd_acknowledge(lctx); /* V(A) is also set here */
+
+ /* Send message, if possible due to acknowledged data */
+ lapd_send_i(lctx, __LINE__);
+
+ return 0;
+ }
+ dl->seq_err_cond = 0;
+
+ /* Increment receiver state */
+ dl->v_recv = inc_mod(dl->v_recv, dl->v_range);
+ LOGP(DLLAPD, LOGL_INFO, "incrementing V(R) to %u\n", dl->v_recv);
+
+ /* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */
+ lapd_acknowledge(lctx); /* V(A) is also set here */
+
+ /* Only if we are not in own receiver busy condition */
+ if (!dl->own_busy) {
+ /* if the frame carries a complete segment */
+ if (!lctx->more && !dl->rcv_buffer) {
+ LOGP(DLLAPD, LOGL_INFO, "message in single I frame\n");
+ /* send a DATA INDICATION to L3 */
+ msg->len = length;
+ msg->tail = msg->data + length;
+ rc = send_dl_l3(PRIM_DL_DATA, PRIM_OP_INDICATION, lctx,
+ msg);
+ } else {
+ /* create rcv_buffer */
+ if (!dl->rcv_buffer) {
+ LOGP(DLLAPD, LOGL_INFO, "message in multiple "
+ "I frames (first message)\n");
+ dl->rcv_buffer = lapd_msgb_alloc(dl->maxf,
+ "LAPD RX");
+ dl->rcv_buffer->l3h = dl->rcv_buffer->data;
+ }
+ /* concat. rcv_buffer */
+ if (msgb_l3len(dl->rcv_buffer) + length > dl->maxf) {
+ LOGP(DLLAPD, LOGL_NOTICE, "Received frame "
+ "overflow!\n");
+ } else {
+ memcpy(msgb_put(dl->rcv_buffer, length),
+ msg->l3h, length);
+ }
+ /* if the last segment was received */
+ if (!lctx->more) {
+ LOGP(DLLAPD, LOGL_INFO, "message in multiple "
+ "I frames (last message)\n");
+ rc = send_dl_l3(PRIM_DL_DATA,
+ PRIM_OP_INDICATION, lctx,
+ dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
+ } else
+ LOGP(DLLAPD, LOGL_INFO, "message in multiple "
+ "I frames (next message)\n");
+ msgb_free(msg);
+
+ }
+ } else
+ LOGP(DLLAPD, LOGL_INFO, "I frame ignored during own receiver "
+ "busy condition\n");
+
+ /* Check for P bit */
+ if (lctx->p_f) {
+ /* 5.5.2.1 */
+ /* check if we are not in own receiver busy */
+ if (!dl->own_busy) {
+ LOGP(DLLAPD, LOGL_INFO, "we are not busy, send RR\n");
+ /* Send RR with F=1 */
+ rc = lapd_send_rr(lctx, 1, 0);
+ } else {
+ LOGP(DLLAPD, LOGL_INFO, "we are busy, send RNR\n");
+ /* Send RNR with F=1 */
+ rc = lapd_send_rnr(lctx, 1, 0);
+ }
+ } else {
+ /* 5.5.2.2 */
+ /* check if we are not in own receiver busy */
+ if (!dl->own_busy) {
+ /* NOTE: V(R) is already set above */
+ rc = lapd_send_i(lctx, __LINE__);
+ if (rc) {
+ LOGP(DLLAPD, LOGL_INFO, "we are not busy and "
+ "have no pending data, send RR\n");
+ /* Send RR with F=0 */
+ return lapd_send_rr(lctx, 0, 0);
+ }
+ /* all I or one RR is sent, we are done */
+ return 0;
+ } else {
+ LOGP(DLLAPD, LOGL_INFO, "we are busy, send RNR\n");
+ /* Send RNR with F=0 */
+ rc = lapd_send_rnr(lctx, 0, 0);
+ }
+ }
+
+ /* Send message, if possible due to acknowledged data */
+ lapd_send_i(lctx, __LINE__);
+
+ return rc;
+}
+
+/* Receive a LAPD message from L1 */
+int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ int rc;
+
+ switch (lctx->format) {
+ case LAPD_FORM_U:
+ rc = lapd_rx_u(msg, lctx);
+ break;
+ case LAPD_FORM_S:
+ rc = lapd_rx_s(msg, lctx);
+ break;
+ case LAPD_FORM_I:
+ rc = lapd_rx_i(msg, lctx);
+ break;
+ default:
+ LOGP(DLLAPD, LOGL_NOTICE, "unknown LAPD format\n");
+ msgb_free(msg);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/* L3 -> L2 */
+
+/* send unit data */
+static int lapd_udata_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct msgb *msg = dp->oph.msg;
+ struct lapd_msg_ctx nctx;
+
+ memcpy(&nctx, lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.cmd;
+ nctx.format = LAPD_FORM_U;
+ nctx.s_u = LAPD_U_UI;
+ /* keep nctx.p_f */
+ nctx.length = msg->len;
+ nctx.more = 0;
+
+ return dl->send_ph_data_req(&nctx, msg);
+}
+
+/* request link establishment */
+static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct msgb *msg = dp->oph.msg;
+ struct lapd_msg_ctx nctx;
+
+ if (msg->len)
+ LOGP(DLLAPD, LOGL_INFO, "perform establishment with content "
+ "(SABM)\n");
+ else
+ LOGP(DLLAPD, LOGL_INFO, "perform normal establishm. (SABM)\n");
+
+ /* Flush send-queue */
+ /* Clear send-buffer */
+ lapd_dl_flush_send(dl);
+ /* be sure that history is empty */
+ lapd_dl_flush_hist(dl);
+
+ /* save message context for further use */
+ memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
+
+ /* Discard partly received L3 message */
+ if (dl->rcv_buffer) {
+ msgb_free(dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
+ }
+
+ /* assemble message */
+ memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.cmd;
+ nctx.format = LAPD_FORM_U;
+ nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM;
+ nctx.p_f = 1;
+ nctx.length = msg->len;
+ nctx.more = 0;
+
+ /* Transmit-buffer carries exactly one segment */
+ dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST");
+ msgb_put(dl->tx_hist[0].msg, msg->len);
+ if (msg->len)
+ memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len);
+ dl->tx_hist[0].more = 0;
+ /* set Vs to 0, because it is used as index when resending SABM */
+ dl->v_send = 0;
+
+ /* Set states */
+ dl->own_busy = dl->peer_busy = 0;
+ dl->retrans_ctr = 0;
+ lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT);
+
+ /* Tramsmit and start T200 */
+ dl->send_ph_data_req(&nctx, msg);
+ lapd_start_t200(dl);
+
+ return 0;
+}
+
+/* send data */
+static int lapd_data_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct msgb *msg = dp->oph.msg;
+
+ if (msgb_l3len(msg) == 0) {
+ LOGP(DLLAPD, LOGL_ERROR,
+ "writing an empty message is not possible.\n");
+ msgb_free(msg);
+ return -1;
+ }
+
+ LOGP(DLLAPD, LOGL_INFO,
+ "writing message to send-queue: l3len: %d\n", msgb_l3len(msg));
+
+ /* Write data into the send queue */
+ msgb_enqueue(&dl->send_queue, msg);
+
+ /* Send message, if possible */
+ lapd_send_i(&dl->lctx, __LINE__);
+
+ return 0;
+}
+
+/* Send next I frame from queued/buffered data */
+static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ uint8_t k = dl->k;
+ uint8_t h;
+ struct msgb *msg;
+ int length, left;
+ int rc = - 1; /* we sent nothing */
+ struct lapd_msg_ctx nctx;
+
+
+ LOGP(DLLAPD, LOGL_INFO, "%s() called from line %d\n", __func__, line);
+
+ next_frame:
+
+ if (dl->peer_busy) {
+ LOGP(DLLAPD, LOGL_INFO, "peer busy, not sending\n");
+ return rc;
+ }
+
+ if (dl->state == LAPD_STATE_TIMER_RECOV) {
+ LOGP(DLLAPD, LOGL_INFO, "timer recovery, not sending\n");
+ return rc;
+ }
+
+ /* If the send state variable V(S) is equal to V(A) plus k
+ * (where k is the maximum number of outstanding I frames - see
+ * subclause 5.8.4), the data link layer entity shall not transmit any
+ * new I frames, but shall retransmit an I frame as a result
+ * of the error recovery procedures as described in subclauses 5.5.4 and
+ * 5.5.7. */
+ if (dl->v_send == add_mod(dl->v_ack, k, dl->v_range)) {
+ LOGP(DLLAPD, LOGL_INFO, "k frames outstanding, not sending "
+ "more (k=%u V(S)=%u V(A)=%u)\n", k, dl->v_send,
+ dl->v_ack);
+ return rc;
+ }
+
+ h = do_mod(dl->v_send, dl->range_hist);
+
+ /* if we have no tx_hist yet, we create it */
+ if (!dl->tx_hist[h].msg) {
+ /* Get next message into send-buffer, if any */
+ if (!dl->send_buffer) {
+ next_message:
+ dl->send_out = 0;
+ dl->send_buffer = msgb_dequeue(&dl->send_queue);
+ /* No more data to be sent */
+ if (!dl->send_buffer)
+ return rc;
+ LOGP(DLLAPD, LOGL_INFO, "get message from "
+ "send-queue\n");
+ }
+
+ /* How much is left in the send-buffer? */
+ left = msgb_l3len(dl->send_buffer) - dl->send_out;
+ /* Segment, if data exceeds N201 */
+ length = left;
+ if (length > lctx->n201)
+ length = lctx->n201;
+ LOGP(DLLAPD, LOGL_INFO, "msg-len %d sent %d left %d N201 %d "
+ "length %d first byte %02x\n",
+ msgb_l3len(dl->send_buffer), dl->send_out, left,
+ lctx->n201, length, dl->send_buffer->l3h[0]);
+ /* If message in send-buffer is completely sent */
+ if (left == 0) {
+ msgb_free(dl->send_buffer);
+ dl->send_buffer = NULL;
+ goto next_message;
+ }
+
+ LOGP(DLLAPD, LOGL_INFO, "send I frame %sV(S)=%d\n",
+ (left > length) ? "segment " : "", dl->v_send);
+
+ /* Create I frame (segment) and transmit-buffer content */
+ msg = lapd_msgb_alloc(length, "LAPD I");
+ msg->l3h = msgb_put(msg, length);
+ /* assemble message */
+ memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.cmd;
+ nctx.format = LAPD_FORM_I;
+ nctx.p_f = 0;
+ nctx.n_send = dl->v_send;
+ nctx.n_recv = dl->v_recv;
+ nctx.length = length;
+ if (left > length)
+ nctx.more = 1;
+ else
+ nctx.more = 0;
+ if (length)
+ memcpy(msg->l3h, dl->send_buffer->l3h + dl->send_out,
+ length);
+ /* store in tx_hist */
+ dl->tx_hist[h].msg = lapd_msgb_alloc(msg->len, "HIST");
+ msgb_put(dl->tx_hist[h].msg, msg->len);
+ if (length)
+ memcpy(dl->tx_hist[h].msg->data, msg->l3h, msg->len);
+ dl->tx_hist[h].more = nctx.more;
+ /* Add length to track how much is already in the tx buffer */
+ dl->send_out += length;
+ } else {
+ LOGP(DLLAPD, LOGL_INFO, "resend I frame from tx buffer "
+ "V(S)=%d\n", dl->v_send);
+
+ /* Create I frame (segment) from tx_hist */
+ length = dl->tx_hist[h].msg->len;
+ msg = lapd_msgb_alloc(length, "LAPD I resend");
+ msg->l3h = msgb_put(msg, length);
+ /* assemble message */
+ memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.cmd;
+ nctx.format = LAPD_FORM_I;
+ nctx.p_f = 0;
+ nctx.n_send = dl->v_send;
+ nctx.n_recv = dl->v_recv;
+ nctx.length = length;
+ nctx.more = dl->tx_hist[h].more;
+ if (length)
+ memcpy(msg->l3h, dl->tx_hist[h].msg->data, length);
+ }
+
+ /* The value of the send state variable V(S) shall be incremented by 1
+ * at the end of the transmission of the I frame */
+ dl->v_send = inc_mod(dl->v_send, dl->v_range);
+
+ /* If timer T200 is not running at the time right before transmitting a
+ * frame, when the PH-READY-TO-SEND primitive is received from the
+ * physical layer., it shall be set. */
+ if (!osmo_timer_pending(&dl->t200)) {
+ /* stop Timer T203, if running */
+ lapd_stop_t203(dl);
+ /* start Timer T200 */
+ lapd_start_t200(dl);
+ }
+
+ dl->send_ph_data_req(&nctx, msg);
+
+ rc = 0; /* we sent something */
+ goto next_frame;
+}
+
+/* request link suspension */
+static int lapd_susp_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct msgb *msg = dp->oph.msg;
+
+ LOGP(DLLAPD, LOGL_INFO, "perform suspension\n");
+
+ /* put back the send-buffer to the send-queue (first position) */
+ if (dl->send_buffer) {
+ LOGP(DLLAPD, LOGL_INFO, "put frame in sendbuffer back to "
+ "queue\n");
+ llist_add(&dl->send_buffer->list, &dl->send_queue);
+ dl->send_buffer = NULL;
+ } else
+ LOGP(DLLAPD, LOGL_INFO, "no frame in sendbuffer\n");
+
+ /* Clear transmit buffer, but keep send buffer */
+ lapd_dl_flush_tx(dl);
+ /* Stop timers (there is no state change, so we must stop all timers */
+ lapd_stop_t200(dl);
+ lapd_stop_t203(dl);
+
+ msgb_free(msg);
+
+ return send_dl_simple(PRIM_DL_SUSP, PRIM_OP_CONFIRM, &dl->lctx);
+}
+
+/* requesst resume or reconnect of link */
+static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct msgb *msg = dp->oph.msg;
+ struct lapd_msg_ctx nctx;
+
+ LOGP(DLLAPD, LOGL_INFO, "perform re-establishment (SABM) length=%d\n",
+ msg->len);
+
+ /* be sure that history is empty */
+ lapd_dl_flush_hist(dl);
+
+ /* save message context for further use */
+ memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
+
+ /* Replace message in the send-buffer (reconnect) */
+ if (dl->send_buffer)
+ msgb_free(dl->send_buffer);
+ dl->send_out = 0;
+ if (msg && msg->len)
+ /* Write data into the send buffer, to be sent first */
+ dl->send_buffer = msg;
+ else
+ dl->send_buffer = NULL;
+
+ /* Discard partly received L3 message */
+ if (dl->rcv_buffer) {
+ msgb_free(dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
+ }
+
+ /* Create new msgb (old one is now free) */
+ msg = lapd_msgb_alloc(0, "LAPD SABM");
+ msg->l3h = msg->data;
+ /* assemble message */
+ memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.cmd;
+ nctx.format = LAPD_FORM_U;
+ nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM;
+ nctx.p_f = 1;
+ nctx.length = 0;
+ nctx.more = 0;
+
+ dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST");
+ msgb_put(dl->tx_hist[0].msg, msg->len);
+ if (msg->len)
+ memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len);
+ dl->tx_hist[0].more = 0;
+ /* set Vs to 0, because it is used as index when resending SABM */
+ dl->v_send = 0;
+
+ /* Set states */
+ dl->own_busy = dl->peer_busy = 0;
+ dl->retrans_ctr = 0;
+ lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT);
+
+ /* Tramsmit and start T200 */
+ dl->send_ph_data_req(&nctx, msg);
+ lapd_start_t200(dl);
+
+ return 0;
+}
+
+/* requesst release of link */
+static int lapd_rel_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct msgb *msg = dp->oph.msg;
+ struct lapd_msg_ctx nctx;
+
+ /* local release */
+ if (dp->u.rel_req.mode) {
+ LOGP(DLLAPD, LOGL_INFO, "perform local release\n");
+ msgb_free(msg);
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* enter idle state, T203 is stopped here, if running */
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ /* flush buffers */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ /* send notification to L3 */
+ return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx);
+ }
+
+ /* in case we are already disconnecting */
+ if (dl->state == LAPD_STATE_DISC_SENT)
+ return -EBUSY;
+
+ /* flush tx_hist */
+ lapd_dl_flush_hist(dl);
+
+ LOGP(DLLAPD, LOGL_INFO, "perform normal release (DISC)\n");
+
+ /* Push LAPD header on msgb */
+ /* assemble message */
+ memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ /* keep nctx.ldp */
+ /* keep nctx.sapi */
+ /* keep nctx.tei */
+ nctx.cr = dl->cr.loc2rem.cmd;
+ nctx.format = LAPD_FORM_U;
+ nctx.s_u = LAPD_U_DISC;
+ nctx.p_f = 1;
+ nctx.length = 0;
+ nctx.more = 0;
+
+ dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST");
+ msgb_put(dl->tx_hist[0].msg, msg->len);
+ if (msg->len)
+ memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len);
+ dl->tx_hist[0].more = 0;
+ /* set Vs to 0, because it is used as index when resending DISC */
+ dl->v_send = 0;
+
+ /* Set states */
+ dl->own_busy = dl->peer_busy = 0;
+ dl->retrans_ctr = 0;
+ lapd_dl_newstate(dl, LAPD_STATE_DISC_SENT);
+
+ /* Tramsmit and start T200 */
+ dl->send_ph_data_req(&nctx, msg);
+ lapd_start_t200(dl);
+
+ return 0;
+}
+
+/* request release of link in idle state */
+static int lapd_rel_req_idle(struct osmo_dlsap_prim *dp,
+ struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct msgb *msg = dp->oph.msg;
+
+ msgb_free(msg);
+
+ /* send notification to L3 */
+ return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx);
+}
+
+/* statefull handling for DL SAP messages from L3 */
+static struct l2downstate {
+ uint32_t states;
+ int prim, op;
+ const char *name;
+ int (*rout) (struct osmo_dlsap_prim *dp,
+ struct lapd_msg_ctx *lctx);
+} l2downstatelist[] = {
+ /* create and send UI command */
+ {ALL_STATES,
+ PRIM_DL_UNIT_DATA, PRIM_OP_REQUEST,
+ "DL-UNIT-DATA-REQUEST", lapd_udata_req},
+
+ /* create and send SABM command */
+ {SBIT(LAPD_STATE_IDLE),
+ PRIM_DL_EST, PRIM_OP_REQUEST,
+ "DL-ESTABLISH-REQUEST", lapd_est_req},
+
+ /* create and send I command */
+ {SBIT(LAPD_STATE_MF_EST) |
+ SBIT(LAPD_STATE_TIMER_RECOV),
+ PRIM_DL_DATA, PRIM_OP_REQUEST,
+ "DL-DATA-REQUEST", lapd_data_req},
+
+ /* suspend datalink */
+ {SBIT(LAPD_STATE_MF_EST) |
+ SBIT(LAPD_STATE_TIMER_RECOV),
+ PRIM_DL_SUSP, PRIM_OP_REQUEST,
+ "DL-SUSPEND-REQUEST", lapd_susp_req},
+
+ /* create and send SABM command (resume) */
+ {SBIT(LAPD_STATE_MF_EST) |
+ SBIT(LAPD_STATE_TIMER_RECOV),
+ PRIM_DL_RES, PRIM_OP_REQUEST,
+ "DL-RESUME-REQUEST", lapd_res_req},
+
+ /* create and send SABM command (reconnect) */
+ {SBIT(LAPD_STATE_IDLE) |
+ SBIT(LAPD_STATE_MF_EST) |
+ SBIT(LAPD_STATE_TIMER_RECOV),
+ PRIM_DL_RECON, PRIM_OP_REQUEST,
+ "DL-RECONNECT-REQUEST", lapd_res_req},
+
+ /* create and send DISC command */
+ {SBIT(LAPD_STATE_SABM_SENT) |
+ SBIT(LAPD_STATE_MF_EST) |
+ SBIT(LAPD_STATE_TIMER_RECOV) |
+ SBIT(LAPD_STATE_DISC_SENT),
+ PRIM_DL_REL, PRIM_OP_REQUEST,
+ "DL-RELEASE-REQUEST", lapd_rel_req},
+
+ /* release in idle state */
+ {SBIT(LAPD_STATE_IDLE),
+ PRIM_DL_REL, PRIM_OP_REQUEST,
+ "DL-RELEASE-REQUEST", lapd_rel_req_idle},
+};
+
+#define L2DOWNSLLEN \
+ (sizeof(l2downstatelist) / sizeof(struct l2downstate))
+
+int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ int i, supported = 0;
+ struct msgb *msg = dp->oph.msg;
+ int rc;
+
+ /* find function for current state and message */
+ for (i = 0; i < L2DOWNSLLEN; i++) {
+ if (dp->oph.primitive == l2downstatelist[i].prim
+ && dp->oph.operation == l2downstatelist[i].op) {
+ supported = 1;
+ if ((SBIT(dl->state) & l2downstatelist[i].states))
+ break;
+ }
+ }
+ if (!supported) {
+ LOGP(DLLAPD, LOGL_NOTICE, "Message %u/%u unsupported.\n",
+ dp->oph.primitive, dp->oph.operation);
+ msgb_free(msg);
+ return 0;
+ }
+ if (i == L2DOWNSLLEN) {
+ LOGP(DLLAPD, LOGL_NOTICE, "Message %u/%u unhandled at this "
+ "state %s.\n", dp->oph.primitive, dp->oph.operation,
+ lapd_state_names[dl->state]);
+ msgb_free(msg);
+ return 0;
+ }
+
+ LOGP(DLLAPD, LOGL_INFO, "Message %s received in state %s\n",
+ l2downstatelist[i].name, lapd_state_names[dl->state]);
+
+ rc = l2downstatelist[i].rout(dp, lctx);
+
+ return rc;
+}
+
diff --git a/src/shared/libosmocore/src/gsm/lapdm.c b/src/shared/libosmocore/src/gsm/lapdm.c
new file mode 100644
index 00000000..e9ce881b
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/lapdm.c
@@ -0,0 +1,1245 @@
+/* GSM LAPDm (TS 04.06) implementation */
+
+/* (C) 2010-2011 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010-2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ */
+
+/*! \addtogroup lapdm
+ * @{
+ */
+
+/*! \file lapdm.c */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/lapdm.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+/* TS 04.06 Figure 4 / Section 3.2 */
+#define LAPDm_LPD_NORMAL 0
+#define LAPDm_LPD_SMSCB 1
+#define LAPDm_SAPI_NORMAL 0
+#define LAPDm_SAPI_SMS 3
+#define LAPDm_ADDR(lpd, sapi, cr) ((((lpd) & 0x3) << 5) | (((sapi) & 0x7) << 2) | (((cr) & 0x1) << 1) | 0x1)
+
+#define LAPDm_ADDR_LPD(addr) (((addr) >> 5) & 0x3)
+#define LAPDm_ADDR_SAPI(addr) (((addr) >> 2) & 0x7)
+#define LAPDm_ADDR_CR(addr) (((addr) >> 1) & 0x1)
+#define LAPDm_ADDR_EA(addr) ((addr) & 0x1)
+
+/* TS 04.06 Table 3 / Section 3.4.3 */
+#define LAPDm_CTRL_I(nr, ns, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((ns) & 0x7) << 1))
+#define LAPDm_CTRL_S(nr, s, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((s) & 0x3) << 2) | 0x1)
+#define LAPDm_CTRL_U(u, p) ((((u) & 0x1c) << (5-2)) | (((p) & 0x1) << 4) | (((u) & 0x3) << 2) | 0x3)
+
+#define LAPDm_CTRL_is_I(ctrl) (((ctrl) & 0x1) == 0)
+#define LAPDm_CTRL_is_S(ctrl) (((ctrl) & 0x3) == 1)
+#define LAPDm_CTRL_is_U(ctrl) (((ctrl) & 0x3) == 3)
+
+#define LAPDm_CTRL_U_BITS(ctrl) ((((ctrl) & 0xC) >> 2) | ((ctrl) & 0xE0) >> 3)
+#define LAPDm_CTRL_PF_BIT(ctrl) (((ctrl) >> 4) & 0x1)
+
+#define LAPDm_CTRL_S_BITS(ctrl) (((ctrl) & 0xC) >> 2)
+
+#define LAPDm_CTRL_I_Ns(ctrl) (((ctrl) & 0xE) >> 1)
+#define LAPDm_CTRL_Nr(ctrl) (((ctrl) & 0xE0) >> 5)
+
+#define LAPDm_LEN(len) ((len << 2) | 0x1)
+#define LAPDm_MORE 0x2
+#define LAPDm_EL 0x1
+
+#define LAPDm_U_UI 0x0
+
+/* TS 04.06 Section 5.8.3 */
+#define N201_AB_SACCH 18
+#define N201_AB_SDCCH 20
+#define N201_AB_FACCH 20
+#define N201_Bbis 23
+#define N201_Bter_SACCH 21
+#define N201_Bter_SDCCH 23
+#define N201_Bter_FACCH 23
+#define N201_B4 19
+
+/* 5.8.2.1 N200 during establish and release */
+#define N200_EST_REL 5
+/* 5.8.2.1 N200 during timer recovery state */
+#define N200_TR_SACCH 5
+#define N200_TR_SDCCH 23
+#define N200_TR_FACCH_FR 34
+#define N200_TR_EFACCH_FR 48
+#define N200_TR_FACCH_HR 29
+/* FIXME: set N200 depending on chan_nr */
+#define N200 N200_TR_SDCCH
+
+enum lapdm_format {
+ LAPDm_FMT_A,
+ LAPDm_FMT_B,
+ LAPDm_FMT_Bbis,
+ LAPDm_FMT_Bter,
+ LAPDm_FMT_B4,
+};
+
+static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg);
+static int send_rslms_dlsap(struct osmo_dlsap_prim *dp,
+ struct lapd_msg_ctx *lctx);
+
+static void lapdm_dl_init(struct lapdm_datalink *dl,
+ struct lapdm_entity *entity, int t200)
+{
+ memset(dl, 0, sizeof(*dl));
+ dl->entity = entity;
+ lapd_dl_init(&dl->dl, 1, 8, 200);
+ dl->dl.reestablish = 0; /* GSM uses no reestablish */
+ dl->dl.send_ph_data_req = lapdm_send_ph_data_req;
+ dl->dl.send_dlsap = send_rslms_dlsap;
+ dl->dl.n200_est_rel = N200_EST_REL;
+ dl->dl.n200 = N200;
+ dl->dl.t203_sec = 0; dl->dl.t203_usec = 0;
+ dl->dl.t200_sec = t200; dl->dl.t200_usec = 0;
+}
+
+/*! \brief initialize a LAPDm entity and all datalinks inside
+ * \param[in] le LAPDm entity
+ * \param[in] mode \ref lapdm_mode (BTS/MS)
+ */
+void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
+ lapdm_dl_init(&le->datalink[i], le, t200);
+
+ lapdm_entity_set_mode(le, mode);
+}
+
+/*! \brief initialize a LAPDm channel and all its channels
+ * \param[in] lc \ref lapdm_channel to be initialized
+ * \param[in] mode \ref lapdm_mode (BTS/MS)
+ *
+ * This really is a convenience wrapper around calling \ref
+ * lapdm_entity_init twice.
+ */
+void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode)
+{
+ lapdm_entity_init(&lc->lapdm_acch, mode, 2);
+ /* FIXME: this depends on chan type */
+ lapdm_entity_init(&lc->lapdm_dcch, mode, 1);
+}
+
+/*! \brief flush and release all resoures in LAPDm entity */
+void lapdm_entity_exit(struct lapdm_entity *le)
+{
+ unsigned int i;
+ struct lapdm_datalink *dl;
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
+ dl = &le->datalink[i];
+ lapd_dl_exit(&dl->dl);
+ }
+}
+
+/* \brief lfush and release all resources in LAPDm channel
+ *
+ * A convenience wrapper calling \ref lapdm_entity_exit on both
+ * entities inside the \ref lapdm_channel
+ */
+void lapdm_channel_exit(struct lapdm_channel *lc)
+{
+ lapdm_entity_exit(&lc->lapdm_acch);
+ lapdm_entity_exit(&lc->lapdm_dcch);
+}
+
+static struct lapdm_datalink *datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi)
+{
+ switch (sapi) {
+ case LAPDm_SAPI_NORMAL:
+ return &le->datalink[0];
+ case LAPDm_SAPI_SMS:
+ return &le->datalink[1];
+ default:
+ return NULL;
+ }
+}
+
+/* remove the L2 header from a MSGB */
+static inline unsigned char *msgb_pull_l2h(struct msgb *msg)
+{
+ unsigned char *ret = msgb_pull(msg, msg->l3h - msg->l2h);
+ msg->l2h = NULL;
+ return ret;
+}
+
+/* Append padding (if required) */
+static void lapdm_pad_msgb(struct msgb *msg, uint8_t n201)
+{
+ int pad_len = n201 - msgb_l2len(msg);
+ uint8_t *data;
+
+ if (pad_len < 0) {
+ LOGP(DLLAPD, LOGL_ERROR,
+ "cannot pad message that is already too big!\n");
+ return;
+ }
+
+ data = msgb_put(msg, pad_len);
+ memset(data, 0x2B, pad_len);
+}
+
+/* input function that L2 calls when sending messages up to L3 */
+static int rslms_sendmsg(struct msgb *msg, struct lapdm_entity *le)
+{
+ if (!le->l3_cb) {
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ /* call the layer2 message handler that is registered */
+ return le->l3_cb(msg, le, le->l3_ctx);
+}
+
+/* write a frame into the tx queue */
+static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg,
+ uint8_t chan_nr, uint8_t link_id, uint8_t pad)
+{
+ struct lapdm_entity *le = dl->entity;
+ struct osmo_phsap_prim pp;
+
+ /* if there is a pending message, queue it */
+ if (le->tx_pending || le->flags & LAPDM_ENT_F_POLLING_ONLY) {
+ *msgb_push(msg, 1) = pad;
+ *msgb_push(msg, 1) = link_id;
+ *msgb_push(msg, 1) = chan_nr;
+ msgb_enqueue(&dl->dl.tx_queue, msg);
+ return -EBUSY;
+ }
+
+ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_REQUEST, msg);
+ pp.u.data.chan_nr = chan_nr;
+ pp.u.data.link_id = link_id;
+
+ /* send the frame now */
+ le->tx_pending = 0; /* disabled flow control */
+ lapdm_pad_msgb(msg, pad);
+
+ return le->l1_prim_cb(&pp.oph, le->l1_ctx);
+}
+
+static struct msgb *tx_dequeue_msgb(struct lapdm_entity *le)
+{
+ struct lapdm_datalink *dl;
+ int last = le->last_tx_dequeue;
+ int i = last, n = ARRAY_SIZE(le->datalink);
+ struct msgb *msg = NULL;
+
+ /* round-robin dequeue */
+ do {
+ /* next */
+ i = (i + 1) % n;
+ dl = &le->datalink[i];
+ if ((msg = msgb_dequeue(&dl->dl.tx_queue)))
+ break;
+ } while (i != last);
+
+ if (msg) {
+ /* Set last dequeue position */
+ le->last_tx_dequeue = i;
+ }
+
+ return msg;
+}
+
+/*! \brief dequeue a msg that's pending transmission via L1 and wrap it into
+ * a osmo_phsap_prim */
+int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp)
+{
+ struct msgb *msg;
+ uint8_t pad;
+
+ msg = tx_dequeue_msgb(le);
+ if (!msg)
+ return -ENODEV;
+
+ /* if we have a message, send PH-DATA.req */
+ osmo_prim_init(&pp->oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_REQUEST, msg);
+
+ /* Pull chan_nr and link_id */
+ pp->u.data.chan_nr = *msg->data;
+ msgb_pull(msg, 1);
+ pp->u.data.link_id = *msg->data;
+ msgb_pull(msg, 1);
+ pad = *msg->data;
+ msgb_pull(msg, 1);
+
+ /* Pad the frame, we can transmit now */
+ lapdm_pad_msgb(msg, pad);
+
+ return 0;
+}
+
+/* get next frame from the tx queue. because the ms has multiple datalinks,
+ * each datalink's queue is read round-robin.
+ */
+static int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le)
+{
+ struct osmo_phsap_prim pp;
+
+ /* we may send again */
+ le->tx_pending = 0;
+
+ /* free confirm message */
+ if (msg)
+ msgb_free(msg);
+
+ if (lapdm_phsap_dequeue_prim(le, &pp) < 0) {
+ /* no message in all queues */
+
+ /* If user didn't request PH-EMPTY_FRAME.req, abort */
+ if (!(le->flags & LAPDM_ENT_F_EMPTY_FRAME))
+ return 0;
+
+ /* otherwise, send PH-EMPTY_FRAME.req */
+ osmo_prim_init(&pp.oph, SAP_GSM_PH,
+ PRIM_PH_EMPTY_FRAME,
+ PRIM_OP_REQUEST, NULL);
+ } else {
+ le->tx_pending = 1;
+ }
+
+ return le->l1_prim_cb(&pp.oph, le->l1_ctx);
+}
+
+/* Create RSLms various RSLms messages */
+static int send_rslms_rll_l3(uint8_t msg_type, struct lapdm_msg_ctx *mctx,
+ struct msgb *msg)
+{
+ /* Add the RSL + RLL header */
+ rsl_rll_push_l3(msg, msg_type, mctx->chan_nr, mctx->link_id, 1);
+
+ /* send off the RSLms message to L3 */
+ return rslms_sendmsg(msg, mctx->dl->entity);
+}
+
+/* Take a B4 format message from L1 and create RSLms UNIT DATA IND */
+static int send_rslms_rll_l3_ui(struct lapdm_msg_ctx *mctx, struct msgb *msg)
+{
+ uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg);
+ struct abis_rsl_rll_hdr *rllh;
+
+ /* Add the RSL + RLL header */
+ msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
+ msgb_push(msg, 2 + 2);
+ rsl_rll_push_hdr(msg, RSL_MT_UNIT_DATA_IND, mctx->chan_nr,
+ mctx->link_id, 1);
+ rllh = (struct abis_rsl_rll_hdr *)msgb_l2(msg);
+
+ rllh->data[0] = RSL_IE_TIMING_ADVANCE;
+ rllh->data[1] = mctx->ta_ind;
+
+ rllh->data[2] = RSL_IE_MS_POWER;
+ rllh->data[3] = mctx->tx_power_ind;
+
+ return rslms_sendmsg(msg, mctx->dl->entity);
+}
+
+static int send_rll_simple(uint8_t msg_type, struct lapdm_msg_ctx *mctx)
+{
+ struct msgb *msg;
+
+ msg = rsl_rll_simple(msg_type, mctx->chan_nr, mctx->link_id, 1);
+
+ /* send off the RSLms message to L3 */
+ return rslms_sendmsg(msg, mctx->dl->entity);
+}
+
+static int rsl_rll_error(uint8_t cause, struct lapdm_msg_ctx *mctx)
+{
+ struct msgb *msg;
+
+ LOGP(DLLAPD, LOGL_NOTICE, "sending MDL-ERROR-IND %d\n", cause);
+ msg = rsl_rll_simple(RSL_MT_ERROR_IND, mctx->chan_nr, mctx->link_id, 1);
+ msg->l2h = msgb_put(msg, sizeof(struct abis_rsl_rll_hdr));
+ msgb_tlv_put(msg, RSL_IE_RLM_CAUSE, 1, &cause);
+ return rslms_sendmsg(msg, mctx->dl->entity);
+}
+
+/* DLSAP L2 -> L3 (RSLms) */
+static int send_rslms_dlsap(struct osmo_dlsap_prim *dp,
+ struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ struct lapdm_datalink *mdl =
+ container_of(dl, struct lapdm_datalink, dl);
+ struct lapdm_msg_ctx *mctx = &mdl->mctx;
+ uint8_t rll_msg = 0;
+
+ switch (OSMO_PRIM_HDR(&dp->oph)) {
+ case OSMO_PRIM(PRIM_DL_EST, PRIM_OP_INDICATION):
+ rll_msg = RSL_MT_EST_IND;
+ break;
+ case OSMO_PRIM(PRIM_DL_EST, PRIM_OP_CONFIRM):
+ rll_msg = RSL_MT_EST_CONF;
+ break;
+ case OSMO_PRIM(PRIM_DL_DATA, PRIM_OP_INDICATION):
+ rll_msg = RSL_MT_DATA_IND;
+ break;
+ case OSMO_PRIM(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION):
+ return send_rslms_rll_l3_ui(mctx, dp->oph.msg);
+ case OSMO_PRIM(PRIM_DL_REL, PRIM_OP_INDICATION):
+ rll_msg = RSL_MT_REL_IND;
+ break;
+ case OSMO_PRIM(PRIM_DL_REL, PRIM_OP_CONFIRM):
+ rll_msg = RSL_MT_REL_CONF;
+ break;
+ case OSMO_PRIM(PRIM_DL_SUSP, PRIM_OP_CONFIRM):
+ rll_msg = RSL_MT_SUSP_CONF;
+ break;
+ case OSMO_PRIM(PRIM_MDL_ERROR, PRIM_OP_INDICATION):
+ rsl_rll_error(dp->u.error_ind.cause, mctx);
+ if (dp->oph.msg)
+ msgb_free(dp->oph.msg);
+ return 0;
+ }
+
+ if (!rll_msg) {
+ LOGP(DLLAPD, LOGL_ERROR, "Unsupported op %d, prim %d. Please "
+ "fix!\n", dp->oph.primitive, dp->oph.operation);
+ return -EINVAL;
+ }
+
+ if (!dp->oph.msg)
+ return send_rll_simple(rll_msg, mctx);
+
+ return send_rslms_rll_l3(rll_msg, mctx, dp->oph.msg);
+}
+
+/* send a data frame to layer 1 */
+static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
+{
+ uint8_t l3_len = msg->tail - msg->data;
+ struct lapd_datalink *dl = lctx->dl;
+ struct lapdm_datalink *mdl =
+ container_of(dl, struct lapdm_datalink, dl);
+ struct lapdm_msg_ctx *mctx = &mdl->mctx;
+ int format = lctx->format;
+
+ /* prepend l2 header */
+ msg->l2h = msgb_push(msg, 3);
+ msg->l2h[0] = LAPDm_ADDR(lctx->lpd, lctx->sapi, lctx->cr);
+ /* EA is set here too */
+ switch (format) {
+ case LAPD_FORM_I:
+ msg->l2h[1] = LAPDm_CTRL_I(lctx->n_recv, lctx->n_send,
+ lctx->p_f);
+ break;
+ case LAPD_FORM_S:
+ msg->l2h[1] = LAPDm_CTRL_S(lctx->n_recv, lctx->s_u, lctx->p_f);
+ break;
+ case LAPD_FORM_U:
+ msg->l2h[1] = LAPDm_CTRL_U(lctx->s_u, lctx->p_f);
+ break;
+ default:
+ msgb_free(msg);
+ return -EINVAL;
+ }
+ msg->l2h[2] = LAPDm_LEN(l3_len); /* EL is set here too */
+ if (lctx->more)
+ msg->l2h[2] |= LAPDm_MORE;
+
+ /* add ACCH header with last indicated tx-power and TA */
+ if ((mctx->link_id & 0x40)) {
+ struct lapdm_entity *le = mdl->entity;
+
+ msg->l2h = msgb_push(msg, 2);
+ msg->l2h[0] = le->tx_power;
+ msg->l2h[1] = le->ta;
+ }
+
+ return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id,
+ 23);
+}
+
+/* input into layer2 (from layer 1) */
+static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
+ uint8_t chan_nr, uint8_t link_id)
+{
+ uint8_t cbits = chan_nr >> 3;
+ uint8_t sapi; /* we cannot take SAPI from link_id, as L1 has no clue */
+ struct lapdm_msg_ctx mctx;
+ struct lapd_msg_ctx lctx;
+ int rc = 0;
+ int n201;
+
+ /* when we reach here, we have a msgb with l2h pointing to the raw
+ * 23byte mac block. The l1h has already been purged. */
+
+ memset(&mctx, 0, sizeof(mctx));
+ mctx.chan_nr = chan_nr;
+ mctx.link_id = link_id;
+
+ /* check for L1 chan_nr/link_id and determine LAPDm hdr format */
+ if (cbits == 0x10 || cbits == 0x12) {
+ /* Format Bbis is used on BCCH and CCCH(PCH, NCH and AGCH) */
+ mctx.lapdm_fmt = LAPDm_FMT_Bbis;
+ n201 = N201_Bbis;
+ sapi = 0;
+ } else {
+ if (mctx.link_id & 0x40) {
+ /* It was received from network on SACCH */
+
+ /* If UI on SACCH sent by BTS, lapdm_fmt must be B4 */
+ if (le->mode == LAPDM_MODE_MS
+ && LAPDm_CTRL_is_U(msg->l2h[3])
+ && LAPDm_CTRL_U_BITS(msg->l2h[3]) == 0) {
+ mctx.lapdm_fmt = LAPDm_FMT_B4;
+ n201 = N201_B4;
+ LOGP(DLLAPD, LOGL_INFO, "fmt=B4\n");
+ } else {
+ mctx.lapdm_fmt = LAPDm_FMT_B;
+ n201 = N201_AB_SACCH;
+ LOGP(DLLAPD, LOGL_INFO, "fmt=B\n");
+ }
+ /* SACCH frames have a two-byte L1 header that
+ * OsmocomBB L1 doesn't strip */
+ mctx.tx_power_ind = msg->l2h[0] & 0x1f;
+ mctx.ta_ind = msg->l2h[1];
+ msgb_pull(msg, 2);
+ msg->l2h += 2;
+ sapi = (msg->l2h[0] >> 2) & 7;
+ } else {
+ mctx.lapdm_fmt = LAPDm_FMT_B;
+ LOGP(DLLAPD, LOGL_INFO, "fmt=B\n");
+ n201 = N201_AB_SDCCH;
+ sapi = (msg->l2h[0] >> 2) & 7;
+ }
+ }
+
+ mctx.dl = datalink_for_sapi(le, sapi);
+ /* G.2.1 No action on frames containing an unallocated SAPI. */
+ if (!mctx.dl) {
+ LOGP(DLLAPD, LOGL_NOTICE, "Received frame for unsupported "
+ "SAPI %d!\n", sapi);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ switch (mctx.lapdm_fmt) {
+ case LAPDm_FMT_A:
+ case LAPDm_FMT_B:
+ case LAPDm_FMT_B4:
+ lctx.dl = &mctx.dl->dl;
+ /* obtain SAPI from address field */
+ mctx.link_id |= LAPDm_ADDR_SAPI(msg->l2h[0]);
+ /* G.2.3 EA bit set to "0" is not allowed in GSM */
+ if (!LAPDm_ADDR_EA(msg->l2h[0])) {
+ LOGP(DLLAPD, LOGL_NOTICE, "EA bit 0 is not allowed in "
+ "GSM\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, &mctx);
+ return -EINVAL;
+ }
+ /* adress field */
+ lctx.lpd = LAPDm_ADDR_LPD(msg->l2h[0]);
+ lctx.sapi = LAPDm_ADDR_SAPI(msg->l2h[0]);
+ lctx.cr = LAPDm_ADDR_CR(msg->l2h[0]);
+ /* command field */
+ if (LAPDm_CTRL_is_I(msg->l2h[1])) {
+ lctx.format = LAPD_FORM_I;
+ lctx.n_send = LAPDm_CTRL_I_Ns(msg->l2h[1]);
+ lctx.n_recv = LAPDm_CTRL_Nr(msg->l2h[1]);
+ } else if (LAPDm_CTRL_is_S(msg->l2h[1])) {
+ lctx.format = LAPD_FORM_S;
+ lctx.n_recv = LAPDm_CTRL_Nr(msg->l2h[1]);
+ lctx.s_u = LAPDm_CTRL_S_BITS(msg->l2h[1]);
+ } else if (LAPDm_CTRL_is_U(msg->l2h[1])) {
+ lctx.format = LAPD_FORM_U;
+ lctx.s_u = LAPDm_CTRL_U_BITS(msg->l2h[1]);
+ } else
+ lctx.format = LAPD_FORM_UKN;
+ lctx.p_f = LAPDm_CTRL_PF_BIT(msg->l2h[1]);
+ if (lctx.sapi != LAPDm_SAPI_NORMAL
+ && lctx.sapi != LAPDm_SAPI_SMS
+ && lctx.format == LAPD_FORM_U
+ && lctx.s_u == LAPDm_U_UI) {
+ /* 5.3.3 UI frames with invalid SAPI values shall be
+ * discarded
+ */
+ LOGP(DLLAPD, LOGL_INFO, "sapi=%u (discarding)\n",
+ lctx.sapi);
+ msgb_free(msg);
+ return 0;
+ }
+ if (mctx.lapdm_fmt == LAPDm_FMT_B4) {
+ lctx.n201 = n201;
+ lctx.length = n201;
+ lctx.more = 0;
+ msg->l3h = msg->l2h + 2;
+ msgb_pull_l2h(msg);
+ } else {
+ /* length field */
+ if (!(msg->l2h[2] & LAPDm_EL)) {
+ /* G.4.1 If the EL bit is set to "0", an
+ * MDL-ERROR-INDICATION primitive with cause
+ * "frame not implemented" is sent to the
+ * mobile management entity. */
+ LOGP(DLLAPD, LOGL_NOTICE, "we don't support "
+ "multi-octet length\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, &mctx);
+ return -EINVAL;
+ }
+ lctx.n201 = n201;
+ lctx.length = msg->l2h[2] >> 2;
+ lctx.more = !!(msg->l2h[2] & LAPDm_MORE);
+ msg->l3h = msg->l2h + 3;
+ msgb_pull_l2h(msg);
+ }
+ /* store context for messages from lapd */
+ memcpy(&mctx.dl->mctx, &mctx, sizeof(mctx.dl->mctx));
+ /* send to LAPD */
+ rc = lapd_ph_data_ind(msg, &lctx);
+ break;
+ case LAPDm_FMT_Bter:
+ /* FIXME */
+ msgb_free(msg);
+ break;
+ case LAPDm_FMT_Bbis:
+ /* directly pass up to layer3 */
+ LOGP(DLLAPD, LOGL_INFO, "fmt=Bbis UI\n");
+ msg->l3h = msg->l2h;
+ msgb_pull_l2h(msg);
+ rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, &mctx, msg);
+ break;
+ default:
+ msgb_free(msg);
+ }
+
+ return rc;
+}
+
+/* input into layer2 (from layer 1) */
+static int l2_ph_rach_ind(struct lapdm_entity *le, uint8_t ra, uint32_t fn, uint8_t acc_delay)
+{
+ struct abis_rsl_cchan_hdr *ch;
+ struct gsm48_req_ref req_ref;
+ struct gsm_time gt;
+ struct msgb *msg = msgb_alloc_headroom(512, 64, "RSL CHAN RQD");
+
+ msg->l2h = msgb_push(msg, sizeof(*ch));
+ ch = (struct abis_rsl_cchan_hdr *)msg->l2h;
+ rsl_init_cchan_hdr(ch, RSL_MT_CHAN_RQD);
+ ch->chan_nr = RSL_CHAN_RACH;
+
+ /* generate a RSL CHANNEL REQUIRED message */
+ gsm_fn2gsmtime(&gt, fn);
+ req_ref.ra = ra;
+ req_ref.t1 = gt.t1; /* FIXME: modulo? */
+ req_ref.t2 = gt.t2;
+ req_ref.t3_low = gt.t3 & 7;
+ req_ref.t3_high = gt.t3 >> 3;
+
+ msgb_tv_fixed_put(msg, RSL_IE_REQ_REFERENCE, 3, (uint8_t *) &req_ref);
+ msgb_tv_put(msg, RSL_IE_ACCESS_DELAY, acc_delay);
+
+ return rslms_sendmsg(msg, le);
+}
+
+static int l2_ph_chan_conf(struct msgb *msg, struct lapdm_entity *le, uint32_t frame_nr);
+
+/*! \brief Receive a PH-SAP primitive from L1 */
+int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le)
+{
+ struct osmo_phsap_prim *pp = (struct osmo_phsap_prim *) oph;
+ int rc = 0;
+
+ if (oph->sap != SAP_GSM_PH) {
+ LOGP(DLLAPD, LOGL_ERROR, "primitive for unknown SAP %u\n",
+ oph->sap);
+ return -ENODEV;
+ }
+
+ switch (oph->primitive) {
+ case PRIM_PH_DATA:
+ if (oph->operation != PRIM_OP_INDICATION) {
+ LOGP(DLLAPD, LOGL_ERROR, "PH_DATA is not INDICATION %u\n",
+ oph->operation);
+ return -ENODEV;
+ }
+ rc = l2_ph_data_ind(oph->msg, le, pp->u.data.chan_nr,
+ pp->u.data.link_id);
+ break;
+ case PRIM_PH_RTS:
+ if (oph->operation != PRIM_OP_INDICATION) {
+ LOGP(DLLAPD, LOGL_ERROR, "PH_RTS is not INDICATION %u\n",
+ oph->operation);
+ return -ENODEV;
+ }
+ rc = l2_ph_data_conf(oph->msg, le);
+ break;
+ case PRIM_PH_RACH:
+ switch (oph->operation) {
+ case PRIM_OP_INDICATION:
+ rc = l2_ph_rach_ind(le, pp->u.rach_ind.ra, pp->u.rach_ind.fn,
+ pp->u.rach_ind.acc_delay);
+ break;
+ case PRIM_OP_CONFIRM:
+ rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn);
+ break;
+ default:
+ return -EIO;
+ }
+ break;
+ }
+
+ return rc;
+}
+
+
+/* L3 -> L2 / RSLMS -> LAPDm */
+
+/* Set LAPDm context for established connection */
+static int set_lapdm_context(struct lapdm_datalink *dl, uint8_t chan_nr,
+ uint8_t link_id, int n201, uint8_t sapi)
+{
+ memset(&dl->mctx, 0, sizeof(dl->mctx));
+ dl->mctx.dl = dl;
+ dl->mctx.chan_nr = chan_nr;
+ dl->mctx.link_id = link_id;
+ dl->dl.lctx.dl = &dl->dl;
+ dl->dl.lctx.n201 = n201;
+ dl->dl.lctx.sapi = sapi;
+
+ return 0;
+}
+
+/* L3 requests establishment of data link */
+static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t chan_nr = rllh->chan_nr;
+ uint8_t link_id = rllh->link_id;
+ uint8_t sapi = rllh->link_id & 7;
+ struct tlv_parsed tv;
+ uint8_t length;
+ uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH;
+ struct osmo_dlsap_prim dp;
+
+ /* Set LAPDm context for established connection */
+ set_lapdm_context(dl, chan_nr, link_id, n201, sapi);
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh));
+ if (TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
+ /* contention resolution establishment procedure */
+ if (sapi != 0) {
+ /* According to clause 6, the contention resolution
+ * procedure is only permitted with SAPI value 0 */
+ LOGP(DLLAPD, LOGL_ERROR, "SAPI != 0 but contention"
+ "resolution (discarding)\n");
+ msgb_free(msg);
+ return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
+ }
+ /* transmit a SABM command with the P bit set to "1". The SABM
+ * command shall contain the layer 3 message unit */
+ length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
+ } else {
+ /* normal establishment procedure */
+ msg->l3h = msg->l2h + sizeof(*rllh);
+ length = 0;
+ }
+
+ /* check if the layer3 message length exceeds N201 */
+ if (length > n201) {
+ LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) "
+ "(discarding)\n", length, n201);
+ msgb_free(msg);
+ return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
+ }
+
+ /* Remove RLL header from msgb and set length to L3-info */
+ msgb_pull_l2h(msg);
+ msg->len = length;
+ msg->tail = msg->l3h + length;
+
+ /* prepare prim */
+ osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
+
+ /* send to L2 */
+ return lapd_recv_dlsap(&dp, &dl->dl.lctx);
+}
+
+/* L3 requests transfer of unnumbered information */
+static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct lapdm_entity *le = dl->entity;
+ int ui_bts = (le->mode == LAPDM_MODE_BTS);
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t chan_nr = rllh->chan_nr;
+ uint8_t link_id = rllh->link_id;
+ uint8_t sapi = link_id & 7;
+ struct tlv_parsed tv;
+ int length;
+
+ /* check if the layer3 message length exceeds N201 */
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+
+ if (TLVP_PRESENT(&tv, RSL_IE_TIMING_ADVANCE)) {
+ le->ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE);
+ }
+ if (TLVP_PRESENT(&tv, RSL_IE_MS_POWER)) {
+ le->tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER);
+ }
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ LOGP(DLLAPD, LOGL_ERROR, "unit data request without message "
+ "error\n");
+ msgb_free(msg);
+ return -EINVAL;
+ }
+ msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
+ length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
+ /* check if the layer3 message length exceeds N201 */
+ if (length + 4 + !ui_bts > 23) {
+ LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) "
+ "(discarding)\n", length, 18 + ui_bts);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ LOGP(DLLAPD, LOGL_INFO, "sending unit data (tx_power=%d, ta=%d)\n",
+ le->tx_power, le->ta);
+
+ /* Remove RLL header from msgb and set length to L3-info */
+ msgb_pull_l2h(msg);
+ msg->len = length;
+ msg->tail = msg->l3h + length;
+
+ /* Push L1 + LAPDm header on msgb */
+ msg->l2h = msgb_push(msg, 4 + !ui_bts);
+ msg->l2h[0] = le->tx_power;
+ msg->l2h[1] = le->ta;
+ msg->l2h[2] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd);
+ msg->l2h[3] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
+ if (!ui_bts)
+ msg->l2h[4] = LAPDm_LEN(length);
+
+ /* Tramsmit */
+ return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, 23);
+}
+
+/* L3 requests transfer of acknowledged information */
+static int rslms_rx_rll_data_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ struct tlv_parsed tv;
+ int length;
+ struct osmo_dlsap_prim dp;
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ LOGP(DLLAPD, LOGL_ERROR, "data request without message "
+ "error\n");
+ msgb_free(msg);
+ return -EINVAL;
+ }
+ msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
+ length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
+
+ /* Remove RLL header from msgb and set length to L3-info */
+ msgb_pull_l2h(msg);
+ msg->len = length;
+ msg->tail = msg->l3h + length;
+
+ /* prepare prim */
+ osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg);
+
+ /* send to L2 */
+ return lapd_recv_dlsap(&dp, &dl->dl.lctx);
+}
+
+/* L3 requests suspension of data link */
+static int rslms_rx_rll_susp_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t sapi = rllh->link_id & 7;
+ struct osmo_dlsap_prim dp;
+
+ if (sapi != 0) {
+ LOGP(DLLAPD, LOGL_ERROR, "SAPI != 0 while suspending\n");
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ /* prepare prim */
+ osmo_prim_init(&dp.oph, 0, PRIM_DL_SUSP, PRIM_OP_REQUEST, msg);
+
+ /* send to L2 */
+ return lapd_recv_dlsap(&dp, &dl->dl.lctx);
+}
+
+/* L3 requests resume of data link */
+static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ int msg_type = rllh->c.msg_type;
+ uint8_t chan_nr = rllh->chan_nr;
+ uint8_t link_id = rllh->link_id;
+ uint8_t sapi = rllh->link_id & 7;
+ struct tlv_parsed tv;
+ uint8_t length;
+ uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH;
+ struct osmo_dlsap_prim dp;
+
+ /* Set LAPDm context for established connection */
+ set_lapdm_context(dl, chan_nr, link_id, n201, sapi);
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ LOGP(DLLAPD, LOGL_ERROR, "resume without message error\n");
+ msgb_free(msg);
+ return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
+ }
+ msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
+ length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
+
+ /* Remove RLL header from msgb and set length to L3-info */
+ msgb_pull_l2h(msg);
+ msg->len = length;
+ msg->tail = msg->l3h + length;
+
+ /* prepare prim */
+ osmo_prim_init(&dp.oph, 0, (msg_type == RSL_MT_RES_REQ) ? PRIM_DL_RES
+ : PRIM_DL_RECON, PRIM_OP_REQUEST, msg);
+
+ /* send to L2 */
+ return lapd_recv_dlsap(&dp, &dl->dl.lctx);
+}
+
+/* L3 requests release of data link */
+static int rslms_rx_rll_rel_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t mode = 0;
+ struct osmo_dlsap_prim dp;
+
+ /* get release mode */
+ if (rllh->data[0] == RSL_IE_RELEASE_MODE)
+ mode = rllh->data[1] & 1;
+
+ /* Pull rllh */
+ msgb_pull(msg, msg->tail - msg->l2h);
+
+ /* prepare prim */
+ osmo_prim_init(&dp.oph, 0, PRIM_DL_REL, PRIM_OP_REQUEST, msg);
+ dp.u.rel_req.mode = mode;
+
+ /* send to L2 */
+ return lapd_recv_dlsap(&dp, &dl->dl.lctx);
+}
+
+/* L3 requests channel in idle state */
+static int rslms_rx_chan_rqd(struct lapdm_channel *lc, struct msgb *msg)
+{
+ struct abis_rsl_cchan_hdr *cch = msgb_l2(msg);
+ void *l1ctx = lc->lapdm_dcch.l1_ctx;
+ struct osmo_phsap_prim pp;
+
+ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH,
+ PRIM_OP_REQUEST, NULL);
+
+ if (msgb_l2len(msg) < sizeof(*cch) + 4 + 2 + 2) {
+ LOGP(DLLAPD, LOGL_ERROR, "Message too short for CHAN RQD!\n");
+ return -EINVAL;
+ }
+ if (cch->data[0] != RSL_IE_REQ_REFERENCE) {
+ LOGP(DLLAPD, LOGL_ERROR, "Missing REQ REFERENCE IE\n");
+ return -EINVAL;
+ }
+ pp.u.rach_req.ra = cch->data[1];
+ pp.u.rach_req.offset = ((cch->data[2] & 0x7f) << 8) | cch->data[3];
+ pp.u.rach_req.is_combined_ccch = cch->data[2] >> 7;
+
+ if (cch->data[4] != RSL_IE_ACCESS_DELAY) {
+ LOGP(DLLAPD, LOGL_ERROR, "Missing ACCESS_DELAY IE\n");
+ return -EINVAL;
+ }
+ /* TA = 0 - delay */
+ pp.u.rach_req.ta = 0 - cch->data[5];
+
+ if (cch->data[6] != RSL_IE_MS_POWER) {
+ LOGP(DLLAPD, LOGL_ERROR, "Missing MS POWER IE\n");
+ return -EINVAL;
+ }
+ pp.u.rach_req.tx_power = cch->data[7];
+
+ msgb_free(msg);
+
+ return lc->lapdm_dcch.l1_prim_cb(&pp.oph, l1ctx);
+}
+
+/* L1 confirms channel request */
+static int l2_ph_chan_conf(struct msgb *msg, struct lapdm_entity *le, uint32_t frame_nr)
+{
+ struct abis_rsl_cchan_hdr *ch;
+ struct gsm_time tm;
+ struct gsm48_req_ref *ref;
+
+ gsm_fn2gsmtime(&tm, frame_nr);
+
+ msgb_pull_l2h(msg);
+ msg->l2h = msgb_push(msg, sizeof(*ch) + sizeof(*ref));
+ ch = (struct abis_rsl_cchan_hdr *)msg->l2h;
+ rsl_init_cchan_hdr(ch, RSL_MT_CHAN_CONF);
+ ch->chan_nr = RSL_CHAN_RACH;
+ ch->data[0] = RSL_IE_REQ_REFERENCE;
+ ref = (struct gsm48_req_ref *) (ch->data + 1);
+ ref->t1 = tm.t1;
+ ref->t2 = tm.t2;
+ ref->t3_low = tm.t3 & 0x7;
+ ref->t3_high = tm.t3 >> 3;
+
+ return rslms_sendmsg(msg, le);
+}
+
+/* incoming RSLms RLL message from L3 */
+static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ int msg_type = rllh->c.msg_type;
+ uint8_t sapi = rllh->link_id & 7;
+ struct lapdm_entity *le;
+ struct lapdm_datalink *dl;
+ int rc = 0;
+
+ if (msgb_l2len(msg) < sizeof(*rllh)) {
+ LOGP(DLLAPD, LOGL_ERROR, "Message too short for RLL hdr!\n");
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ if (rllh->link_id & 0x40)
+ le = &lc->lapdm_acch;
+ else
+ le = &lc->lapdm_dcch;
+
+ /* G.2.1 No action schall be taken on frames containing an unallocated
+ * SAPI.
+ */
+ dl = datalink_for_sapi(le, sapi);
+ if (!dl) {
+ LOGP(DLLAPD, LOGL_ERROR, "No instance for SAPI %d!\n", sapi);
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ LOGP(DLLAPD, LOGL_INFO, "(%p) RLL Message '%s' received. (sapi %d)\n",
+ lc->name, rsl_msg_name(msg_type), sapi);
+
+ switch (msg_type) {
+ case RSL_MT_UNIT_DATA_REQ:
+ rc = rslms_rx_rll_udata_req(msg, dl);
+ break;
+ case RSL_MT_EST_REQ:
+ rc = rslms_rx_rll_est_req(msg, dl);
+ break;
+ case RSL_MT_DATA_REQ:
+ rc = rslms_rx_rll_data_req(msg, dl);
+ break;
+ case RSL_MT_SUSP_REQ:
+ rc = rslms_rx_rll_susp_req(msg, dl);
+ break;
+ case RSL_MT_RES_REQ:
+ rc = rslms_rx_rll_res_req(msg, dl);
+ break;
+ case RSL_MT_RECON_REQ:
+ rc = rslms_rx_rll_res_req(msg, dl);
+ break;
+ case RSL_MT_REL_REQ:
+ rc = rslms_rx_rll_rel_req(msg, dl);
+ break;
+ default:
+ LOGP(DLLAPD, LOGL_NOTICE, "Message unsupported.\n");
+ msgb_free(msg);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+/* incoming RSLms COMMON CHANNEL message from L3 */
+static int rslms_rx_com_chan(struct msgb *msg, struct lapdm_channel *lc)
+{
+ struct abis_rsl_cchan_hdr *cch = msgb_l2(msg);
+ int msg_type = cch->c.msg_type;
+ int rc = 0;
+
+ if (msgb_l2len(msg) < sizeof(*cch)) {
+ LOGP(DLLAPD, LOGL_ERROR, "Message too short for COM CHAN hdr!\n");
+ return -EINVAL;
+ }
+
+ switch (msg_type) {
+ case RSL_MT_CHAN_RQD:
+ /* create and send RACH request */
+ rc = rslms_rx_chan_rqd(lc, msg);
+ break;
+ default:
+ LOGP(DLLAPD, LOGL_NOTICE, "Unknown COMMON CHANNEL msg %d!\n",
+ msg_type);
+ msgb_free(msg);
+ return 0;
+ }
+
+ return rc;
+}
+
+/*! \brief Receive a RSLms \ref msgb from Layer 3 */
+int lapdm_rslms_recvmsg(struct msgb *msg, struct lapdm_channel *lc)
+{
+ struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+ int rc = 0;
+
+ if (msgb_l2len(msg) < sizeof(*rslh)) {
+ LOGP(DLLAPD, LOGL_ERROR, "Message too short RSL hdr!\n");
+ return -EINVAL;
+ }
+
+ switch (rslh->msg_discr & 0xfe) {
+ case ABIS_RSL_MDISC_RLL:
+ rc = rslms_rx_rll(msg, lc);
+ break;
+ case ABIS_RSL_MDISC_COM_CHAN:
+ rc = rslms_rx_com_chan(msg, lc);
+ break;
+ default:
+ LOGP(DLLAPD, LOGL_ERROR, "unknown RSLms message "
+ "discriminator 0x%02x", rslh->msg_discr);
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+/*! \brief Set the \ref lapdm_mode of a LAPDm entity */
+int lapdm_entity_set_mode(struct lapdm_entity *le, enum lapdm_mode mode)
+{
+ int i;
+ enum lapd_mode lm;
+
+ switch (mode) {
+ case LAPDM_MODE_MS:
+ lm = LAPD_MODE_USER;
+ break;
+ case LAPDM_MODE_BTS:
+ lm = LAPD_MODE_NETWORK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
+ lapd_set_mode(&le->datalink[i].dl, lm);
+ }
+
+ le->mode = mode;
+
+ return 0;
+}
+
+/*! \brief Set the \ref lapdm_mode of a LAPDm channel*/
+int lapdm_channel_set_mode(struct lapdm_channel *lc, enum lapdm_mode mode)
+{
+ int rc;
+
+ rc = lapdm_entity_set_mode(&lc->lapdm_dcch, mode);
+ if (rc < 0)
+ return rc;
+
+ return lapdm_entity_set_mode(&lc->lapdm_acch, mode);
+}
+
+/*! \brief Set the L1 callback and context of a LAPDm channel */
+void lapdm_channel_set_l1(struct lapdm_channel *lc, osmo_prim_cb cb, void *ctx)
+{
+ lc->lapdm_dcch.l1_prim_cb = cb;
+ lc->lapdm_acch.l1_prim_cb = cb;
+ lc->lapdm_dcch.l1_ctx = ctx;
+ lc->lapdm_acch.l1_ctx = ctx;
+}
+
+/*! \brief Set the L3 callback and context of a LAPDm channel */
+void lapdm_channel_set_l3(struct lapdm_channel *lc, lapdm_cb_t cb, void *ctx)
+{
+ lc->lapdm_dcch.l3_cb = cb;
+ lc->lapdm_acch.l3_cb = cb;
+ lc->lapdm_dcch.l3_ctx = ctx;
+ lc->lapdm_acch.l3_ctx = ctx;
+}
+
+/*! \brief Reset an entire LAPDm entity and all its datalinks */
+void lapdm_entity_reset(struct lapdm_entity *le)
+{
+ struct lapdm_datalink *dl;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
+ dl = &le->datalink[i];
+ lapd_dl_reset(&dl->dl);
+ }
+}
+
+/*! \brief Reset a LAPDm channel with all its entities */
+void lapdm_channel_reset(struct lapdm_channel *lc)
+{
+ lapdm_entity_reset(&lc->lapdm_dcch);
+ lapdm_entity_reset(&lc->lapdm_acch);
+}
+
+/*! \brief Set the flags of a LAPDm entity */
+void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags)
+{
+ le->flags = flags;
+}
+
+/*! \brief Set the flags of all LAPDm entities in a LAPDm channel */
+void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags)
+{
+ lapdm_entity_set_flags(&lc->lapdm_dcch, flags);
+ lapdm_entity_set_flags(&lc->lapdm_acch, flags);
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/gsm/milenage/aes-encblock.c b/src/shared/libosmocore/src/gsm/milenage/aes-encblock.c
new file mode 100644
index 00000000..8f35caa2
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/aes-encblock.c
@@ -0,0 +1,38 @@
+/*
+ * AES encrypt_block
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_encrypt_block - Perform one AES 128-bit block operation
+ * @key: Key for AES
+ * @in: Input data (16 bytes)
+ * @out: Output of the AES block operation (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+ void *ctx;
+ ctx = aes_encrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+ aes_encrypt(ctx, in, out);
+ aes_encrypt_deinit(ctx);
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/gsm/milenage/aes-internal-enc.c b/src/shared/libosmocore/src/gsm/milenage/aes-internal-enc.c
new file mode 100644
index 00000000..8726aa72
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/aes-internal-enc.c
@@ -0,0 +1,121 @@
+/*
+ * AES (Rijndael) cipher - encrypt
+ *
+ * Modifications to public domain implementation:
+ * - support only 128-bit keys
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ * cost of reduced throughput (quite small difference on Pentium 4,
+ * 10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes_i.h"
+
+static void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16])
+{
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+ const int Nr = 10;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(pt ) ^ rk[0];
+ s1 = GETU32(pt + 4) ^ rk[1];
+ s2 = GETU32(pt + 8) ^ rk[2];
+ s3 = GETU32(pt + 12) ^ rk[3];
+
+#define ROUND(i,d,s) \
+d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \
+d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \
+d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \
+d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3]
+
+#ifdef FULL_UNROLL
+
+ ROUND(1,t,s);
+ ROUND(2,s,t);
+ ROUND(3,t,s);
+ ROUND(4,s,t);
+ ROUND(5,t,s);
+ ROUND(6,s,t);
+ ROUND(7,t,s);
+ ROUND(8,s,t);
+ ROUND(9,t,s);
+
+ rk += Nr << 2;
+
+#else /* !FULL_UNROLL */
+
+ /* Nr - 1 full rounds: */
+ r = Nr >> 1;
+ for (;;) {
+ ROUND(1,t,s);
+ rk += 8;
+ if (--r == 0)
+ break;
+ ROUND(0,s,t);
+ }
+
+#endif /* ?FULL_UNROLL */
+
+#undef ROUND
+
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0];
+ PUTU32(ct , s0);
+ s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1];
+ PUTU32(ct + 4, s1);
+ s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2];
+ PUTU32(ct + 8, s2);
+ s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3];
+ PUTU32(ct + 12, s3);
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ u32 *rk;
+ if (len != 16)
+ return NULL;
+ rk = os_malloc(AES_PRIV_SIZE);
+ if (rk == NULL)
+ return NULL;
+ rijndaelKeySetupEnc(rk, key);
+ return rk;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ rijndaelEncrypt(ctx, plain, crypt);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ os_memset(ctx, 0, AES_PRIV_SIZE);
+ os_free(ctx);
+}
diff --git a/src/shared/libosmocore/src/gsm/milenage/aes-internal.c b/src/shared/libosmocore/src/gsm/milenage/aes-internal.c
new file mode 100644
index 00000000..41612202
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/aes-internal.c
@@ -0,0 +1,805 @@
+/*
+ * AES (Rijndael) cipher
+ *
+ * Modifications to public domain implementation:
+ * - support only 128-bit keys
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ * cost of reduced throughput (quite small difference on Pentium 4,
+ * 10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes_i.h"
+
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+const u32 Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Te1[256] = {
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+const u32 Te2[256] = {
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+const u32 Te3[256] = {
+
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+const u32 Te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+#endif /* AES_SMALL_TABLES */
+const u32 Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Td1[256] = {
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+const u32 Td2[256] = {
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+const u32 Td3[256] = {
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+const u32 Td4[256] = {
+ 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+ 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+ 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+ 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+ 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+ 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+ 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+ 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+ 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+ 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+ 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+ 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+ 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+ 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+ 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+ 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+ 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+ 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+ 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+ 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+ 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+ 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+ 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+ 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+ 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+ 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+ 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+ 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+ 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+ 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+ 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+ 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+ 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+ 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+ 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+ 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+ 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+ 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+ 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+ 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+ 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+ 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+ 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+ 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+ 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+ 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+ 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+ 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+ 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+ 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+ 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+ 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+ 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+ 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+ 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+ 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+ 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+ 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+ 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+ 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+ 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+ 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+ 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+ 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+const u32 rcon[] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#else /* AES_SMALL_TABLES */
+const u8 Td4s[256] = {
+ 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,
+ 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
+ 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,
+ 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+ 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,
+ 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,
+ 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,
+ 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,
+ 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,
+ 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,
+ 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+ 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,
+ 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+ 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,
+ 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,
+ 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,
+ 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,
+ 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,
+ 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,
+ 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,
+ 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,
+ 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+ 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,
+ 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,
+ 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,
+ 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,
+ 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,
+ 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,
+ 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+ 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,
+ 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,
+ 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU,
+};
+const u8 rcons[] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+ /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#endif /* AES_SMALL_TABLES */
+/**
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[])
+{
+ int i;
+ u32 temp;
+
+ rk[0] = GETU32(cipherKey );
+ rk[1] = GETU32(cipherKey + 4);
+ rk[2] = GETU32(cipherKey + 8);
+ rk[3] = GETU32(cipherKey + 12);
+ for (i = 0; i < 10; i++) {
+ temp = rk[3];
+ rk[4] = rk[0] ^
+ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^
+ RCON(i);
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ rk += 4;
+ }
+}
diff --git a/src/shared/libosmocore/src/gsm/milenage/aes.h b/src/shared/libosmocore/src/gsm/milenage/aes.h
new file mode 100644
index 00000000..ba384a9d
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/aes.h
@@ -0,0 +1,27 @@
+/*
+ * AES functions
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef AES_H
+#define AES_H
+
+#define AES_BLOCK_SIZE 16
+
+void * aes_encrypt_init(const u8 *key, size_t len);
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+void aes_encrypt_deinit(void *ctx);
+void * aes_decrypt_init(const u8 *key, size_t len);
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+void aes_decrypt_deinit(void *ctx);
+
+#endif /* AES_H */
diff --git a/src/shared/libosmocore/src/gsm/milenage/aes_i.h b/src/shared/libosmocore/src/gsm/milenage/aes_i.h
new file mode 100644
index 00000000..6b40bc78
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/aes_i.h
@@ -0,0 +1,122 @@
+/*
+ * AES (Rijndael) cipher
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef AES_I_H
+#define AES_I_H
+
+#include "aes.h"
+
+/* #define FULL_UNROLL */
+#define AES_SMALL_TABLES
+
+extern const u32 Te0[256];
+extern const u32 Te1[256];
+extern const u32 Te2[256];
+extern const u32 Te3[256];
+extern const u32 Te4[256];
+extern const u32 Td0[256];
+extern const u32 Td1[256];
+extern const u32 Td2[256];
+extern const u32 Td3[256];
+extern const u32 Td4[256];
+extern const u32 rcon[10];
+extern const u8 Td4s[256];
+extern const u8 rcons[10];
+
+#ifndef AES_SMALL_TABLES
+
+#define RCON(i) rcon[(i)]
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) Te1[((i) >> 16) & 0xff]
+#define TE2(i) Te2[((i) >> 8) & 0xff]
+#define TE3(i) Te3[(i) & 0xff]
+#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000)
+#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff)
+#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000)
+#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff)
+#define TE4(i) (Te4[(i)] & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) Td1[((i) >> 16) & 0xff]
+#define TD2(i) Td2[((i) >> 8) & 0xff]
+#define TD3(i) Td3[(i) & 0xff]
+#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000)
+#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff)
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) Td1[(i) & 0xff]
+#define TD2_(i) Td2[(i) & 0xff]
+#define TD3_(i) Td3[(i) & 0xff]
+
+#else /* AES_SMALL_TABLES */
+
+#define RCON(i) (rcons[(i)] << 24)
+
+static inline u32 rotr(u32 val, int bits)
+{
+ return (val >> bits) | (val << (32 - bits));
+}
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8)
+#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16)
+#define TE3(i) rotr(Te0[(i) & 0xff], 24)
+#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
+#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000)
+#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff)
+#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8)
+#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16)
+#define TD3(i) rotr(Td0[(i) & 0xff], 24)
+#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24)
+#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16)
+#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8)
+#define TD44(i) (Td4s[(i) & 0xff])
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) rotr(Td0[(i) & 0xff], 8)
+#define TD2_(i) rotr(Td0[(i) & 0xff], 16)
+#define TD3_(i) rotr(Td0[(i) & 0xff], 24)
+
+#endif /* AES_SMALL_TABLES */
+
+#ifdef _MSC_VER
+#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+#define GETU32(p) SWAP(*((u32 *)(p)))
+#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
+#else
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \
+((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+#define PUTU32(ct, st) { \
+(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \
+(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
+#endif
+
+#define AES_PRIV_SIZE (4 * 44)
+
+void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]);
+
+#endif /* AES_I_H */
diff --git a/src/shared/libosmocore/src/gsm/milenage/aes_wrap.h b/src/shared/libosmocore/src/gsm/milenage/aes_wrap.h
new file mode 100644
index 00000000..4b1c7b08
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/aes_wrap.h
@@ -0,0 +1,48 @@
+/*
+ * AES-based functions
+ *
+ * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * - One-Key CBC MAC (OMAC1) hash with AES-128
+ * - AES-128 CTR mode encryption
+ * - AES-128 EAX mode encryption/decryption
+ * - AES-128 CBC
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef AES_WRAP_H
+#define AES_WRAP_H
+
+int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher);
+int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain);
+int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len,
+ u8 *mac);
+int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len,
+ u8 *mac);
+int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+ u8 *data, size_t data_len);
+int __must_check aes_128_eax_encrypt(const u8 *key,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, u8 *tag);
+int __must_check aes_128_eax_decrypt(const u8 *key,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, const u8 *tag);
+int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len);
+int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len);
+
+#endif /* AES_WRAP_H */
diff --git a/src/shared/libosmocore/src/gsm/milenage/common.h b/src/shared/libosmocore/src/gsm/milenage/common.h
new file mode 100644
index 00000000..aaf82b97
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/common.h
@@ -0,0 +1,20 @@
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MSG_DEBUG
+#define wpa_hexdump(x, args...)
+#define wpa_hexdump_key(x, args...)
+#define wpa_printf(x, args...)
+
+#define os_memcpy(x, y, z) memcpy(x, y, z)
+#define os_memcmp(x, y, z) memcmp(x, y, z)
+#define os_memset(x, y, z) memset(x, y, z)
+#define os_malloc(x) malloc(x)
+#define os_free(x) free(x)
+
+typedef uint8_t u8;
+typedef uint32_t u32;
+
+#define __must_check
diff --git a/src/shared/libosmocore/src/gsm/milenage/crypto.h b/src/shared/libosmocore/src/gsm/milenage/crypto.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/crypto.h
diff --git a/src/shared/libosmocore/src/gsm/milenage/includes.h b/src/shared/libosmocore/src/gsm/milenage/includes.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/includes.h
diff --git a/src/shared/libosmocore/src/gsm/milenage/milenage.c b/src/shared/libosmocore/src/gsm/milenage/milenage.c
new file mode 100644
index 00000000..cc4e95c5
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/milenage.c
@@ -0,0 +1,329 @@
+/*
+ * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file implements an example authentication algorithm defined for 3GPP
+ * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow
+ * EAP-AKA to be tested properly with real USIM cards.
+ *
+ * This implementations assumes that the r1..r5 and c1..c5 constants defined in
+ * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00,
+ * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to
+ * be AES (Rijndael).
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes_wrap.h"
+#include "milenage.h"
+
+
+/**
+ * milenage_f1 - Milenage f1 and f1* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sqn: SQN = 48-bit sequence number
+ * @amf: AMF = 16-bit authentication management field
+ * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL
+ * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+ const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s)
+{
+ u8 tmp1[16], tmp2[16], tmp3[16];
+ int i;
+
+ /* tmp1 = TEMP = E_K(RAND XOR OP_C) */
+ for (i = 0; i < 16; i++)
+ tmp1[i] = _rand[i] ^ opc[i];
+ if (aes_128_encrypt_block(k, tmp1, tmp1))
+ return -1;
+
+ /* tmp2 = IN1 = SQN || AMF || SQN || AMF */
+ os_memcpy(tmp2, sqn, 6);
+ os_memcpy(tmp2 + 6, amf, 2);
+ os_memcpy(tmp2 + 8, tmp2, 8);
+
+ /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */
+
+ /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */
+ for (i = 0; i < 16; i++)
+ tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i];
+ /* XOR with TEMP = E_K(RAND XOR OP_C) */
+ for (i = 0; i < 16; i++)
+ tmp3[i] ^= tmp1[i];
+ /* XOR with c1 (= ..00, i.e., NOP) */
+
+ /* f1 || f1* = E_K(tmp3) XOR OP_c */
+ if (aes_128_encrypt_block(k, tmp3, tmp1))
+ return -1;
+ for (i = 0; i < 16; i++)
+ tmp1[i] ^= opc[i];
+ if (mac_a)
+ os_memcpy(mac_a, tmp1, 8); /* f1 */
+ if (mac_s)
+ os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */
+ return 0;
+}
+
+
+/**
+ * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL
+ * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+ u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar)
+{
+ u8 tmp1[16], tmp2[16], tmp3[16];
+ int i;
+
+ /* tmp2 = TEMP = E_K(RAND XOR OP_C) */
+ for (i = 0; i < 16; i++)
+ tmp1[i] = _rand[i] ^ opc[i];
+ if (aes_128_encrypt_block(k, tmp1, tmp2))
+ return -1;
+
+ /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */
+ /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */
+ /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */
+ /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */
+
+ /* f2 and f5 */
+ /* rotate by r2 (= 0, i.e., NOP) */
+ for (i = 0; i < 16; i++)
+ tmp1[i] = tmp2[i] ^ opc[i];
+ tmp1[15] ^= 1; /* XOR c2 (= ..01) */
+ /* f5 || f2 = E_K(tmp1) XOR OP_c */
+ if (aes_128_encrypt_block(k, tmp1, tmp3))
+ return -1;
+ for (i = 0; i < 16; i++)
+ tmp3[i] ^= opc[i];
+ if (res)
+ os_memcpy(res, tmp3 + 8, 8); /* f2 */
+ if (ak)
+ os_memcpy(ak, tmp3, 6); /* f5 */
+
+ /* f3 */
+ if (ck) {
+ /* rotate by r3 = 0x20 = 4 bytes */
+ for (i = 0; i < 16; i++)
+ tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i];
+ tmp1[15] ^= 2; /* XOR c3 (= ..02) */
+ if (aes_128_encrypt_block(k, tmp1, ck))
+ return -1;
+ for (i = 0; i < 16; i++)
+ ck[i] ^= opc[i];
+ }
+
+ /* f4 */
+ if (ik) {
+ /* rotate by r4 = 0x40 = 8 bytes */
+ for (i = 0; i < 16; i++)
+ tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i];
+ tmp1[15] ^= 4; /* XOR c4 (= ..04) */
+ if (aes_128_encrypt_block(k, tmp1, ik))
+ return -1;
+ for (i = 0; i < 16; i++)
+ ik[i] ^= opc[i];
+ }
+
+ /* f5* */
+ if (akstar) {
+ /* rotate by r5 = 0x60 = 12 bytes */
+ for (i = 0; i < 16; i++)
+ tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i];
+ tmp1[15] ^= 8; /* XOR c5 (= ..08) */
+ if (aes_128_encrypt_block(k, tmp1, tmp1))
+ return -1;
+ for (i = 0; i < 6; i++)
+ akstar[i] = tmp1[i] ^ opc[i];
+ }
+
+ return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @amf: AMF = 16-bit authentication management field
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: Buffer for AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Max length for res; set to used length or 0 on failure
+ */
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+ const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+ u8 *ck, u8 *res, size_t *res_len)
+{
+ int i;
+ u8 mac_a[8], ak[6];
+
+ if (*res_len < 8) {
+ *res_len = 0;
+ return;
+ }
+ if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) ||
+ milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) {
+ *res_len = 0;
+ return;
+ }
+ *res_len = 8;
+
+ /* AUTN = (SQN ^ AK) || AMF || MAC */
+ for (i = 0; i < 6; i++)
+ autn[i] = sqn[i] ^ ak[i];
+ os_memcpy(autn + 6, amf, 2);
+ os_memcpy(autn + 8, mac_a, 8);
+}
+
+
+/**
+ * milenage_auts - Milenage AUTS validation
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @auts: AUTS = 112-bit authentication token from client
+ * @sqn: Buffer for SQN = 48-bit sequence number
+ * Returns: 0 = success (sqn filled), -1 on failure
+ */
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+ u8 *sqn)
+{
+ u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+ u8 ak[6], mac_s[8];
+ int i;
+
+ if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+ return -1;
+ for (i = 0; i < 6; i++)
+ sqn[i] = auts[i] ^ ak[i];
+ if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) ||
+ memcmp(mac_s, auts + 6, 8) != 0)
+ return -1;
+ return 0;
+}
+
+
+/**
+ * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sres: Buffer for SRES = 32-bit SRES
+ * @kc: Buffer for Kc = 64-bit Kc
+ * Returns: 0 on success, -1 on failure
+ */
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc)
+{
+ u8 res[8], ck[16], ik[16];
+ int i;
+
+ if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL))
+ return -1;
+
+ for (i = 0; i < 8; i++)
+ kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
+
+#ifdef GSM_MILENAGE_ALT_SRES
+ os_memcpy(sres, res, 4);
+#else /* GSM_MILENAGE_ALT_SRES */
+ for (i = 0; i < 4; i++)
+ sres[i] = res[i] ^ res[i + 4];
+#endif /* GSM_MILENAGE_ALT_SRES */
+ return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Variable that will be set to RES length
+ * @auts: 112-bit buffer for AUTS
+ * Returns: 0 on success, -1 on failure, or -2 on synchronization failure
+ */
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+ const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+ u8 *auts)
+{
+ int i;
+ u8 mac_a[8], ak[6], rx_sqn[6];
+ const u8 *amf;
+
+ wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16);
+ wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16);
+
+ if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
+ return -1;
+
+ *res_len = 8;
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len);
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16);
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16);
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6);
+
+ /* AUTN = (SQN ^ AK) || AMF || MAC */
+ for (i = 0; i < 6; i++)
+ rx_sqn[i] = autn[i] ^ ak[i];
+ wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6);
+
+ if (os_memcmp(rx_sqn, sqn, 6) <= 0) {
+ u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+ if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6);
+ for (i = 0; i < 6; i++)
+ auts[i] = sqn[i] ^ ak[i];
+ if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14);
+ return -2;
+ }
+
+ amf = autn + 6;
+ wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2);
+ if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL))
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8);
+
+ if (os_memcmp(mac_a, autn + 8, 8) != 0) {
+ wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch");
+ wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A",
+ autn + 8, 8);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/gsm/milenage/milenage.h b/src/shared/libosmocore/src/gsm/milenage/milenage.h
new file mode 100644
index 00000000..d5054d6d
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/milenage/milenage.h
@@ -0,0 +1,33 @@
+/*
+ * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef MILENAGE_H
+#define MILENAGE_H
+
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+ const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+ u8 *ck, u8 *res, size_t *res_len);
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+ u8 *sqn);
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres,
+ u8 *kc);
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+ const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+ u8 *auts);
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+ const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s);
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+ u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar);
+
+#endif /* MILENAGE_H */
diff --git a/src/shared/libosmocore/src/gsm/rsl.c b/src/shared/libosmocore/src/gsm/rsl.c
new file mode 100644
index 00000000..db276a2a
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/rsl.c
@@ -0,0 +1,507 @@
+/* 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 <stdio.h>
+#include <errno.h>
+
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/rsl.h>
+
+/*! \addtogroup rsl
+ * @{
+ */
+
+/*! \file rsl.c */
+
+/*! \brief Size for RSL \ref msgb_alloc */
+#define RSL_ALLOC_SIZE 200
+/*! \brief Headroom size for RSL \ref msgb_alloc */
+#define RSL_ALLOC_HEADROOM 56
+
+/*! \brief Initialize a RSL RLL header */
+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;
+}
+
+/*! \brief Initialize a RSL Common Channel header */
+void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type)
+{
+ ch->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN;
+ ch->c.msg_type = msg_type;
+ ch->ie_chan = RSL_IE_CHAN_NR;
+}
+
+/* \brief TLV parser definition for RSL */
+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 },
+ },
+};
+
+/*! \brief 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;
+}
+
+/*! \brief Decode RSL channel number
+ * \param[in] chan_nr Channel Number
+ * \param[out] type Channel Type
+ * \param[out] subch Sub-channel Number
+ * \param[out] timeslot Timeslot
+ */
+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;
+}
+
+/*! \brief Get human-readable string for RSL channel number */
+const char *rsl_chan_nr_str(uint8_t chan_nr)
+{
+ static char str[20];
+ int ts = chan_nr & 7;
+ uint8_t cbits = chan_nr >> 3;
+
+ if (cbits == 0x01)
+ sprintf(str, "TCH/F on TS%d", ts);
+ else if ((cbits & 0x1e) == 0x02)
+ sprintf(str, "TCH/H(%u) on TS%d", cbits & 0x01, ts);
+ else if ((cbits & 0x1c) == 0x04)
+ sprintf(str, "SDCCH/4(%u) on TS%d", cbits & 0x03, ts);
+ else if ((cbits & 0x18) == 0x08)
+ sprintf(str, "SDCCH/8(%u) on TS%d", cbits & 0x07, ts);
+ else if (cbits == 0x10)
+ sprintf(str, "BCCH on TS%d", ts);
+ else if (cbits == 0x11)
+ sprintf(str, "RACH on TS%d", ts);
+ else if (cbits == 0x12)
+ sprintf(str, "PCH/AGCH on TS%d", ts);
+ else
+ sprintf(str, "UNKNOWN on TS%d", ts);
+
+ return str;
+}
+
+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 }
+};
+
+/*! \brief Get human-readable name for RSL Error */
+const char *rsl_err_name(uint8_t err)
+{
+ return get_value_string(rsl_err_vals, err);
+}
+
+/* Names for Radio Link Layer Management */
+static const struct value_string rsl_msgt_names[] = {
+ { RSL_MT_DATA_REQ, "DATA_REQ" },
+ { RSL_MT_DATA_IND, "DATA_IND" },
+ { RSL_MT_ERROR_IND, "ERROR_IND" },
+ { RSL_MT_EST_REQ, "EST_REQ" },
+ { RSL_MT_EST_CONF, "EST_CONF" },
+ { RSL_MT_EST_IND, "EST_IND" },
+ { RSL_MT_REL_REQ, "REL_REQ" },
+ { RSL_MT_REL_CONF, "REL_CONF" },
+ { RSL_MT_REL_IND, "REL_IND" },
+ { RSL_MT_UNIT_DATA_REQ, "UNIT_DATA_REQ" },
+ { RSL_MT_UNIT_DATA_IND, "UNIT_DATA_IND" },
+ { RSL_MT_SUSP_REQ, "SUSP_REQ" },
+ { RSL_MT_SUSP_CONF, "SUSP_CONF" },
+ { RSL_MT_RES_REQ, "RES_REQ" },
+ { RSL_MT_RECON_REQ, "RECON_REQ" },
+
+ { RSL_MT_BCCH_INFO, "BCCH_INFO" },
+ { RSL_MT_CCCH_LOAD_IND, "CCCH_LOAD_IND" },
+ { RSL_MT_CHAN_RQD, "CHAN_RQD" },
+ { RSL_MT_DELETE_IND, "DELETE_IND" },
+ { RSL_MT_PAGING_CMD, "PAGING_CMD" },
+ { RSL_MT_IMMEDIATE_ASSIGN_CMD, "IMM_ASS_CMD" },
+ { RSL_MT_SMS_BC_REQ, "SMS_BC_REQ" },
+ { RSL_MT_CHAN_CONF, "CHAN_CONF" },
+
+ { RSL_MT_RF_RES_IND, "RF_RES_IND" },
+ { RSL_MT_SACCH_FILL, "SACCH_FILL" },
+ { RSL_MT_OVERLOAD, "OVERLOAD" },
+ { RSL_MT_ERROR_REPORT, "ERROR_REPORT" },
+ { RSL_MT_SMS_BC_CMD, "SMS_BC_CMD" },
+ { RSL_MT_CBCH_LOAD_IND, "CBCH_LOAD_IND" },
+ { RSL_MT_NOT_CMD, "NOTIFY_CMD" },
+
+ { RSL_MT_CHAN_ACTIV, "CHAN_ACTIV" },
+ { RSL_MT_CHAN_ACTIV_ACK, "CHAN_ACTIV_ACK" },
+ { RSL_MT_CHAN_ACTIV_NACK, "CHAN_ACTIV_NACK" },
+ { RSL_MT_CONN_FAIL, "CONN_FAIL" },
+ { RSL_MT_DEACTIVATE_SACCH, "DEACTIVATE_SACCH" },
+ { RSL_MT_ENCR_CMD, "ENCR_CMD" },
+ { RSL_MT_HANDO_DET, "HANDOVER_DETECT" },
+ { RSL_MT_MEAS_RES, "MEAS_RES" },
+ { RSL_MT_MODE_MODIFY_REQ, "MODE_MODIFY_REQ" },
+ { RSL_MT_MODE_MODIFY_ACK, "MODE_MODIFY_ACK" },
+ { RSL_MT_MODE_MODIFY_NACK, "MODE_MODIFY_NACK" },
+ { RSL_MT_PHY_CONTEXT_REQ, "PHY_CONTEXT_REQ" },
+ { RSL_MT_PHY_CONTEXT_CONF, "PHY_CONTEXT_CONF" },
+ { RSL_MT_RF_CHAN_REL, "RF_CHAN_REL" },
+ { RSL_MT_MS_POWER_CONTROL, "MS_POWER_CONTROL" },
+ { RSL_MT_BS_POWER_CONTROL, "BS_POWER_CONTROL" },
+ { RSL_MT_PREPROC_CONFIG, "PREPROC_CONFIG" },
+ { RSL_MT_PREPROC_MEAS_RES, "PREPROC_MEAS_RES" },
+ { RSL_MT_RF_CHAN_REL_ACK, "RF_CHAN_REL_ACK" },
+ { RSL_MT_SACCH_INFO_MODIFY, "SACCH_INFO_MODIFY" },
+ { RSL_MT_TALKER_DET, "TALKER_DETECT" },
+ { RSL_MT_LISTENER_DET, "LISTENER_DETECT" },
+ { RSL_MT_REMOTE_CODEC_CONF_REP, "REM_CODEC_CONF_REP" },
+ { RSL_MT_RTD_REP, "RTD_REQ" },
+ { RSL_MT_PRE_HANDO_NOTIF, "HANDO_NOTIF" },
+ { RSL_MT_MR_CODEC_MOD_REQ, "CODEC_MOD_REQ" },
+ { RSL_MT_MR_CODEC_MOD_ACK, "CODEC_MOD_ACK" },
+ { RSL_MT_MR_CODEC_MOD_NACK, "CODEC_MOD_NACK" },
+ { RSL_MT_MR_CODEC_MOD_PER, "CODEC_MODE_PER" },
+ { RSL_MT_TFO_REP, "TFO_REP" },
+ { RSL_MT_TFO_MOD_REQ, "TFO_MOD_REQ" },
+ { RSL_MT_LOCATION_INFO, "LOCATION_INFO" },
+ { 0, NULL }
+};
+
+
+/*! \brief Get human-readable string for RSL Message Type */
+const char *rsl_msg_name(uint8_t msg_type)
+{
+ return get_value_string(rsl_msgt_names, msg_type);
+}
+
+/*! \brief ip.access specific */
+static const struct value_string rsl_ipac_msgt_names[] = {
+ { RSL_MT_IPAC_PDCH_ACT, "IPAC_PDCH_ACT" },
+ { RSL_MT_IPAC_PDCH_ACT_ACK, "IPAC_PDCH_ACT_ACK" },
+ { RSL_MT_IPAC_PDCH_ACT_NACK, "IPAC_PDCH_ACT_NACK" },
+ { RSL_MT_IPAC_PDCH_DEACT, "IPAC_PDCH_DEACT" },
+ { RSL_MT_IPAC_PDCH_DEACT_ACK, "IPAC_PDCH_DEACT_ACK" },
+ { RSL_MT_IPAC_PDCH_DEACT_NACK, "IPAC_PDCH_DEACT_NACK" },
+ { RSL_MT_IPAC_CONNECT_MUX, "IPAC_CONNECT_MUX" },
+ { RSL_MT_IPAC_CONNECT_MUX_ACK, "IPAC_CONNECT_MUX_ACK" },
+ { RSL_MT_IPAC_CONNECT_MUX_NACK, "IPAC_CONNECT_MUX_NACK" },
+ { RSL_MT_IPAC_BIND_MUX, "IPAC_BIND_MUX" },
+ { RSL_MT_IPAC_BIND_MUX_ACK, "IPAC_BIND_MUX_ACK" },
+ { RSL_MT_IPAC_BIND_MUX_NACK, "IPAC_BIND_MUX_NACK" },
+ { RSL_MT_IPAC_DISC_MUX, "IPAC_DISC_MUX" },
+ { RSL_MT_IPAC_DISC_MUX_ACK, "IPAC_DISC_MUX_ACK" },
+ { RSL_MT_IPAC_DISC_MUX_NACK, "IPAC_DISC_MUX_NACK" },
+ { RSL_MT_IPAC_CRCX, "IPAC_CRCX" },
+ { RSL_MT_IPAC_CRCX_ACK, "IPAC_CRCX_ACK" },
+ { RSL_MT_IPAC_CRCX_NACK, "IPAC_CRCX_NACK" },
+ { RSL_MT_IPAC_MDCX, "IPAC_MDCX" },
+ { RSL_MT_IPAC_MDCX_ACK, "IPAC_MDCX_ACK" },
+ { RSL_MT_IPAC_MDCX_NACK, "IPAC_MDCX_NACK" },
+ { RSL_MT_IPAC_DLCX_IND, "IPAC_DLCX_IND" },
+ { RSL_MT_IPAC_DLCX, "IPAC_DLCX" },
+ { RSL_MT_IPAC_DLCX_ACK, "IPAC_DLCX_ACK" },
+ { RSL_MT_IPAC_DLCX_NACK, "IPAC_DLCX_NACK" },
+ { 0, NULL }
+};
+
+/*! \brief Get human-readable name of ip.access RSL msg type */
+const char *rsl_ipac_msg_name(uint8_t msg_type)
+{
+ return get_value_string(rsl_ipac_msgt_names, msg_type);
+}
+
+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 },
+};
+
+/*! \brief Get human-readable string for RLM cause */
+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;
+ }
+}
+
+/*! \brief Push a RSL RLL header onto an existing msgb */
+void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
+ uint8_t link_id, int transparent)
+{
+ struct abis_rsl_rll_hdr *rh;
+
+ 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;
+}
+
+/*! \brief 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);
+
+ /* 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 */
+ rsl_rll_push_hdr(msg, msg_type, chan_nr, link_id, transparent);
+}
+
+/*! \brief Create msgb with RSL RLL header */
+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/src/shared/libosmocore/src/gsm/rxlev_stat.c b/src/shared/libosmocore/src/gsm/rxlev_stat.c
new file mode 100644
index 00000000..d226861e
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/rxlev_stat.c
@@ -0,0 +1,82 @@
+/* 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 <osmocom/core/bitvec.h>
+#include <osmocom/gsm/rxlev_stat.h>
+
+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 = (uint8_t *) 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/src/shared/libosmocore/src/gsm/sysinfo.c b/src/shared/libosmocore/src/gsm/sysinfo.c
new file mode 100644
index 00000000..1408f6bf
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/sysinfo.c
@@ -0,0 +1,136 @@
+/* GSM 04.08 System Information (SI) encoding and decoding
+ * 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>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/sysinfo.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+/* verify the sizes of the system information type structs */
+
+/* rest octets are not part of the struct */
+osmo_static_assert(sizeof(struct gsm48_system_information_type_header) == 3, _si_header_size);
+osmo_static_assert(sizeof(struct gsm48_rach_control) == 3, _si_rach_control);
+osmo_static_assert(sizeof(struct gsm48_system_information_type_1) == 22, _si1_size);
+osmo_static_assert(sizeof(struct gsm48_system_information_type_2) == 23, _si2_size);
+osmo_static_assert(sizeof(struct gsm48_system_information_type_3) == 19, _si3_size);
+osmo_static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_size);
+
+/* bs11 forgot the l2 len, 0-6 rest octets */
+osmo_static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size);
+osmo_static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size);
+
+osmo_static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size);
+
+static const uint8_t sitype2rsl[_MAX_SYSINFO_TYPE] = {
+ [SYSINFO_TYPE_1] = RSL_SYSTEM_INFO_1,
+ [SYSINFO_TYPE_2] = RSL_SYSTEM_INFO_2,
+ [SYSINFO_TYPE_3] = RSL_SYSTEM_INFO_3,
+ [SYSINFO_TYPE_4] = RSL_SYSTEM_INFO_4,
+ [SYSINFO_TYPE_5] = RSL_SYSTEM_INFO_5,
+ [SYSINFO_TYPE_6] = RSL_SYSTEM_INFO_6,
+ [SYSINFO_TYPE_7] = RSL_SYSTEM_INFO_7,
+ [SYSINFO_TYPE_8] = RSL_SYSTEM_INFO_8,
+ [SYSINFO_TYPE_9] = RSL_SYSTEM_INFO_9,
+ [SYSINFO_TYPE_10] = RSL_SYSTEM_INFO_10,
+ [SYSINFO_TYPE_13] = RSL_SYSTEM_INFO_13,
+ [SYSINFO_TYPE_16] = RSL_SYSTEM_INFO_16,
+ [SYSINFO_TYPE_17] = RSL_SYSTEM_INFO_17,
+ [SYSINFO_TYPE_18] = RSL_SYSTEM_INFO_18,
+ [SYSINFO_TYPE_19] = RSL_SYSTEM_INFO_19,
+ [SYSINFO_TYPE_20] = RSL_SYSTEM_INFO_20,
+ [SYSINFO_TYPE_2bis] = RSL_SYSTEM_INFO_2bis,
+ [SYSINFO_TYPE_2ter] = RSL_SYSTEM_INFO_2ter,
+ [SYSINFO_TYPE_2quater] = RSL_SYSTEM_INFO_2quater,
+ [SYSINFO_TYPE_5bis] = RSL_SYSTEM_INFO_5bis,
+ [SYSINFO_TYPE_5ter] = RSL_SYSTEM_INFO_5ter,
+ [SYSINFO_TYPE_EMO] = RSL_EXT_MEAS_ORDER,
+ [SYSINFO_TYPE_MEAS_INFO]= RSL_MEAS_INFO,
+};
+
+static const uint8_t rsl2sitype[256] = {
+ [RSL_SYSTEM_INFO_1] = SYSINFO_TYPE_1,
+ [RSL_SYSTEM_INFO_2] = SYSINFO_TYPE_2,
+ [RSL_SYSTEM_INFO_3] = SYSINFO_TYPE_3,
+ [RSL_SYSTEM_INFO_4] = SYSINFO_TYPE_4,
+ [RSL_SYSTEM_INFO_5] = SYSINFO_TYPE_5,
+ [RSL_SYSTEM_INFO_6] = SYSINFO_TYPE_6,
+ [RSL_SYSTEM_INFO_7] = SYSINFO_TYPE_7,
+ [RSL_SYSTEM_INFO_8] = SYSINFO_TYPE_8,
+ [RSL_SYSTEM_INFO_9] = SYSINFO_TYPE_9,
+ [RSL_SYSTEM_INFO_10] = SYSINFO_TYPE_10,
+ [RSL_SYSTEM_INFO_13] = SYSINFO_TYPE_13,
+ [RSL_SYSTEM_INFO_16] = SYSINFO_TYPE_16,
+ [RSL_SYSTEM_INFO_17] = SYSINFO_TYPE_17,
+ [RSL_SYSTEM_INFO_18] = SYSINFO_TYPE_18,
+ [RSL_SYSTEM_INFO_19] = SYSINFO_TYPE_19,
+ [RSL_SYSTEM_INFO_20] = SYSINFO_TYPE_20,
+ [RSL_SYSTEM_INFO_2bis] = SYSINFO_TYPE_2bis,
+ [RSL_SYSTEM_INFO_2ter] = SYSINFO_TYPE_2ter,
+ [RSL_SYSTEM_INFO_2quater] = SYSINFO_TYPE_2quater,
+ [RSL_SYSTEM_INFO_5bis] = SYSINFO_TYPE_5bis,
+ [RSL_SYSTEM_INFO_5ter] = SYSINFO_TYPE_5ter,
+ [RSL_EXT_MEAS_ORDER] = SYSINFO_TYPE_EMO,
+ [RSL_MEAS_INFO] = SYSINFO_TYPE_MEAS_INFO,
+};
+
+const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE] = {
+ { SYSINFO_TYPE_1, "1" },
+ { SYSINFO_TYPE_2, "2" },
+ { SYSINFO_TYPE_3, "3" },
+ { SYSINFO_TYPE_4, "4" },
+ { SYSINFO_TYPE_5, "5" },
+ { SYSINFO_TYPE_6, "6" },
+ { SYSINFO_TYPE_7, "7" },
+ { SYSINFO_TYPE_8, "8" },
+ { SYSINFO_TYPE_9, "9" },
+ { SYSINFO_TYPE_10, "10" },
+ { SYSINFO_TYPE_13, "13" },
+ { SYSINFO_TYPE_16, "16" },
+ { SYSINFO_TYPE_17, "17" },
+ { SYSINFO_TYPE_18, "18" },
+ { SYSINFO_TYPE_19, "19" },
+ { SYSINFO_TYPE_20, "20" },
+ { SYSINFO_TYPE_2bis, "2bis" },
+ { SYSINFO_TYPE_2ter, "2ter" },
+ { SYSINFO_TYPE_2quater, "2quater" },
+ { SYSINFO_TYPE_5bis, "5bis" },
+ { SYSINFO_TYPE_5ter, "5ter" },
+ { SYSINFO_TYPE_EMO, "EMO" },
+ { SYSINFO_TYPE_MEAS_INFO, "MI" },
+ { 0, NULL }
+};
+
+uint8_t osmo_sitype2rsl(enum osmo_sysinfo_type si_type)
+{
+ return sitype2rsl[si_type];
+}
+
+enum osmo_sysinfo_type osmo_rsl2sitype(uint8_t rsl_si)
+{
+ return rsl2sitype[rsl_si];
+}
diff --git a/src/shared/libosmocore/src/gsm/tlv_parser.c b/src/shared/libosmocore/src/gsm/tlv_parser.c
new file mode 100644
index 00000000..c832d56f
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm/tlv_parser.c
@@ -0,0 +1,189 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/tlv.h>
+
+/*! \addtogroup tlv
+ * @{
+ */
+/*! \file tlv.c */
+
+struct tlv_definition tvlv_att_def;
+
+/*! \brief Dump pasred TLV structure to stdout */
+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;
+}
+
+/*! \brief Parse a single TLV encoded IE
+ * \param[out] o_tag the tag of the IE that was found
+ * \param[out] o_len length of the IE that was found
+ * \param[out] o_val pointer to the data of the IE that was found
+ * \param[in] def structure defining the valid TLV tags / configurations
+ * \param[in] buf the input data buffer to be parsed
+ * \param[in] buf_len length of the input data buffer
+ * \returns number of bytes consumed by the TLV entry / IE parsed
+ */
+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;
+
+ /* single octet TV IE */
+ if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
+ *o_tag = tag & 0xf0;
+ *o_val = buf;
+ *o_len = 1;
+ return 1;
+ }
+
+ /* 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;
+}
+
+/*! \brief Parse an entire buffer of TLV encoded Information Eleemnts
+ * \param[out] dec caller-allocated pointer to \ref tlv_parsed
+ * \param[in] def structure defining the valid TLV tags / configurations
+ * \param[in] buf the input data buffer to be parsed
+ * \param[in] buf_len length of the input data buffer
+ * \param[in] lv_tag an initial LV tag at the start of the buffer
+ * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
+ * \returns number of bytes consumed by the TLV entry / IE parsed
+ */
+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;
+}
+
+/*! \brief 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/src/shared/libosmocore/src/gsmtap_util.c b/src/shared/libosmocore/src/gsmtap_util.c
new file mode 100644
index 00000000..ce722da9
--- /dev/null
+++ b/src/shared/libosmocore/src/gsmtap_util.c
@@ -0,0 +1,362 @@
+/* GSMTAP support code in libmsomcore */
+/*
+ * (C) 2010-2011 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 "../config.h"
+
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/rsl.h>
+
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+/*! \addtogroup gsmtap
+ * @{
+ */
+/*! \file gsmtap_util.c */
+
+
+/*! \brief convert RSL channel number to GSMTAP channel type
+ * \param[in] rsl_cantype RSL channel type
+ * \param[in] link_id RSL link identifier
+ * \returns GSMTAP channel type
+ */
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
+{
+ uint8_t ret = GSMTAP_CHANNEL_UNKNOWN;
+
+ switch (rsl_chantype) {
+ case RSL_CHAN_Bm_ACCHs:
+ ret = GSMTAP_CHANNEL_TCH_F;
+ break;
+ case RSL_CHAN_Lm_ACCHs:
+ ret = GSMTAP_CHANNEL_TCH_H;
+ break;
+ case RSL_CHAN_SDCCH4_ACCH:
+ ret = GSMTAP_CHANNEL_SDCCH4;
+ break;
+ case RSL_CHAN_SDCCH8_ACCH:
+ ret = GSMTAP_CHANNEL_SDCCH8;
+ break;
+ case RSL_CHAN_BCCH:
+ ret = GSMTAP_CHANNEL_BCCH;
+ break;
+ case RSL_CHAN_RACH:
+ ret = GSMTAP_CHANNEL_RACH;
+ break;
+ case RSL_CHAN_PCH_AGCH:
+ /* it could also be AGCH... */
+ ret = GSMTAP_CHANNEL_PCH;
+ break;
+ }
+
+ if (link_id & 0x40)
+ ret |= GSMTAP_CHANNEL_ACCH;
+
+ return ret;
+}
+
+/*! \brief create an arbitrary type GSMTAP message
+ * \param[in] type The GSMTAP_TYPE_xxx constant of the message to create
+ * \param[in] arfcn GSM ARFCN (Channel Number)
+ * \param[in] ts GSM time slot
+ * \param[in] chan_type Channel Type
+ * \param[in] ss Sub-slot
+ * \param[in] fn GSM Frame Number
+ * \param[in] signal_dbm Signal Strength (dBm)
+ * \param[in] snr Signal/Noise Ratio (SNR)
+ * \param[in] data Pointer to data buffer
+ * \param[in] len Length of \ref data
+ *
+ * This function will allocate a new msgb and fill it with a GSMTAP
+ * header containing the information
+ */
+struct msgb *gsmtap_makemsg_ex(uint8_t type, uint16_t arfcn, uint8_t ts, uint8_t chan_type,
+ uint8_t ss, uint32_t fn, int8_t signal_dbm,
+ uint8_t snr, const uint8_t *data, unsigned int len)
+{
+ struct msgb *msg;
+ struct gsmtap_hdr *gh;
+ uint8_t *dst;
+
+ msg = msgb_alloc(sizeof(*gh) + len, "gsmtap_tx");
+ if (!msg)
+ return NULL;
+
+ gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->version = GSMTAP_VERSION;
+ gh->hdr_len = sizeof(*gh)/4;
+ gh->type = type;
+ gh->timeslot = ts;
+ gh->sub_slot = ss;
+ gh->arfcn = htons(arfcn);
+ gh->snr_db = snr;
+ gh->signal_dbm = signal_dbm;
+ gh->frame_number = htonl(fn);
+ gh->sub_type = chan_type;
+ gh->antenna_nr = 0;
+
+ dst = msgb_put(msg, len);
+ memcpy(dst, data, len);
+
+ return msg;
+}
+
+/*! \brief create L1/L2 data and put it into GSMTAP
+ * \param[in] arfcn GSM ARFCN (Channel Number)
+ * \param[in] ts GSM time slot
+ * \param[in] chan_type Channel Type
+ * \param[in] ss Sub-slot
+ * \param[in] fn GSM Frame Number
+ * \param[in] signal_dbm Signal Strength (dBm)
+ * \param[in] snr Signal/Noise Ratio (SNR)
+ * \param[in] data Pointer to data buffer
+ * \param[in] len Length of \ref data
+ *
+ * This function will allocate a new msgb and fill it with a GSMTAP
+ * header containing the information
+ */
+struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
+ uint8_t ss, uint32_t fn, int8_t signal_dbm,
+ uint8_t snr, const uint8_t *data, unsigned int len)
+{
+ return gsmtap_makemsg_ex(GSMTAP_TYPE_UM, arfcn, ts, chan_type,
+ ss, fn, signal_dbm, snr, data, len);
+}
+
+#ifdef HAVE_SYS_SOCKET_H
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/*! \brief Create a new (sending) GSMTAP source socket
+ * \param[in] host host name or IP address in string format
+ * \param[in] port UDP port number in host byte order
+ *
+ * Opens a GSMTAP source (sending) socket, conncet it to host/port and
+ * return resulting fd. If \a host is NULL, the destination address
+ * will be localhost. If \a port is 0, the default \ref
+ * GSMTAP_UDP_PORT will be used.
+ * */
+int gsmtap_source_init_fd(const char *host, uint16_t port)
+{
+ if (port == 0)
+ port = GSMTAP_UDP_PORT;
+ if (host == NULL)
+ host = "localhost";
+
+ return osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port,
+ OSMO_SOCK_F_CONNECT);
+}
+
+/*! \brief Add a local sink to an existing GSMTAP source and return fd */
+int gsmtap_source_add_sink_fd(int gsmtap_fd)
+{
+ struct sockaddr_storage ss;
+ socklen_t ss_len = sizeof(ss);
+ int rc;
+
+ rc = getpeername(gsmtap_fd, (struct sockaddr *)&ss, &ss_len);
+ if (rc < 0)
+ return rc;
+
+ if (osmo_sockaddr_is_local((struct sockaddr *)&ss, ss_len) == 1) {
+ rc = osmo_sock_init_sa((struct sockaddr *)&ss, SOCK_DGRAM,
+ IPPROTO_UDP, OSMO_SOCK_F_BIND);
+ if (rc >= 0)
+ return rc;
+ }
+
+ return -ENODEV;
+}
+
+/*! \brief Send a \ref msgb through a GSMTAP source
+ * \param[in] gti GSMTAP instance
+ * \param[in] msgb message buffer
+ */
+int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg)
+{
+ if (!gti)
+ return -ENODEV;
+
+ if (gti->ofd_wq_mode)
+ return osmo_wqueue_enqueue(&gti->wq, msg);
+ else {
+ /* try immediate send and return error if any */
+ int rc;
+
+ rc = write(gsmtap_inst_fd(gti), msg->data, msg->len);
+ if (rc <= 0) {
+ return rc;
+ } else if (rc >= msg->len) {
+ msgb_free(msg);
+ return 0;
+ } else {
+ /* short write */
+ return -EIO;
+ }
+ }
+}
+
+/*! \brief send an arbitrary type through GSMTAP.
+ * See \ref gsmtap_makemsg_ex for arguments
+ */
+int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, uint16_t arfcn, uint8_t ts,
+ uint8_t chan_type, uint8_t ss, uint32_t fn,
+ int8_t signal_dbm, uint8_t snr, const uint8_t *data,
+ unsigned int len)
+{
+ struct msgb *msg;
+
+ if (!gti)
+ return -ENODEV;
+
+ msg = gsmtap_makemsg_ex(type, arfcn, ts, chan_type, ss, fn, signal_dbm,
+ snr, data, len);
+ if (!msg)
+ return -ENOMEM;
+
+ return gsmtap_sendmsg(gti, msg);
+}
+
+/*! \brief send a message from L1/L2 through GSMTAP.
+ * See \ref gsmtap_makemsg for arguments
+ */
+int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts,
+ uint8_t chan_type, uint8_t ss, uint32_t fn,
+ int8_t signal_dbm, uint8_t snr, const uint8_t *data,
+ unsigned int len)
+{
+ return gsmtap_send_ex(gti, GSMTAP_TYPE_UM, arfcn, ts, chan_type, ss, fn,
+ signal_dbm, snr, data, len);
+}
+
+/* Callback from select layer if we can write to the socket */
+static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg)
+{
+ int rc;
+
+ rc = write(ofd->fd, msg->data, msg->len);
+ if (rc < 0) {
+ perror("writing msgb to gsmtap fd");
+ return rc;
+ }
+ if (rc != msg->len) {
+ perror("short write to gsmtap fd");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* Callback from select layer if we can read from the sink socket */
+static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
+{
+ int rc;
+ uint8_t buf[4096];
+
+ if (!(flags & BSC_FD_READ))
+ return 0;
+
+ rc = read(fd->fd, buf, sizeof(buf));
+ if (rc < 0) {
+ perror("reading from gsmtap sink fd");
+ return rc;
+ }
+ /* simply discard any data arriving on the socket */
+
+ return 0;
+}
+
+/*! \brief Add a local sink to an existing GSMTAP source instance */
+int gsmtap_source_add_sink(struct gsmtap_inst *gti)
+{
+ int fd;
+
+ fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti));
+ if (fd < 0)
+ return fd;
+
+ if (gti->ofd_wq_mode) {
+ struct osmo_fd *sink_ofd;
+
+ sink_ofd = &gti->sink_ofd;
+ sink_ofd->fd = fd;
+ sink_ofd->when = BSC_FD_READ;
+ sink_ofd->cb = gsmtap_sink_fd_cb;
+
+ osmo_fd_register(sink_ofd);
+ }
+
+ return fd;
+}
+
+
+/*! \brief Open GSMTAP source socket, connect and register osmo_fd
+ * \param[in] host host name or IP address in string format
+ * \param[in] port UDP port number in host byte order
+ * \param[in] osmo_wq_mode Register \ref osmo_wqueue (1) or not (0)
+ *
+ * Open GSMTAP source (sending) socket, connect it to host/port,
+ * allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue
+ * registration. This means it is like \ref gsmtap_init2 but integrated
+ * with libosmocore \ref select */
+struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
+ int ofd_wq_mode)
+{
+ struct gsmtap_inst *gti;
+ int fd;
+
+ fd = gsmtap_source_init_fd(host, port);
+ if (fd < 0)
+ return NULL;
+
+ gti = talloc_zero(NULL, struct gsmtap_inst);
+ gti->ofd_wq_mode = ofd_wq_mode;
+ gti->wq.bfd.fd = fd;
+ gti->sink_ofd.fd = -1;
+
+ if (ofd_wq_mode) {
+ osmo_wqueue_init(&gti->wq, 64);
+ gti->wq.write_cb = &gsmtap_wq_w_cb;
+
+ osmo_fd_register(&gti->wq.bfd);
+ }
+
+ return gti;
+}
+
+#endif /* HAVE_SYS_SOCKET_H */
diff --git a/src/shared/libosmocore/src/logging.c b/src/shared/libosmocore/src/logging.c
new file mode 100644
index 00000000..f8ed4cb1
--- /dev/null
+++ b/src/shared/libosmocore/src/logging.c
@@ -0,0 +1,757 @@
+/* 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.
+ *
+ */
+
+/* \addtogroup logging
+ * @{
+ */
+
+/* \file logging.c */
+
+#include "../config.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <time.h>
+#include <errno.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
+
+struct log_info *osmo_log_info;
+
+static struct log_context log_context;
+static void *tall_log_ctx = NULL;
+LLIST_HEAD(osmo_log_target_list);
+
+#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
+
+static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
+ { 0, "EVERYTHING" },
+ { LOGL_DEBUG, "DEBUG" },
+ { LOGL_INFO, "INFO" },
+ { LOGL_NOTICE, "NOTICE" },
+ { LOGL_ERROR, "ERROR" },
+ { LOGL_FATAL, "FATAL" },
+ { 0, NULL },
+};
+
+#define INT2IDX(x) (-1*(x)-1)
+static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
+ [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
+ .name = "DLGLOBAL",
+ .description = "Library-internal global log family",
+ .loglevel = LOGL_NOTICE,
+ .enabled = 1,
+ },
+ [INT2IDX(DLLAPD)] = { /* -2 becomes 1 */
+ .name = "DLLAPD",
+ .description = "LAPD in libosmogsm",
+ .loglevel = LOGL_NOTICE,
+ .enabled = 1,
+ },
+ [INT2IDX(DLINP)] = {
+ .name = "DLINP",
+ .description = "A-bis Intput Subsystem",
+ .loglevel = LOGL_NOTICE,
+ .enabled = 1,
+ },
+ [INT2IDX(DLMUX)] = {
+ .name = "DLMUX",
+ .description = "A-bis B-Subchannel TRAU Frame Multiplex",
+ .loglevel = LOGL_NOTICE,
+ .enabled = 1,
+ },
+ [INT2IDX(DLMI)] = {
+ .name = "DLMI",
+ .description = "A-bis Input Driver for Signalling",
+ .enabled = 0, .loglevel = LOGL_NOTICE,
+ },
+ [INT2IDX(DLMIB)] = {
+ .name = "DLMIB",
+ .description = "A-bis Input Driver for B-Channels (voice)",
+ .enabled = 0, .loglevel = LOGL_NOTICE,
+ },
+ [INT2IDX(DLSMS)] = {
+ .name = "DLSMS",
+ .description = "Layer3 Short Message Service (SMS)",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[1;38m",
+ },
+};
+
+/* You have to keep this in sync with the structure loglevel_strs. */
+const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
+ "Log simply everything",
+ "Log debug messages and higher levels",
+ "Log informational messages and higher levels",
+ "Log noticable messages and higher levels",
+ "Log error messages and higher levels",
+ "Log only fatal messages",
+ NULL,
+};
+
+/* special magic for negative (library-internal) log subsystem numbers */
+static int subsys_lib2index(int subsys)
+{
+ return (subsys * -1) + (osmo_log_info->num_cat_user-1);
+}
+
+/*! \brief Parse a human-readable log level into a numeric value */
+int log_parse_level(const char *lvl)
+{
+ return get_string_value(loglevel_strs, lvl);
+}
+
+/*! \brief convert a numeric log level into human-readable string */
+const char *log_level_str(unsigned int lvl)
+{
+ return get_value_string(loglevel_strs, lvl);
+}
+
+/*! \brief parse a human-readable log category into numeric form
+ * \param[in] category human-readable log category name
+ * \returns numeric category value, or -EINVAL otherwise
+ */
+int log_parse_category(const char *category)
+{
+ int i;
+
+ for (i = 0; i < osmo_log_info->num_cat; ++i) {
+ if (osmo_log_info->cat[i].name == NULL)
+ continue;
+ if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+/*! \brief parse the log category mask
+ * \param[in] target log target to be configured
+ * \param[in] _mask log category mask string
+ *
+ * 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 < osmo_log_info->num_cat; ++i)
+ target->categories[i].enabled = 0;
+
+ category_token = strtok(mask, ":");
+ do {
+ for (i = 0; i < osmo_log_info->num_cat; ++i) {
+ char* colon = strstr(category_token, ",");
+ int length = strlen(category_token);
+ int cat_length = strlen(osmo_log_info->cat[i].name);
+
+ /* Use longest length not to match subocurrences. */
+ if (cat_length > length)
+ length = cat_length;
+
+ if (!osmo_log_info->cat[i].name)
+ continue;
+
+ if (colon)
+ length = colon - category_token;
+
+ if (strncasecmp(osmo_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 < osmo_log_info->num_cat)
+ return osmo_log_info->cat[subsys].color;
+
+ return NULL;
+}
+
+static void _output(struct log_target *target, unsigned int subsys,
+ unsigned int level, char *file, int line, int cont,
+ const char *format, va_list ap)
+{
+ char buf[4096];
+ int ret, len = 0, offset = 0, rem = sizeof(buf);
+
+ /* are we using color */
+ if (target->use_color) {
+ const char *c = color(subsys);
+ if (c) {
+ ret = snprintf(buf + offset, rem, "%s", color(subsys));
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ }
+ if (!cont) {
+ if (target->print_timestamp) {
+ char *timestr;
+ time_t tm;
+ tm = time(NULL);
+ timestr = ctime(&tm);
+ timestr[strlen(timestr)-1] = '\0';
+ ret = snprintf(buf + offset, rem, "%s ", timestr);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
+ subsys, file, line);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ ret = vsnprintf(buf + offset, rem, format, ap);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+
+ ret = snprintf(buf + offset, rem, "%s",
+ target->use_color ? "\033[0;m" : "");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+err:
+ buf[sizeof(buf)-1] = '\0';
+ target->output(target, level, buf);
+}
+
+/*! \brief vararg version of logging function */
+void osmo_vlogp(int subsys, int level, char *file, int line,
+ int cont, const char *format, va_list ap)
+{
+ struct log_target *tar;
+
+ if (subsys < 0)
+ subsys = subsys_lib2index(subsys);
+
+ if (subsys > osmo_log_info->num_cat)
+ subsys = DLGLOBAL;
+
+ llist_for_each_entry(tar, &osmo_log_target_list, entry) {
+ struct log_category *category;
+ int output = 0;
+ va_list bp;
+
+ 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 (osmo_log_info->filter_fn)
+ output = osmo_log_info->filter_fn(&log_context,
+ tar);
+ if (!output)
+ continue;
+
+ /* According to the manpage, vsnprintf leaves the value of ap
+ * in undefined state. Since _output uses vsnprintf and it may
+ * be called several times, we have to pass a copy of ap. */
+ va_copy(bp, ap);
+ _output(tar, subsys, level, file, line, cont, format, bp);
+ va_end(bp);
+ }
+}
+
+void logp(int subsys, char *file, int line, int cont,
+ const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
+ va_end(ap);
+}
+
+void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ osmo_vlogp(subsys, level, file, line, cont, format, ap);
+ va_end(ap);
+}
+
+/*! \brief Register a new log target with the logging core
+ * \param[in] target Log target to be registered
+ */
+void log_add_target(struct log_target *target)
+{
+ llist_add_tail(&target->entry, &osmo_log_target_list);
+}
+
+/*! \brief Unregister a log target from the logging core
+ * \param[in] target Log target to be unregistered
+ */
+void log_del_target(struct log_target *target)
+{
+ llist_del(&target->entry);
+}
+
+/*! \brief Reset (clear) the logging context */
+void log_reset_context(void)
+{
+ memset(&log_context, 0, sizeof(log_context));
+}
+
+/*! \brief Set the logging context
+ * \param[in] ctx_nr logging context number
+ * \param[in] value value to which the context is to be set
+ *
+ * A logging context is something like the subscriber identity to which
+ * the currently processed message relates, or the BTS through which it
+ * was received. As soon as this data is known, it can be set using
+ * this function. The main use of context information is for logging
+ * filters.
+ */
+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;
+}
+
+/*! \brief Enable the \ref LOG_FILTER_ALL log filter
+ * \param[in] target Log target to be affected
+ * \param[in] all enable (1) or disable (0) the ALL filter
+ *
+ * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
+ * be printed. It acts as a wildcard. Setting it to \a 1 means there
+ * is no filtering.
+ */
+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;
+}
+
+/*! \brief Enable or disable the use of colored output
+ * \param[in] target Log target to be affected
+ * \param[in] use_color Use color (1) or don't use color (0)
+ */
+void log_set_use_color(struct log_target *target, int use_color)
+{
+ target->use_color = use_color;
+}
+
+/*! \brief Enable or disable printing of timestamps while logging
+ * \param[in] target Log target to be affected
+ * \param[in] print_timestamp Enable (1) or disable (0) timestamps
+ */
+void log_set_print_timestamp(struct log_target *target, int print_timestamp)
+{
+ target->print_timestamp = print_timestamp;
+}
+
+/*! \brief Set the global log level for a given log target
+ * \param[in] target Log target to be affected
+ * \param[in] log_level New global log level
+ */
+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 >= osmo_log_info->num_cat)
+ return;
+ target->categories[category].enabled = !!enable;
+ target->categories[category].loglevel = level;
+}
+
+static void _file_output(struct log_target *target, unsigned int level,
+ const char *log)
+{
+ fprintf(target->tgt_file.out, "%s", log);
+ fflush(target->tgt_file.out);
+}
+
+/*! \brief Create a new log target skeleton */
+struct log_target *log_target_create(void)
+{
+ struct log_target *target;
+ unsigned int i;
+
+ target = talloc_zero(tall_log_ctx, struct log_target);
+ if (!target)
+ return NULL;
+
+ target->categories = talloc_zero_array(target,
+ struct log_category,
+ osmo_log_info->num_cat);
+ if (!target->categories) {
+ talloc_free(target);
+ return NULL;
+ }
+
+ INIT_LLIST_HEAD(&target->entry);
+
+ /* initialize the per-category enabled/loglevel from defaults */
+ for (i = 0; i < osmo_log_info->num_cat; i++) {
+ struct log_category *cat = &target->categories[i];
+ cat->enabled = osmo_log_info->cat[i].enabled;
+ cat->loglevel = osmo_log_info->cat[i].loglevel;
+ }
+
+ /* global settings */
+ target->use_color = 1;
+ target->print_timestamp = 0;
+
+ /* global log level */
+ target->loglevel = 0;
+ return target;
+}
+
+/*! \brief Create the STDERR log target */
+struct log_target *log_target_create_stderr(void)
+{
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
+ struct log_target *target;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->type = LOG_TGT_TYPE_STDERR;
+ target->tgt_file.out = stderr;
+ target->output = _file_output;
+ return target;
+#else
+ return NULL;
+#endif /* stderr */
+}
+
+/*! \brief Create a new file-based log target
+ * \param[in] fname File name of the new log file
+ * \returns Log target in case of success, NULL otherwise
+ */
+struct log_target *log_target_create_file(const char *fname)
+{
+ struct log_target *target;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->type = LOG_TGT_TYPE_FILE;
+ target->tgt_file.out = fopen(fname, "a");
+ if (!target->tgt_file.out)
+ return NULL;
+
+ target->output = _file_output;
+
+ target->tgt_file.fname = talloc_strdup(target, fname);
+
+ return target;
+}
+
+/*! \brief Find a registered log target
+ * \param[in] type Log target type
+ * \param[in] fname File name
+ * \returns Log target (if found), NULL otherwise
+ */
+struct log_target *log_target_find(int type, const char *fname)
+{
+ struct log_target *tgt;
+
+ llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
+ if (tgt->type != type)
+ continue;
+ if (tgt->type == LOG_TGT_TYPE_FILE) {
+ if (!strcmp(fname, tgt->tgt_file.fname))
+ return tgt;
+ } else
+ return tgt;
+ }
+ return NULL;
+}
+
+/*! \brief Unregister, close and delete a log target */
+void log_target_destroy(struct log_target *target)
+{
+
+ /* just in case, to make sure we don't have any references */
+ log_del_target(target);
+
+ if (target->output == &_file_output) {
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
+ /* don't close stderr */
+ if (target->tgt_file.out != stderr)
+#endif
+ {
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = NULL;
+ }
+ }
+
+ talloc_free(target);
+}
+
+/*! \brief close and re-open a log file (for log file rotation) */
+int log_target_file_reopen(struct log_target *target)
+{
+ fclose(target->tgt_file.out);
+
+ target->tgt_file.out = fopen(target->tgt_file.fname, "a");
+ if (!target->tgt_file.out)
+ return -errno;
+
+ /* we assume target->output already to be set */
+
+ return 0;
+}
+
+/*! \brief Generates the logging command string for VTY
+ * \param[in] unused_info Deprecated parameter, no longer used!
+ */
+const char *log_vty_command_string(const struct log_info *unused_info)
+{
+ struct log_info *info = osmo_log_info;
+ int len = 0, offset = 0, ret, i, rem;
+ int size = strlen("logging level () ()") + 1;
+ char *str;
+
+ for (i = 0; i < info->num_cat; i++) {
+ if (info->cat[i].name == NULL)
+ continue;
+ size += strlen(info->cat[i].name) + 1;
+ }
+
+ for (i = 0; i < LOGLEVEL_DEFS; i++)
+ size += strlen(loglevel_strs[i].str) + 1;
+
+ rem = size;
+ str = talloc_zero_size(tall_log_ctx, size);
+ if (!str)
+ return NULL;
+
+ ret = snprintf(str + offset, rem, "logging level (all|");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+
+ for (i = 0; i < info->num_cat; i++) {
+ if (info->cat[i].name) {
+ int j, name_len = strlen(info->cat[i].name)+1;
+ char name[name_len];
+
+ for (j = 0; j < name_len; j++)
+ name[j] = tolower(info->cat[i].name[j]);
+
+ name[name_len-1] = '\0';
+ ret = snprintf(str + offset, rem, "%s|", name+1);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ }
+ offset--; /* to remove the trailing | */
+ rem++;
+
+ ret = snprintf(str + offset, rem, ") (");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+
+ for (i = 0; i < LOGLEVEL_DEFS; i++) {
+ int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
+ char loglevel_str[loglevel_str_len];
+
+ for (j = 0; j < loglevel_str_len; j++)
+ loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
+
+ loglevel_str[loglevel_str_len-1] = '\0';
+ ret = snprintf(str + offset, rem, "%s|", loglevel_str);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ offset--; /* to remove the trailing | */
+ rem++;
+
+ ret = snprintf(str + offset, rem, ")");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+err:
+ str[size-1] = '\0';
+ return str;
+}
+
+/*! \brief Generates the logging command description for VTY
+ * \param[in] unused_info Deprecated parameter, no longer used!
+ */
+const char *log_vty_command_description(const struct log_info *unused_info)
+{
+ struct log_info *info = osmo_log_info;
+ char *str;
+ int i, ret, len = 0, offset = 0, rem;
+ unsigned int size =
+ strlen(LOGGING_STR
+ "Set the log level for a specified category\n") + 1;
+
+ for (i = 0; i < info->num_cat; i++) {
+ if (info->cat[i].name == NULL)
+ continue;
+ size += strlen(info->cat[i].description) + 1;
+ }
+
+ for (i = 0; i < LOGLEVEL_DEFS; i++)
+ size += strlen(loglevel_descriptions[i]) + 1;
+
+ size += strlen("Global setting for all subsystems") + 1;
+ rem = size;
+ str = talloc_zero_size(tall_log_ctx, size);
+ if (!str)
+ return NULL;
+
+ ret = snprintf(str + offset, rem, LOGGING_STR
+ "Set the log level for a specified category\n");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+
+ ret = snprintf(str + offset, rem,
+ "Global setting for all subsystems\n");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+
+ for (i = 0; i < info->num_cat; i++) {
+ if (info->cat[i].name == NULL)
+ continue;
+ ret = snprintf(str + offset, rem, "%s\n",
+ info->cat[i].description);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ for (i = 0; i < LOGLEVEL_DEFS; i++) {
+ ret = snprintf(str + offset, rem, "%s\n",
+ loglevel_descriptions[i]);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+err:
+ str[size-1] = '\0';
+ return str;
+}
+
+/*! \brief Initialize the Osmocom logging core
+ * \param[in] inf Information regarding logging categories
+ * \param[in] ctx \ref talloc context for logging allocations
+ * \returns 0 in case of success, negative in case of error
+ */
+int log_init(const struct log_info *inf, void *ctx)
+{
+ int i;
+
+ tall_log_ctx = talloc_named_const(ctx, 1, "logging");
+ if (!tall_log_ctx)
+ return -ENOMEM;
+
+ osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
+ if (!osmo_log_info)
+ return -ENOMEM;
+
+ osmo_log_info->num_cat_user = inf->num_cat;
+ /* total number = number of user cat + library cat */
+ osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
+
+ osmo_log_info->cat = talloc_zero_array(osmo_log_info,
+ struct log_info_cat,
+ osmo_log_info->num_cat);
+ if (!osmo_log_info->cat) {
+ talloc_free(osmo_log_info);
+ osmo_log_info = NULL;
+ return -ENOMEM;
+ }
+
+ /* copy over the user part */
+ for (i = 0; i < inf->num_cat; i++) {
+ memcpy(&osmo_log_info->cat[i], &inf->cat[i],
+ sizeof(struct log_info_cat));
+ }
+
+ /* copy over the library part */
+ for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
+ unsigned int cn = osmo_log_info->num_cat_user + i;
+ memcpy(&osmo_log_info->cat[cn],
+ &internal_cat[i], sizeof(struct log_info_cat));
+ }
+
+ return 0;
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/logging_syslog.c b/src/shared/libosmocore/src/logging_syslog.c
new file mode 100644
index 00000000..119dd740
--- /dev/null
+++ b/src/shared/libosmocore/src/logging_syslog.c
@@ -0,0 +1,92 @@
+/* Syslog logging support code */
+
+/* (C) 2011 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.
+ *
+ */
+
+/*! \addtogroup logging
+ * @{
+ */
+
+/*! \file logging_syslog.c */
+
+#include "../config.h"
+
+#ifdef HAVE_SYSLOG_H
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+static int logp2syslog_level(unsigned int level)
+{
+ if (level >= LOGL_FATAL)
+ return LOG_CRIT;
+ else if (level >= LOGL_ERROR)
+ return LOG_ERR;
+ else if (level >= LOGL_NOTICE)
+ return LOG_NOTICE;
+ else if (level >= LOGL_INFO)
+ return LOG_INFO;
+ else
+ return LOG_DEBUG;
+}
+
+static void _syslog_output(struct log_target *target,
+ unsigned int level, const char *log)
+{
+ syslog(logp2syslog_level(level), "%s", log);
+}
+
+/*! \brief Create a new logging target for syslog logging
+ * \param[in] ident syslog string identifier
+ * \param[in] option syslog options
+ * \param[in] facility syslog facility
+ * \returns Log target in case of success, NULL in case of error
+ */
+struct log_target *log_target_create_syslog(const char *ident, int option,
+ int facility)
+{
+ struct log_target *target;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->tgt_syslog.facility = facility;
+ target->type = LOG_TGT_TYPE_SYSLOG;
+ target->output = _syslog_output;
+
+ openlog(ident, option, facility);
+
+ return target;
+}
+
+#endif /* HAVE_SYSLOG_H */
+
+/* }@ */
diff --git a/src/shared/libosmocore/src/msgb.c b/src/shared/libosmocore/src/msgb.c
new file mode 100644
index 00000000..a403580b
--- /dev/null
+++ b/src/shared/libosmocore/src/msgb.c
@@ -0,0 +1,156 @@
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 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.
+ *
+ */
+
+/*! \addtogroup msgb
+ * @{
+ */
+
+/*! \file msgb.c
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <osmocom/core/msgb.h>
+//#include <openbsc/gsm_data.h>
+#include <osmocom/core/talloc.h>
+//#include <openbsc/debug.h>
+
+void *tall_msgb_ctx;
+
+/*! \brief Allocate a new message buffer
+ * \param[in] size Length in octets, including headroom
+ * \param[in] name Human-readable name to be associated with msgb
+ *
+ * This function allocates a 'struct msgb' as well as the underlying
+ * memory buffer for the actual message data (size specified by \a size)
+ * using the talloc memory context previously set by \ref msgb_set_talloc_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;
+}
+
+/*! \brief Release given message buffer
+ * \param[in] m Message buffer to be free'd
+ */
+void msgb_free(struct msgb *m)
+{
+ talloc_free(m);
+}
+
+/*! \brief Enqueue message buffer to tail of a queue
+ * \param[in] queue linked list header of queue
+ * \param[in] msgb message buffer to be added to the queue
+ *
+ * The function will append the specified message buffer \a msg to the
+ * queue implemented by \ref llist_head \a queue
+ */
+void msgb_enqueue(struct llist_head *queue, struct msgb *msg)
+{
+ llist_add_tail(&msg->list, queue);
+}
+
+/*! \brief Dequeue message buffer from head of queue
+ * \param[in] queue linked list header of queue
+ * \returns message buffer (if any) or NULL if queue empty
+ *
+ * The function will remove the first message buffer from the queue
+ * implemented by 'ref llist_head \a 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);
+}
+
+/*! \brief Re-set all message buffer pointers
+ * \param[in] m message buffer that is to be resetted
+ *
+ * This will re-set the various internal pointers into the underlying
+ * message buffer, i.e. remvoe all headroom and treat the msgb as
+ * completely empty. It also initializes the control buffer to zero.
+ */
+void msgb_reset(struct msgb *msg)
+{
+ msg->len = 0;
+ msg->data = msg->_data;
+ msg->head = msg->_data;
+ msg->tail = msg->_data;
+
+ msg->trx = NULL;
+ msg->lchan = NULL;
+ msg->l2h = NULL;
+ msg->l3h = NULL;
+ msg->l4h = NULL;
+
+ memset(&msg->cb, 0, sizeof(msg->cb));
+}
+
+/*! \brief get pointer to data section of message buffer
+ * \param[in] msg message buffer
+ * \returns pointer to data section of message buffer
+ */
+uint8_t *msgb_data(const struct msgb *msg)
+{
+ return msg->data;
+}
+
+/*! \brief get length of message buffer
+ * \param[in] msg message buffer
+ * \returns length of data section in message buffer
+ */
+uint16_t msgb_length(const struct msgb *msg)
+{
+ return msg->len;
+}
+
+/*! \brief Set the talloc context for \ref msgb_alloc
+ * \param[in] ctx talloc context to be used as root for msgb allocations
+ */
+void msgb_set_talloc_ctx(void *ctx)
+{
+ tall_msgb_ctx = ctx;
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/msgfile.c b/src/shared/libosmocore/src/msgfile.c
new file mode 100644
index 00000000..d2b180d7
--- /dev/null
+++ b/src/shared/libosmocore/src/msgfile.c
@@ -0,0 +1,123 @@
+/*
+ * Parse a simple file with messages, e.g used for USSD messages
+ *
+ * (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 <osmocom/core/msgfile.h>
+#include <osmocom/core/talloc.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+static struct osmo_config_entry *
+alloc_entry(struct osmo_config_list *entries,
+ const char *mcc, const char *mnc,
+ const char *option, const char *text)
+{
+ struct osmo_config_entry *entry =
+ talloc_zero(entries, struct osmo_config_entry);
+ if (!entry)
+ return NULL;
+
+ entry->mcc = talloc_strdup(entry, mcc);
+ entry->mnc = talloc_strdup(entry, mnc);
+ entry->option = talloc_strdup(entry, option);
+ entry->text = talloc_strdup(entry, text);
+
+ llist_add_tail(&entry->list, &entries->entry);
+ return entry;
+}
+
+static struct osmo_config_list *alloc_entries(void *ctx)
+{
+ struct osmo_config_list *entries;
+
+ entries = talloc_zero(ctx, struct osmo_config_list);
+ if (!entries)
+ return NULL;
+
+ INIT_LLIST_HEAD(&entries->entry);
+ return entries;
+}
+
+/*
+ * split a line like 'foo:Text'.
+ */
+static void handle_line(struct osmo_config_list *entries, char *line)
+{
+ int i;
+ const int len = strlen(line);
+
+ char *items[3];
+ int last_item = 0;
+
+ /* Skip comments from the file */
+ if (line[0] == '#')
+ return;
+
+ for (i = 0; i < len; ++i) {
+ if (line[i] == '\n' || line[i] == '\r')
+ line[i] = '\0';
+ else if (line[i] == ':' && last_item < 3) {
+ line[i] = '\0';
+
+ items[last_item++] = &line[i + 1];
+ }
+ }
+
+ if (last_item == 3) {
+ alloc_entry(entries, &line[0] , items[0], items[1], items[2]);
+ return;
+ }
+
+ /* nothing found */
+}
+
+struct osmo_config_list *osmo_config_list_parse(void *ctx, const char *filename)
+{
+ struct osmo_config_list *entries;
+ size_t n;
+ char *line;
+ FILE *file;
+
+ file = fopen(filename, "r");
+ if (!file)
+ return NULL;
+
+ entries = alloc_entries(ctx);
+ if (!entries) {
+ fclose(file);
+ return NULL;
+ }
+
+ n = 2342;
+ line = NULL;
+ while (getline(&line, &n, file) != -1) {
+ handle_line(entries, line);
+ free(line);
+ line = NULL;
+ }
+
+ fclose(file);
+ return entries;
+}
diff --git a/src/shared/libosmocore/src/panic.c b/src/shared/libosmocore/src/panic.c
new file mode 100644
index 00000000..be644ff1
--- /dev/null
+++ b/src/shared/libosmocore/src/panic.c
@@ -0,0 +1,82 @@
+/* Panic handling */
+/*
+ * (C) 2010 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.
+ *
+ */
+
+/*! \addtogroup utils
+ * @{
+ */
+
+/*! \file panic.c */
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/core/panic.h>
+#include <osmocom/core/backtrace.h>
+
+#include "../config.h"
+
+
+static osmo_panic_handler_t osmo_panic_handler = (void*)0;
+
+
+#ifndef PANIC_INFLOOP
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void osmo_panic_default(const char *fmt, va_list args)
+{
+ vfprintf(stderr, fmt, args);
+ osmo_generate_backtrace();
+ abort();
+}
+
+#else
+
+static void osmo_panic_default(const char *fmt, va_list args)
+{
+ while (1);
+}
+
+#endif
+
+
+/*! \brief Terminate the current program with a panic */
+void osmo_panic(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ if (osmo_panic_handler)
+ osmo_panic_handler(fmt, args);
+ else
+ osmo_panic_default(fmt, args);
+
+ va_end(args);
+}
+
+
+/*! \brief Set the panic handler */
+void osmo_set_panic_handler(osmo_panic_handler_t h)
+{
+ osmo_panic_handler = h;
+}
+
diff --git a/src/shared/libosmocore/src/plugin.c b/src/shared/libosmocore/src/plugin.c
new file mode 100644
index 00000000..998bca35
--- /dev/null
+++ b/src/shared/libosmocore/src/plugin.c
@@ -0,0 +1,62 @@
+/* plugin infrastructure */
+
+/* (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 "../config.h"
+
+#if HAVE_DLFCN_H
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <osmocom/core/plugin.h>
+
+int osmo_plugin_load_all(const char *directory)
+{
+ unsigned int num = 0;
+ char fname[PATH_MAX];
+ DIR *dir;
+ struct dirent *entry;
+
+ dir = opendir(directory);
+ if (!dir)
+ return -errno;
+
+ while ((entry = readdir(dir))) {
+ snprintf(fname, sizeof(fname), "%s/%s", directory,
+ entry->d_name);
+ if (dlopen(fname, RTLD_NOW))
+ num++;
+ }
+
+ closedir(dir);
+
+ return num;
+}
+#else
+int osmo_plugin_load_all(const char *directory)
+{
+ return 0;
+}
+#endif /* HAVE_DLFCN_H */
diff --git a/src/shared/libosmocore/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c
new file mode 100644
index 00000000..24166b84
--- /dev/null
+++ b/src/shared/libosmocore/src/rate_ctr.c
@@ -0,0 +1,180 @@
+/* utility routines for keeping conters about events and the event rates */
+
+/* (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.
+ *
+ */
+
+/*! \addtogroup rate_ctr
+ * @{
+ */
+
+/*! \file rate_ctr.c */
+
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/rate_ctr.h>
+
+static LLIST_HEAD(rate_ctr_groups);
+
+static void *tall_rate_ctr_ctx;
+
+/*! \brief Allocate a new group of counters according to description
+ * \param[in] ctx \ref talloc context
+ * \param[in] desc Rate counter group description
+ * \param[in] idx Index of new counter group
+ */
+struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
+ const struct rate_ctr_group_desc *desc,
+ unsigned int idx)
+{
+ unsigned int size;
+ struct rate_ctr_group *group;
+
+ size = sizeof(struct rate_ctr_group) +
+ desc->num_ctr * sizeof(struct rate_ctr);
+
+ if (!ctx)
+ ctx = tall_rate_ctr_ctx;
+
+ group = talloc_zero_size(ctx, size);
+ if (!group)
+ return NULL;
+
+ group->desc = desc;
+ group->idx = idx;
+
+ llist_add(&group->list, &rate_ctr_groups);
+
+ return group;
+}
+
+/*! \brief Free the memory for the specified group of counters */
+void rate_ctr_group_free(struct rate_ctr_group *grp)
+{
+ llist_del(&grp->list);
+ talloc_free(grp);
+}
+
+/*! \brief Add a number to the counter */
+void rate_ctr_add(struct rate_ctr *ctr, int inc)
+{
+ ctr->current += inc;
+}
+
+static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
+{
+ /* calculate rate over last interval */
+ ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last;
+ /* save current counter for next interval */
+ ctr->intv[intv].last = ctr->current;
+
+ /* update the rate of the next bigger interval. This will
+ * be overwritten when that next larger interval expires */
+ if (intv + 1 < ARRAY_SIZE(ctr->intv))
+ ctr->intv[intv+1].rate += ctr->intv[intv].rate;
+}
+
+static struct osmo_timer_list rate_ctr_timer;
+static uint64_t timer_ticks;
+
+/* The one-second interval has expired */
+static void rate_ctr_group_intv(struct rate_ctr_group *grp)
+{
+ unsigned int i;
+
+ for (i = 0; i < grp->desc->num_ctr; i++) {
+ struct rate_ctr *ctr = &grp->ctr[i];
+
+ interval_expired(ctr, RATE_CTR_INTV_SEC);
+ if ((timer_ticks % 60) == 0)
+ interval_expired(ctr, RATE_CTR_INTV_MIN);
+ if ((timer_ticks % (60*60)) == 0)
+ interval_expired(ctr, RATE_CTR_INTV_HOUR);
+ if ((timer_ticks % (24*60*60)) == 0)
+ interval_expired(ctr, RATE_CTR_INTV_DAY);
+ }
+}
+
+static void rate_ctr_timer_cb(void *data)
+{
+ struct rate_ctr_group *ctrg;
+
+ /* Increment number of ticks before we calculate intervals,
+ * as a counter value of 0 would already wrap all counters */
+ timer_ticks++;
+
+ llist_for_each_entry(ctrg, &rate_ctr_groups, list)
+ rate_ctr_group_intv(ctrg);
+
+ osmo_timer_schedule(&rate_ctr_timer, 1, 0);
+}
+
+/*! \brief Initialize the counter module */
+int rate_ctr_init(void *tall_ctx)
+{
+ tall_rate_ctr_ctx = tall_ctx;
+ rate_ctr_timer.cb = rate_ctr_timer_cb;
+ osmo_timer_schedule(&rate_ctr_timer, 1, 0);
+
+ return 0;
+}
+
+/*! \brief Search for counter group based on group name and index */
+struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx)
+{
+ struct rate_ctr_group *ctrg;
+
+ llist_for_each_entry(ctrg, &rate_ctr_groups, list) {
+ if (!ctrg->desc)
+ continue;
+
+ if (!strcmp(ctrg->desc->group_name_prefix, name) &&
+ ctrg->idx == idx) {
+ return ctrg;
+ }
+ }
+ return NULL;
+}
+
+/*! \brief Search for counter group based on group name */
+const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name)
+{
+ int i;
+ const struct rate_ctr_desc *ctr_desc;
+
+ if (!ctrg->desc)
+ return NULL;
+
+ for (i = 0; i < ctrg->desc->num_ctr; i++) {
+ ctr_desc = &ctrg->desc->ctr_desc[i];
+
+ if (!strcmp(ctr_desc->name, name)) {
+ return &ctrg->ctr[i];
+ }
+ }
+ return NULL;
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/rbtree.c b/src/shared/libosmocore/src/rbtree.c
new file mode 100644
index 00000000..4e7c0f3a
--- /dev/null
+++ b/src/shared/libosmocore/src/rbtree.c
@@ -0,0 +1,383 @@
+/*
+ Red Black Trees
+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
+ (C) 2002 David Woodhouse <dwmw2@infradead.org>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ linux/lib/rbtree.c
+*/
+
+#include <osmocom/core/linuxrbtree.h>
+
+static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *right = node->rb_right;
+ struct rb_node *parent = rb_parent(node);
+
+ if ((node->rb_right = right->rb_left))
+ rb_set_parent(right->rb_left, node);
+ right->rb_left = node;
+
+ rb_set_parent(right, parent);
+
+ if (parent)
+ {
+ if (node == parent->rb_left)
+ parent->rb_left = right;
+ else
+ parent->rb_right = right;
+ }
+ else
+ root->rb_node = right;
+ rb_set_parent(node, right);
+}
+
+static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *left = node->rb_left;
+ struct rb_node *parent = rb_parent(node);
+
+ if ((node->rb_left = left->rb_right))
+ rb_set_parent(left->rb_right, node);
+ left->rb_right = node;
+
+ rb_set_parent(left, parent);
+
+ if (parent)
+ {
+ if (node == parent->rb_right)
+ parent->rb_right = left;
+ else
+ parent->rb_left = left;
+ }
+ else
+ root->rb_node = left;
+ rb_set_parent(node, left);
+}
+
+void rb_insert_color(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *parent, *gparent;
+
+ while ((parent = rb_parent(node)) && rb_is_red(parent))
+ {
+ gparent = rb_parent(parent);
+
+ if (parent == gparent->rb_left)
+ {
+ {
+ register struct rb_node *uncle = gparent->rb_right;
+ if (uncle && rb_is_red(uncle))
+ {
+ rb_set_black(uncle);
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_right == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_left(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ __rb_rotate_right(gparent, root);
+ } else {
+ {
+ register struct rb_node *uncle = gparent->rb_left;
+ if (uncle && rb_is_red(uncle))
+ {
+ rb_set_black(uncle);
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_left == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_right(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ __rb_rotate_left(gparent, root);
+ }
+ }
+
+ rb_set_black(root->rb_node);
+}
+
+static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
+ struct rb_root *root)
+{
+ struct rb_node *other;
+
+ while ((!node || rb_is_black(node)) && node != root->rb_node)
+ {
+ if (parent->rb_left == node)
+ {
+ other = parent->rb_right;
+ if (rb_is_red(other))
+ {
+ rb_set_black(other);
+ rb_set_red(parent);
+ __rb_rotate_left(parent, root);
+ other = parent->rb_right;
+ }
+ if ((!other->rb_left || rb_is_black(other->rb_left)) &&
+ (!other->rb_right || rb_is_black(other->rb_right)))
+ {
+ rb_set_red(other);
+ node = parent;
+ parent = rb_parent(node);
+ }
+ else
+ {
+ if (!other->rb_right || rb_is_black(other->rb_right))
+ {
+ rb_set_black(other->rb_left);
+ rb_set_red(other);
+ __rb_rotate_right(other, root);
+ other = parent->rb_right;
+ }
+ rb_set_color(other, rb_color(parent));
+ rb_set_black(parent);
+ rb_set_black(other->rb_right);
+ __rb_rotate_left(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ else
+ {
+ other = parent->rb_left;
+ if (rb_is_red(other))
+ {
+ rb_set_black(other);
+ rb_set_red(parent);
+ __rb_rotate_right(parent, root);
+ other = parent->rb_left;
+ }
+ if ((!other->rb_left || rb_is_black(other->rb_left)) &&
+ (!other->rb_right || rb_is_black(other->rb_right)))
+ {
+ rb_set_red(other);
+ node = parent;
+ parent = rb_parent(node);
+ }
+ else
+ {
+ if (!other->rb_left || rb_is_black(other->rb_left))
+ {
+ rb_set_black(other->rb_right);
+ rb_set_red(other);
+ __rb_rotate_left(other, root);
+ other = parent->rb_left;
+ }
+ rb_set_color(other, rb_color(parent));
+ rb_set_black(parent);
+ rb_set_black(other->rb_left);
+ __rb_rotate_right(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ }
+ if (node)
+ rb_set_black(node);
+}
+
+void rb_erase(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *child, *parent;
+ int color;
+
+ if (!node->rb_left)
+ child = node->rb_right;
+ else if (!node->rb_right)
+ child = node->rb_left;
+ else
+ {
+ struct rb_node *old = node, *left;
+
+ node = node->rb_right;
+ while ((left = node->rb_left) != NULL)
+ node = left;
+
+ if (rb_parent(old)) {
+ if (rb_parent(old)->rb_left == old)
+ rb_parent(old)->rb_left = node;
+ else
+ rb_parent(old)->rb_right = node;
+ } else
+ root->rb_node = node;
+
+ child = node->rb_right;
+ parent = rb_parent(node);
+ color = rb_color(node);
+
+ if (parent == old) {
+ parent = node;
+ } else {
+ if (child)
+ rb_set_parent(child, parent);
+ parent->rb_left = child;
+
+ node->rb_right = old->rb_right;
+ rb_set_parent(old->rb_right, node);
+ }
+
+ node->rb_parent_color = old->rb_parent_color;
+ node->rb_left = old->rb_left;
+ rb_set_parent(old->rb_left, node);
+
+ goto color;
+ }
+
+ parent = rb_parent(node);
+ color = rb_color(node);
+
+ if (child)
+ rb_set_parent(child, parent);
+ if (parent)
+ {
+ if (parent->rb_left == node)
+ parent->rb_left = child;
+ else
+ parent->rb_right = child;
+ }
+ else
+ root->rb_node = child;
+
+ color:
+ if (color == RB_BLACK)
+ __rb_erase_color(child, parent, root);
+}
+
+/*
+ * This function returns the first node (in sort order) of the tree.
+ */
+struct rb_node *rb_first(const struct rb_root *root)
+{
+ struct rb_node *n;
+
+ n = root->rb_node;
+ if (!n)
+ return NULL;
+ while (n->rb_left)
+ n = n->rb_left;
+ return n;
+}
+
+struct rb_node *rb_last(const struct rb_root *root)
+{
+ struct rb_node *n;
+
+ n = root->rb_node;
+ if (!n)
+ return NULL;
+ while (n->rb_right)
+ n = n->rb_right;
+ return n;
+}
+
+struct rb_node *rb_next(const struct rb_node *node)
+{
+ struct rb_node *parent;
+
+ if (rb_parent(node) == node)
+ return NULL;
+
+ /* If we have a right-hand child, go down and then left as far
+ as we can. */
+ if (node->rb_right) {
+ node = node->rb_right;
+ while (node->rb_left)
+ node=node->rb_left;
+ return (struct rb_node *)node;
+ }
+
+ /* No right-hand children. Everything down and left is
+ smaller than us, so any 'next' node must be in the general
+ direction of our parent. Go up the tree; any time the
+ ancestor is a right-hand child of its parent, keep going
+ up. First time it's a left-hand child of its parent, said
+ parent is our 'next' node. */
+ while ((parent = rb_parent(node)) && node == parent->rb_right)
+ node = parent;
+
+ return parent;
+}
+
+struct rb_node *rb_prev(const struct rb_node *node)
+{
+ struct rb_node *parent;
+
+ if (rb_parent(node) == node)
+ return NULL;
+
+ /* If we have a left-hand child, go down and then right as far
+ as we can. */
+ if (node->rb_left) {
+ node = node->rb_left;
+ while (node->rb_right)
+ node=node->rb_right;
+ return (struct rb_node *)node;
+ }
+
+ /* No left-hand children. Go up till we find an ancestor which
+ is a right-hand child of its parent */
+ while ((parent = rb_parent(node)) && node == parent->rb_left)
+ node = parent;
+
+ return parent;
+}
+
+void rb_replace_node(struct rb_node *victim, struct rb_node *new,
+ struct rb_root *root)
+{
+ struct rb_node *parent = rb_parent(victim);
+
+ /* Set the surrounding nodes to point to the replacement */
+ if (parent) {
+ if (victim == parent->rb_left)
+ parent->rb_left = new;
+ else
+ parent->rb_right = new;
+ } else {
+ root->rb_node = new;
+ }
+ if (victim->rb_left)
+ rb_set_parent(victim->rb_left, new);
+ if (victim->rb_right)
+ rb_set_parent(victim->rb_right, new);
+
+ /* Copy the pointers/colour from the victim to the replacement */
+ *new = *victim;
+}
diff --git a/src/shared/libosmocore/src/select.c b/src/shared/libosmocore/src/select.c
new file mode 100644
index 00000000..c0705d31
--- /dev/null
+++ b/src/shared/libosmocore/src/select.c
@@ -0,0 +1,172 @@
+/* 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+
+#include "../config.h"
+
+#ifdef HAVE_SYS_SELECT_H
+
+/*! \addtogroup select
+ * @{
+ */
+
+/*! \file select.c
+ * \brief select loop abstraction
+ */
+
+static int maxfd = 0;
+static LLIST_HEAD(osmo_fds);
+static int unregistered_count;
+
+/*! \brief Register a new file descriptor with select loop abstraction
+ * \param[in] fd osmocom file descriptor to be registered
+ */
+int osmo_fd_register(struct osmo_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;
+
+ /* set close-on-exec flag */
+ flags = fcntl(fd->fd, F_GETFD);
+ if (flags < 0)
+ return flags;
+ flags |= FD_CLOEXEC;
+ flags = fcntl(fd->fd, F_SETFD, flags);
+ if (flags < 0)
+ return flags;
+
+ /* Register FD */
+ if (fd->fd > maxfd)
+ maxfd = fd->fd;
+
+#ifdef BSC_FD_CHECK
+ struct osmo_fd *entry;
+ llist_for_each_entry(entry, &osmo_fds, list) {
+ if (entry == fd) {
+ fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
+ return 0;
+ }
+ }
+#endif
+
+ llist_add_tail(&fd->list, &osmo_fds);
+
+ return 0;
+}
+
+/*! \brief Unregister a file descriptor from select loop abstraction
+ * \param[in] fd osmocom file descriptor to be unregistered
+ */
+void osmo_fd_unregister(struct osmo_fd *fd)
+{
+ unregistered_count++;
+ llist_del(&fd->list);
+}
+
+/*! \brief select main loop integration
+ * \param[in] polling should we pollonly (1) or block on select (0)
+ */
+int osmo_select_main(int polling)
+{
+ struct osmo_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, &osmo_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);
+ }
+
+ osmo_timers_check();
+
+ if (!polling)
+ osmo_timers_prepare();
+ rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
+ if (rc < 0)
+ return 0;
+
+ /* fire timers */
+ osmo_timers_update();
+
+ /* call registered callback functions */
+restart:
+ unregistered_count = 0;
+ llist_for_each_entry_safe(ufd, tmp, &osmo_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 */
+ /* this seems to happen with the last element of the list as well */
+ if (unregistered_count >= 1)
+ goto restart;
+ }
+ return work;
+}
+
+/*! }@ */
+
+#endif /* _HAVE_SYS_SELECT_H */
diff --git a/src/shared/libosmocore/src/serial.c b/src/shared/libosmocore/src/serial.c
new file mode 100644
index 00000000..bc64b8ab
--- /dev/null
+++ b/src/shared/libosmocore/src/serial.c
@@ -0,0 +1,229 @@
+/*
+ * serial.c
+ *
+ * Utility functions to deal with serial ports
+ *
+ * Copyright (C) 2011 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.
+ */
+
+/*! \addtogroup serial
+ * @{
+ */
+
+/*! \file serial.c
+ * \file Osmocom serial port helpers
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __linux__
+#include <linux/serial.h>
+#endif
+
+#include <osmocom/core/serial.h>
+
+
+#if 0
+# define dbg_perror(x) perror(x)
+#else
+# define dbg_perror(x) do { } while (0)
+#endif
+
+/*! \brief Open serial device and does base init
+ * \param[in] dev Path to the device node to open
+ * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
+ * \returns >=0 file descriptor in case of success or negative errno.
+ */
+int
+osmo_serial_init(const char *dev, speed_t baudrate)
+{
+ int rc, fd=0, v24;
+ struct termios tio;
+
+ /* Open device */
+ fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
+ if (fd < 0) {
+ dbg_perror("open");
+ return -errno;
+ }
+
+ /* Configure serial interface */
+ rc = tcgetattr(fd, &tio);
+ if (rc < 0) {
+ dbg_perror("tcgetattr()");
+ rc = -errno;
+ goto error;
+ }
+
+ cfsetispeed(&tio, baudrate);
+ cfsetospeed(&tio, baudrate);
+
+ tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
+ tio.c_cflag |= (CREAD | CLOCAL | CS8);
+ tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+ tio.c_iflag |= (INPCK | ISTRIP);
+ tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
+ tio.c_oflag &= ~(OPOST | ONLCR);
+
+ rc = tcsetattr(fd, TCSANOW, &tio);
+ if (rc < 0) {
+ dbg_perror("tcsetattr()");
+ rc = -errno;
+ goto error;
+ }
+
+ /* Set ready to read/write */
+ v24 = TIOCM_DTR | TIOCM_RTS;
+ rc = ioctl(fd, TIOCMBIS, &v24);
+ if (rc < 0) {
+ dbg_perror("ioctl(TIOCMBIS)");
+ rc = -errno;
+ goto error;
+ }
+
+ return fd;
+
+error:
+ if (fd)
+ close(fd);
+ return rc;
+}
+
+static int
+_osmo_serial_set_baudrate(int fd, speed_t baudrate)
+{
+ int rc;
+ struct termios tio;
+
+ rc = tcgetattr(fd, &tio);
+ if (rc < 0) {
+ dbg_perror("tcgetattr()");
+ return -errno;
+ }
+ cfsetispeed(&tio, baudrate);
+ cfsetospeed(&tio, baudrate);
+
+ rc = tcsetattr(fd, TCSANOW, &tio);
+ if (rc < 0) {
+ dbg_perror("tcgetattr()");
+ return -errno;
+ }
+
+ return 0;
+}
+
+/*! \brief Change current baudrate
+ * \param[in] fd File descriptor of the open device
+ * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
+ * \returns 0 for success or negative errno.
+ */
+int
+osmo_serial_set_baudrate(int fd, speed_t baudrate)
+{
+ osmo_serial_clear_custom_baudrate(fd);
+ return _osmo_serial_set_baudrate(fd, baudrate);
+}
+
+/*! \brief Change current baudrate to a custom one using OS specific method
+ * \param[in] fd File descriptor of the open device
+ * \param[in] baudrate Baudrate as integer
+ * \returns 0 for success or negative errno.
+ *
+ * This function might not work on all OS or with all type of serial adapters
+ */
+int
+osmo_serial_set_custom_baudrate(int fd, int baudrate)
+{
+#ifdef __linux__
+ int rc;
+ struct serial_struct ser_info;
+
+ rc = ioctl(fd, TIOCGSERIAL, &ser_info);
+ if (rc < 0) {
+ dbg_perror("ioctl(TIOCGSERIAL)");
+ return -errno;
+ }
+
+ ser_info.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
+ ser_info.custom_divisor = ser_info.baud_base / baudrate;
+
+ rc = ioctl(fd, TIOCSSERIAL, &ser_info);
+ if (rc < 0) {
+ dbg_perror("ioctl(TIOCSSERIAL)");
+ return -errno;
+ }
+
+ return _osmo_serial_set_baudrate(fd, B38400); /* 38400 is a kind of magic ... */
+#elif defined(__APPLE__)
+#ifndef IOSSIOSPEED
+#define IOSSIOSPEED _IOW('T', 2, speed_t)
+#endif
+ int rc;
+
+ unsigned int speed = baudrate;
+ rc = ioctl(fd, IOSSIOSPEED, &speed);
+ if (rc < 0) {
+ dbg_perror("ioctl(IOSSIOSPEED)");
+ return -errno;
+ }
+ return 0;
+#else
+#warning osmo_serial_set_custom_baudrate: unsupported platform
+ return 0;
+#endif
+}
+
+/*! \brief Clear any custom baudrate
+ * \param[in] fd File descriptor of the open device
+ * \returns 0 for success or negative errno.
+ *
+ * This function might not work on all OS or with all type of serial adapters
+ */
+int
+osmo_serial_clear_custom_baudrate(int fd)
+{
+#ifdef __linux__
+ int rc;
+ struct serial_struct ser_info;
+
+ rc = ioctl(fd, TIOCGSERIAL, &ser_info);
+ if (rc < 0) {
+ dbg_perror("ioctl(TIOCGSERIAL)");
+ return -errno;
+ }
+
+ ser_info.flags = ASYNC_LOW_LATENCY;
+ ser_info.custom_divisor = 0;
+
+ rc = ioctl(fd, TIOCSSERIAL, &ser_info);
+ if (rc < 0) {
+ dbg_perror("ioctl(TIOCSSERIAL)");
+ return -errno;
+ }
+#endif
+ return 0;
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/signal.c b/src/shared/libosmocore/src/signal.c
new file mode 100644
index 00000000..7ddc2e04
--- /dev/null
+++ b/src/shared/libosmocore/src/signal.c
@@ -0,0 +1,109 @@
+/* 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 <osmocom/core/signal.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/linuxlist.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/*! \addtogroup signal
+ * @{
+ */
+/*! \file signal.c */
+
+
+void *tall_sigh_ctx;
+static LLIST_HEAD(signal_handler_list);
+
+struct signal_handler {
+ struct llist_head entry;
+ unsigned int subsys;
+ osmo_signal_cbfn *cbfn;
+ void *data;
+};
+
+
+/*! \brief Register a new signal handler
+ * \param[in] subsys Subsystem number
+ * \param[in] cbfn Callback function
+ * \param[in] data Data passed through to callback
+ */
+int osmo_signal_register_handler(unsigned int subsys,
+ osmo_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;
+}
+
+/*! \brief Unregister signal handler
+ * \param[in] subsys Subsystem number
+ * \param[in] cbfn Callback function
+ * \param[in] data Data passed through to callback
+ */
+void osmo_signal_unregister_handler(unsigned int subsys,
+ osmo_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;
+ }
+ }
+}
+
+/*! \brief dispatch (deliver) a new signal to all registered handlers
+ * \param[in] subsys Subsystem number
+ * \param[in] signal Signal number,
+ * \param[in] signal_data Data to be passed along to handlers
+ */
+void osmo_signal_dispatch(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/src/shared/libosmocore/src/socket.c b/src/shared/libosmocore/src/socket.c
new file mode 100644
index 00000000..8a8829b7
--- /dev/null
+++ b/src/shared/libosmocore/src/socket.c
@@ -0,0 +1,249 @@
+#include "../config.h"
+
+/*! \addtogroup socket
+ * @{
+ */
+
+/*! \file socket.c
+ * \brief Osmocom socket convenience functions
+ */
+
+#ifdef HAVE_SYS_SOCKET_H
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+
+/*! \brief Initialize a socket (including bind/connect)
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] host remote host name or IP address in string form
+ * \param[in] port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ *
+ * This function creates a new socket of the designated \a family, \a
+ * type and \a proto and optionally binds or connects it, depending on
+ * the value of \a flags parameter.
+ */
+int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
+ const char *host, uint16_t port, unsigned int flags)
+{
+ struct addrinfo hints, *result, *rp;
+ int sfd, rc, on = 1;
+ char portbuf[16];
+
+ if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
+ (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
+ return -EINVAL;
+
+ sprintf(portbuf, "%u", port);
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = family;
+ hints.ai_socktype = type;
+ hints.ai_flags = 0;
+ hints.ai_protocol = proto;
+
+ if (flags & OSMO_SOCK_F_BIND)
+ hints.ai_flags |= AI_PASSIVE;
+
+ rc = getaddrinfo(host, portbuf, &hints, &result);
+ if (rc != 0) {
+ perror("getaddrinfo returned NULL");
+ return -EINVAL;
+ }
+
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (sfd == -1)
+ continue;
+ if (flags & OSMO_SOCK_F_NONBLOCK) {
+ if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
+ perror("cannot set this socket unblocking");
+ close(sfd);
+ return -EINVAL;
+ }
+ }
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
+ if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
+ break;
+ } else {
+ rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on));
+ if (rc < 0) {
+ perror("cannot setsockopt socket");
+ break;
+ }
+ if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
+ break;
+ }
+ close(sfd);
+ }
+ freeaddrinfo(result);
+
+ if (rp == NULL) {
+ perror("unable to connect/bind socket");
+ return -ENODEV;
+ }
+
+ setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ /* Make sure to call 'listen' on a bound, connection-oriented sock */
+ if (flags & OSMO_SOCK_F_BIND) {
+ switch (type) {
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ listen(sfd, 10);
+ break;
+ }
+ }
+ return sfd;
+}
+
+/*! \brief Initialize a socket and fill \ref osmo_fd
+ * \param[out] osmocom file descriptor (will be filled in)
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] host remote host name or IP address in string form
+ * \param[in] port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ *
+ * This function creates (and optionall binds/connects) a socket using
+ * \ref osmo_sock_init, but also fills the \a ofd structure.
+ */
+int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
+ const char *host, uint16_t port, unsigned int flags)
+{
+ int sfd, rc;
+
+ sfd = osmo_sock_init(family, type, proto, host, port, flags);
+ if (sfd < 0)
+ return sfd;
+
+ ofd->fd = sfd;
+ ofd->when = BSC_FD_READ;
+
+ rc = osmo_fd_register(ofd);
+ if (rc < 0) {
+ close(sfd);
+ return rc;
+ }
+
+ return sfd;
+}
+
+/*! \brief Initialize a socket and fill \ref sockaddr
+ * \param[out] ss socket address (will be filled in)
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ *
+ * This function creates (and optionall binds/connects) a socket using
+ * \ref osmo_sock_init, but also fills the \a ss structure.
+ */
+int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
+ uint8_t proto, unsigned int flags)
+{
+ char host[NI_MAXHOST];
+ uint16_t port;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int s, sa_len;
+
+ /* determine port and host from ss */
+ switch (ss->sa_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *) ss;
+ sa_len = sizeof(struct sockaddr_in);
+ port = ntohs(sin->sin_port);
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) ss;
+ sa_len = sizeof(struct sockaddr_in6);
+ port = ntohs(sin6->sin6_port);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
+ NULL, 0, NI_NUMERICHOST);
+ if (s != 0) {
+ perror("getnameinfo failed");
+ return s;
+ }
+
+ return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
+}
+
+static int sockaddr_equal(const struct sockaddr *a,
+ const struct sockaddr *b, unsigned int len)
+{
+ struct sockaddr_in *sin_a, *sin_b;
+ struct sockaddr_in6 *sin6_a, *sin6_b;
+
+ if (a->sa_family != b->sa_family)
+ return 0;
+
+ switch (a->sa_family) {
+ case AF_INET:
+ sin_a = (struct sockaddr_in *)a;
+ sin_b = (struct sockaddr_in *)b;
+ if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
+ sizeof(struct in_addr)))
+ return 1;
+ break;
+ case AF_INET6:
+ sin6_a = (struct sockaddr_in6 *)a;
+ sin6_b = (struct sockaddr_in6 *)b;
+ if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
+ sizeof(struct in6_addr)))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+/*! \brief Determine if the given address is a local address
+ * \param[in] addr Socket Address
+ * \param[in] addrlen Length of socket address in bytes
+ * \returns 1 if address is local, 0 otherwise.
+ */
+int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen)
+{
+ struct ifaddrs *ifaddr, *ifa;
+
+ if (getifaddrs(&ifaddr) == -1) {
+ perror("getifaddrs");
+ return -EIO;
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
+ if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif /* HAVE_SYS_SOCKET_H */
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/statistics.c b/src/shared/libosmocore/src/statistics.c
new file mode 100644
index 00000000..e28541ba
--- /dev/null
+++ b/src/shared/libosmocore/src/statistics.c
@@ -0,0 +1,76 @@
+/* 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 <string.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/statistics.h>
+
+static LLIST_HEAD(counters);
+
+void *tall_ctr_ctx;
+
+struct osmo_counter *osmo_counter_alloc(const char *name)
+{
+ struct osmo_counter *ctr = talloc_zero(tall_ctr_ctx, struct osmo_counter);
+
+ if (!ctr)
+ return NULL;
+
+ ctr->name = name;
+ llist_add_tail(&ctr->list, &counters);
+
+ return ctr;
+}
+
+void osmo_counter_free(struct osmo_counter *ctr)
+{
+ llist_del(&ctr->list);
+ talloc_free(ctr);
+}
+
+int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *),
+ void *data)
+{
+ struct osmo_counter *ctr;
+ int rc = 0;
+
+ llist_for_each_entry(ctr, &counters, list) {
+ rc = handle_counter(ctr, data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
+struct osmo_counter *osmo_counter_get_by_name(const char *name)
+{
+ struct osmo_counter *ctr;
+
+ llist_for_each_entry(ctr, &counters, list) {
+ if (!strcmp(ctr->name, name))
+ return ctr;
+ }
+ return NULL;
+}
diff --git a/src/shared/libosmocore/src/talloc.c b/src/shared/libosmocore/src/talloc.c
new file mode 100644
index 00000000..d3a0690f
--- /dev/null
+++ b/src/shared/libosmocore/src/talloc.c
@@ -0,0 +1,1804 @@
+/*
+ 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 <unistd.h>
+#include <stdio.h>
+#include <stdbool.h>
+#define __USE_GNU
+#include <string.h>
+#undef __USE_GNU
+#include <osmocom/core/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/src/shared/libosmocore/src/timer.c b/src/shared/libosmocore/src/timer.c
new file mode 100644
index 00000000..79d4ad6d
--- /dev/null
+++ b/src/shared/libosmocore/src/timer.c
@@ -0,0 +1,236 @@
+/*
+ * (C) 2008,2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2011 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * Authors: Holger Hans Peter Freyther <zecke@selfish.org>
+ * Harald Welte <laforge@gnumonks.org>
+ * Pablo Neira Ayuso <pablo@gnumonks.org>
+ *
+ * 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.
+ *
+ */
+
+/* These store the amount of time that we wait until next timer expires. */
+static struct timeval nearest;
+static struct timeval *nearest_p;
+
+/*! \addtogroup timer
+ * @{
+ */
+
+/*! \file timer.c
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <limits.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/timer_compat.h>
+#include <osmocom/core/linuxlist.h>
+
+static struct rb_root timer_root = RB_ROOT;
+
+static void __add_timer(struct osmo_timer_list *timer)
+{
+ struct rb_node **new = &(timer_root.rb_node);
+ struct rb_node *parent = NULL;
+
+ while (*new) {
+ struct osmo_timer_list *this;
+
+ this = container_of(*new, struct osmo_timer_list, node);
+
+ parent = *new;
+ if (timercmp(&timer->timeout, &this->timeout, <))
+ new = &((*new)->rb_left);
+ else
+ new = &((*new)->rb_right);
+ }
+
+ rb_link_node(&timer->node, parent, new);
+ rb_insert_color(&timer->node, &timer_root);
+}
+
+/*! \brief add a new timer to the timer management
+ * \param[in] timer the timer that should be added
+ */
+void osmo_timer_add(struct osmo_timer_list *timer)
+{
+ osmo_timer_del(timer);
+ timer->active = 1;
+ INIT_LLIST_HEAD(&timer->list);
+ __add_timer(timer);
+}
+
+/*! \brief schedule a timer at a given future relative time
+ * \param[in] timer the to-be-added timer
+ * \param[in] seconds number of seconds from now
+ * \param[in] microseconds number of microseconds from now
+ *
+ * This function can be used to (re-)schedule a given timer at a
+ * specified number of seconds+microseconds in the future. It will
+ * internally add it to the timer management data structures, thus
+ * osmo_timer_add() is automatically called.
+ */
+void
+osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds)
+{
+ struct timeval current_time;
+
+ gettimeofday(&current_time, NULL);
+ timer->timeout.tv_sec = seconds;
+ timer->timeout.tv_usec = microseconds;
+ timeradd(&timer->timeout, &current_time, &timer->timeout);
+ osmo_timer_add(timer);
+}
+
+/*! \brief delete a timer from timer management
+ * \param[in] timer the to-be-deleted timer
+ *
+ * This function can be used to delete a previously added/scheduled
+ * timer from the timer management code.
+ */
+void osmo_timer_del(struct osmo_timer_list *timer)
+{
+ if (timer->active) {
+ timer->active = 0;
+ rb_erase(&timer->node, &timer_root);
+ /* make sure this is not already scheduled for removal. */
+ if (!llist_empty(&timer->list))
+ llist_del_init(&timer->list);
+ }
+}
+
+/*! \brief check if given timer is still pending
+ * \param[in] timer the to-be-checked timer
+ * \return 1 if pending, 0 otherwise
+ *
+ * This function can be used to determine whether a given timer
+ * has alredy expired (returns 0) or is still pending (returns 1)
+ */
+int osmo_timer_pending(struct osmo_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 *osmo_timers_nearest(void)
+{
+ /* nearest_p is exactly what we need already: NULL if nothing is
+ * waiting, {0,0} if we must dispatch immediately, and the correct
+ * delay if we need to wait */
+ return nearest_p;
+}
+
+static void update_nearest(struct timeval *cand, struct timeval *current)
+{
+ if (cand->tv_sec != LONG_MAX) {
+ if (timercmp(cand, current, >))
+ timersub(cand, current, &nearest);
+ else {
+ /* loop again inmediately */
+ nearest.tv_sec = 0;
+ nearest.tv_usec = 0;
+ }
+ nearest_p = &nearest;
+ } else {
+ nearest_p = NULL;
+ }
+}
+
+/*
+ * Find the nearest time and update s_nearest_time
+ */
+void osmo_timers_prepare(void)
+{
+ struct rb_node *node;
+ struct timeval current;
+
+ gettimeofday(&current, NULL);
+
+ node = rb_first(&timer_root);
+ if (node) {
+ struct osmo_timer_list *this;
+ this = container_of(node, struct osmo_timer_list, node);
+ update_nearest(&this->timeout, &current);
+ } else {
+ nearest_p = NULL;
+ }
+}
+
+/*
+ * fire all timers... and remove them
+ */
+int osmo_timers_update(void)
+{
+ struct timeval current_time;
+ struct rb_node *node;
+ struct llist_head timer_eviction_list;
+ struct osmo_timer_list *this;
+ int work = 0;
+
+ gettimeofday(&current_time, NULL);
+
+ INIT_LLIST_HEAD(&timer_eviction_list);
+ for (node = rb_first(&timer_root); node; node = rb_next(node)) {
+ this = container_of(node, struct osmo_timer_list, node);
+
+ if (timercmp(&this->timeout, &current_time, >))
+ break;
+
+ llist_add(&this->list, &timer_eviction_list);
+ }
+
+ /*
+ * The callbacks might mess with our list and in this case
+ * even llist_for_each_entry_safe is not safe to use. To allow
+ * osmo_timer_del to be called from within the callback we need
+ * to restart the iteration for each element scheduled for removal.
+ *
+ * The problematic scenario is the following: Given two timers A
+ * and B that have expired at the same time. Thus, they are both
+ * in the eviction list in this order: A, then B. If we remove
+ * timer B from the A's callback, we continue with B in the next
+ * iteration step, leading to an access-after-release.
+ */
+restart:
+ llist_for_each_entry(this, &timer_eviction_list, list) {
+ osmo_timer_del(this);
+ this->cb(this->data);
+ work = 1;
+ goto restart;
+ }
+
+ return work;
+}
+
+int osmo_timers_check(void)
+{
+ struct rb_node *node;
+ int i = 0;
+
+ for (node = rb_first(&timer_root); node; node = rb_next(node)) {
+ i++;
+ }
+ return i;
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/utils.c b/src/shared/libosmocore/src/utils.c
new file mode 100644
index 00000000..0a970ac8
--- /dev/null
+++ b/src/shared/libosmocore/src/utils.c
@@ -0,0 +1,215 @@
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <osmocom/core/utils.h>
+
+/*! \addtogroup utils
+ * @{
+ */
+
+/*! \file utils.c */
+
+static char namebuf[255];
+
+/*! \brief get human-readable string for given value
+ * \param[in] vs Array of value_string tuples
+ * \param[in] val Value to be converted
+ * \returns pointer to human-readable string
+ */
+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;
+}
+
+/*! \brief get numeric value for given human-readable string
+ * \param[in] vs Array of value_string tuples
+ * \param[in] str human-readable string
+ * \returns numeric value (>0) or negative numer in case of error
+ */
+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;
+}
+
+/*! \brief Convert BCD-encoded digit into printable character
+ * \param[in] bcd A single BCD-encoded digit
+ * \returns single printable character
+ */
+char osmo_bcd2char(uint8_t bcd)
+{
+ if (bcd < 0xa)
+ return '0' + bcd;
+ else
+ return 'A' + (bcd - 0xa);
+}
+
+/* only works for numbers in ascii */
+uint8_t osmo_char2bcd(char c)
+{
+ return c - 0x30;
+}
+
+int osmo_hexparse(const char *str, uint8_t *b, int max_len)
+
+{
+ int i, l, v;
+
+ l = strlen(str);
+ if ((l&1) || ((l>>1) > max_len))
+ return -1;
+
+ memset(b, 0x00, max_len);
+
+ for (i=0; i<l; i++) {
+ char c = str[i];
+ if (c >= '0' && c <= '9')
+ v = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ v = 10 + (c - 'a');
+ else if (c >= 'A' && c <= 'F')
+ v = 10 + (c - 'A');
+ else
+ return -1;
+ b[i>>1] |= v << (i&1 ? 0 : 4);
+ }
+
+ return i>>1;
+}
+
+static char hexd_buff[4096];
+
+static char *_osmo_hexdump(const unsigned char *buf, int len, char *delim)
+{
+ 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);
+ if (len_remain <= 0)
+ break;
+ int rc = snprintf(cur, len_remain, "%02x%s", buf[i], delim);
+ if (rc <= 0)
+ break;
+ cur += rc;
+ }
+ hexd_buff[sizeof(hexd_buff)-1] = 0;
+ return hexd_buff;
+}
+
+/*! \brief Convert a sequence of unpacked bits to ASCII string
+ * \param[in] bits A sequence of unpacked bits
+ * \param[in] len Length of bits
+ */
+char *osmo_ubit_dump(const uint8_t *bits, unsigned int len)
+{
+ int i;
+
+ if (len > sizeof(hexd_buff)-1)
+ len = sizeof(hexd_buff)-1;
+ memset(hexd_buff, 0, sizeof(hexd_buff));
+
+ for (i = 0; i < len; i++) {
+ char outch;
+ switch (bits[i]) {
+ case 0:
+ outch = '0';
+ break;
+ case 0xff:
+ outch = '?';
+ break;
+ case 1:
+ outch = '1';
+ break;
+ default:
+ outch = 'E';
+ break;
+ }
+ hexd_buff[i] = outch;
+ }
+ hexd_buff[sizeof(hexd_buff)-1] = 0;
+ return hexd_buff;
+}
+
+/*! \brief Convert binary sequence to hexadecimal ASCII string
+ * \param[in] buf pointer to sequence of bytes
+ * \param[in] len length of buf in number of bytes
+ * \returns pointer to zero-terminated string
+ *
+ * This function will print a sequence of bytes as hexadecimal numbers,
+ * adding one space character between each byte (e.g. "1a ef d9")
+ */
+char *osmo_hexdump(const unsigned char *buf, int len)
+{
+ return _osmo_hexdump(buf, len, " ");
+}
+
+/*! \brief Convert binary sequence to hexadecimal ASCII string
+ * \param[in] buf pointer to sequence of bytes
+ * \param[in] len length of buf in number of bytes
+ * \returns pointer to zero-terminated string
+ *
+ * This function will print a sequence of bytes as hexadecimal numbers,
+ * without any space character between each byte (e.g. "1aefd9")
+ */
+char *osmo_hexdump_nospc(const unsigned char *buf, int len)
+{
+ return _osmo_hexdump(buf, len, "");
+}
+
+ /* Compat with previous typo to preserve abi */
+char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len)
+ __attribute__((weak, alias("osmo_hexdump_nospc")));
+
+#include "../config.h"
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+/*! \brief Convert an entire string to lower case
+ * \param[out] out output string, caller-allocated
+ * \param[in] in input string
+ */
+void osmo_str2lower(char *out, const char *in)
+{
+ unsigned int i;
+
+ for (i = 0; i < strlen(in); i++)
+ out[i] = tolower(in[i]);
+ out[strlen(in)] = '\0';
+}
+
+/*! \brief Convert an entire string to upper case
+ * \param[out] out output string, caller-allocated
+ * \param[in] in input string
+ */
+void osmo_str2upper(char *out, const char *in)
+{
+ unsigned int i;
+
+ for (i = 0; i < strlen(in); i++)
+ out[i] = toupper(in[i]);
+ out[strlen(in)] = '\0';
+}
+#endif /* HAVE_CTYPE_H */
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/vty/Makefile.am b/src/shared/libosmocore/src/vty/Makefile.am
new file mode 100644
index 00000000..b7be6f84
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/Makefile.am
@@ -0,0 +1,15 @@
+# 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
+
+if ENABLE_VTY
+lib_LTLIBRARIES = libosmovty.la
+
+libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \
+ telnet_interface.c logging_vty.c
+libosmovty_la_LDFLAGS = -version-info $(LIBVERSION)
+libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la
+endif
diff --git a/src/shared/libosmocore/src/vty/buffer.c b/src/shared/libosmocore/src/vty/buffer.c
new file mode 100644
index 00000000..e385f9fd
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/buffer.c
@@ -0,0 +1,463 @@
+/*
+ * Buffering of output and input.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stddef.h>
+#include <sys/uio.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/vty.h>
+
+/* Buffer master. */
+struct buffer {
+ /* Data list. */
+ struct buffer_data *head;
+ struct buffer_data *tail;
+
+ /* Size of each buffer_data chunk. */
+ size_t size;
+};
+
+/* Data container. */
+struct buffer_data {
+ struct buffer_data *next;
+
+ /* Location to add new data. */
+ size_t cp;
+
+ /* Pointer to data not yet flushed. */
+ size_t sp;
+
+ /* Actual data stream (variable length). */
+ unsigned char data[0]; /* real dimension is buffer->size */
+};
+
+/* It should always be true that: 0 <= sp <= cp <= size */
+
+/* Default buffer size (used if none specified). It is rounded up to the
+ next page boundery. */
+#define BUFFER_SIZE_DEFAULT 4096
+
+#define BUFFER_DATA_FREE(D) talloc_free((D))
+
+/* Make new buffer. */
+struct buffer *buffer_new(void *ctx, size_t size)
+{
+ struct buffer *b;
+
+ b = talloc_zero(ctx, struct buffer);
+
+ if (size)
+ b->size = size;
+ else {
+ static size_t default_size;
+ if (!default_size) {
+ long pgsz = sysconf(_SC_PAGESIZE);
+ default_size =
+ ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz);
+ }
+ b->size = default_size;
+ }
+
+ return b;
+}
+
+/* Free buffer. */
+void buffer_free(struct buffer *b)
+{
+ buffer_reset(b);
+ talloc_free(b);
+}
+
+/* Make string clone. */
+char *buffer_getstr(struct buffer *b)
+{
+ size_t totlen = 0;
+ struct buffer_data *data;
+ char *s;
+ char *p;
+
+ for (data = b->head; data; data = data->next)
+ totlen += data->cp - data->sp;
+ if (!(s = _talloc_zero(tall_vty_ctx, (totlen + 1), "buffer_getstr")))
+ return NULL;
+ p = s;
+ for (data = b->head; data; data = data->next) {
+ memcpy(p, data->data + data->sp, data->cp - data->sp);
+ p += data->cp - data->sp;
+ }
+ *p = '\0';
+ return s;
+}
+
+/* Return 1 if buffer is empty. */
+int buffer_empty(struct buffer *b)
+{
+ return (b->head == NULL);
+}
+
+/* Clear and free all allocated data. */
+void buffer_reset(struct buffer *b)
+{
+ struct buffer_data *data;
+ struct buffer_data *next;
+
+ for (data = b->head; data; data = next) {
+ next = data->next;
+ BUFFER_DATA_FREE(data);
+ }
+ b->head = b->tail = NULL;
+}
+
+/* Add buffer_data to the end of buffer. */
+static struct buffer_data *buffer_add(struct buffer *b)
+{
+ struct buffer_data *d;
+
+ d = _talloc_zero(b,
+ offsetof(struct buffer_data, data[b->size]),
+ "buffer_add");
+ if (!d)
+ return NULL;
+ d->cp = d->sp = 0;
+ d->next = NULL;
+
+ if (b->tail)
+ b->tail->next = d;
+ else
+ b->head = d;
+ b->tail = d;
+
+ return d;
+}
+
+/* Write data to buffer. */
+void buffer_put(struct buffer *b, const void *p, size_t size)
+{
+ struct buffer_data *data = b->tail;
+ const char *ptr = p;
+
+ /* We use even last one byte of data buffer. */
+ while (size) {
+ size_t chunk;
+
+ /* If there is no data buffer add it. */
+ if (data == NULL || data->cp == b->size)
+ data = buffer_add(b);
+
+ chunk =
+ ((size <=
+ (b->size - data->cp)) ? size : (b->size - data->cp));
+ memcpy((data->data + data->cp), ptr, chunk);
+ size -= chunk;
+ ptr += chunk;
+ data->cp += chunk;
+ }
+}
+
+/* Insert character into the buffer. */
+void buffer_putc(struct buffer *b, u_char c)
+{
+ buffer_put(b, &c, 1);
+}
+
+/* Put string to the buffer. */
+void buffer_putstr(struct buffer *b, const char *c)
+{
+ buffer_put(b, c, strlen(c));
+}
+
+/* Keep flushing data to the fd until the buffer is empty or an error is
+ encountered or the operation would block. */
+buffer_status_t buffer_flush_all(struct buffer *b, int fd)
+{
+ buffer_status_t ret;
+ struct buffer_data *head;
+ size_t head_sp;
+
+ if (!b->head)
+ return BUFFER_EMPTY;
+ head_sp = (head = b->head)->sp;
+ /* Flush all data. */
+ while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
+ if ((b->head == head) && (head_sp == head->sp)
+ && (errno != EINTR))
+ /* No data was flushed, so kernel buffer must be full. */
+ return ret;
+ head_sp = (head = b->head)->sp;
+ }
+
+ return ret;
+}
+
+#if 0
+/* Flush enough data to fill a terminal window of the given scene (used only
+ by vty telnet interface). */
+buffer_status_t
+buffer_flush_window(struct buffer * b, int fd, int width, int height,
+ int erase_flag, int no_more_flag)
+{
+ int nbytes;
+ int iov_alloc;
+ int iov_index;
+ struct iovec *iov;
+ struct iovec small_iov[3];
+ char more[] = " --More-- ";
+ char erase[] =
+ { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+ };
+ struct buffer_data *data;
+ int column;
+
+ if (!b->head)
+ return BUFFER_EMPTY;
+
+ if (height < 1) {
+ zlog_warn
+ ("%s called with non-positive window height %d, forcing to 1",
+ __func__, height);
+ height = 1;
+ } else if (height >= 2)
+ height--;
+ if (width < 1) {
+ zlog_warn
+ ("%s called with non-positive window width %d, forcing to 1",
+ __func__, width);
+ width = 1;
+ }
+
+ /* For erase and more data add two to b's buffer_data count. */
+ if (b->head->next == NULL) {
+ iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]);
+ iov = small_iov;
+ } else {
+ iov_alloc = ((height * (width + 2)) / b->size) + 10;
+ iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
+ }
+ iov_index = 0;
+
+ /* Previously print out is performed. */
+ if (erase_flag) {
+ iov[iov_index].iov_base = erase;
+ iov[iov_index].iov_len = sizeof erase;
+ iov_index++;
+ }
+
+ /* Output data. */
+ column = 1; /* Column position of next character displayed. */
+ for (data = b->head; data && (height > 0); data = data->next) {
+ size_t cp;
+
+ cp = data->sp;
+ while ((cp < data->cp) && (height > 0)) {
+ /* Calculate lines remaining and column position after displaying
+ this character. */
+ if (data->data[cp] == '\r')
+ column = 1;
+ else if ((data->data[cp] == '\n') || (column == width)) {
+ column = 1;
+ height--;
+ } else
+ column++;
+ cp++;
+ }
+ iov[iov_index].iov_base = (char *)(data->data + data->sp);
+ iov[iov_index++].iov_len = cp - data->sp;
+ data->sp = cp;
+
+ if (iov_index == iov_alloc)
+ /* This should not ordinarily happen. */
+ {
+ iov_alloc *= 2;
+ if (iov != small_iov) {
+ zlog_warn("%s: growing iov array to %d; "
+ "width %d, height %d, size %lu",
+ __func__, iov_alloc, width, height,
+ (u_long) b->size);
+ iov =
+ XREALLOC(MTYPE_TMP, iov,
+ iov_alloc * sizeof(*iov));
+ } else {
+ /* This should absolutely never occur. */
+ zlog_err
+ ("%s: corruption detected: iov_small overflowed; "
+ "head %p, tail %p, head->next %p",
+ __func__, b->head, b->tail, b->head->next);
+ iov =
+ XMALLOC(MTYPE_TMP,
+ iov_alloc * sizeof(*iov));
+ memcpy(iov, small_iov, sizeof(small_iov));
+ }
+ }
+ }
+
+ /* In case of `more' display need. */
+ if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
+ iov[iov_index].iov_base = more;
+ iov[iov_index].iov_len = sizeof more;
+ iov_index++;
+ }
+#ifdef IOV_MAX
+ /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
+ example: Solaris2.6 are defined IOV_MAX size at 16. */
+ {
+ struct iovec *c_iov = iov;
+ nbytes = 0; /* Make sure it's initialized. */
+
+ while (iov_index > 0) {
+ int iov_size;
+
+ iov_size =
+ ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
+ if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
+ zlog_warn("%s: writev to fd %d failed: %s",
+ __func__, fd, safe_strerror(errno));
+ break;
+ }
+
+ /* move pointer io-vector */
+ c_iov += iov_size;
+ iov_index -= iov_size;
+ }
+ }
+#else /* IOV_MAX */
+ if ((nbytes = writev(fd, iov, iov_index)) < 0)
+ zlog_warn("%s: writev to fd %d failed: %s",
+ __func__, fd, safe_strerror(errno));
+#endif /* IOV_MAX */
+
+ /* Free printed buffer data. */
+ while (b->head && (b->head->sp == b->head->cp)) {
+ struct buffer_data *del;
+ if (!(b->head = (del = b->head)->next))
+ b->tail = NULL;
+ BUFFER_DATA_FREE(del);
+ }
+
+ if (iov != small_iov)
+ XFREE(MTYPE_TMP, iov);
+
+ return (nbytes < 0) ? BUFFER_ERROR :
+ (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
+}
+#endif
+
+/* This function (unlike other buffer_flush* functions above) is designed
+to work with non-blocking sockets. It does not attempt to write out
+all of the queued data, just a "big" chunk. It returns 0 if it was
+able to empty out the buffers completely, 1 if more flushing is
+required later, or -1 on a fatal write error. */
+buffer_status_t buffer_flush_available(struct buffer * b, int fd)
+{
+
+/* These are just reasonable values to make sure a significant amount of
+data is written. There's no need to go crazy and try to write it all
+in one shot. */
+#ifdef IOV_MAX
+#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
+#else
+#define MAX_CHUNKS 16
+#endif
+#define MAX_FLUSH 131072
+
+ struct buffer_data *d;
+ size_t written;
+ struct iovec iov[MAX_CHUNKS];
+ size_t iovcnt = 0;
+ size_t nbyte = 0;
+
+ for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
+ d = d->next, iovcnt++) {
+ iov[iovcnt].iov_base = d->data + d->sp;
+ nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
+ }
+
+ if (!nbyte)
+ /* No data to flush: should we issue a warning message? */
+ return BUFFER_EMPTY;
+
+ /* only place where written should be sign compared */
+ if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) {
+ if (ERRNO_IO_RETRY(errno))
+ /* Calling code should try again later. */
+ return BUFFER_PENDING;
+ return BUFFER_ERROR;
+ }
+
+ /* Free printed buffer data. */
+ while (written > 0) {
+ struct buffer_data *d;
+ if (!(d = b->head))
+ break;
+ if (written < d->cp - d->sp) {
+ d->sp += written;
+ return BUFFER_PENDING;
+ }
+
+ written -= (d->cp - d->sp);
+ if (!(b->head = d->next))
+ b->tail = NULL;
+ BUFFER_DATA_FREE(d);
+ }
+
+ return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+
+#undef MAX_CHUNKS
+#undef MAX_FLUSH
+}
+
+buffer_status_t
+buffer_write(struct buffer * b, int fd, const void *p, size_t size)
+{
+ ssize_t nbytes;
+
+#if 0
+ /* Should we attempt to drain any previously buffered data? This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */
+
+ if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
+ return BUFFER_ERROR;
+#endif
+ if (b->head)
+ /* Buffer is not empty, so do not attempt to write the new data. */
+ nbytes = 0;
+ else if ((nbytes = write(fd, p, size)) < 0) {
+ if (ERRNO_IO_RETRY(errno))
+ nbytes = 0;
+ else
+ return BUFFER_ERROR;
+ }
+ /* Add any remaining data to the buffer. */
+ {
+ size_t written = nbytes;
+ if (written < size)
+ buffer_put(b, ((const char *)p) + written,
+ size - written);
+ }
+ return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+}
diff --git a/src/shared/libosmocore/src/vty/command.c b/src/shared/libosmocore/src/vty/command.c
new file mode 100644
index 00000000..ab1eacaa
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/command.c
@@ -0,0 +1,3241 @@
+/*
+ $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
+
+ Command interpreter routine for virtual terminal [aka TeletYpe]
+ Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra 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, or (at your
+option) any later version.
+
+GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#define _XOPEN_SOURCE
+#include <unistd.h>
+#include <assert.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <osmocom/vty/vector.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+
+#include <osmocom/core/talloc.h>
+
+/*! \addtogroup command
+ * @{
+ */
+/*! \file command.c */
+
+#define CONFIGFILE_MASK 022
+
+void *tall_vty_cmd_ctx;
+
+/* Command vector which includes some level of command lists. Normally
+ each daemon maintains each own cmdvec. */
+vector cmdvec;
+
+/* Host information structure. */
+struct host host;
+
+/* Standard command node structures. */
+struct cmd_node auth_node = {
+ AUTH_NODE,
+ "Password: ",
+};
+
+struct cmd_node view_node = {
+ VIEW_NODE,
+ "%s> ",
+};
+
+struct cmd_node auth_enable_node = {
+ AUTH_ENABLE_NODE,
+ "Password: ",
+};
+
+struct cmd_node enable_node = {
+ ENABLE_NODE,
+ "%s# ",
+};
+
+struct cmd_node config_node = {
+ CONFIG_NODE,
+ "%s(config)# ",
+ 1
+};
+
+/* Default motd string. */
+const char *default_motd = "";
+
+/*! \brief print the version (and optionally copyright) information
+ *
+ * This is called from main when a daemon is invoked with -v or --version. */
+void print_version(int print_copyright)
+{
+ printf("%s version %s\n", host.app_info->name, host.app_info->version);
+ if (print_copyright)
+ printf("\n%s\n", host.app_info->copyright);
+}
+
+/* Utility function to concatenate argv argument into a single string
+ with inserting ' ' character between each argument. */
+char *argv_concat(const char **argv, int argc, int shift)
+{
+ int i;
+ size_t len;
+ char *str;
+ char *p;
+
+ len = 0;
+ for (i = shift; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+ if (!len)
+ return NULL;
+ p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
+ for (i = shift; i < argc; i++) {
+ size_t arglen;
+ memcpy(p, argv[i], (arglen = strlen(argv[i])));
+ p += arglen;
+ *p++ = ' ';
+ }
+ *(p - 1) = '\0';
+ return str;
+}
+
+/*! \brief Install top node of command vector. */
+void install_node(struct cmd_node *node, int (*func) (struct vty *))
+{
+ vector_set_index(cmdvec, node->node, node);
+ node->func = func;
+ node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
+}
+
+/* Compare two command's string. Used in sort_node (). */
+static int cmp_node(const void *p, const void *q)
+{
+ struct cmd_element *a = *(struct cmd_element **)p;
+ struct cmd_element *b = *(struct cmd_element **)q;
+
+ return strcmp(a->string, b->string);
+}
+
+static int cmp_desc(const void *p, const void *q)
+{
+ struct desc *a = *(struct desc **)p;
+ struct desc *b = *(struct desc **)q;
+
+ return strcmp(a->cmd, b->cmd);
+}
+
+static int is_config(struct vty *vty)
+{
+ if (vty->node <= CONFIG_NODE)
+ return 0;
+ else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE)
+ return 1;
+ else if (host.app_info->is_config_node)
+ return host.app_info->is_config_node(vty, vty->node);
+ else
+ return vty->node > CONFIG_NODE;
+}
+
+/*! \brief Sort each node's command element according to command string. */
+void sort_node(void)
+{
+ unsigned int i, j;
+ struct cmd_node *cnode;
+ vector descvec;
+ struct cmd_element *cmd_element;
+
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((cnode = vector_slot(cmdvec, i)) != NULL) {
+ vector cmd_vector = cnode->cmd_vector;
+ qsort(cmd_vector->index, vector_active(cmd_vector),
+ sizeof(void *), cmp_node);
+
+ for (j = 0; j < vector_active(cmd_vector); j++)
+ if ((cmd_element =
+ vector_slot(cmd_vector, j)) != NULL
+ && vector_active(cmd_element->strvec)) {
+ descvec =
+ vector_slot(cmd_element->strvec,
+ vector_active
+ (cmd_element->strvec) -
+ 1);
+ qsort(descvec->index,
+ vector_active(descvec),
+ sizeof(void *), cmp_desc);
+ }
+ }
+}
+
+/*! Breaking up string into each command piece. I assume given
+ character is separated by a space character. Return value is a
+ vector which includes char ** data element. */
+vector cmd_make_strvec(const char *string)
+{
+ const char *cp, *start;
+ char *token;
+ int strlen;
+ vector strvec;
+
+ if (string == NULL)
+ return NULL;
+
+ cp = string;
+
+ /* Skip white spaces. */
+ while (isspace((int)*cp) && *cp != '\0')
+ cp++;
+
+ /* Return if there is only white spaces */
+ if (*cp == '\0')
+ return NULL;
+
+ if (*cp == '!' || *cp == '#')
+ return NULL;
+
+ /* Prepare return vector. */
+ strvec = vector_init(VECTOR_MIN_SIZE);
+
+ /* Copy each command piece and set into vector. */
+ while (1) {
+ start = cp;
+ while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
+ *cp != '\0')
+ cp++;
+ strlen = cp - start;
+ token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
+ memcpy(token, start, strlen);
+ *(token + strlen) = '\0';
+ vector_set(strvec, token);
+
+ while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
+ *cp != '\0')
+ cp++;
+
+ if (*cp == '\0')
+ return strvec;
+ }
+}
+
+/*! \brief Free allocated string vector. */
+void cmd_free_strvec(vector v)
+{
+ unsigned int i;
+ char *cp;
+
+ if (!v)
+ return;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((cp = vector_slot(v, i)) != NULL)
+ talloc_free(cp);
+
+ vector_free(v);
+}
+
+/*! \brief Fetch next description. Used in \ref cmd_make_descvec(). */
+static char *cmd_desc_str(const char **string)
+{
+ const char *cp, *start;
+ char *token;
+ int strlen;
+
+ cp = *string;
+
+ if (cp == NULL)
+ return NULL;
+
+ /* Skip white spaces. */
+ while (isspace((int)*cp) && *cp != '\0')
+ cp++;
+
+ /* Return if there is only white spaces */
+ if (*cp == '\0')
+ return NULL;
+
+ start = cp;
+
+ while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
+ cp++;
+
+ strlen = cp - start;
+ token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
+ memcpy(token, start, strlen);
+ *(token + strlen) = '\0';
+
+ *string = cp;
+
+ return token;
+}
+
+/*! \brief New string vector. */
+static vector cmd_make_descvec(const char *string, const char *descstr)
+{
+ int multiple = 0;
+ const char *sp;
+ char *token;
+ int len;
+ const char *cp;
+ const char *dp;
+ vector allvec;
+ vector strvec = NULL;
+ struct desc *desc;
+
+ cp = string;
+ dp = descstr;
+
+ if (cp == NULL)
+ return NULL;
+
+ allvec = vector_init(VECTOR_MIN_SIZE);
+
+ while (1) {
+ while (isspace((int)*cp) && *cp != '\0')
+ cp++;
+
+ if (*cp == '(') {
+ multiple = 1;
+ cp++;
+ }
+ if (*cp == ')') {
+ multiple = 0;
+ cp++;
+ }
+ if (*cp == '|') {
+ if (!multiple) {
+ fprintf(stderr, "Command parse error!: %s\n",
+ string);
+ exit(1);
+ }
+ cp++;
+ }
+
+ while (isspace((int)*cp) && *cp != '\0')
+ cp++;
+
+ if (*cp == '(') {
+ multiple = 1;
+ cp++;
+ }
+
+ if (*cp == '\0')
+ return allvec;
+
+ sp = cp;
+
+ while (!
+ (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
+ || *cp == ')' || *cp == '|') && *cp != '\0')
+ cp++;
+
+ len = cp - sp;
+
+ token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
+ memcpy(token, sp, len);
+ *(token + len) = '\0';
+
+ desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
+ desc->cmd = token;
+ desc->str = cmd_desc_str(&dp);
+
+ if (multiple) {
+ if (multiple == 1) {
+ strvec = vector_init(VECTOR_MIN_SIZE);
+ vector_set(allvec, strvec);
+ }
+ multiple++;
+ } else {
+ strvec = vector_init(VECTOR_MIN_SIZE);
+ vector_set(allvec, strvec);
+ }
+ vector_set(strvec, desc);
+ }
+}
+
+/* Count mandantory string vector size. This is to determine inputed
+ command has enough command length. */
+static int cmd_cmdsize(vector strvec)
+{
+ unsigned int i;
+ int size = 0;
+ vector descvec;
+ struct desc *desc;
+
+ for (i = 0; i < vector_active(strvec); i++)
+ if ((descvec = vector_slot(strvec, i)) != NULL) {
+ if ((vector_active(descvec)) == 1
+ && (desc = vector_slot(descvec, 0)) != NULL) {
+ if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
+ return size;
+ else
+ size++;
+ } else
+ size++;
+ }
+ return size;
+}
+
+/*! \brief Return prompt character of specified node. */
+const char *cmd_prompt(enum node_type node)
+{
+ struct cmd_node *cnode;
+
+ cnode = vector_slot(cmdvec, node);
+ return cnode->prompt;
+}
+
+/*! \brief Install a command into a node
+ * \param[in] ntype Node Type
+ * \param[cmd] element to be installed
+ */
+void install_element(enum node_type ntype, struct cmd_element *cmd)
+{
+ struct cmd_node *cnode;
+
+ cnode = vector_slot(cmdvec, ntype);
+
+ if (cnode == NULL) {
+ fprintf(stderr,
+ "Command node %d doesn't exist, please check it\n",
+ ntype);
+ exit(1);
+ }
+
+ vector_set(cnode->cmd_vector, cmd);
+
+ cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
+ cmd->cmdsize = cmd_cmdsize(cmd->strvec);
+}
+
+/* Install a command into VIEW and ENABLE node */
+void install_element_ve(struct cmd_element *cmd)
+{
+ install_element(VIEW_NODE, cmd);
+ install_element(ENABLE_NODE, cmd);
+}
+
+#ifdef VTY_CRYPT_PW
+static unsigned char itoa64[] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void to64(char *s, long v, int n)
+{
+ while (--n >= 0) {
+ *s++ = itoa64[v & 0x3f];
+ v >>= 6;
+ }
+}
+
+static char *zencrypt(const char *passwd)
+{
+ char salt[6];
+ struct timeval tv;
+ char *crypt(const char *, const char *);
+
+ gettimeofday(&tv, 0);
+
+ to64(&salt[0], random(), 3);
+ to64(&salt[3], tv.tv_usec, 3);
+ salt[5] = '\0';
+
+ return crypt(passwd, salt);
+}
+#endif
+
+/* This function write configuration of this host. */
+static int config_write_host(struct vty *vty)
+{
+ if (host.name)
+ vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
+
+ if (host.encrypt) {
+ if (host.password_encrypt)
+ vty_out(vty, "password 8 %s%s", host.password_encrypt,
+ VTY_NEWLINE);
+ if (host.enable_encrypt)
+ vty_out(vty, "enable password 8 %s%s",
+ host.enable_encrypt, VTY_NEWLINE);
+ } else {
+ if (host.password)
+ vty_out(vty, "password %s%s", host.password,
+ VTY_NEWLINE);
+ if (host.enable)
+ vty_out(vty, "enable password %s%s", host.enable,
+ VTY_NEWLINE);
+ }
+
+ if (host.advanced)
+ vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
+
+ if (host.encrypt)
+ vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
+
+ if (host.lines >= 0)
+ vty_out(vty, "service terminal-length %d%s", host.lines,
+ VTY_NEWLINE);
+
+ if (host.motdfile)
+ vty_out(vty, "banner motd file %s%s", host.motdfile,
+ VTY_NEWLINE);
+ else if (!host.motd)
+ vty_out(vty, "no banner motd%s", VTY_NEWLINE);
+
+ return 1;
+}
+
+/* Utility function for getting command vector. */
+static vector cmd_node_vector(vector v, enum node_type ntype)
+{
+ struct cmd_node *cnode = vector_slot(v, ntype);
+ return cnode->cmd_vector;
+}
+
+/* Completion match types. */
+enum match_type {
+ no_match,
+ extend_match,
+ ipv4_prefix_match,
+ ipv4_match,
+ ipv6_prefix_match,
+ ipv6_match,
+ range_match,
+ vararg_match,
+ partly_match,
+ exact_match
+};
+
+static enum match_type cmd_ipv4_match(const char *str)
+{
+ const char *sp;
+ int dots = 0, nums = 0;
+ char buf[4];
+
+ if (str == NULL)
+ return partly_match;
+
+ for (;;) {
+ memset(buf, 0, sizeof(buf));
+ sp = str;
+ while (*str != '\0') {
+ if (*str == '.') {
+ if (dots >= 3)
+ return no_match;
+
+ if (*(str + 1) == '.')
+ return no_match;
+
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ dots++;
+ break;
+ }
+ if (!isdigit((int)*str))
+ return no_match;
+
+ str++;
+ }
+
+ if (str - sp > 3)
+ return no_match;
+
+ strncpy(buf, sp, str - sp);
+ if (atoi(buf) > 255)
+ return no_match;
+
+ nums++;
+
+ if (*str == '\0')
+ break;
+
+ str++;
+ }
+
+ if (nums < 4)
+ return partly_match;
+
+ return exact_match;
+}
+
+static enum match_type cmd_ipv4_prefix_match(const char *str)
+{
+ const char *sp;
+ int dots = 0;
+ char buf[4];
+
+ if (str == NULL)
+ return partly_match;
+
+ for (;;) {
+ memset(buf, 0, sizeof(buf));
+ sp = str;
+ while (*str != '\0' && *str != '/') {
+ if (*str == '.') {
+ if (dots == 3)
+ return no_match;
+
+ if (*(str + 1) == '.' || *(str + 1) == '/')
+ return no_match;
+
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ dots++;
+ break;
+ }
+
+ if (!isdigit((int)*str))
+ return no_match;
+
+ str++;
+ }
+
+ if (str - sp > 3)
+ return no_match;
+
+ strncpy(buf, sp, str - sp);
+ if (atoi(buf) > 255)
+ return no_match;
+
+ if (dots == 3) {
+ if (*str == '/') {
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ str++;
+ break;
+ } else if (*str == '\0')
+ return partly_match;
+ }
+
+ if (*str == '\0')
+ return partly_match;
+
+ str++;
+ }
+
+ sp = str;
+ while (*str != '\0') {
+ if (!isdigit((int)*str))
+ return no_match;
+
+ str++;
+ }
+
+ if (atoi(sp) > 32)
+ return no_match;
+
+ return exact_match;
+}
+
+#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
+#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
+#define STATE_START 1
+#define STATE_COLON 2
+#define STATE_DOUBLE 3
+#define STATE_ADDR 4
+#define STATE_DOT 5
+#define STATE_SLASH 6
+#define STATE_MASK 7
+
+#ifdef HAVE_IPV6
+
+static enum match_type cmd_ipv6_match(const char *str)
+{
+ int state = STATE_START;
+ int colons = 0, nums = 0, double_colon = 0;
+ const char *sp = NULL;
+ struct sockaddr_in6 sin6_dummy;
+ int ret;
+
+ if (str == NULL)
+ return partly_match;
+
+ if (strspn(str, IPV6_ADDR_STR) != strlen(str))
+ return no_match;
+
+ /* use inet_pton that has a better support,
+ * for example inet_pton can support the automatic addresses:
+ * ::1.2.3.4
+ */
+ ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
+
+ if (ret == 1)
+ return exact_match;
+
+ while (*str != '\0') {
+ switch (state) {
+ case STATE_START:
+ if (*str == ':') {
+ if (*(str + 1) != ':' && *(str + 1) != '\0')
+ return no_match;
+ colons--;
+ state = STATE_COLON;
+ } else {
+ sp = str;
+ state = STATE_ADDR;
+ }
+
+ continue;
+ case STATE_COLON:
+ colons++;
+ if (*(str + 1) == ':')
+ state = STATE_DOUBLE;
+ else {
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+ break;
+ case STATE_DOUBLE:
+ if (double_colon)
+ return no_match;
+
+ if (*(str + 1) == ':')
+ return no_match;
+ else {
+ if (*(str + 1) != '\0')
+ colons++;
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+
+ double_colon++;
+ nums++;
+ break;
+ case STATE_ADDR:
+ if (*(str + 1) == ':' || *(str + 1) == '\0') {
+ if (str - sp > 3)
+ return no_match;
+
+ nums++;
+ state = STATE_COLON;
+ }
+ if (*(str + 1) == '.')
+ state = STATE_DOT;
+ break;
+ case STATE_DOT:
+ state = STATE_ADDR;
+ break;
+ default:
+ break;
+ }
+
+ if (nums > 8)
+ return no_match;
+
+ if (colons > 7)
+ return no_match;
+
+ str++;
+ }
+
+#if 0
+ if (nums < 11)
+ return partly_match;
+#endif /* 0 */
+
+ return exact_match;
+}
+
+static enum match_type cmd_ipv6_prefix_match(const char *str)
+{
+ int state = STATE_START;
+ int colons = 0, nums = 0, double_colon = 0;
+ int mask;
+ const char *sp = NULL;
+ char *endptr = NULL;
+
+ if (str == NULL)
+ return partly_match;
+
+ if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
+ return no_match;
+
+ while (*str != '\0' && state != STATE_MASK) {
+ switch (state) {
+ case STATE_START:
+ if (*str == ':') {
+ if (*(str + 1) != ':' && *(str + 1) != '\0')
+ return no_match;
+ colons--;
+ state = STATE_COLON;
+ } else {
+ sp = str;
+ state = STATE_ADDR;
+ }
+
+ continue;
+ case STATE_COLON:
+ colons++;
+ if (*(str + 1) == '/')
+ return no_match;
+ else if (*(str + 1) == ':')
+ state = STATE_DOUBLE;
+ else {
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+ break;
+ case STATE_DOUBLE:
+ if (double_colon)
+ return no_match;
+
+ if (*(str + 1) == ':')
+ return no_match;
+ else {
+ if (*(str + 1) != '\0' && *(str + 1) != '/')
+ colons++;
+ sp = str + 1;
+
+ if (*(str + 1) == '/')
+ state = STATE_SLASH;
+ else
+ state = STATE_ADDR;
+ }
+
+ double_colon++;
+ nums += 1;
+ break;
+ case STATE_ADDR:
+ if (*(str + 1) == ':' || *(str + 1) == '.'
+ || *(str + 1) == '\0' || *(str + 1) == '/') {
+ if (str - sp > 3)
+ return no_match;
+
+ for (; sp <= str; sp++)
+ if (*sp == '/')
+ return no_match;
+
+ nums++;
+
+ if (*(str + 1) == ':')
+ state = STATE_COLON;
+ else if (*(str + 1) == '.')
+ state = STATE_DOT;
+ else if (*(str + 1) == '/')
+ state = STATE_SLASH;
+ }
+ break;
+ case STATE_DOT:
+ state = STATE_ADDR;
+ break;
+ case STATE_SLASH:
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ state = STATE_MASK;
+ break;
+ default:
+ break;
+ }
+
+ if (nums > 11)
+ return no_match;
+
+ if (colons > 7)
+ return no_match;
+
+ str++;
+ }
+
+ if (state < STATE_MASK)
+ return partly_match;
+
+ mask = strtol(str, &endptr, 10);
+ if (*endptr != '\0')
+ return no_match;
+
+ if (mask < 0 || mask > 128)
+ return no_match;
+
+/* I don't know why mask < 13 makes command match partly.
+ Forgive me to make this comments. I Want to set static default route
+ because of lack of function to originate default in ospf6d; sorry
+ yasu
+ if (mask < 13)
+ return partly_match;
+*/
+
+ return exact_match;
+}
+
+#endif /* HAVE_IPV6 */
+
+#define DECIMAL_STRLEN_MAX 10
+
+static int cmd_range_match(const char *range, const char *str)
+{
+ char *p;
+ char buf[DECIMAL_STRLEN_MAX + 1];
+ char *endptr = NULL;
+
+ if (str == NULL)
+ return 1;
+
+ if (range[1] == '-') {
+ signed long min = 0, max = 0, val;
+
+ val = strtol(str, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ range += 2;
+ p = strchr(range, '-');
+ if (p == NULL)
+ return 0;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return 0;
+ strncpy(buf, range, p - range);
+ buf[p - range] = '\0';
+ min = -strtol(buf, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ range = p + 1;
+ p = strchr(range, '>');
+ if (p == NULL)
+ return 0;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return 0;
+ strncpy(buf, range, p - range);
+ buf[p - range] = '\0';
+ max = strtol(buf, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ if (val < min || val > max)
+ return 0;
+ } else {
+ unsigned long min, max, val;
+
+ val = strtoul(str, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ range++;
+ p = strchr(range, '-');
+ if (p == NULL)
+ return 0;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return 0;
+ strncpy(buf, range, p - range);
+ buf[p - range] = '\0';
+ min = strtoul(buf, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ range = p + 1;
+ p = strchr(range, '>');
+ if (p == NULL)
+ return 0;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return 0;
+ strncpy(buf, range, p - range);
+ buf[p - range] = '\0';
+ max = strtoul(buf, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ if (val < min || val > max)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Make completion match and return match type flag. */
+static enum match_type
+cmd_filter_by_completion(char *command, vector v, unsigned int index)
+{
+ unsigned int i;
+ const char *str;
+ struct cmd_element *cmd_element;
+ enum match_type match_type;
+ vector descvec;
+ struct desc *desc;
+
+ match_type = no_match;
+
+ /* If command and cmd_element string does not match set NULL to vector */
+ for (i = 0; i < vector_active(v); i++)
+ if ((cmd_element = vector_slot(v, i)) != NULL) {
+ if (index >= vector_active(cmd_element->strvec))
+ vector_slot(v, i) = NULL;
+ else {
+ unsigned int j;
+ int matched = 0;
+
+ descvec =
+ vector_slot(cmd_element->strvec, index);
+
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ str = desc->cmd;
+
+ if (CMD_VARARG(str)) {
+ if (match_type <
+ vararg_match)
+ match_type =
+ vararg_match;
+ matched++;
+ } else if (CMD_RANGE(str)) {
+ if (cmd_range_match
+ (str, command)) {
+ if (match_type <
+ range_match)
+ match_type
+ =
+ range_match;
+
+ matched++;
+ }
+ }
+#ifdef HAVE_IPV6
+ else if (CMD_IPV6(str)) {
+ if (cmd_ipv6_match
+ (command)) {
+ if (match_type <
+ ipv6_match)
+ match_type
+ =
+ ipv6_match;
+
+ matched++;
+ }
+ } else if (CMD_IPV6_PREFIX(str)) {
+ if (cmd_ipv6_prefix_match(command)) {
+ if (match_type <
+ ipv6_prefix_match)
+ match_type
+ =
+ ipv6_prefix_match;
+
+ matched++;
+ }
+ }
+#endif /* HAVE_IPV6 */
+ else if (CMD_IPV4(str)) {
+ if (cmd_ipv4_match
+ (command)) {
+ if (match_type <
+ ipv4_match)
+ match_type
+ =
+ ipv4_match;
+
+ matched++;
+ }
+ } else if (CMD_IPV4_PREFIX(str)) {
+ if (cmd_ipv4_prefix_match(command)) {
+ if (match_type <
+ ipv4_prefix_match)
+ match_type
+ =
+ ipv4_prefix_match;
+ matched++;
+ }
+ } else
+ /* Check is this point's argument optional ? */
+ if (CMD_OPTION(str)
+ ||
+ CMD_VARIABLE(str)) {
+ if (match_type <
+ extend_match)
+ match_type =
+ extend_match;
+ matched++;
+ } else
+ if (strncmp
+ (command, str,
+ strlen(command)) ==
+ 0) {
+ if (strcmp(command, str)
+ == 0)
+ match_type =
+ exact_match;
+ else {
+ if (match_type <
+ partly_match)
+ match_type
+ =
+ partly_match;
+ }
+ matched++;
+ }
+ }
+ if (!matched)
+ vector_slot(v, i) = NULL;
+ }
+ }
+ return match_type;
+}
+
+/* Filter vector by command character with index. */
+static enum match_type
+cmd_filter_by_string(char *command, vector v, unsigned int index)
+{
+ unsigned int i;
+ const char *str;
+ struct cmd_element *cmd_element;
+ enum match_type match_type;
+ vector descvec;
+ struct desc *desc;
+
+ match_type = no_match;
+
+ /* If command and cmd_element string does not match set NULL to vector */
+ for (i = 0; i < vector_active(v); i++)
+ if ((cmd_element = vector_slot(v, i)) != NULL) {
+ /* If given index is bigger than max string vector of command,
+ set NULL */
+ if (index >= vector_active(cmd_element->strvec))
+ vector_slot(v, i) = NULL;
+ else {
+ unsigned int j;
+ int matched = 0;
+
+ descvec =
+ vector_slot(cmd_element->strvec, index);
+
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ str = desc->cmd;
+
+ if (CMD_VARARG(str)) {
+ if (match_type <
+ vararg_match)
+ match_type =
+ vararg_match;
+ matched++;
+ } else if (CMD_RANGE(str)) {
+ if (cmd_range_match
+ (str, command)) {
+ if (match_type <
+ range_match)
+ match_type
+ =
+ range_match;
+ matched++;
+ }
+ }
+#ifdef HAVE_IPV6
+ else if (CMD_IPV6(str)) {
+ if (cmd_ipv6_match
+ (command) ==
+ exact_match) {
+ if (match_type <
+ ipv6_match)
+ match_type
+ =
+ ipv6_match;
+ matched++;
+ }
+ } else if (CMD_IPV6_PREFIX(str)) {
+ if (cmd_ipv6_prefix_match(command) == exact_match) {
+ if (match_type <
+ ipv6_prefix_match)
+ match_type
+ =
+ ipv6_prefix_match;
+ matched++;
+ }
+ }
+#endif /* HAVE_IPV6 */
+ else if (CMD_IPV4(str)) {
+ if (cmd_ipv4_match
+ (command) ==
+ exact_match) {
+ if (match_type <
+ ipv4_match)
+ match_type
+ =
+ ipv4_match;
+ matched++;
+ }
+ } else if (CMD_IPV4_PREFIX(str)) {
+ if (cmd_ipv4_prefix_match(command) == exact_match) {
+ if (match_type <
+ ipv4_prefix_match)
+ match_type
+ =
+ ipv4_prefix_match;
+ matched++;
+ }
+ } else if (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+ {
+ if (match_type <
+ extend_match)
+ match_type =
+ extend_match;
+ matched++;
+ } else {
+ if (strcmp(command, str)
+ == 0) {
+ match_type =
+ exact_match;
+ matched++;
+ }
+ }
+ }
+ if (!matched)
+ vector_slot(v, i) = NULL;
+ }
+ }
+ return match_type;
+}
+
+/* Check ambiguous match */
+static int
+is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
+{
+ unsigned int i;
+ unsigned int j;
+ const char *str = NULL;
+ struct cmd_element *cmd_element;
+ const char *matched = NULL;
+ vector descvec;
+ struct desc *desc;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((cmd_element = vector_slot(v, i)) != NULL) {
+ int match = 0;
+
+ descvec = vector_slot(cmd_element->strvec, index);
+
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ enum match_type ret;
+
+ str = desc->cmd;
+
+ switch (type) {
+ case exact_match:
+ if (!
+ (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+&& strcmp(command, str) == 0)
+ match++;
+ break;
+ case partly_match:
+ if (!
+ (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+&& strncmp(command, str, strlen(command)) == 0) {
+ if (matched
+ && strcmp(matched,
+ str) != 0)
+ return 1; /* There is ambiguous match. */
+ else
+ matched = str;
+ match++;
+ }
+ break;
+ case range_match:
+ if (cmd_range_match
+ (str, command)) {
+ if (matched
+ && strcmp(matched,
+ str) != 0)
+ return 1;
+ else
+ matched = str;
+ match++;
+ }
+ break;
+#ifdef HAVE_IPV6
+ case ipv6_match:
+ if (CMD_IPV6(str))
+ match++;
+ break;
+ case ipv6_prefix_match:
+ if ((ret =
+ cmd_ipv6_prefix_match
+ (command)) != no_match) {
+ if (ret == partly_match)
+ return 2; /* There is incomplete match. */
+
+ match++;
+ }
+ break;
+#endif /* HAVE_IPV6 */
+ case ipv4_match:
+ if (CMD_IPV4(str))
+ match++;
+ break;
+ case ipv4_prefix_match:
+ if ((ret =
+ cmd_ipv4_prefix_match
+ (command)) != no_match) {
+ if (ret == partly_match)
+ return 2; /* There is incomplete match. */
+
+ match++;
+ }
+ break;
+ case extend_match:
+ if (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+ match++;
+ break;
+ case no_match:
+ default:
+ break;
+ }
+ }
+ if (!match)
+ vector_slot(v, i) = NULL;
+ }
+ return 0;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+static const char *cmd_entry_function(const char *src, const char *dst)
+{
+ /* Skip variable arguments. */
+ if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
+ CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
+ return NULL;
+
+ /* In case of 'command \t', given src is NULL string. */
+ if (src == NULL)
+ return dst;
+
+ /* Matched with input string. */
+ if (strncmp(src, dst, strlen(src)) == 0)
+ return dst;
+
+ return NULL;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+/* This version will return the dst string always if it is
+ CMD_VARIABLE for '?' key processing */
+static const char *cmd_entry_function_desc(const char *src, const char *dst)
+{
+ if (CMD_VARARG(dst))
+ return dst;
+
+ if (CMD_RANGE(dst)) {
+ if (cmd_range_match(dst, src))
+ return dst;
+ else
+ return NULL;
+ }
+#ifdef HAVE_IPV6
+ if (CMD_IPV6(dst)) {
+ if (cmd_ipv6_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV6_PREFIX(dst)) {
+ if (cmd_ipv6_prefix_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+#endif /* HAVE_IPV6 */
+
+ if (CMD_IPV4(dst)) {
+ if (cmd_ipv4_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV4_PREFIX(dst)) {
+ if (cmd_ipv4_prefix_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ /* Optional or variable commands always match on '?' */
+ if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
+ return dst;
+
+ /* In case of 'command \t', given src is NULL string. */
+ if (src == NULL)
+ return dst;
+
+ if (strncmp(src, dst, strlen(src)) == 0)
+ return dst;
+ else
+ return NULL;
+}
+
+/* Check same string element existence. If it isn't there return
+ 1. */
+static int cmd_unique_string(vector v, const char *str)
+{
+ unsigned int i;
+ char *match;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((match = vector_slot(v, i)) != NULL)
+ if (strcmp(match, str) == 0)
+ return 0;
+ return 1;
+}
+
+/* Compare string to description vector. If there is same string
+ return 1 else return 0. */
+static int desc_unique_string(vector v, const char *str)
+{
+ unsigned int i;
+ struct desc *desc;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((desc = vector_slot(v, i)) != NULL)
+ if (strcmp(desc->cmd, str) == 0)
+ return 1;
+ return 0;
+}
+
+static int cmd_try_do_shortcut(enum node_type node, char *first_word)
+{
+ if (first_word != NULL &&
+ node != AUTH_NODE &&
+ node != VIEW_NODE &&
+ node != AUTH_ENABLE_NODE &&
+ node != ENABLE_NODE && 0 == strcmp("do", first_word))
+ return 1;
+ return 0;
+}
+
+/* '?' describe command support. */
+static vector
+cmd_describe_command_real(vector vline, struct vty *vty, int *status)
+{
+ unsigned int i;
+ vector cmd_vector;
+#define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_element *cmd_element;
+ unsigned int index;
+ int ret;
+ enum match_type match;
+ char *command;
+ static struct desc desc_cr = { "<cr>", "" };
+
+ /* Set index. */
+ if (vector_active(vline) == 0) {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ } else
+ index = vector_active(vline) - 1;
+
+ /* Make copy vector of current node's command vector. */
+ cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+ /* Prepare match vector */
+ matchvec = vector_init(INIT_MATCHVEC_SIZE);
+
+ /* Filter commands. */
+ /* Only words precedes current word will be checked in this loop. */
+ for (i = 0; i < index; i++)
+ if ((command = vector_slot(vline, i))) {
+ match =
+ cmd_filter_by_completion(command, cmd_vector, i);
+
+ if (match == vararg_match) {
+ struct cmd_element *cmd_element;
+ vector descvec;
+ unsigned int j, k;
+
+ for (j = 0; j < vector_active(cmd_vector); j++)
+ if ((cmd_element =
+ vector_slot(cmd_vector, j)) != NULL
+ &&
+ (vector_active
+ (cmd_element->strvec))) {
+ descvec =
+ vector_slot(cmd_element->
+ strvec,
+ vector_active
+ (cmd_element->
+ strvec) - 1);
+ for (k = 0;
+ k < vector_active(descvec);
+ k++) {
+ struct desc *desc =
+ vector_slot(descvec,
+ k);
+ vector_set(matchvec,
+ desc);
+ }
+ }
+
+ vector_set(matchvec, &desc_cr);
+ vector_free(cmd_vector);
+
+ return matchvec;
+ }
+
+ if ((ret =
+ is_cmd_ambiguous(command, cmd_vector, i,
+ match)) == 1) {
+ vector_free(cmd_vector);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ } else if (ret == 2) {
+ vector_free(cmd_vector);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ }
+
+ /* Prepare match vector */
+ /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
+
+ /* Make sure that cmd_vector is filtered based on current word */
+ command = vector_slot(vline, index);
+ if (command)
+ match = cmd_filter_by_completion(command, cmd_vector, index);
+
+ /* Make description vector. */
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if ((cmd_element = vector_slot(cmd_vector, i)) != NULL) {
+ const char *string = NULL;
+ vector strvec = cmd_element->strvec;
+
+ /* if command is NULL, index may be equal to vector_active */
+ if (command && index >= vector_active(strvec))
+ vector_slot(cmd_vector, i) = NULL;
+ else {
+ /* Check if command is completed. */
+ if (command == NULL
+ && index == vector_active(strvec)) {
+ string = "<cr>";
+ if (!desc_unique_string
+ (matchvec, string))
+ vector_set(matchvec, &desc_cr);
+ } else {
+ unsigned int j;
+ vector descvec =
+ vector_slot(strvec, index);
+ struct desc *desc;
+
+ for (j = 0; j < vector_active(descvec);
+ j++)
+ if ((desc =
+ vector_slot(descvec, j))) {
+ string =
+ cmd_entry_function_desc
+ (command,
+ desc->cmd);
+ if (string) {
+ /* Uniqueness check */
+ if (!desc_unique_string(matchvec, string))
+ vector_set
+ (matchvec,
+ desc);
+ }
+ }
+ }
+ }
+ }
+ vector_free(cmd_vector);
+
+ if (vector_slot(matchvec, 0) == NULL) {
+ vector_free(matchvec);
+ *status = CMD_ERR_NO_MATCH;
+ } else
+ *status = CMD_SUCCESS;
+
+ return matchvec;
+}
+
+vector cmd_describe_command(vector vline, struct vty * vty, int *status)
+{
+ vector ret;
+
+ if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+ enum node_type onode;
+ vector shifted_vline;
+ unsigned int index;
+
+ onode = vty->node;
+ vty->node = ENABLE_NODE;
+ /* We can try it on enable node, cos' the vty is authenticated */
+
+ shifted_vline = vector_init(vector_count(vline));
+ /* use memcpy? */
+ for (index = 1; index < vector_active(vline); index++) {
+ vector_set_index(shifted_vline, index - 1,
+ vector_lookup(vline, index));
+ }
+
+ ret = cmd_describe_command_real(shifted_vline, vty, status);
+
+ vector_free(shifted_vline);
+ vty->node = onode;
+ return ret;
+ }
+
+ return cmd_describe_command_real(vline, vty, status);
+}
+
+/* Check LCD of matched command. */
+static int cmd_lcd(char **matched)
+{
+ int i;
+ int j;
+ int lcd = -1;
+ char *s1, *s2;
+ char c1, c2;
+
+ if (matched[0] == NULL || matched[1] == NULL)
+ return 0;
+
+ for (i = 1; matched[i] != NULL; i++) {
+ s1 = matched[i - 1];
+ s2 = matched[i];
+
+ for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
+ if (c1 != c2)
+ break;
+
+ if (lcd < 0)
+ lcd = j;
+ else {
+ if (lcd > j)
+ lcd = j;
+ }
+ }
+ return lcd;
+}
+
+/* Command line completion support. */
+static char **cmd_complete_command_real(vector vline, struct vty *vty,
+ int *status)
+{
+ unsigned int i;
+ vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+#define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_element *cmd_element;
+ unsigned int index;
+ char **match_str;
+ struct desc *desc;
+ vector descvec;
+ char *command;
+ int lcd;
+
+ if (vector_active(vline) == 0) {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ } else
+ index = vector_active(vline) - 1;
+
+ /* First, filter by preceeding command string */
+ for (i = 0; i < index; i++)
+ if ((command = vector_slot(vline, i))) {
+ enum match_type match;
+ int ret;
+
+ /* First try completion match, if there is exactly match return 1 */
+ match =
+ cmd_filter_by_completion(command, cmd_vector, i);
+
+ /* If there is exact match then filter ambiguous match else check
+ ambiguousness. */
+ if ((ret =
+ is_cmd_ambiguous(command, cmd_vector, i,
+ match)) == 1) {
+ vector_free(cmd_vector);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ }
+ /*
+ else if (ret == 2)
+ {
+ vector_free (cmd_vector);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ */
+ }
+
+ /* Prepare match vector. */
+ matchvec = vector_init(INIT_MATCHVEC_SIZE);
+
+ /* Now we got into completion */
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if ((cmd_element = vector_slot(cmd_vector, i))) {
+ const char *string;
+ vector strvec = cmd_element->strvec;
+
+ /* Check field length */
+ if (index >= vector_active(strvec))
+ vector_slot(cmd_vector, i) = NULL;
+ else {
+ unsigned int j;
+
+ descvec = vector_slot(strvec, index);
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd)))
+ if (cmd_unique_string (matchvec, string))
+ vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
+ }
+ }
+ }
+
+ /* We don't need cmd_vector any more. */
+ vector_free(cmd_vector);
+
+ /* No matched command */
+ if (vector_slot(matchvec, 0) == NULL) {
+ vector_free(matchvec);
+
+ /* In case of 'command \t' pattern. Do you need '?' command at
+ the end of the line. */
+ if (vector_slot(vline, index) == '\0')
+ *status = CMD_ERR_NOTHING_TODO;
+ else
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+
+ /* Only one matched */
+ if (vector_slot(matchvec, 1) == NULL) {
+ match_str = (char **)matchvec->index;
+ vector_only_wrapper_free(matchvec);
+ *status = CMD_COMPLETE_FULL_MATCH;
+ return match_str;
+ }
+ /* Make it sure last element is NULL. */
+ vector_set(matchvec, NULL);
+
+ /* Check LCD of matched strings. */
+ if (vector_slot(vline, index) != NULL) {
+ lcd = cmd_lcd((char **)matchvec->index);
+
+ if (lcd) {
+ int len = strlen(vector_slot(vline, index));
+
+ if (len < lcd) {
+ char *lcdstr;
+
+ lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
+ "complete-lcdstr");
+ memcpy(lcdstr, matchvec->index[0], lcd);
+ lcdstr[lcd] = '\0';
+
+ /* match_str = (char **) &lcdstr; */
+
+ /* Free matchvec. */
+ for (i = 0; i < vector_active(matchvec); i++) {
+ if (vector_slot(matchvec, i))
+ talloc_free(vector_slot(matchvec, i));
+ }
+ vector_free(matchvec);
+
+ /* Make new matchvec. */
+ matchvec = vector_init(INIT_MATCHVEC_SIZE);
+ vector_set(matchvec, lcdstr);
+ match_str = (char **)matchvec->index;
+ vector_only_wrapper_free(matchvec);
+
+ *status = CMD_COMPLETE_MATCH;
+ return match_str;
+ }
+ }
+ }
+
+ match_str = (char **)matchvec->index;
+ vector_only_wrapper_free(matchvec);
+ *status = CMD_COMPLETE_LIST_MATCH;
+ return match_str;
+}
+
+char **cmd_complete_command(vector vline, struct vty *vty, int *status)
+{
+ char **ret;
+
+ if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+ enum node_type onode;
+ vector shifted_vline;
+ unsigned int index;
+
+ onode = vty->node;
+ vty->node = ENABLE_NODE;
+ /* We can try it on enable node, cos' the vty is authenticated */
+
+ shifted_vline = vector_init(vector_count(vline));
+ /* use memcpy? */
+ for (index = 1; index < vector_active(vline); index++) {
+ vector_set_index(shifted_vline, index - 1,
+ vector_lookup(vline, index));
+ }
+
+ ret = cmd_complete_command_real(shifted_vline, vty, status);
+
+ vector_free(shifted_vline);
+ vty->node = onode;
+ return ret;
+ }
+
+ return cmd_complete_command_real(vline, vty, status);
+}
+
+/* return parent node */
+/* MUST eventually converge on CONFIG_NODE */
+enum node_type vty_go_parent(struct vty *vty)
+{
+ assert(vty->node > CONFIG_NODE);
+
+ if (host.app_info->go_parent_cb)
+ host.app_info->go_parent_cb(vty);
+ else
+ vty->node = CONFIG_NODE;
+
+ return vty->node;
+}
+
+/* Execute command by argument vline vector. */
+static int
+cmd_execute_command_real(vector vline, struct vty *vty,
+ struct cmd_element **cmd)
+{
+ unsigned int i;
+ unsigned int index;
+ vector cmd_vector;
+ struct cmd_element *cmd_element;
+ struct cmd_element *matched_element;
+ unsigned int matched_count, incomplete_count;
+ int argc;
+ const char *argv[CMD_ARGC_MAX];
+ enum match_type match = 0;
+ int varflag;
+ char *command;
+
+ /* Make copy of command elements. */
+ cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+ for (index = 0; index < vector_active(vline); index++)
+ if ((command = vector_slot(vline, index))) {
+ int ret;
+
+ match =
+ cmd_filter_by_completion(command, cmd_vector,
+ index);
+
+ if (match == vararg_match)
+ break;
+
+ ret =
+ is_cmd_ambiguous(command, cmd_vector, index, match);
+
+ if (ret == 1) {
+ vector_free(cmd_vector);
+ return CMD_ERR_AMBIGUOUS;
+ } else if (ret == 2) {
+ vector_free(cmd_vector);
+ return CMD_ERR_NO_MATCH;
+ }
+ }
+
+ /* Check matched count. */
+ matched_element = NULL;
+ matched_count = 0;
+ incomplete_count = 0;
+
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if ((cmd_element = vector_slot(cmd_vector, i))) {
+ if (match == vararg_match
+ || index >= cmd_element->cmdsize) {
+ matched_element = cmd_element;
+#if 0
+ printf("DEBUG: %s\n", cmd_element->string);
+#endif
+ matched_count++;
+ } else {
+ incomplete_count++;
+ }
+ }
+
+ /* Finish of using cmd_vector. */
+ vector_free(cmd_vector);
+
+ /* To execute command, matched_count must be 1. */
+ if (matched_count == 0) {
+ if (incomplete_count)
+ return CMD_ERR_INCOMPLETE;
+ else
+ return CMD_ERR_NO_MATCH;
+ }
+
+ if (matched_count > 1)
+ return CMD_ERR_AMBIGUOUS;
+
+ /* Argument treatment */
+ varflag = 0;
+ argc = 0;
+
+ for (i = 0; i < vector_active(vline); i++) {
+ if (varflag)
+ argv[argc++] = vector_slot(vline, i);
+ else {
+ vector descvec =
+ vector_slot(matched_element->strvec, i);
+
+ if (vector_active(descvec) == 1) {
+ struct desc *desc = vector_slot(descvec, 0);
+
+ if (CMD_VARARG(desc->cmd))
+ varflag = 1;
+
+ if (varflag || CMD_VARIABLE(desc->cmd)
+ || CMD_OPTION(desc->cmd))
+ argv[argc++] = vector_slot(vline, i);
+ } else
+ argv[argc++] = vector_slot(vline, i);
+ }
+
+ if (argc >= CMD_ARGC_MAX)
+ return CMD_ERR_EXEED_ARGC_MAX;
+ }
+
+ /* For vtysh execution. */
+ if (cmd)
+ *cmd = matched_element;
+
+ if (matched_element->daemon)
+ return CMD_SUCCESS_DAEMON;
+
+ /* Execute matched command. */
+ return (*matched_element->func) (matched_element, vty, argc, argv);
+}
+
+int
+cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
+ int vtysh)
+{
+ int ret, saved_ret, tried = 0;
+ enum node_type onode;
+ void *oindex;
+
+ onode = vty->node;
+ oindex = vty->index;
+
+ if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+ vector shifted_vline;
+ unsigned int index;
+
+ vty->node = ENABLE_NODE;
+ /* We can try it on enable node, cos' the vty is authenticated */
+
+ shifted_vline = vector_init(vector_count(vline));
+ /* use memcpy? */
+ for (index = 1; index < vector_active(vline); index++) {
+ vector_set_index(shifted_vline, index - 1,
+ vector_lookup(vline, index));
+ }
+
+ ret = cmd_execute_command_real(shifted_vline, vty, cmd);
+
+ vector_free(shifted_vline);
+ vty->node = onode;
+ return ret;
+ }
+
+ saved_ret = ret = cmd_execute_command_real(vline, vty, cmd);
+
+ if (vtysh)
+ return saved_ret;
+
+ /* Go to parent for config nodes to attempt to find the right command */
+ while (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && is_config(vty)) {
+ vty_go_parent(vty);
+ ret = cmd_execute_command_real(vline, vty, cmd);
+ tried = 1;
+ if (ret == CMD_SUCCESS || ret == CMD_WARNING) {
+ /* succesfull command, leave the node as is */
+ return ret;
+ }
+ }
+ /* no command succeeded, reset the vty to the original node and
+ return the error for this node */
+ if (tried) {
+ vty->node = onode;
+ vty->index = oindex;
+ }
+ return saved_ret;
+}
+
+/* Execute command by argument readline. */
+int
+cmd_execute_command_strict(vector vline, struct vty *vty,
+ struct cmd_element **cmd)
+{
+ unsigned int i;
+ unsigned int index;
+ vector cmd_vector;
+ struct cmd_element *cmd_element;
+ struct cmd_element *matched_element;
+ unsigned int matched_count, incomplete_count;
+ int argc;
+ const char *argv[CMD_ARGC_MAX];
+ int varflag;
+ enum match_type match = 0;
+ char *command;
+
+ /* Make copy of command element */
+ cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+ for (index = 0; index < vector_active(vline); index++)
+ if ((command = vector_slot(vline, index))) {
+ int ret;
+
+ match = cmd_filter_by_string(vector_slot(vline, index),
+ cmd_vector, index);
+
+ /* If command meets '.VARARG' then finish matching. */
+ if (match == vararg_match)
+ break;
+
+ ret =
+ is_cmd_ambiguous(command, cmd_vector, index, match);
+ if (ret == 1) {
+ vector_free(cmd_vector);
+ return CMD_ERR_AMBIGUOUS;
+ }
+ if (ret == 2) {
+ vector_free(cmd_vector);
+ return CMD_ERR_NO_MATCH;
+ }
+ }
+
+ /* Check matched count. */
+ matched_element = NULL;
+ matched_count = 0;
+ incomplete_count = 0;
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if (vector_slot(cmd_vector, i) != NULL) {
+ cmd_element = vector_slot(cmd_vector, i);
+
+ if (match == vararg_match
+ || index >= cmd_element->cmdsize) {
+ matched_element = cmd_element;
+ matched_count++;
+ } else
+ incomplete_count++;
+ }
+
+ /* Finish of using cmd_vector. */
+ vector_free(cmd_vector);
+
+ /* To execute command, matched_count must be 1. */
+ if (matched_count == 0) {
+ if (incomplete_count)
+ return CMD_ERR_INCOMPLETE;
+ else
+ return CMD_ERR_NO_MATCH;
+ }
+
+ if (matched_count > 1)
+ return CMD_ERR_AMBIGUOUS;
+
+ /* Argument treatment */
+ varflag = 0;
+ argc = 0;
+
+ for (i = 0; i < vector_active(vline); i++) {
+ if (varflag)
+ argv[argc++] = vector_slot(vline, i);
+ else {
+ vector descvec =
+ vector_slot(matched_element->strvec, i);
+
+ if (vector_active(descvec) == 1) {
+ struct desc *desc = vector_slot(descvec, 0);
+
+ if (CMD_VARARG(desc->cmd))
+ varflag = 1;
+
+ if (varflag || CMD_VARIABLE(desc->cmd)
+ || CMD_OPTION(desc->cmd))
+ argv[argc++] = vector_slot(vline, i);
+ } else
+ argv[argc++] = vector_slot(vline, i);
+ }
+
+ if (argc >= CMD_ARGC_MAX)
+ return CMD_ERR_EXEED_ARGC_MAX;
+ }
+
+ /* For vtysh execution. */
+ if (cmd)
+ *cmd = matched_element;
+
+ if (matched_element->daemon)
+ return CMD_SUCCESS_DAEMON;
+
+ /* Now execute matched command */
+ return (*matched_element->func) (matched_element, vty, argc, argv);
+}
+
+/* Configration make from file. */
+int config_from_file(struct vty *vty, FILE * fp)
+{
+ int ret;
+ vector vline;
+
+ while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
+ vline = cmd_make_strvec(vty->buf);
+
+ /* In case of comment line */
+ if (vline == NULL)
+ continue;
+ /* Execute configuration command : this is strict match */
+ ret = cmd_execute_command_strict(vline, vty, NULL);
+
+ /* Try again with setting node to CONFIG_NODE */
+ while (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && ret != CMD_ERR_NOTHING_TODO
+ && vty->node != CONFIG_NODE && is_config(vty)) {
+ vty_go_parent(vty);
+ ret = cmd_execute_command_strict(vline, vty, NULL);
+ }
+
+ cmd_free_strvec(vline);
+
+ if (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && ret != CMD_ERR_NOTHING_TODO)
+ return ret;
+ }
+ return CMD_SUCCESS;
+}
+
+/* Configration from terminal */
+DEFUN(config_terminal,
+ config_terminal_cmd,
+ "configure terminal",
+ "Configuration from vty interface\n" "Configuration terminal\n")
+{
+ if (vty_config_lock(vty))
+ vty->node = CONFIG_NODE;
+ else {
+ vty_out(vty, "VTY configuration is locked by other VTY%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+/* Enable command */
+DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
+{
+ /* If enable password is NULL, change to ENABLE_NODE */
+ if ((host.enable == NULL && host.enable_encrypt == NULL) ||
+ vty->type == VTY_SHELL_SERV)
+ vty->node = ENABLE_NODE;
+ else
+ vty->node = AUTH_ENABLE_NODE;
+
+ return CMD_SUCCESS;
+}
+
+/* Disable command */
+DEFUN(disable,
+ config_disable_cmd, "disable", "Turn off privileged mode command\n")
+{
+ if (vty->node == ENABLE_NODE)
+ vty->node = VIEW_NODE;
+ return CMD_SUCCESS;
+}
+
+/* Down vty node level. */
+gDEFUN(config_exit,
+ config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
+{
+ switch (vty->node) {
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ if (0) //vty_shell (vty))
+ exit(0);
+ else
+ vty->status = VTY_CLOSE;
+ break;
+ case CONFIG_NODE:
+ vty->node = ENABLE_NODE;
+ vty_config_unlock(vty);
+ break;
+ case VTY_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+ case CFG_LOG_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+ default:
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+/* End of configuration. */
+ gDEFUN(config_end,
+ config_end_cmd, "end", "End current mode and change to enable mode.")
+{
+ switch (vty->node) {
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ /* Nothing to do. */
+ break;
+ case CFG_LOG_NODE:
+ case CONFIG_NODE:
+ case VTY_NODE:
+ vty_config_unlock(vty);
+ vty->node = ENABLE_NODE;
+ break;
+ default:
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+/* Show version. */
+DEFUN(show_version,
+ show_version_cmd, "show version", SHOW_STR "Displays program version\n")
+{
+ vty_out(vty, "%s %s (%s).%s", host.app_info->name,
+ host.app_info->version,
+ host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
+ vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+/* Help display function for all node. */
+gDEFUN(config_help,
+ config_help_cmd, "help", "Description of the interactive help system\n")
+{
+ vty_out(vty,
+ "This VTY provides advanced help features. When you need help,%s\
+anytime at the command line please press '?'.%s\
+%s\
+If nothing matches, the help list will be empty and you must backup%s\
+ until entering a '?' shows the available options.%s\
+Two styles of help are provided:%s\
+1. Full help is available when you are ready to enter a%s\
+command argument (e.g. 'show ?') and describes each possible%s\
+argument.%s\
+2. Partial help is provided when an abbreviated argument is entered%s\
+ and you want to know what arguments match the input%s\
+ (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+/* Help display function for all node. */
+gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
+{
+ unsigned int i;
+ struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
+ struct cmd_element *cmd;
+
+ for (i = 0; i < vector_active(cnode->cmd_vector); i++)
+ if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
+ && !(cmd->attr == CMD_ATTR_DEPRECATED
+ || cmd->attr == CMD_ATTR_HIDDEN))
+ vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+/* Write current configuration into file. */
+DEFUN(config_write_file,
+ config_write_file_cmd,
+ "write file",
+ "Write running configuration to memory, network, or terminal\n"
+ "Write to configuration file\n")
+{
+ unsigned int i;
+ int fd;
+ struct cmd_node *node;
+ char *config_file;
+ char *config_file_tmp = NULL;
+ char *config_file_sav = NULL;
+ struct vty *file_vty;
+
+ /* Check and see if we are operating under vtysh configuration */
+ if (host.config == NULL) {
+ vty_out(vty, "Can't save to configuration file, using vtysh.%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Get filename. */
+ config_file = host.config;
+
+ config_file_sav =
+ _talloc_zero(tall_vty_cmd_ctx,
+ strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
+ "config_file_sav");
+ strcpy(config_file_sav, config_file);
+ strcat(config_file_sav, CONF_BACKUP_EXT);
+
+ config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
+ "config_file_tmp");
+ sprintf(config_file_tmp, "%s.XXXXXX", config_file);
+
+ /* Open file to configuration write. */
+ fd = mkstemp(config_file_tmp);
+ if (fd < 0) {
+ vty_out(vty, "Can't open configuration file %s.%s",
+ config_file_tmp, VTY_NEWLINE);
+ talloc_free(config_file_tmp);
+ talloc_free(config_file_sav);
+ return CMD_WARNING;
+ }
+
+ /* Make vty for configuration file. */
+ file_vty = vty_new();
+ file_vty->fd = fd;
+ file_vty->type = VTY_FILE;
+
+ /* Config file header print. */
+ vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
+ host.app_info->name, host.app_info->version);
+ //vty_time_print (file_vty, 1);
+ vty_out(file_vty, "!\n");
+
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((node = vector_slot(cmdvec, i)) && node->func) {
+ if ((*node->func) (file_vty))
+ vty_out(file_vty, "!\n");
+ }
+ vty_close(file_vty);
+
+ if (unlink(config_file_sav) != 0)
+ if (errno != ENOENT) {
+ vty_out(vty,
+ "Can't unlink backup configuration file %s.%s",
+ config_file_sav, VTY_NEWLINE);
+ talloc_free(config_file_sav);
+ talloc_free(config_file_tmp);
+ unlink(config_file_tmp);
+ return CMD_WARNING;
+ }
+ if (link(config_file, config_file_sav) != 0) {
+ vty_out(vty, "Can't backup old configuration file %s.%s",
+ config_file_sav, VTY_NEWLINE);
+ talloc_free(config_file_sav);
+ talloc_free(config_file_tmp);
+ unlink(config_file_tmp);
+ return CMD_WARNING;
+ }
+ sync();
+ if (unlink(config_file) != 0) {
+ vty_out(vty, "Can't unlink configuration file %s.%s",
+ config_file, VTY_NEWLINE);
+ talloc_free(config_file_sav);
+ talloc_free(config_file_tmp);
+ unlink(config_file_tmp);
+ return CMD_WARNING;
+ }
+ if (link(config_file_tmp, config_file) != 0) {
+ vty_out(vty, "Can't save configuration file %s.%s", config_file,
+ VTY_NEWLINE);
+ talloc_free(config_file_sav);
+ talloc_free(config_file_tmp);
+ unlink(config_file_tmp);
+ return CMD_WARNING;
+ }
+ unlink(config_file_tmp);
+ sync();
+
+ talloc_free(config_file_sav);
+ talloc_free(config_file_tmp);
+
+ if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
+ vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
+ config_file, strerror(errno), errno, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+ALIAS(config_write_file,
+ config_write_cmd,
+ "write", "Write running configuration to memory, network, or terminal\n")
+
+ ALIAS(config_write_file,
+ config_write_memory_cmd,
+ "write memory",
+ "Write running configuration to memory, network, or terminal\n"
+ "Write configuration to the file (same as write file)\n")
+
+ ALIAS(config_write_file,
+ copy_runningconfig_startupconfig_cmd,
+ "copy running-config startup-config",
+ "Copy configuration\n"
+ "Copy running config to... \n"
+ "Copy running config to startup config (same as write file)\n")
+
+/* Write current configuration into the terminal. */
+ DEFUN(config_write_terminal,
+ config_write_terminal_cmd,
+ "write terminal",
+ "Write running configuration to memory, network, or terminal\n"
+ "Write to terminal\n")
+{
+ unsigned int i;
+ struct cmd_node *node;
+
+ if (vty->type == VTY_SHELL_SERV) {
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((node = vector_slot(cmdvec, i)) && node->func
+ && node->vtysh) {
+ if ((*node->func) (vty))
+ vty_out(vty, "!%s", VTY_NEWLINE);
+ }
+ } else {
+ vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
+ VTY_NEWLINE);
+ vty_out(vty, "!%s", VTY_NEWLINE);
+
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((node = vector_slot(cmdvec, i)) && node->func) {
+ if ((*node->func) (vty))
+ vty_out(vty, "!%s", VTY_NEWLINE);
+ }
+ vty_out(vty, "end%s", VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
+/* Write current configuration into the terminal. */
+ALIAS(config_write_terminal,
+ show_running_config_cmd,
+ "show running-config", SHOW_STR "running configuration\n")
+
+/* Write startup configuration into the terminal. */
+ DEFUN(show_startup_config,
+ show_startup_config_cmd,
+ "show startup-config", SHOW_STR "Contentes of startup configuration\n")
+{
+ char buf[BUFSIZ];
+ FILE *confp;
+
+ confp = fopen(host.config, "r");
+ if (confp == NULL) {
+ vty_out(vty, "Can't open configuration file [%s]%s",
+ host.config, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ while (fgets(buf, BUFSIZ, confp)) {
+ char *cp = buf;
+
+ while (*cp != '\r' && *cp != '\n' && *cp != '\0')
+ cp++;
+ *cp = '\0';
+
+ vty_out(vty, "%s%s", buf, VTY_NEWLINE);
+ }
+
+ fclose(confp);
+
+ return CMD_SUCCESS;
+}
+
+/* Hostname configuration */
+DEFUN(config_hostname,
+ hostname_cmd,
+ "hostname WORD",
+ "Set system's network name\n" "This system's network name\n")
+{
+ if (!isalpha((int)*argv[0])) {
+ vty_out(vty, "Please specify string starting with alphabet%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (host.name)
+ talloc_free(host.name);
+
+ host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_no_hostname,
+ no_hostname_cmd,
+ "no hostname [HOSTNAME]",
+ NO_STR "Reset system's network name\n" "Host name of this router\n")
+{
+ if (host.name)
+ talloc_free(host.name);
+ host.name = NULL;
+ return CMD_SUCCESS;
+}
+
+/* VTY interface password set. */
+DEFUN(config_password, password_cmd,
+ "password (8|) WORD",
+ "Assign the terminal connection password\n"
+ "Specifies a HIDDEN password will follow\n"
+ "dummy string \n" "The HIDDEN line password string\n")
+{
+ /* Argument check. */
+ if (argc == 0) {
+ vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (argc == 2) {
+ if (*argv[0] == '8') {
+ if (host.password)
+ talloc_free(host.password);
+ host.password = NULL;
+ if (host.password_encrypt)
+ talloc_free(host.password_encrypt);
+ host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
+ return CMD_SUCCESS;
+ } else {
+ vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ if (!isalnum((int)*argv[0])) {
+ vty_out(vty,
+ "Please specify string starting with alphanumeric%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (host.password)
+ talloc_free(host.password);
+ host.password = NULL;
+
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt) {
+ if (host.password_encrypt)
+ talloc_free(host.password_encrypt);
+ host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
+ } else
+#endif
+ host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(config_password, password_text_cmd,
+ "password LINE",
+ "Assign the terminal connection password\n"
+ "The UNENCRYPTED (cleartext) line password\n")
+
+/* VTY enable password set. */
+ DEFUN(config_enable_password, enable_password_cmd,
+ "enable password (8|) WORD",
+ "Modify enable password parameters\n"
+ "Assign the privileged level password\n"
+ "Specifies a HIDDEN password will follow\n"
+ "dummy string \n" "The HIDDEN 'enable' password string\n")
+{
+ /* Argument check. */
+ if (argc == 0) {
+ vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Crypt type is specified. */
+ if (argc == 2) {
+ if (*argv[0] == '8') {
+ if (host.enable)
+ talloc_free(host.enable);
+ host.enable = NULL;
+
+ if (host.enable_encrypt)
+ talloc_free(host.enable_encrypt);
+ host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
+
+ return CMD_SUCCESS;
+ } else {
+ vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ if (!isalnum((int)*argv[0])) {
+ vty_out(vty,
+ "Please specify string starting with alphanumeric%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (host.enable)
+ talloc_free(host.enable);
+ host.enable = NULL;
+
+ /* Plain password input. */
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt) {
+ if (host.enable_encrypt)
+ talloc_free(host.enable_encrypt);
+ host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
+ } else
+#endif
+ host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(config_enable_password,
+ enable_password_text_cmd,
+ "enable password LINE",
+ "Modify enable password parameters\n"
+ "Assign the privileged level password\n"
+ "The UNENCRYPTED (cleartext) 'enable' password\n")
+
+/* VTY enable password delete. */
+ DEFUN(no_config_enable_password, no_enable_password_cmd,
+ "no enable password",
+ NO_STR
+ "Modify enable password parameters\n"
+ "Assign the privileged level password\n")
+{
+ if (host.enable)
+ talloc_free(host.enable);
+ host.enable = NULL;
+
+ if (host.enable_encrypt)
+ talloc_free(host.enable_encrypt);
+ host.enable_encrypt = NULL;
+
+ return CMD_SUCCESS;
+}
+
+#ifdef VTY_CRYPT_PW
+DEFUN(service_password_encrypt,
+ service_password_encrypt_cmd,
+ "service password-encryption",
+ "Set up miscellaneous service\n" "Enable encrypted passwords\n")
+{
+ if (host.encrypt)
+ return CMD_SUCCESS;
+
+ host.encrypt = 1;
+
+ if (host.password) {
+ if (host.password_encrypt)
+ talloc_free(host.password_encrypt);
+ host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
+ }
+ if (host.enable) {
+ if (host.enable_encrypt)
+ talloc_free(host.enable_encrypt);
+ host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_service_password_encrypt,
+ no_service_password_encrypt_cmd,
+ "no service password-encryption",
+ NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
+{
+ if (!host.encrypt)
+ return CMD_SUCCESS;
+
+ host.encrypt = 0;
+
+ if (host.password_encrypt)
+ talloc_free(host.password_encrypt);
+ host.password_encrypt = NULL;
+
+ if (host.enable_encrypt)
+ talloc_free(host.enable_encrypt);
+ host.enable_encrypt = NULL;
+
+ return CMD_SUCCESS;
+}
+#endif
+
+DEFUN(config_terminal_length, config_terminal_length_cmd,
+ "terminal length <0-512>",
+ "Set terminal line parameters\n"
+ "Set number of lines on a screen\n"
+ "Number of lines on screen (0 for no pausing)\n")
+{
+ int lines;
+ char *endptr = NULL;
+
+ lines = strtol(argv[0], &endptr, 10);
+ if (lines < 0 || lines > 512 || *endptr != '\0') {
+ vty_out(vty, "length is malformed%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ vty->lines = lines;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
+ "terminal no length",
+ "Set terminal line parameters\n"
+ NO_STR "Set number of lines on a screen\n")
+{
+ vty->lines = -1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(service_terminal_length, service_terminal_length_cmd,
+ "service terminal-length <0-512>",
+ "Set up miscellaneous service\n"
+ "System wide terminal length configuration\n"
+ "Number of lines of VTY (0 means no line control)\n")
+{
+ int lines;
+ char *endptr = NULL;
+
+ lines = strtol(argv[0], &endptr, 10);
+ if (lines < 0 || lines > 512 || *endptr != '\0') {
+ vty_out(vty, "length is malformed%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ host.lines = lines;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
+ "no service terminal-length [<0-512>]",
+ NO_STR
+ "Set up miscellaneous service\n"
+ "System wide terminal length configuration\n"
+ "Number of lines of VTY (0 means no line control)\n")
+{
+ host.lines = -1;
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(do_echo,
+ echo_cmd,
+ "echo .MESSAGE",
+ "Echo a message back to the vty\n" "The message to echo\n")
+{
+ char *message;
+
+ vty_out(vty, "%s%s",
+ ((message =
+ argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
+ if (message)
+ talloc_free(message);
+ return CMD_SUCCESS;
+}
+
+#if 0
+DEFUN(config_logmsg,
+ config_logmsg_cmd,
+ "logmsg " LOG_LEVELS " .MESSAGE",
+ "Send a message to enabled logging destinations\n"
+ LOG_LEVEL_DESC "The message to send\n")
+{
+ int level;
+ char *message;
+
+ if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+
+ zlog(NULL, level,
+ ((message = argv_concat(argv, argc, 1)) ? message : ""));
+ if (message)
+ talloc_free(message);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_logging,
+ show_logging_cmd,
+ "show logging", SHOW_STR "Show current logging configuration\n")
+{
+ struct zlog *zl = zlog_default;
+
+ vty_out(vty, "Syslog logging: ");
+ if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s, facility %s, ident %s",
+ zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
+ facility_name(zl->facility), zl->ident);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "Stdout logging: ");
+ if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s",
+ zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "Monitor logging: ");
+ if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s",
+ zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "File logging: ");
+ if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s, filename %s",
+ zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
+ zl->filename);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "Protocol name: %s%s",
+ zlog_proto_names[zl->protocol], VTY_NEWLINE);
+ vty_out(vty, "Record priority: %s%s",
+ (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_stdout,
+ config_log_stdout_cmd,
+ "log stdout", "Logging control\n" "Set stdout logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_stdout_level,
+ config_log_stdout_level_cmd,
+ "log stdout " LOG_LEVELS,
+ "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
+{
+ int level;
+
+ if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_stdout,
+ no_config_log_stdout_cmd,
+ "no log stdout [LEVEL]",
+ NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_monitor,
+ config_log_monitor_cmd,
+ "log monitor",
+ "Logging control\n" "Set terminal line (monitor) logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_monitor_level,
+ config_log_monitor_level_cmd,
+ "log monitor " LOG_LEVELS,
+ "Logging control\n"
+ "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
+{
+ int level;
+
+ if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_monitor,
+ no_config_log_monitor_cmd,
+ "no log monitor [LEVEL]",
+ NO_STR
+ "Logging control\n"
+ "Disable terminal line (monitor) logging\n" "Logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+ return CMD_SUCCESS;
+}
+
+static int set_log_file(struct vty *vty, const char *fname, int loglevel)
+{
+ int ret;
+ char *p = NULL;
+ const char *fullpath;
+
+ /* Path detection. */
+ if (!IS_DIRECTORY_SEP(*fname)) {
+ char cwd[MAXPATHLEN + 1];
+ cwd[MAXPATHLEN] = '\0';
+
+ if (getcwd(cwd, MAXPATHLEN) == NULL) {
+ zlog_err("config_log_file: Unable to alloc mem!");
+ return CMD_WARNING;
+ }
+
+ if ((p = _talloc_zero(tall_vcmd_ctx,
+ strlen(cwd) + strlen(fname) + 2),
+ "set_log_file")
+ == NULL) {
+ zlog_err("config_log_file: Unable to alloc mem!");
+ return CMD_WARNING;
+ }
+ sprintf(p, "%s/%s", cwd, fname);
+ fullpath = p;
+ } else
+ fullpath = fname;
+
+ ret = zlog_set_file(NULL, fullpath, loglevel);
+
+ if (p)
+ talloc_free(p);
+
+ if (!ret) {
+ vty_out(vty, "can't open logfile %s\n", fname);
+ return CMD_WARNING;
+ }
+
+ if (host.logfile)
+ talloc_free(host.logfile);
+
+ host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_file,
+ config_log_file_cmd,
+ "log file FILENAME",
+ "Logging control\n" "Logging to file\n" "Logging filename\n")
+{
+ return set_log_file(vty, argv[0], zlog_default->default_lvl);
+}
+
+DEFUN(config_log_file_level,
+ config_log_file_level_cmd,
+ "log file FILENAME " LOG_LEVELS,
+ "Logging control\n"
+ "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
+{
+ int level;
+
+ if ((level = level_match(argv[1])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ return set_log_file(vty, argv[0], level);
+}
+
+DEFUN(no_config_log_file,
+ no_config_log_file_cmd,
+ "no log file [FILENAME]",
+ NO_STR
+ "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
+{
+ zlog_reset_file(NULL);
+
+ if (host.logfile)
+ talloc_free(host.logfile);
+
+ host.logfile = NULL;
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_config_log_file,
+ no_config_log_file_level_cmd,
+ "no log file FILENAME LEVEL",
+ NO_STR
+ "Logging control\n"
+ "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
+
+ DEFUN(config_log_syslog,
+ config_log_syslog_cmd,
+ "log syslog", "Logging control\n" "Set syslog logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_syslog_level,
+ config_log_syslog_level_cmd,
+ "log syslog " LOG_LEVELS,
+ "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
+{
+ int level;
+
+ if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(config_log_syslog_facility,
+ config_log_syslog_facility_cmd,
+ "log syslog facility " LOG_FACILITIES,
+ "Logging control\n"
+ "Logging goes to syslog\n"
+ "(Deprecated) Facility parameter for syslog messages\n"
+ LOG_FACILITY_DESC)
+{
+ int facility;
+
+ if ((facility = facility_match(argv[0])) < 0)
+ return CMD_ERR_NO_MATCH;
+
+ zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+ zlog_default->facility = facility;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_syslog,
+ no_config_log_syslog_cmd,
+ "no log syslog [LEVEL]",
+ NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_config_log_syslog,
+ no_config_log_syslog_facility_cmd,
+ "no log syslog facility " LOG_FACILITIES,
+ NO_STR
+ "Logging control\n"
+ "Logging goes to syslog\n"
+ "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
+
+ DEFUN(config_log_facility,
+ config_log_facility_cmd,
+ "log facility " LOG_FACILITIES,
+ "Logging control\n"
+ "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
+{
+ int facility;
+
+ if ((facility = facility_match(argv[0])) < 0)
+ return CMD_ERR_NO_MATCH;
+ zlog_default->facility = facility;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_facility,
+ no_config_log_facility_cmd,
+ "no log facility [FACILITY]",
+ NO_STR
+ "Logging control\n"
+ "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
+{
+ zlog_default->facility = LOG_DAEMON;
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(config_log_trap,
+ config_log_trap_cmd,
+ "log trap " LOG_LEVELS,
+ "Logging control\n"
+ "(Deprecated) Set logging level and default for all destinations\n"
+ LOG_LEVEL_DESC)
+{
+ int new_level;
+ int i;
+
+ if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+
+ zlog_default->default_lvl = new_level;
+ for (i = 0; i < ZLOG_NUM_DESTS; i++)
+ if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
+ zlog_default->maxlvl[i] = new_level;
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(no_config_log_trap,
+ no_config_log_trap_cmd,
+ "no log trap [LEVEL]",
+ NO_STR
+ "Logging control\n"
+ "Permit all logging information\n" "Logging level\n")
+{
+ zlog_default->default_lvl = LOG_DEBUG;
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_record_priority,
+ config_log_record_priority_cmd,
+ "log record-priority",
+ "Logging control\n"
+ "Log the priority of the message within the message\n")
+{
+ zlog_default->record_priority = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_record_priority,
+ no_config_log_record_priority_cmd,
+ "no log record-priority",
+ NO_STR
+ "Logging control\n"
+ "Do not log the priority of the message within the message\n")
+{
+ zlog_default->record_priority = 0;
+ return CMD_SUCCESS;
+}
+#endif
+
+DEFUN(banner_motd_file,
+ banner_motd_file_cmd,
+ "banner motd file [FILE]",
+ "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
+{
+ if (host.motdfile)
+ talloc_free(host.motdfile);
+ host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(banner_motd_default,
+ banner_motd_default_cmd,
+ "banner motd default",
+ "Set banner string\n" "Strings for motd\n" "Default string\n")
+{
+ host.motd = default_motd;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_banner_motd,
+ no_banner_motd_cmd,
+ "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
+{
+ host.motd = NULL;
+ if (host.motdfile)
+ talloc_free(host.motdfile);
+ host.motdfile = NULL;
+ return CMD_SUCCESS;
+}
+
+/* Set config filename. Called from vty.c */
+void host_config_set(const char *filename)
+{
+ host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
+}
+
+void install_default(enum node_type node)
+{
+ install_element(node, &config_help_cmd);
+ install_element(node, &config_list_cmd);
+
+ install_element(node, &config_write_terminal_cmd);
+ install_element(node, &config_write_file_cmd);
+ install_element(node, &config_write_memory_cmd);
+ install_element(node, &config_write_cmd);
+ install_element(node, &show_running_config_cmd);
+}
+
+/* Initialize command interface. Install basic nodes and commands. */
+void cmd_init(int terminal)
+{
+ /* Allocate initial top vector of commands. */
+ cmdvec = vector_init(VECTOR_MIN_SIZE);
+
+ /* Default host value settings. */
+ host.name = NULL;
+ host.password = NULL;
+ host.enable = NULL;
+ host.logfile = NULL;
+ host.config = NULL;
+ host.lines = -1;
+ host.motd = default_motd;
+ host.motdfile = NULL;
+
+ /* Install top nodes. */
+ install_node(&view_node, NULL);
+ install_node(&enable_node, NULL);
+ install_node(&auth_node, NULL);
+ install_node(&auth_enable_node, NULL);
+ install_node(&config_node, config_write_host);
+
+ /* Each node's basic commands. */
+ install_element(VIEW_NODE, &show_version_cmd);
+ if (terminal) {
+ install_element(VIEW_NODE, &config_list_cmd);
+ install_element(VIEW_NODE, &config_exit_cmd);
+ install_element(VIEW_NODE, &config_help_cmd);
+ install_element(VIEW_NODE, &config_enable_cmd);
+ install_element(VIEW_NODE, &config_terminal_length_cmd);
+ install_element(VIEW_NODE, &config_terminal_no_length_cmd);
+ install_element(VIEW_NODE, &echo_cmd);
+ }
+
+ if (terminal) {
+ install_element(ENABLE_NODE, &config_exit_cmd);
+ install_default(ENABLE_NODE);
+ install_element(ENABLE_NODE, &config_disable_cmd);
+ install_element(ENABLE_NODE, &config_terminal_cmd);
+ install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
+ }
+ install_element (ENABLE_NODE, &show_startup_config_cmd);
+ install_element(ENABLE_NODE, &show_version_cmd);
+
+ if (terminal) {
+ install_element(ENABLE_NODE, &config_terminal_length_cmd);
+ install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
+ install_element(ENABLE_NODE, &echo_cmd);
+
+ install_default(CONFIG_NODE);
+ install_element(CONFIG_NODE, &config_exit_cmd);
+ }
+
+ install_element(CONFIG_NODE, &hostname_cmd);
+ install_element(CONFIG_NODE, &no_hostname_cmd);
+
+ if (terminal) {
+ install_element(CONFIG_NODE, &password_cmd);
+ install_element(CONFIG_NODE, &password_text_cmd);
+ install_element(CONFIG_NODE, &enable_password_cmd);
+ install_element(CONFIG_NODE, &enable_password_text_cmd);
+ install_element(CONFIG_NODE, &no_enable_password_cmd);
+
+#ifdef VTY_CRYPT_PW
+ install_element(CONFIG_NODE, &service_password_encrypt_cmd);
+ install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
+#endif
+ install_element(CONFIG_NODE, &banner_motd_default_cmd);
+ install_element(CONFIG_NODE, &banner_motd_file_cmd);
+ install_element(CONFIG_NODE, &no_banner_motd_cmd);
+ install_element(CONFIG_NODE, &service_terminal_length_cmd);
+ install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
+
+ }
+ srand(time(NULL));
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/vty/logging_vty.c b/src/shared/libosmocore/src/vty/logging_vty.c
new file mode 100644
index 00000000..6be30b4a
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/logging_vty.c
@@ -0,0 +1,604 @@
+/* OpenBSC logging helper for the VTY */
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2010 by Holger Hans Peter Freyther
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../config.h"
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+//#include <openbsc/vty.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+
+#define LOG_STR "Configure logging sub-system\n"
+
+extern const struct log_info *osmo_log_info;
+
+static void _vty_output(struct log_target *tgt,
+ unsigned int level, const char *line)
+{
+ struct vty *vty = tgt->tgt_vty.vty;
+ vty_out(vty, "%s", line);
+ /* This is an ugly hack, but there is no easy way... */
+ if (strchr(line, '\n'))
+ vty_out(vty, "\r");
+}
+
+struct log_target *log_target_create_vty(struct vty *vty)
+{
+ struct log_target *target;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->tgt_vty.vty = vty;
+ target->output = _vty_output;
+ return target;
+}
+
+DEFUN(enable_logging,
+ enable_logging_cmd,
+ "logging enable",
+ LOGGING_STR
+ "Enables logging to this vty\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (conn->dbg) {
+ vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ conn->dbg = log_target_create_vty(vty);
+ if (!conn->dbg)
+ return CMD_WARNING;
+
+ log_add_target(conn->dbg);
+ return CMD_SUCCESS;
+}
+
+struct log_target *osmo_log_vty2tgt(struct vty *vty)
+{
+ struct telnet_connection *conn;
+
+ if (vty->node == CFG_LOG_NODE)
+ return vty->index;
+
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg)
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+
+ return conn->dbg;
+}
+
+DEFUN(logging_fltr_all,
+ logging_fltr_all_cmd,
+ "logging filter all (0|1)",
+ LOGGING_STR FILTER_STR
+ "Do you want to log all messages?\n"
+ "Only print messages matched by other filters\n"
+ "Bypass filter and print all messages\n")
+{
+ struct log_target *tgt = osmo_log_vty2tgt(vty);
+
+ if (!tgt)
+ return CMD_WARNING;
+
+ log_set_all_filter(tgt, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_use_clr,
+ logging_use_clr_cmd,
+ "logging color (0|1)",
+ LOGGING_STR "Configure color-printing for log messages\n"
+ "Don't use color for printing messages\n"
+ "Use color for printing messages\n")
+{
+ struct log_target *tgt = osmo_log_vty2tgt(vty);
+
+ if (!tgt)
+ return CMD_WARNING;
+
+ log_set_use_color(tgt, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_prnt_timestamp,
+ logging_prnt_timestamp_cmd,
+ "logging timestamp (0|1)",
+ LOGGING_STR "Configure log message timestamping\n"
+ "Don't prefix each log message\n"
+ "Prefix each log message with current timestamp\n")
+{
+ struct log_target *tgt = osmo_log_vty2tgt(vty);
+
+ if (!tgt)
+ return CMD_WARNING;
+
+ log_set_print_timestamp(tgt, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_level,
+ logging_level_cmd,
+ NULL, /* cmdstr is dynamically set in logging_vty_add_cmds(). */
+ NULL) /* same thing for helpstr. */
+{
+ int category = log_parse_category(argv[0]);
+ int level = log_parse_level(argv[1]);
+ struct log_target *tgt = osmo_log_vty2tgt(vty);
+
+ if (!tgt)
+ return CMD_WARNING;
+
+ if (level < 0) {
+ vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Check for special case where we want to set global log level */
+ if (!strcmp(argv[0], "all")) {
+ log_set_log_level(tgt, level);
+ return CMD_SUCCESS;
+ }
+
+ if (category < 0) {
+ vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ tgt->categories[category].enabled = 1;
+ tgt->categories[category].loglevel = level;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_category_mask,
+ logging_set_category_mask_cmd,
+ "logging set-log-mask MASK",
+ LOGGING_STR
+ "Set the logmask of this logging target\n"
+ "The logmask to use\n")
+{
+ struct log_target *tgt = osmo_log_vty2tgt(vty);
+
+ if (!tgt)
+ return CMD_WARNING;
+
+ log_parse_category_mask(tgt, argv[0]);
+ return CMD_SUCCESS;
+}
+
+ALIAS_DEPRECATED(logging_set_category_mask,
+ logging_set_category_mask_old_cmd,
+ "logging set log mask MASK",
+ LOGGING_STR
+ "Decide which categories to output.\n"
+ "Log commands\n" "Mask commands\n" "The logmask to use\n");
+
+
+DEFUN(diable_logging,
+ disable_logging_cmd,
+ "logging disable",
+ LOGGING_STR
+ "Disables logging to this vty\n")
+{
+ struct log_target *tgt = osmo_log_vty2tgt(vty);
+ struct telnet_connection *conn = (struct telnet_connection *) vty->priv;
+
+ if (!tgt)
+ return CMD_WARNING;
+
+ log_del_target(tgt);
+ talloc_free(tgt);
+ conn->dbg = NULL;
+
+ return CMD_SUCCESS;
+}
+
+static void vty_print_logtarget(struct vty *vty, const struct log_info *info,
+ const struct log_target *tgt)
+{
+ unsigned int i;
+
+ vty_out(vty, " Global Loglevel: %s%s",
+ log_level_str(tgt->loglevel), VTY_NEWLINE);
+ vty_out(vty, " Use color: %s, Print Timestamp: %s%s",
+ tgt->use_color ? "On" : "Off",
+ tgt->print_timestamp ? "On" : "Off", VTY_NEWLINE);
+
+ vty_out(vty, " Log Level specific information:%s", VTY_NEWLINE);
+
+ for (i = 0; i < info->num_cat; i++) {
+ const struct log_category *cat = &tgt->categories[i];
+ vty_out(vty, " %-10s %-10s %-8s %s%s",
+ info->cat[i].name+1, log_level_str(cat->loglevel),
+ cat->enabled ? "Enabled" : "Disabled",
+ info->cat[i].description,
+ VTY_NEWLINE);
+ }
+}
+
+#define SHOW_LOG_STR "Show current logging configuration\n"
+
+DEFUN(show_logging_vty,
+ show_logging_vty_cmd,
+ "show logging vty",
+ SHOW_STR SHOW_LOG_STR
+ "Show current logging configuration for this vty\n")
+{
+ struct log_target *tgt = osmo_log_vty2tgt(vty);
+
+ if (!tgt)
+ return CMD_WARNING;
+
+ vty_print_logtarget(vty, osmo_log_info, tgt);
+
+ return CMD_SUCCESS;
+}
+
+gDEFUN(cfg_description, cfg_description_cmd,
+ "description .TEXT",
+ "Save human-readable decription of the object\n")
+{
+ char **dptr = vty->index_sub;
+
+ if (!dptr) {
+ vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (*dptr)
+ talloc_free(*dptr);
+ *dptr = argv_concat(argv, argc, 0);
+ if (!dptr)
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+gDEFUN(cfg_no_description, cfg_no_description_cmd,
+ "no description",
+ NO_STR
+ "Remove description of the object\n")
+{
+ char **dptr = vty->index_sub;
+
+ if (!dptr) {
+ vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (*dptr) {
+ talloc_free(*dptr);
+ *dptr = NULL;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Support for configuration of log targets != the current vty */
+
+struct cmd_node cfg_log_node = {
+ CFG_LOG_NODE,
+ "%s(config-log)# ",
+ 1
+};
+
+#ifdef HAVE_SYSLOG_H
+
+#include <syslog.h>
+
+static const int local_sysl_map[] = {
+ [0] = LOG_LOCAL0,
+ [1] = LOG_LOCAL1,
+ [2] = LOG_LOCAL2,
+ [3] = LOG_LOCAL3,
+ [4] = LOG_LOCAL4,
+ [5] = LOG_LOCAL5,
+ [6] = LOG_LOCAL6,
+ [7] = LOG_LOCAL7
+};
+
+/* From VTY core code */
+extern struct host host;
+
+static int _cfg_log_syslog(struct vty *vty, int facility)
+{
+ struct log_target *tgt;
+
+ /* First delete the old syslog target, if any */
+ tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL);
+ if (tgt)
+ log_target_destroy(tgt);
+
+ tgt = log_target_create_syslog(host.app_info->name, 0, facility);
+ if (!tgt) {
+ vty_out(vty, "%% Unable to open syslog%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ log_add_target(tgt);
+
+ vty->index = tgt;
+ vty->node = CFG_LOG_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_log_syslog_local, cfg_log_syslog_local_cmd,
+ "log syslog local <0-7>",
+ LOG_STR "Logging via syslog\n" "Syslog LOCAL facility\n"
+ "Local facility number\n")
+{
+ int local = atoi(argv[0]);
+ int facility = local_sysl_map[local];
+
+ return _cfg_log_syslog(vty, facility);
+}
+
+static struct value_string sysl_level_names[] = {
+ { LOG_AUTHPRIV, "authpriv" },
+ { LOG_CRON, "cron" },
+ { LOG_DAEMON, "daemon" },
+ { LOG_FTP, "ftp" },
+ { LOG_LPR, "lpr" },
+ { LOG_MAIL, "mail" },
+ { LOG_NEWS, "news" },
+ { LOG_USER, "user" },
+ { LOG_UUCP, "uucp" },
+ /* only for value -> string conversion */
+ { LOG_LOCAL0, "local 0" },
+ { LOG_LOCAL1, "local 1" },
+ { LOG_LOCAL2, "local 2" },
+ { LOG_LOCAL3, "local 3" },
+ { LOG_LOCAL4, "local 4" },
+ { LOG_LOCAL5, "local 5" },
+ { LOG_LOCAL6, "local 6" },
+ { LOG_LOCAL7, "local 7" },
+ { 0, NULL }
+};
+
+DEFUN(cfg_log_syslog, cfg_log_syslog_cmd,
+ "log syslog (authpriv|cron|daemon|ftp|lpr|mail|news|user|uucp)",
+ LOG_STR "Logging via syslog\n"
+ "Security/authorization messages facility\n"
+ "Clock daemon (cron/at) facility\n"
+ "General system daemon facility\n"
+ "Ftp daemon facility\n"
+ "Line printer facility\n"
+ "Mail facility\n"
+ "News facility\n"
+ "Generic facility\n"
+ "UUCP facility\n")
+{
+ int facility = get_string_value(sysl_level_names, argv[0]);
+
+ return _cfg_log_syslog(vty, facility);
+}
+
+DEFUN(cfg_no_log_syslog, cfg_no_log_syslog_cmd,
+ "no log syslog",
+ NO_STR LOG_STR "Logging via syslog\n")
+{
+ struct log_target *tgt;
+
+ tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL);
+ if (!tgt) {
+ vty_out(vty, "%% No syslog target found%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ log_target_destroy(tgt);
+
+ return CMD_SUCCESS;
+}
+#endif /* HAVE_SYSLOG_H */
+
+DEFUN(cfg_log_stderr, cfg_log_stderr_cmd,
+ "log stderr",
+ LOG_STR "Logging via STDERR of the process\n")
+{
+ struct log_target *tgt;
+
+ tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL);
+ if (!tgt) {
+ tgt = log_target_create_stderr();
+ if (!tgt) {
+ vty_out(vty, "%% Unable to create stderr log%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ log_add_target(tgt);
+ }
+
+ vty->index = tgt;
+ vty->node = CFG_LOG_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_log_stderr, cfg_no_log_stderr_cmd,
+ "no log stderr",
+ NO_STR LOG_STR "Logging via STDERR of the process\n")
+{
+ struct log_target *tgt;
+
+ tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL);
+ if (!tgt) {
+ vty_out(vty, "%% No stderr logging active%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ log_target_destroy(tgt);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_log_file, cfg_log_file_cmd,
+ "log file .FILENAME",
+ LOG_STR "Logging to text file\n" "Filename\n")
+{
+ const char *fname = argv[0];
+ struct log_target *tgt;
+
+ tgt = log_target_find(LOG_TGT_TYPE_FILE, fname);
+ if (!tgt) {
+ tgt = log_target_create_file(fname);
+ if (!tgt) {
+ vty_out(vty, "%% Unable to create file `%s'%s",
+ fname, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ log_add_target(tgt);
+ }
+
+ vty->index = tgt;
+ vty->node = CFG_LOG_NODE;
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN(cfg_no_log_file, cfg_no_log_file_cmd,
+ "no log file .FILENAME",
+ NO_STR LOG_STR "Logging to text file\n" "Filename\n")
+{
+ const char *fname = argv[0];
+ struct log_target *tgt;
+
+ tgt = log_target_find(LOG_TGT_TYPE_FILE, fname);
+ if (!tgt) {
+ vty_out(vty, "%% No such log file `%s'%s",
+ fname, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ log_target_destroy(tgt);
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_log_single(struct vty *vty, struct log_target *tgt)
+{
+ int i;
+ char level_lower[32];
+
+ switch (tgt->type) {
+ case LOG_TGT_TYPE_VTY:
+ return 1;
+ break;
+ case LOG_TGT_TYPE_STDERR:
+ vty_out(vty, "log stderr%s", VTY_NEWLINE);
+ break;
+ case LOG_TGT_TYPE_SYSLOG:
+#ifdef HAVE_SYSLOG_H
+ vty_out(vty, "log syslog %s%s",
+ get_value_string(sysl_level_names,
+ tgt->tgt_syslog.facility),
+ VTY_NEWLINE);
+#endif
+ break;
+ case LOG_TGT_TYPE_FILE:
+ vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE);
+ break;
+ }
+
+ vty_out(vty, " logging color %u%s", tgt->use_color ? 1 : 0,
+ VTY_NEWLINE);
+ vty_out(vty, " logging timestamp %u%s", tgt->print_timestamp ? 1 : 0,
+ VTY_NEWLINE);
+
+ /* stupid old osmo logging API uses uppercase strings... */
+ osmo_str2lower(level_lower, log_level_str(tgt->loglevel));
+ vty_out(vty, " logging level all %s%s", level_lower, VTY_NEWLINE);
+
+ for (i = 0; i < osmo_log_info->num_cat; i++) {
+ const struct log_category *cat = &tgt->categories[i];
+ char cat_lower[32];
+
+ /* stupid old osmo logging API uses uppercase strings... */
+ osmo_str2lower(cat_lower, osmo_log_info->cat[i].name+1);
+ osmo_str2lower(level_lower, log_level_str(cat->loglevel));
+
+ vty_out(vty, " logging level %s %s%s", cat_lower, level_lower,
+ VTY_NEWLINE);
+ }
+
+ /* FIXME: levels */
+
+ return 1;
+}
+
+static int config_write_log(struct vty *vty)
+{
+ struct log_target *dbg = vty->index;
+
+ llist_for_each_entry(dbg, &osmo_log_target_list, entry)
+ config_write_log_single(vty, dbg);
+
+ return 1;
+}
+
+void logging_vty_add_cmds(const struct log_info *cat)
+{
+ install_element_ve(&enable_logging_cmd);
+ install_element_ve(&disable_logging_cmd);
+ install_element_ve(&logging_fltr_all_cmd);
+ install_element_ve(&logging_use_clr_cmd);
+ install_element_ve(&logging_prnt_timestamp_cmd);
+ install_element_ve(&logging_set_category_mask_cmd);
+ install_element_ve(&logging_set_category_mask_old_cmd);
+
+ /* Logging level strings are generated dynamically. */
+ logging_level_cmd.string = log_vty_command_string(cat);
+ logging_level_cmd.doc = log_vty_command_description(cat);
+ install_element_ve(&logging_level_cmd);
+ install_element_ve(&show_logging_vty_cmd);
+
+ install_node(&cfg_log_node, config_write_log);
+ install_element(CFG_LOG_NODE, &logging_fltr_all_cmd);
+ install_element(CFG_LOG_NODE, &logging_use_clr_cmd);
+ install_element(CFG_LOG_NODE, &logging_prnt_timestamp_cmd);
+ install_element(CFG_LOG_NODE, &logging_level_cmd);
+
+ install_element(CONFIG_NODE, &cfg_log_stderr_cmd);
+ install_element(CONFIG_NODE, &cfg_no_log_stderr_cmd);
+ install_element(CONFIG_NODE, &cfg_log_file_cmd);
+ install_element(CONFIG_NODE, &cfg_no_log_file_cmd);
+#ifdef HAVE_SYSLOG_H
+ install_element(CONFIG_NODE, &cfg_log_syslog_cmd);
+ install_element(CONFIG_NODE, &cfg_log_syslog_local_cmd);
+ install_element(CONFIG_NODE, &cfg_no_log_syslog_cmd);
+#endif
+}
diff --git a/src/shared/libosmocore/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c
new file mode 100644
index 00000000..167acc18
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/telnet_interface.c
@@ -0,0 +1,236 @@
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (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 <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/command.h>
+
+/*! \addtogroup telnet_interface
+ * @{
+ */
+/*! \file telnet_interface.c */
+
+/* per connection data */
+LLIST_HEAD(active_connections);
+
+static void *tall_telnet_ctx;
+
+/* per network data */
+static int telnet_new_connection(struct osmo_fd *fd, unsigned int what);
+
+static struct osmo_fd server_socket = {
+ .when = BSC_FD_READ,
+ .cb = telnet_new_connection,
+ .priv_nr = 0,
+};
+
+/*! \brief Initialize telnet based VTY interface
+ * \param[in] tall_ctx \ref talloc context
+ * \param[in] priv private data to be passed to callback
+ * \param[in] port UDP port number
+ */
+int telnet_init(void *tall_ctx, void *priv, int port)
+{
+ struct sockaddr_in sock_addr;
+ int fd, rc, on = 1;
+
+ tall_telnet_ctx = talloc_named_const(tall_ctx, 1,
+ "telnet_connection");
+
+ /* FIXME: use new socket.c code of libosmocore */
+ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (fd < 0) {
+ LOGP(0, LOGL_ERROR, "Telnet interface socket creation failed\n");
+ return fd;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_port = htons(port);
+ sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ rc = bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr));
+ if (rc < 0) {
+ LOGP(0, LOGL_ERROR, "Telnet interface failed to bind\n");
+ close(fd);
+ return rc;
+ }
+
+ rc = listen(fd, 0);
+ if (rc < 0) {
+ LOGP(0, LOGL_ERROR, "Telnet interface failed to listen\n");
+ close(fd);
+ return rc;
+ }
+
+ server_socket.data = priv;
+ server_socket.fd = fd;
+ osmo_fd_register(&server_socket);
+
+ return 0;
+}
+
+extern struct host host;
+
+static void print_welcome(int fd)
+{
+ static const char *msg1 = "Welcome to the ";
+ static const char *msg2 = " control interface\r\n";
+ const char *app_name = "<unnamed>";
+
+ if (host.app_info->name)
+ app_name = host.app_info->name;
+
+ write(fd, msg1, strlen(msg1));
+ write(fd, app_name, strlen(app_name));
+ write(fd, msg2, strlen(msg2));
+
+ if (host.app_info->copyright)
+ write(fd, host.app_info->copyright, strlen(host.app_info->copyright));
+}
+
+/*! \brief close a telnet connection */
+int telnet_close_client(struct osmo_fd *fd)
+{
+ struct telnet_connection *conn = (struct telnet_connection*)fd->data;
+
+ close(fd->fd);
+ osmo_fd_unregister(fd);
+
+ if (conn->dbg) {
+ log_del_target(conn->dbg);
+ talloc_free(conn->dbg);
+ }
+
+ llist_del(&conn->entry);
+ talloc_free(conn);
+ return 0;
+}
+
+static int client_data(struct osmo_fd *fd, unsigned int what)
+{
+ struct telnet_connection *conn = fd->data;
+ int rc = 0;
+
+ if (what & BSC_FD_READ) {
+ conn->fd.when &= ~BSC_FD_READ;
+ rc = vty_read(conn->vty);
+ }
+
+ /* vty might have been closed from vithin vty_read() */
+ if (!conn->vty)
+ return rc;
+
+ if (what & BSC_FD_WRITE) {
+ rc = buffer_flush_all(conn->vty->obuf, fd->fd);
+ if (rc == BUFFER_EMPTY)
+ conn->fd.when &= ~BSC_FD_WRITE;
+ }
+
+ return rc;
+}
+
+static int telnet_new_connection(struct osmo_fd *fd, unsigned int what)
+{
+ struct telnet_connection *connection;
+ struct sockaddr_in sockaddr;
+ socklen_t len = sizeof(sockaddr);
+ int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
+
+ if (new_connection < 0) {
+ LOGP(0, LOGL_ERROR, "telnet accept failed\n");
+ return new_connection;
+ }
+
+ connection = talloc_zero(tall_telnet_ctx, struct telnet_connection);
+ connection->priv = fd->data;
+ connection->fd.data = connection;
+ connection->fd.fd = new_connection;
+ connection->fd.when = BSC_FD_READ;
+ connection->fd.cb = client_data;
+ osmo_fd_register(&connection->fd);
+ llist_add_tail(&connection->entry, &active_connections);
+
+ print_welcome(new_connection);
+
+ connection->vty = vty_create(new_connection, connection);
+ if (!connection->vty) {
+ LOGP(0, LOGL_ERROR, "couldn't create VTY\n");
+ close(new_connection);
+ talloc_free(connection);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*! \brief callback from core VTY code about VTY related events */
+void vty_event(enum event event, int sock, struct vty *vty)
+{
+ struct telnet_connection *connection = vty->priv;
+ struct osmo_fd *bfd = &connection->fd;
+
+ if (vty->type != VTY_TERM)
+ return;
+
+ switch (event) {
+ case VTY_READ:
+ bfd->when |= BSC_FD_READ;
+ break;
+ case VTY_WRITE:
+ bfd->when |= BSC_FD_WRITE;
+ break;
+ case VTY_CLOSED:
+ /* vty layer is about to free() vty */
+ connection->vty = NULL;
+ telnet_close_client(bfd);
+ break;
+ default:
+ break;
+ }
+}
+
+void telnet_exit(void)
+{
+ struct telnet_connection *tc, *tc2;
+
+ llist_for_each_entry_safe(tc, tc2, &active_connections, entry)
+ telnet_close_client(&tc->fd);
+
+ osmo_fd_unregister(&server_socket);
+ close(server_socket.fd);
+ talloc_free(tall_telnet_ctx);
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/vty/utils.c b/src/shared/libosmocore/src/vty/utils.c
new file mode 100644
index 00000000..47b9931f
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/utils.c
@@ -0,0 +1,63 @@
+/* utility routines for printing common objects in the Osmocom world */
+
+/* (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 <stdint.h>
+#include <inttypes.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/rate_ctr.h>
+
+#include <osmocom/vty/vty.h>
+
+/* \file utils.c */
+
+/*! \addtogroup rate_ctr
+ * @{
+ */
+
+/*! \brief print a rate counter group to given VTY
+ * \param[in] vty The VTY to which it should be printed
+ * \param[in] prefix Any additional log prefix ahead of each line
+ * \param[in] ctrg Rate counter group to be printed
+ */
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg)
+{
+ unsigned int i;
+
+ vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE);
+ for (i = 0; i < ctrg->desc->num_ctr; i++) {
+ struct rate_ctr *ctr = &ctrg->ctr[i];
+ vty_out(vty, " %s%s: %8" PRIu64 " "
+ "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
+ prefix, ctrg->desc->ctr_desc[i].description, ctr->current,
+ ctr->intv[RATE_CTR_INTV_SEC].rate,
+ ctr->intv[RATE_CTR_INTV_MIN].rate,
+ ctr->intv[RATE_CTR_INTV_HOUR].rate,
+ ctr->intv[RATE_CTR_INTV_DAY].rate,
+ VTY_NEWLINE);
+ };
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/vty/vector.c b/src/shared/libosmocore/src/vty/vector.c
new file mode 100644
index 00000000..4012f24b
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/vector.c
@@ -0,0 +1,192 @@
+/* Generic vector interface routine
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <osmocom/vty/vector.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/core/talloc.h>
+#include <memory.h>
+
+void *tall_vty_vec_ctx;
+
+/* Initialize vector : allocate memory and return vector. */
+vector vector_init(unsigned int size)
+{
+ vector v = talloc_zero(tall_vty_vec_ctx, struct _vector);
+ if (!v)
+ return NULL;
+
+ /* allocate at least one slot */
+ if (size == 0)
+ size = 1;
+
+ v->alloced = size;
+ v->active = 0;
+ v->index = _talloc_zero(tall_vty_vec_ctx, sizeof(void *) * size,
+ "vector_init:index");
+ if (!v->index) {
+ talloc_free(v);
+ return NULL;
+ }
+ return v;
+}
+
+void vector_only_wrapper_free(vector v)
+{
+ talloc_free(v);
+}
+
+void vector_only_index_free(void *index)
+{
+ talloc_free(index);
+}
+
+void vector_free(vector v)
+{
+ talloc_free(v->index);
+ talloc_free(v);
+}
+
+vector vector_copy(vector v)
+{
+ unsigned int size;
+ vector new = talloc_zero(tall_vty_vec_ctx, struct _vector);
+ if (!new)
+ return NULL;
+
+ new->active = v->active;
+ new->alloced = v->alloced;
+
+ size = sizeof(void *) * (v->alloced);
+ new->index = _talloc_zero(tall_vty_vec_ctx, size, "vector_copy:index");
+ if (!new->index) {
+ talloc_free(new);
+ return NULL;
+ }
+ memcpy(new->index, v->index, size);
+
+ return new;
+}
+
+/* Check assigned index, and if it runs short double index pointer */
+void vector_ensure(vector v, unsigned int num)
+{
+ if (v->alloced > num)
+ return;
+
+ v->index = talloc_realloc_size(tall_vty_vec_ctx, v->index,
+ sizeof(void *) * (v->alloced * 2));
+ memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced);
+ v->alloced *= 2;
+
+ if (v->alloced <= num)
+ vector_ensure(v, num);
+}
+
+/* This function only returns next empty slot index. It dose not mean
+ the slot's index memory is assigned, please call vector_ensure()
+ after calling this function. */
+int vector_empty_slot(vector v)
+{
+ unsigned int i;
+
+ if (v->active == 0)
+ return 0;
+
+ for (i = 0; i < v->active; i++)
+ if (v->index[i] == 0)
+ return i;
+
+ return i;
+}
+
+/* Set value to the smallest empty slot. */
+int vector_set(vector v, void *val)
+{
+ unsigned int i;
+
+ i = vector_empty_slot(v);
+ vector_ensure(v, i);
+
+ v->index[i] = val;
+
+ if (v->active <= i)
+ v->active = i + 1;
+
+ return i;
+}
+
+/* Set value to specified index slot. */
+int vector_set_index(vector v, unsigned int i, void *val)
+{
+ vector_ensure(v, i);
+
+ v->index[i] = val;
+
+ if (v->active <= i)
+ v->active = i + 1;
+
+ return i;
+}
+
+/* Look up vector. */
+void *vector_lookup(vector v, unsigned int i)
+{
+ if (i >= v->active)
+ return NULL;
+ return v->index[i];
+}
+
+/* Lookup vector, ensure it. */
+void *vector_lookup_ensure(vector v, unsigned int i)
+{
+ vector_ensure(v, i);
+ return v->index[i];
+}
+
+/* Unset value at specified index slot. */
+void vector_unset(vector v, unsigned int i)
+{
+ if (i >= v->alloced)
+ return;
+
+ v->index[i] = NULL;
+
+ if (i + 1 == v->active) {
+ v->active--;
+ while (i && v->index[--i] == NULL && v->active--) ; /* Is this ugly ? */
+ }
+}
+
+/* Count the number of not emplty slot. */
+unsigned int vector_count(vector v)
+{
+ unsigned int i;
+ unsigned count = 0;
+
+ for (i = 0; i < v->active; i++)
+ if (v->index[i] != NULL)
+ count++;
+
+ return count;
+}
diff --git a/src/shared/libosmocore/src/vty/vty.c b/src/shared/libosmocore/src/vty/vty.c
new file mode 100644
index 00000000..da035969
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/vty.c
@@ -0,0 +1,1771 @@
+
+/*! \mainpage libosmovty Documentation
+ *
+ * \section sec_intro Introduction
+ * This library is a collection of common code used in various
+ * GSM related sub-projects inside the Osmocom family of projects. It
+ * has been imported/derived from the GNU Zebra project.
+ * \n\n
+ * libosmovty implements the interactive command-line on the VTY
+ * (Virtual TTY) as well as configuration file parsing.
+ * \n\n
+ * Please note that C language projects inside Osmocom are typically
+ * single-threaded event-loop state machine designs. As such,
+ * routines in libosmovty are not thread-safe. If you must use them in
+ * a multi-threaded context, you have to add your own locking.
+ *
+ * \section sec_copyright Copyright and License
+ * Copyright © 1997-2007 - Kuninhiro Ishiguro\n
+ * Copyright © 2008-2011 - Harald Welte, Holger Freyther and contributors\n
+ * All rights reserved. \n\n
+ * The source code of libosmovty is licensed 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.\n
+ * See <http://www.gnu.org/licenses/> or COPYING included in the source
+ * code package istelf.\n
+ * The information detailed here is provided AS IS with NO WARRANTY OF
+ * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * \n\n
+ *
+ * \section sec_contact Contact and Support
+ * Community-based support is available at the OpenBSC mailing list
+ * <http://lists.osmocom.org/mailman/listinfo/openbsc>\n
+ * Commercial support options available upon request from
+ * <http://sysmocom.de/>
+ */
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <termios.h>
+
+#include <sys/utsname.h>
+#include <sys/param.h>
+
+#include <arpa/telnet.h>
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/core/talloc.h>
+
+/* \addtogroup vty
+ * @{
+ */
+/*! \file vty.c */
+
+#define SYSCONFDIR "/usr/local/etc"
+
+/* our callback, located in telnet_interface.c */
+void vty_event(enum event event, int sock, struct vty *vty);
+
+extern struct host host;
+
+/* Vector which store each vty structure. */
+static vector vtyvec;
+
+vector Vvty_serv_thread;
+
+char *vty_cwd = NULL;
+
+/* Configure lock. */
+static int vty_config;
+
+static int no_password_check = 1;
+
+void *tall_vty_ctx;
+
+static void vty_clear_buf(struct vty *vty)
+{
+ memset(vty->buf, 0, vty->max);
+}
+
+/*! \brief Allocate a new vty interface structure */
+struct vty *vty_new(void)
+{
+ struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
+
+ if (!new)
+ goto out;
+
+ new->obuf = buffer_new(new, 0); /* Use default buffer size. */
+ if (!new->obuf)
+ goto out_new;
+ new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
+ if (!new->buf)
+ goto out_obuf;
+
+ new->max = VTY_BUFSIZ;
+
+ return new;
+
+out_obuf:
+ buffer_free(new->obuf);
+out_new:
+ talloc_free(new);
+ new = NULL;
+out:
+ return new;
+}
+
+/* Authentication of vty */
+static void vty_auth(struct vty *vty, char *buf)
+{
+ char *passwd = NULL;
+ enum node_type next_node = 0;
+ int fail;
+ char *crypt(const char *, const char *);
+
+ switch (vty->node) {
+ case AUTH_NODE:
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt)
+ passwd = host.password_encrypt;
+ else
+#endif
+ passwd = host.password;
+ if (host.advanced)
+ next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
+ else
+ next_node = VIEW_NODE;
+ break;
+ case AUTH_ENABLE_NODE:
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt)
+ passwd = host.enable_encrypt;
+ else
+#endif
+ passwd = host.enable;
+ next_node = ENABLE_NODE;
+ break;
+ }
+
+ if (passwd) {
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt)
+ fail = strcmp(crypt(buf, passwd), passwd);
+ else
+#endif
+ fail = strcmp(buf, passwd);
+ } else
+ fail = 1;
+
+ if (!fail) {
+ vty->fail = 0;
+ vty->node = next_node; /* Success ! */
+ } else {
+ vty->fail++;
+ if (vty->fail >= 3) {
+ if (vty->node == AUTH_NODE) {
+ vty_out(vty,
+ "%% Bad passwords, too many failures!%s",
+ VTY_NEWLINE);
+ vty->status = VTY_CLOSE;
+ } else {
+ /* AUTH_ENABLE_NODE */
+ vty->fail = 0;
+ vty_out(vty,
+ "%% Bad enable passwords, too many failures!%s",
+ VTY_NEWLINE);
+ vty->node = VIEW_NODE;
+ }
+ }
+ }
+}
+
+/*! \brief Close a given vty interface. */
+void vty_close(struct vty *vty)
+{
+ int i;
+
+ if (vty->obuf) {
+ /* Flush buffer. */
+ buffer_flush_all(vty->obuf, vty->fd);
+
+ /* Free input buffer. */
+ buffer_free(vty->obuf);
+ vty->obuf = NULL;
+ }
+
+ /* Free command history. */
+ for (i = 0; i < VTY_MAXHIST; i++)
+ if (vty->hist[i])
+ talloc_free(vty->hist[i]);
+
+ /* Unset vector. */
+ vector_unset(vtyvec, vty->fd);
+
+ /* Close socket. */
+ if (vty->fd > 0)
+ close(vty->fd);
+
+ if (vty->buf) {
+ talloc_free(vty->buf);
+ vty->buf = NULL;
+ }
+
+ /* Check configure. */
+ vty_config_unlock(vty);
+
+ /* VTY_CLOSED is handled by the telnet_interface */
+ vty_event(VTY_CLOSED, vty->fd, vty);
+
+ /* OK free vty. */
+ talloc_free(vty);
+}
+
+/*! \brief Return if this VTY is a shell or not */
+int vty_shell(struct vty *vty)
+{
+ return vty->type == VTY_SHELL ? 1 : 0;
+}
+
+
+/*! \brief VTY standard output function
+ * \param[in] vty VTY to which we should print
+ * \param[in] format variable-length format string
+ */
+int vty_out(struct vty *vty, const char *format, ...)
+{
+ va_list args;
+ int len = 0;
+ int size = 1024;
+ char buf[1024];
+ char *p = NULL;
+
+ if (vty_shell(vty)) {
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ } else {
+ /* Try to write to initial buffer. */
+ va_start(args, format);
+ len = vsnprintf(buf, sizeof buf, format, args);
+ va_end(args);
+
+ /* Initial buffer is not enough. */
+ if (len < 0 || len >= size) {
+ while (1) {
+ if (len > -1)
+ size = len + 1;
+ else
+ size = size * 2;
+
+ p = talloc_realloc_size(vty, p, size);
+ if (!p)
+ return -1;
+
+ va_start(args, format);
+ len = vsnprintf(p, size, format, args);
+ va_end(args);
+
+ if (len > -1 && len < size)
+ break;
+ }
+ }
+
+ /* When initial buffer is enough to store all output. */
+ if (!p)
+ p = buf;
+
+ /* Pointer p must point out buffer. */
+ buffer_put(vty->obuf, (u_char *) p, len);
+
+ /* If p is not different with buf, it is allocated buffer. */
+ if (p != buf)
+ talloc_free(p);
+ }
+
+ vty_event(VTY_WRITE, vty->fd, vty);
+
+ return len;
+}
+
+/*! \brief print a newline on the given VTY */
+int vty_out_newline(struct vty *vty)
+{
+ char *p = vty_newline(vty);
+ buffer_put(vty->obuf, p, strlen(p));
+ return 0;
+}
+
+/*! \brief return the current index of a given VTY */
+void *vty_current_index(struct vty *vty)
+{
+ return vty->index;
+}
+
+/*! \brief return the current node of a given VTY */
+int vty_current_node(struct vty *vty)
+{
+ return vty->node;
+}
+
+/*! \brief Lock the configuration to a given VTY
+ * \param[in] vty VTY to which the config shall be locked
+ * \returns 1 on success, 0 on error
+ *
+ * This shall be used to make sure only one VTY at a given time has
+ * access to modify the configuration */
+int vty_config_lock(struct vty *vty)
+{
+ if (vty_config == 0) {
+ vty->config = 1;
+ vty_config = 1;
+ }
+ return vty->config;
+}
+
+/*! \brief Unlock the configuration from a given VTY
+ * \param[in] vty VTY from which the configuration shall be unlocked
+ * \returns 0 in case of success
+ */
+int vty_config_unlock(struct vty *vty)
+{
+ if (vty_config == 1 && vty->config == 1) {
+ vty->config = 0;
+ vty_config = 0;
+ }
+ return vty->config;
+}
+
+/* Say hello to vty interface. */
+void vty_hello(struct vty *vty)
+{
+ if (host.motdfile) {
+ FILE *f;
+ char buf[4096];
+
+ f = fopen(host.motdfile, "r");
+ if (f) {
+ while (fgets(buf, sizeof(buf), f)) {
+ char *s;
+ /* work backwards to ignore trailling isspace() */
+ for (s = buf + strlen(buf);
+ (s > buf) && isspace(*(s - 1)); s--) ;
+ *s = '\0';
+ vty_out(vty, "%s%s", buf, VTY_NEWLINE);
+ }
+ fclose(f);
+ } else
+ vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
+ } else if (host.motd)
+ vty_out(vty, "%s", host.motd);
+}
+
+/* Put out prompt and wait input from user. */
+static void vty_prompt(struct vty *vty)
+{
+ struct utsname names;
+ const char *hostname;
+
+ if (vty->type == VTY_TERM) {
+ hostname = host.app_info->name;
+ if (!hostname) {
+ uname(&names);
+ hostname = names.nodename;
+ }
+ vty_out(vty, cmd_prompt(vty->node), hostname);
+ }
+}
+
+/* Command execution over the vty interface. */
+static int vty_command(struct vty *vty, char *buf)
+{
+ int ret;
+ vector vline;
+
+ /* Split readline string up into the vector */
+ vline = cmd_make_strvec(buf);
+
+ if (vline == NULL)
+ return CMD_SUCCESS;
+
+ ret = cmd_execute_command(vline, vty, NULL, 0);
+ if (ret != CMD_SUCCESS)
+ switch (ret) {
+ case CMD_WARNING:
+ if (vty->type == VTY_FILE)
+ vty_out(vty, "Warning...%s", VTY_NEWLINE);
+ break;
+ case CMD_ERR_AMBIGUOUS:
+ vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ break;
+ case CMD_ERR_NO_MATCH:
+ vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
+ break;
+ case CMD_ERR_INCOMPLETE:
+ vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
+ break;
+ }
+ cmd_free_strvec(vline);
+
+ return ret;
+}
+
+static const char telnet_backward_char = 0x08;
+static const char telnet_space_char = ' ';
+
+/* Basic function to write buffer to vty. */
+static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
+{
+ if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
+ return;
+
+ /* Should we do buffering here ? And make vty_flush (vty) ? */
+ buffer_put(vty->obuf, buf, nbytes);
+}
+
+/* Ensure length of input buffer. Is buffer is short, double it. */
+static void vty_ensure(struct vty *vty, int length)
+{
+ if (vty->max <= length) {
+ vty->max *= 2;
+ vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
+ // FIXME: check return
+ }
+}
+
+/* Basic function to insert character into vty. */
+static void vty_self_insert(struct vty *vty, char c)
+{
+ int i;
+ int length;
+
+ vty_ensure(vty, vty->length + 1);
+ length = vty->length - vty->cp;
+ memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
+ vty->buf[vty->cp] = c;
+
+ vty_write(vty, &vty->buf[vty->cp], length + 1);
+ for (i = 0; i < length; i++)
+ vty_write(vty, &telnet_backward_char, 1);
+
+ vty->cp++;
+ vty->length++;
+}
+
+/* Self insert character 'c' in overwrite mode. */
+static void vty_self_insert_overwrite(struct vty *vty, char c)
+{
+ vty_ensure(vty, vty->length + 1);
+ vty->buf[vty->cp++] = c;
+
+ if (vty->cp > vty->length)
+ vty->length++;
+
+ if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
+ return;
+
+ vty_write(vty, &c, 1);
+}
+
+/* Insert a word into vty interface with overwrite mode. */
+static void vty_insert_word_overwrite(struct vty *vty, char *str)
+{
+ int len = strlen(str);
+ vty_write(vty, str, len);
+ strcpy(&vty->buf[vty->cp], str);
+ vty->cp += len;
+ vty->length = vty->cp;
+}
+
+/* Forward character. */
+static void vty_forward_char(struct vty *vty)
+{
+ if (vty->cp < vty->length) {
+ vty_write(vty, &vty->buf[vty->cp], 1);
+ vty->cp++;
+ }
+}
+
+/* Backward character. */
+static void vty_backward_char(struct vty *vty)
+{
+ if (vty->cp > 0) {
+ vty->cp--;
+ vty_write(vty, &telnet_backward_char, 1);
+ }
+}
+
+/* Move to the beginning of the line. */
+static void vty_beginning_of_line(struct vty *vty)
+{
+ while (vty->cp)
+ vty_backward_char(vty);
+}
+
+/* Move to the end of the line. */
+static void vty_end_of_line(struct vty *vty)
+{
+ while (vty->cp < vty->length)
+ vty_forward_char(vty);
+}
+
+/* Add current command line to the history buffer. */
+static void vty_hist_add(struct vty *vty)
+{
+ int index;
+
+ if (vty->length == 0)
+ return;
+
+ index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
+
+ /* Ignore the same string as previous one. */
+ if (vty->hist[index])
+ if (strcmp(vty->buf, vty->hist[index]) == 0) {
+ vty->hp = vty->hindex;
+ return;
+ }
+
+ /* Insert history entry. */
+ if (vty->hist[vty->hindex])
+ talloc_free(vty->hist[vty->hindex]);
+ vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
+
+ /* History index rotation. */
+ vty->hindex++;
+ if (vty->hindex == VTY_MAXHIST)
+ vty->hindex = 0;
+
+ vty->hp = vty->hindex;
+}
+
+/* Get telnet window size. */
+static int
+vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
+{
+#ifdef TELNET_OPTION_DEBUG
+ int i;
+
+ for (i = 0; i < nbytes; i++)
+ {
+ switch (buf[i])
+ {
+ case IAC:
+ vty_out (vty, "IAC ");
+ break;
+ case WILL:
+ vty_out (vty, "WILL ");
+ break;
+ case WONT:
+ vty_out (vty, "WONT ");
+ break;
+ case DO:
+ vty_out (vty, "DO ");
+ break;
+ case DONT:
+ vty_out (vty, "DONT ");
+ break;
+ case SB:
+ vty_out (vty, "SB ");
+ break;
+ case SE:
+ vty_out (vty, "SE ");
+ break;
+ case TELOPT_ECHO:
+ vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
+ break;
+ case TELOPT_SGA:
+ vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
+ break;
+ case TELOPT_NAWS:
+ vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
+ break;
+ default:
+ vty_out (vty, "%x ", buf[i]);
+ break;
+ }
+ }
+ vty_out (vty, "%s", VTY_NEWLINE);
+
+#endif /* TELNET_OPTION_DEBUG */
+
+ switch (buf[0])
+ {
+ case SB:
+ vty->sb_len = 0;
+ vty->iac_sb_in_progress = 1;
+ return 0;
+ break;
+ case SE:
+ {
+ if (!vty->iac_sb_in_progress)
+ return 0;
+
+ if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
+ {
+ vty->iac_sb_in_progress = 0;
+ return 0;
+ }
+ switch (vty->sb_buf[0])
+ {
+ case TELOPT_NAWS:
+ if (vty->sb_len != TELNET_NAWS_SB_LEN)
+ vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
+ "should send %d characters, but we received %lu",
+ TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
+ else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
+ vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
+ "too small to handle the telnet NAWS option",
+ (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
+ else
+ {
+ vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
+ vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
+#ifdef TELNET_OPTION_DEBUG
+ vty_out(vty, "TELNET NAWS window size negotiation completed: "
+ "width %d, height %d%s",
+ vty->width, vty->height, VTY_NEWLINE);
+#endif
+ }
+ break;
+ }
+ vty->iac_sb_in_progress = 0;
+ return 0;
+ break;
+ }
+ default:
+ break;
+ }
+ return 1;
+}
+
+/* Execute current command line. */
+static int vty_execute(struct vty *vty)
+{
+ int ret;
+
+ ret = CMD_SUCCESS;
+
+ switch (vty->node) {
+ case AUTH_NODE:
+ case AUTH_ENABLE_NODE:
+ vty_auth(vty, vty->buf);
+ break;
+ default:
+ ret = vty_command(vty, vty->buf);
+ if (vty->type == VTY_TERM)
+ vty_hist_add(vty);
+ break;
+ }
+
+ /* Clear command line buffer. */
+ vty->cp = vty->length = 0;
+ vty_clear_buf(vty);
+
+ if (vty->status != VTY_CLOSE)
+ vty_prompt(vty);
+
+ return ret;
+}
+
+/* Send WILL TELOPT_ECHO to remote server. */
+static void
+vty_will_echo (struct vty *vty)
+{
+ unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
+ vty_out (vty, "%s", cmd);
+}
+
+/* Make suppress Go-Ahead telnet option. */
+static void
+vty_will_suppress_go_ahead (struct vty *vty)
+{
+ unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
+ vty_out (vty, "%s", cmd);
+}
+
+/* Make don't use linemode over telnet. */
+static void
+vty_dont_linemode (struct vty *vty)
+{
+ unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
+ vty_out (vty, "%s", cmd);
+}
+
+/* Use window size. */
+static void
+vty_do_window_size (struct vty *vty)
+{
+ unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
+ vty_out (vty, "%s", cmd);
+}
+
+static void vty_kill_line_from_beginning(struct vty *);
+static void vty_redraw_line(struct vty *);
+
+/* Print command line history. This function is called from
+ vty_next_line and vty_previous_line. */
+static void vty_history_print(struct vty *vty)
+{
+ int length;
+
+ vty_kill_line_from_beginning(vty);
+
+ /* Get previous line from history buffer */
+ length = strlen(vty->hist[vty->hp]);
+ memcpy(vty->buf, vty->hist[vty->hp], length);
+ vty->cp = vty->length = length;
+
+ /* Redraw current line */
+ vty_redraw_line(vty);
+}
+
+/* Show next command line history. */
+static void vty_next_line(struct vty *vty)
+{
+ int try_index;
+
+ if (vty->hp == vty->hindex)
+ return;
+
+ /* Try is there history exist or not. */
+ try_index = vty->hp;
+ if (try_index == (VTY_MAXHIST - 1))
+ try_index = 0;
+ else
+ try_index++;
+
+ /* If there is not history return. */
+ if (vty->hist[try_index] == NULL)
+ return;
+ else
+ vty->hp = try_index;
+
+ vty_history_print(vty);
+}
+
+/* Show previous command line history. */
+static void vty_previous_line(struct vty *vty)
+{
+ int try_index;
+
+ try_index = vty->hp;
+ if (try_index == 0)
+ try_index = VTY_MAXHIST - 1;
+ else
+ try_index--;
+
+ if (vty->hist[try_index] == NULL)
+ return;
+ else
+ vty->hp = try_index;
+
+ vty_history_print(vty);
+}
+
+/* This function redraw all of the command line character. */
+static void vty_redraw_line(struct vty *vty)
+{
+ vty_write(vty, vty->buf, vty->length);
+ vty->cp = vty->length;
+}
+
+/* Forward word. */
+static void vty_forward_word(struct vty *vty)
+{
+ while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
+ vty_forward_char(vty);
+
+ while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
+ vty_forward_char(vty);
+}
+
+/* Backward word without skipping training space. */
+static void vty_backward_pure_word(struct vty *vty)
+{
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+ vty_backward_char(vty);
+}
+
+/* Backward word. */
+static void vty_backward_word(struct vty *vty)
+{
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
+ vty_backward_char(vty);
+
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+ vty_backward_char(vty);
+}
+
+/* When '^D' is typed at the beginning of the line we move to the down
+ level. */
+static void vty_down_level(struct vty *vty)
+{
+ vty_out(vty, "%s", VTY_NEWLINE);
+ /* call the exit function of the specific node */
+ if (vty->node > CONFIG_NODE)
+ vty_go_parent(vty);
+ else
+ (*config_exit_cmd.func) (NULL, vty, 0, NULL);
+ vty_prompt(vty);
+ vty->cp = 0;
+}
+
+/* When '^Z' is received from vty, move down to the enable mode. */
+static void vty_end_config(struct vty *vty)
+{
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ /* FIXME: we need to call the exit function of the specific node
+ * in question, not this generic one that doesn't know all nodes */
+ switch (vty->node) {
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ /* Nothing to do. */
+ break;
+ case CONFIG_NODE:
+ case VTY_NODE:
+ vty_config_unlock(vty);
+ vty->node = ENABLE_NODE;
+ break;
+ case CFG_LOG_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+ default:
+ /* Unknown node, we have to ignore it. */
+ break;
+ }
+
+ vty_prompt(vty);
+ vty->cp = 0;
+}
+
+/* Delete a charcter at the current point. */
+static void vty_delete_char(struct vty *vty)
+{
+ int i;
+ int size;
+
+ if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+ return;
+
+ if (vty->length == 0) {
+ vty_down_level(vty);
+ return;
+ }
+
+ if (vty->cp == vty->length)
+ return; /* completion need here? */
+
+ size = vty->length - vty->cp;
+
+ vty->length--;
+ memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
+ vty->buf[vty->length] = '\0';
+
+ vty_write(vty, &vty->buf[vty->cp], size - 1);
+ vty_write(vty, &telnet_space_char, 1);
+
+ for (i = 0; i < size; i++)
+ vty_write(vty, &telnet_backward_char, 1);
+}
+
+/* Delete a character before the point. */
+static void vty_delete_backward_char(struct vty *vty)
+{
+ if (vty->cp == 0)
+ return;
+
+ vty_backward_char(vty);
+ vty_delete_char(vty);
+}
+
+/* Kill rest of line from current point. */
+static void vty_kill_line(struct vty *vty)
+{
+ int i;
+ int size;
+
+ size = vty->length - vty->cp;
+
+ if (size == 0)
+ return;
+
+ for (i = 0; i < size; i++)
+ vty_write(vty, &telnet_space_char, 1);
+ for (i = 0; i < size; i++)
+ vty_write(vty, &telnet_backward_char, 1);
+
+ memset(&vty->buf[vty->cp], 0, size);
+ vty->length = vty->cp;
+}
+
+/* Kill line from the beginning. */
+static void vty_kill_line_from_beginning(struct vty *vty)
+{
+ vty_beginning_of_line(vty);
+ vty_kill_line(vty);
+}
+
+/* Delete a word before the point. */
+static void vty_forward_kill_word(struct vty *vty)
+{
+ while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
+ vty_delete_char(vty);
+ while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
+ vty_delete_char(vty);
+}
+
+/* Delete a word before the point. */
+static void vty_backward_kill_word(struct vty *vty)
+{
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
+ vty_delete_backward_char(vty);
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+ vty_delete_backward_char(vty);
+}
+
+/* Transpose chars before or at the point. */
+static void vty_transpose_chars(struct vty *vty)
+{
+ char c1, c2;
+
+ /* If length is short or point is near by the beginning of line then
+ return. */
+ if (vty->length < 2 || vty->cp < 1)
+ return;
+
+ /* In case of point is located at the end of the line. */
+ if (vty->cp == vty->length) {
+ c1 = vty->buf[vty->cp - 1];
+ c2 = vty->buf[vty->cp - 2];
+
+ vty_backward_char(vty);
+ vty_backward_char(vty);
+ vty_self_insert_overwrite(vty, c1);
+ vty_self_insert_overwrite(vty, c2);
+ } else {
+ c1 = vty->buf[vty->cp];
+ c2 = vty->buf[vty->cp - 1];
+
+ vty_backward_char(vty);
+ vty_self_insert_overwrite(vty, c1);
+ vty_self_insert_overwrite(vty, c2);
+ }
+}
+
+/* Do completion at vty interface. */
+static void vty_complete_command(struct vty *vty)
+{
+ int i;
+ int ret;
+ char **matched = NULL;
+ vector vline;
+
+ if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+ return;
+
+ vline = cmd_make_strvec(vty->buf);
+ if (vline == NULL)
+ return;
+
+ /* In case of 'help \t'. */
+ if (isspace((int)vty->buf[vty->length - 1]))
+ vector_set(vline, '\0');
+
+ matched = cmd_complete_command(vline, vty, &ret);
+
+ cmd_free_strvec(vline);
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+ switch (ret) {
+ case CMD_ERR_AMBIGUOUS:
+ vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ break;
+ case CMD_ERR_NO_MATCH:
+ /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ break;
+ case CMD_COMPLETE_FULL_MATCH:
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ vty_backward_pure_word(vty);
+ vty_insert_word_overwrite(vty, matched[0]);
+ vty_self_insert(vty, ' ');
+ talloc_free(matched[0]);
+ break;
+ case CMD_COMPLETE_MATCH:
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ vty_backward_pure_word(vty);
+ vty_insert_word_overwrite(vty, matched[0]);
+ talloc_free(matched[0]);
+ break;
+ case CMD_COMPLETE_LIST_MATCH:
+ for (i = 0; matched[i] != NULL; i++) {
+ if (i != 0 && ((i % 6) == 0))
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "%-10s ", matched[i]);
+ talloc_free(matched[i]);
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ break;
+ case CMD_ERR_NOTHING_TODO:
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ break;
+ default:
+ break;
+ }
+ if (matched)
+ vector_only_index_free(matched);
+}
+
+static void
+vty_describe_fold(struct vty *vty, int cmd_width,
+ unsigned int desc_width, struct desc *desc)
+{
+ char *buf;
+ const char *cmd, *p;
+ int pos;
+
+ cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
+
+ if (desc_width <= 0) {
+ vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
+ VTY_NEWLINE);
+ return;
+ }
+
+ buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
+ if (!buf)
+ return;
+
+ for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
+ for (pos = desc_width; pos > 0; pos--)
+ if (*(p + pos) == ' ')
+ break;
+
+ if (pos == 0)
+ break;
+
+ strncpy(buf, p, pos);
+ buf[pos] = '\0';
+ vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
+
+ cmd = "";
+ }
+
+ vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
+
+ talloc_free(buf);
+}
+
+/* Describe matched command function. */
+static void vty_describe_command(struct vty *vty)
+{
+ int ret;
+ vector vline;
+ vector describe;
+ unsigned int i, width, desc_width;
+ struct desc *desc, *desc_cr = NULL;
+
+ vline = cmd_make_strvec(vty->buf);
+
+ /* In case of '> ?'. */
+ if (vline == NULL) {
+ vline = vector_init(1);
+ vector_set(vline, '\0');
+ } else if (isspace((int)vty->buf[vty->length - 1]))
+ vector_set(vline, '\0');
+
+ describe = cmd_describe_command(vline, vty, &ret);
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ /* Ambiguous error. */
+ switch (ret) {
+ case CMD_ERR_AMBIGUOUS:
+ cmd_free_strvec(vline);
+ vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ return;
+ break;
+ case CMD_ERR_NO_MATCH:
+ cmd_free_strvec(vline);
+ vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ return;
+ break;
+ }
+
+ /* Get width of command string. */
+ width = 0;
+ for (i = 0; i < vector_active(describe); i++)
+ if ((desc = vector_slot(describe, i)) != NULL) {
+ unsigned int len;
+
+ if (desc->cmd[0] == '\0')
+ continue;
+
+ len = strlen(desc->cmd);
+ if (desc->cmd[0] == '.')
+ len--;
+
+ if (width < len)
+ width = len;
+ }
+
+ /* Get width of description string. */
+ desc_width = vty->width - (width + 6);
+
+ /* Print out description. */
+ for (i = 0; i < vector_active(describe); i++)
+ if ((desc = vector_slot(describe, i)) != NULL) {
+ if (desc->cmd[0] == '\0')
+ continue;
+
+ if (strcmp(desc->cmd, "<cr>") == 0) {
+ desc_cr = desc;
+ continue;
+ }
+
+ if (!desc->str)
+ vty_out(vty, " %-s%s",
+ desc->cmd[0] ==
+ '.' ? desc->cmd + 1 : desc->cmd,
+ VTY_NEWLINE);
+ else if (desc_width >= strlen(desc->str))
+ vty_out(vty, " %-*s %s%s", width,
+ desc->cmd[0] ==
+ '.' ? desc->cmd + 1 : desc->cmd,
+ desc->str, VTY_NEWLINE);
+ else
+ vty_describe_fold(vty, width, desc_width, desc);
+
+#if 0
+ vty_out(vty, " %-*s %s%s", width
+ desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+ desc->str ? desc->str : "", VTY_NEWLINE);
+#endif /* 0 */
+ }
+
+ if ((desc = desc_cr)) {
+ if (!desc->str)
+ vty_out(vty, " %-s%s",
+ desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+ VTY_NEWLINE);
+ else if (desc_width >= strlen(desc->str))
+ vty_out(vty, " %-*s %s%s", width,
+ desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+ desc->str, VTY_NEWLINE);
+ else
+ vty_describe_fold(vty, width, desc_width, desc);
+ }
+
+ cmd_free_strvec(vline);
+ vector_free(describe);
+
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+}
+
+/* ^C stop current input and do not add command line to the history. */
+static void vty_stop_input(struct vty *vty)
+{
+ vty->cp = vty->length = 0;
+ vty_clear_buf(vty);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ switch (vty->node) {
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ /* Nothing to do. */
+ break;
+ case CONFIG_NODE:
+ case VTY_NODE:
+ vty_config_unlock(vty);
+ vty->node = ENABLE_NODE;
+ break;
+ case CFG_LOG_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+ default:
+ /* Unknown node, we have to ignore it. */
+ break;
+ }
+ vty_prompt(vty);
+
+ /* Set history pointer to the latest one. */
+ vty->hp = vty->hindex;
+}
+
+#define CONTROL(X) ((X) - '@')
+#define VTY_NORMAL 0
+#define VTY_PRE_ESCAPE 1
+#define VTY_ESCAPE 2
+
+/* Escape character command map. */
+static void vty_escape_map(unsigned char c, struct vty *vty)
+{
+ switch (c) {
+ case ('A'):
+ vty_previous_line(vty);
+ break;
+ case ('B'):
+ vty_next_line(vty);
+ break;
+ case ('C'):
+ vty_forward_char(vty);
+ break;
+ case ('D'):
+ vty_backward_char(vty);
+ break;
+ default:
+ break;
+ }
+
+ /* Go back to normal mode. */
+ vty->escape = VTY_NORMAL;
+}
+
+/* Quit print out to the buffer. */
+static void vty_buffer_reset(struct vty *vty)
+{
+ buffer_reset(vty->obuf);
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+}
+
+/*! \brief Read data via vty socket. */
+int vty_read(struct vty *vty)
+{
+ int i;
+ int nbytes;
+ unsigned char buf[VTY_READ_BUFSIZ];
+
+ int vty_sock = vty->fd;
+
+ /* Read raw data from socket */
+ if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
+ if (nbytes < 0) {
+ if (ERRNO_IO_RETRY(errno)) {
+ vty_event(VTY_READ, vty_sock, vty);
+ return 0;
+ }
+ }
+ buffer_reset(vty->obuf);
+ vty->status = VTY_CLOSE;
+ }
+
+ for (i = 0; i < nbytes; i++) {
+ if (buf[i] == IAC) {
+ if (!vty->iac) {
+ vty->iac = 1;
+ continue;
+ } else {
+ vty->iac = 0;
+ }
+ }
+
+ if (vty->iac_sb_in_progress && !vty->iac) {
+ if (vty->sb_len < sizeof(vty->sb_buf))
+ vty->sb_buf[vty->sb_len] = buf[i];
+ vty->sb_len++;
+ continue;
+ }
+
+ if (vty->iac) {
+ /* In case of telnet command */
+ int ret = 0;
+ ret = vty_telnet_option(vty, buf + i, nbytes - i);
+ vty->iac = 0;
+ i += ret;
+ continue;
+ }
+
+ if (vty->status == VTY_MORE) {
+ switch (buf[i]) {
+ case CONTROL('C'):
+ case 'q':
+ case 'Q':
+ vty_buffer_reset(vty);
+ break;
+#if 0 /* More line does not work for "show ip bgp". */
+ case '\n':
+ case '\r':
+ vty->status = VTY_MORELINE;
+ break;
+#endif
+ default:
+ break;
+ }
+ continue;
+ }
+
+ /* Escape character. */
+ if (vty->escape == VTY_ESCAPE) {
+ vty_escape_map(buf[i], vty);
+ continue;
+ }
+
+ /* Pre-escape status. */
+ if (vty->escape == VTY_PRE_ESCAPE) {
+ switch (buf[i]) {
+ case '[':
+ vty->escape = VTY_ESCAPE;
+ break;
+ case 'b':
+ vty_backward_word(vty);
+ vty->escape = VTY_NORMAL;
+ break;
+ case 'f':
+ vty_forward_word(vty);
+ vty->escape = VTY_NORMAL;
+ break;
+ case 'd':
+ vty_forward_kill_word(vty);
+ vty->escape = VTY_NORMAL;
+ break;
+ case CONTROL('H'):
+ case 0x7f:
+ vty_backward_kill_word(vty);
+ vty->escape = VTY_NORMAL;
+ break;
+ default:
+ vty->escape = VTY_NORMAL;
+ break;
+ }
+ continue;
+ }
+
+ switch (buf[i]) {
+ case CONTROL('A'):
+ vty_beginning_of_line(vty);
+ break;
+ case CONTROL('B'):
+ vty_backward_char(vty);
+ break;
+ case CONTROL('C'):
+ vty_stop_input(vty);
+ break;
+ case CONTROL('D'):
+ vty_delete_char(vty);
+ break;
+ case CONTROL('E'):
+ vty_end_of_line(vty);
+ break;
+ case CONTROL('F'):
+ vty_forward_char(vty);
+ break;
+ case CONTROL('H'):
+ case 0x7f:
+ vty_delete_backward_char(vty);
+ break;
+ case CONTROL('K'):
+ vty_kill_line(vty);
+ break;
+ case CONTROL('N'):
+ vty_next_line(vty);
+ break;
+ case CONTROL('P'):
+ vty_previous_line(vty);
+ break;
+ case CONTROL('T'):
+ vty_transpose_chars(vty);
+ break;
+ case CONTROL('U'):
+ vty_kill_line_from_beginning(vty);
+ break;
+ case CONTROL('W'):
+ vty_backward_kill_word(vty);
+ break;
+ case CONTROL('Z'):
+ vty_end_config(vty);
+ break;
+ case '\n':
+ case '\r':
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_execute(vty);
+ break;
+ case '\t':
+ vty_complete_command(vty);
+ break;
+ case '?':
+ if (vty->node == AUTH_NODE
+ || vty->node == AUTH_ENABLE_NODE)
+ vty_self_insert(vty, buf[i]);
+ else
+ vty_describe_command(vty);
+ break;
+ case '\033':
+ if (i + 1 < nbytes && buf[i + 1] == '[') {
+ vty->escape = VTY_ESCAPE;
+ i++;
+ } else
+ vty->escape = VTY_PRE_ESCAPE;
+ break;
+ default:
+ if (buf[i] > 31 && buf[i] < 127)
+ vty_self_insert(vty, buf[i]);
+ break;
+ }
+ }
+
+ /* Check status. */
+ if (vty->status == VTY_CLOSE)
+ vty_close(vty);
+ else {
+ vty_event(VTY_WRITE, vty_sock, vty);
+ vty_event(VTY_READ, vty_sock, vty);
+ }
+ return 0;
+}
+
+/* Read up configuration file */
+static int
+vty_read_file(FILE *confp, void *priv)
+{
+ int ret;
+ struct vty *vty;
+
+ vty = vty_new();
+ vty->fd = 0;
+ vty->type = VTY_FILE;
+ vty->node = CONFIG_NODE;
+ vty->priv = priv;
+
+ ret = config_from_file(vty, confp);
+
+ if (ret != CMD_SUCCESS) {
+ switch (ret) {
+ case CMD_ERR_AMBIGUOUS:
+ fprintf(stderr, "Ambiguous command.\n");
+ break;
+ case CMD_ERR_NO_MATCH:
+ fprintf(stderr, "There is no such command.\n");
+ break;
+ }
+ fprintf(stderr, "Error occurred during reading below "
+ "line:\n%s\n", vty->buf);
+ vty_close(vty);
+ return -EINVAL;
+ }
+
+ vty_close(vty);
+ return 0;
+}
+
+/*! \brief Create new vty structure. */
+struct vty *
+vty_create (int vty_sock, void *priv)
+{
+ struct vty *vty;
+
+ struct termios t;
+
+ tcgetattr(vty_sock, &t);
+ cfmakeraw(&t);
+ tcsetattr(vty_sock, TCSANOW, &t);
+
+ /* Allocate new vty structure and set up default values. */
+ vty = vty_new ();
+ vty->fd = vty_sock;
+ vty->priv = priv;
+ vty->type = VTY_TERM;
+ if (no_password_check)
+ {
+ if (host.advanced)
+ vty->node = ENABLE_NODE;
+ else
+ vty->node = VIEW_NODE;
+ }
+ else
+ vty->node = AUTH_NODE;
+ vty->fail = 0;
+ vty->cp = 0;
+ vty_clear_buf (vty);
+ vty->length = 0;
+ memset (vty->hist, 0, sizeof (vty->hist));
+ vty->hp = 0;
+ vty->hindex = 0;
+ vector_set_index (vtyvec, vty_sock, vty);
+ vty->status = VTY_NORMAL;
+ if (host.lines >= 0)
+ vty->lines = host.lines;
+ else
+ vty->lines = -1;
+
+ if (! no_password_check)
+ {
+ /* Vty is not available if password isn't set. */
+ if (host.password == NULL && host.password_encrypt == NULL)
+ {
+ vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
+ vty->status = VTY_CLOSE;
+ vty_close (vty);
+ return NULL;
+ }
+ }
+
+ /* Say hello to the world. */
+ vty_hello (vty);
+ if (! no_password_check)
+ vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+ /* Setting up terminal. */
+ vty_will_echo (vty);
+ vty_will_suppress_go_ahead (vty);
+
+ vty_dont_linemode (vty);
+ vty_do_window_size (vty);
+ /* vty_dont_lflow_ahead (vty); */
+
+ vty_prompt (vty);
+
+ /* Add read/write thread. */
+ vty_event (VTY_WRITE, vty_sock, vty);
+ vty_event (VTY_READ, vty_sock, vty);
+
+ return vty;
+}
+
+DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
+{
+ unsigned int i;
+ struct vty *v;
+
+ for (i = 0; i < vector_active(vtyvec); i++)
+ if ((v = vector_slot(vtyvec, i)) != NULL)
+ vty_out(vty, "%svty[%d] %s",
+ v->config ? "*" : " ", i, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+/* Move to vty configuration mode. */
+DEFUN(line_vty,
+ line_vty_cmd,
+ "line vty", "Configure a terminal line\n" "Virtual terminal\n")
+{
+ vty->node = VTY_NODE;
+ return CMD_SUCCESS;
+}
+
+/* vty login. */
+DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
+{
+ no_password_check = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_vty_login,
+ no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
+{
+ no_password_check = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(service_advanced_vty,
+ service_advanced_vty_cmd,
+ "service advanced-vty",
+ "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
+{
+ host.advanced = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_service_advanced_vty,
+ no_service_advanced_vty_cmd,
+ "no service advanced-vty",
+ NO_STR
+ "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
+{
+ host.advanced = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN(terminal_monitor,
+ terminal_monitor_cmd,
+ "terminal monitor",
+ "Set terminal line parameters\n"
+ "Copy debug output to the current terminal line\n")
+{
+ vty->monitor = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(terminal_no_monitor,
+ terminal_no_monitor_cmd,
+ "terminal no monitor",
+ "Set terminal line parameters\n"
+ NO_STR "Copy debug output to the current terminal line\n")
+{
+ vty->monitor = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_history,
+ show_history_cmd,
+ "show history", SHOW_STR "Display the session command history\n")
+{
+ int index;
+
+ for (index = vty->hindex + 1; index != vty->hindex;) {
+ if (index == VTY_MAXHIST) {
+ index = 0;
+ continue;
+ }
+
+ if (vty->hist[index] != NULL)
+ vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
+
+ index++;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Display current configuration. */
+static int vty_config_write(struct vty *vty)
+{
+ vty_out(vty, "line vty%s", VTY_NEWLINE);
+
+ /* login */
+ if (no_password_check)
+ vty_out(vty, " no login%s", VTY_NEWLINE);
+
+ vty_out(vty, "!%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+struct cmd_node vty_node = {
+ VTY_NODE,
+ "%s(config-line)# ",
+ 1,
+};
+
+/*! \brief Reset all VTY status. */
+void vty_reset(void)
+{
+ unsigned int i;
+ struct vty *vty;
+ struct thread *vty_serv_thread;
+
+ for (i = 0; i < vector_active(vtyvec); i++)
+ if ((vty = vector_slot(vtyvec, i)) != NULL) {
+ buffer_reset(vty->obuf);
+ vty->status = VTY_CLOSE;
+ vty_close(vty);
+ }
+
+ for (i = 0; i < vector_active(Vvty_serv_thread); i++)
+ if ((vty_serv_thread =
+ vector_slot(Vvty_serv_thread, i)) != NULL) {
+ //thread_cancel (vty_serv_thread);
+ vector_slot(Vvty_serv_thread, i) = NULL;
+ close(i);
+ }
+}
+
+static void vty_save_cwd(void)
+{
+ char cwd[MAXPATHLEN];
+ char *c ;
+
+ c = getcwd(cwd, MAXPATHLEN);
+
+ if (!c) {
+ if (chdir(SYSCONFDIR) != 0)
+ perror("chdir failed");
+ if (getcwd(cwd, MAXPATHLEN) == NULL)
+ perror("getcwd failed");
+ }
+
+ vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
+ strcpy(vty_cwd, cwd);
+}
+
+char *vty_get_cwd(void)
+{
+ return vty_cwd;
+}
+
+int vty_shell_serv(struct vty *vty)
+{
+ return vty->type == VTY_SHELL_SERV ? 1 : 0;
+}
+
+void vty_init_vtysh(void)
+{
+ vtyvec = vector_init(VECTOR_MIN_SIZE);
+}
+
+extern void *tall_bsc_ctx;
+
+/*! \brief Initialize VTY layer
+ * \param[in] app_info application information
+ */
+/* Install vty's own commands like `who' command. */
+void vty_init(struct vty_app_info *app_info)
+{
+ tall_vty_ctx = talloc_named_const(app_info->tall_ctx, 0, "vty");
+ tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
+ tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
+
+ cmd_init(1);
+
+ host.app_info = app_info;
+
+ /* For further configuration read, preserve current directory. */
+ vty_save_cwd();
+
+ vtyvec = vector_init(VECTOR_MIN_SIZE);
+
+ /* Install bgp top node. */
+ install_node(&vty_node, vty_config_write);
+
+ install_element_ve(&config_who_cmd);
+ install_element_ve(&show_history_cmd);
+ install_element(CONFIG_NODE, &line_vty_cmd);
+ install_element(CONFIG_NODE, &service_advanced_vty_cmd);
+ install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
+ install_element(CONFIG_NODE, &show_history_cmd);
+ install_element(ENABLE_NODE, &terminal_monitor_cmd);
+ install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
+
+ install_default(VTY_NODE);
+ install_element(VTY_NODE, &vty_login_cmd);
+ install_element(VTY_NODE, &no_vty_login_cmd);
+}
+
+/*! \brief Read the configuration file using the VTY code
+ * \param[in] file_name file name of the configuration file
+ * \param[in] priv private data to be passed to \ref vty_read_file
+ */
+int vty_read_config_file(const char *file_name, void *priv)
+{
+ FILE *cfile;
+ int rc;
+
+ cfile = fopen(file_name, "r");
+ if (!cfile)
+ return -ENOENT;
+
+ rc = vty_read_file(cfile, priv);
+ fclose(cfile);
+
+ host_config_set(file_name);
+
+ return rc;
+}
+
+/*! }@ */
diff --git a/src/shared/libosmocore/src/write_queue.c b/src/shared/libosmocore/src/write_queue.c
new file mode 100644
index 00000000..9ddc3af4
--- /dev/null
+++ b/src/shared/libosmocore/src/write_queue.c
@@ -0,0 +1,118 @@
+/* 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 <osmocom/core/write_queue.h>
+
+/*! \addtogroup write_queue
+ * @{
+ */
+
+/*! \file write_queue.c */
+
+/*! \brief Select loop function for write queue handling
+ * \param[in] fd osmocom file descriptor
+ * \param[in] what bit-mask of events that have happened
+ *
+ * This function is provided so that it can be registered with the
+ * select loop abstraction code (\ref osmo_fd::cb).
+ */
+int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what)
+{
+ struct osmo_wqueue *queue;
+
+ queue = container_of(fd, struct osmo_wqueue, bfd);
+
+ if (what & BSC_FD_READ)
+ queue->read_cb(fd);
+
+ if (what & BSC_FD_EXCEPT)
+ queue->except_cb(fd);
+
+ if (what & BSC_FD_WRITE) {
+ struct msgb *msg;
+
+ fd->when &= ~BSC_FD_WRITE;
+
+ /* the queue might have been emptied */
+ if (!llist_empty(&queue->msg_queue)) {
+ --queue->current_length;
+
+ msg = msgb_dequeue(&queue->msg_queue);
+ queue->write_cb(fd, msg);
+ msgb_free(msg);
+
+ if (!llist_empty(&queue->msg_queue))
+ fd->when |= BSC_FD_WRITE;
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief Initialize a \ref osmo_wqueue structure
+ * \param[in] queue Write queue to operate on
+ * \param[in] max_length Maximum length of write queue
+ */
+void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length)
+{
+ queue->max_length = max_length;
+ queue->current_length = 0;
+ queue->read_cb = NULL;
+ queue->write_cb = NULL;
+ queue->bfd.cb = osmo_wqueue_bfd_cb;
+ INIT_LLIST_HEAD(&queue->msg_queue);
+}
+
+/*! \brief Enqueue a new \ref msgb into a write queue
+ * \param[in] queue Write queue to be used
+ * \param[in] data to-be-enqueued message buffer
+ */
+int osmo_wqueue_enqueue(struct osmo_wqueue *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;
+}
+
+/*! \brief Clear a \ref osmo_wqueue
+ * \param[in] queue Write queue to be cleared
+ *
+ * This function will clear (remove/release) all messages in it.
+ */
+void osmo_wqueue_clear(struct osmo_wqueue *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/src/shared/libosmocore/tests/Makefile.am b/src/shared/libosmocore/tests/Makefile.am
new file mode 100644
index 00000000..eff1ac44
--- /dev/null
+++ b/src/shared/libosmocore/tests/Makefile.am
@@ -0,0 +1,47 @@
+if ENABLE_TESTS
+SUBDIRS = timer sms ussd smscb bits a5 conv auth lapd gsm0808
+if ENABLE_MSGFILE
+SUBDIRS += msgfile
+endif
+
+
+# The `:;' works around a Bash 3.2 bug when the output is not writeable.
+$(srcdir)/package.m4: $(top_srcdir)/configure.ac
+ :;{ \
+ echo '# Signature of the current package.' && \
+ echo 'm4_define([AT_PACKAGE_NAME],' && \
+ echo ' [$(PACKAGE_NAME)])' && \
+ echo 'm4_define([AT_PACKAGE_TARNAME],' && \
+ echo ' [$(PACKAGE_TARNAME)])' && \
+ echo 'm4_define([AT_PACKAGE_VERSION],' && \
+ echo ' [$(PACKAGE_VERSION)])' && \
+ echo 'm4_define([AT_PACKAGE_STRING],' && \
+ echo ' [$(PACKAGE_STRING)])' && \
+ echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
+ echo ' [$(PACKAGE_BUGREPORT)])'; \
+ echo 'm4_define([AT_PACKAGE_URL],' && \
+ echo ' [$(PACKAGE_URL)])'; \
+ } >'$(srcdir)/package.m4'
+
+EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)
+TESTSUITE = $(srcdir)/testsuite
+
+check-local: atconfig $(TESTSUITE)
+ $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
+
+installcheck-local: atconfig $(TESTSUITE)
+ $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
+ $(TESTSUITEFLAGS)
+
+clean-local:
+ test ! -f '$(TESTSUITE)' || \
+ $(SHELL) '$(TESTSUITE)' --clean
+ $(RM) -f atconfig
+
+AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
+AUTOTEST = $(AUTOM4TE) --language=autotest
+$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
+ $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
+ mv $@.tmp $@
+
+endif
diff --git a/src/shared/libosmocore/tests/a5/Makefile.am b/src/shared/libosmocore/tests/a5/Makefile.am
new file mode 100644
index 00000000..3c6e6ba4
--- /dev/null
+++ b/src/shared/libosmocore/tests/a5/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = a5_test
+EXTRA_DIST = a5_test.ok
+
+a5_test_SOURCES = a5_test.c
+a5_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
diff --git a/src/shared/libosmocore/tests/a5/a5_test.c b/src/shared/libosmocore/tests/a5/a5_test.c
new file mode 100644
index 00000000..14436f19
--- /dev/null
+++ b/src/shared/libosmocore/tests/a5/a5_test.c
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/a5.h>
+
+static const uint8_t key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
+static const uint32_t fn = 123456;
+static const uint8_t dl[] = {
+ /* A5/0 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* A5/1 */
+ 0xcb, 0xa2, 0x55, 0x76, 0x17, 0x5d, 0x3b, 0x1c,
+ 0x7b, 0x2f, 0x29, 0xa8, 0xc1, 0xb6, 0x00,
+
+ /* A5/2 */
+ 0x45, 0x9c, 0x88, 0xc3, 0x82, 0xb7, 0xff, 0xb3,
+ 0x98, 0xd2, 0xf9, 0x6e, 0x0f, 0x14, 0x80,
+};
+static const uint8_t ul[] = {
+ /* A5/0 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* A5/1 */
+ 0xd9, 0x03, 0x5e, 0x0f, 0x2a, 0xec, 0x13, 0x9a,
+ 0x05, 0xd4, 0xa8, 0x7b, 0xb1, 0x64, 0x80,
+
+ /* A5/2 */
+ 0xf0, 0x3a, 0xac, 0xde, 0xe3, 0x5b, 0x5e, 0x65,
+ 0x80, 0xba, 0xab, 0xc0, 0x59, 0x26, 0x40,
+};
+
+static const char *
+binstr(ubit_t *d, int n)
+{
+ static char str[256];
+ int i;
+
+ for (i=0; i<n; i++)
+ str[i] = d[i] ? '1' : '0';
+
+ str[i] = '\0';
+
+ return str;
+}
+
+int main(int argc, char **argv)
+{
+ ubit_t exp[114];
+ ubit_t out[114];
+ int n, i;
+
+ for (n=0; n<3; n++) {
+ /* "Randomize" */
+ for (i=0; i<114; i++)
+ out[i] = i & 1;
+
+ /* DL */
+ osmo_pbit2ubit(exp, &dl[15*n], 114);
+
+ osmo_a5(n, key, fn, out, NULL);
+
+ printf("A5/%d - DL: %s", n, binstr(out, 114));
+
+ if (!memcmp(exp, out, 114))
+ printf(" => OK\n");
+ else {
+ printf(" => BAD\n");
+ printf(" Expected: %s", binstr(out, 114));
+ fprintf(stderr, "[!] A5/%d DL failed", n);
+ exit(1);
+ }
+
+ /* UL */
+ osmo_pbit2ubit(exp, &ul[15*n], 114);
+
+ osmo_a5(n, key, fn, NULL, out);
+
+ printf("A5/%d - UL: %s", n, binstr(out, 114));
+
+ if (!memcmp(exp, out, 114))
+ printf(" => OK\n");
+ else {
+ printf(" => BAD\n");
+ printf(" Expected: %s", binstr(out, 114));
+ fprintf(stderr, "[!] A5/%d UL failed", n);
+ exit(1);
+ }
+ }
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/a5/a5_test.ok b/src/shared/libosmocore/tests/a5/a5_test.ok
new file mode 100644
index 00000000..4497e14a
--- /dev/null
+++ b/src/shared/libosmocore/tests/a5/a5_test.ok
@@ -0,0 +1,6 @@
+A5/0 - DL: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 => OK
+A5/0 - UL: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 => OK
+A5/1 - DL: 110010111010001001010101011101100001011101011101001110110001110001111011001011110010100110101000110000011011011000 => OK
+A5/1 - UL: 110110010000001101011110000011110010101011101100000100111001101000000101110101001010100001111011101100010110010010 => OK
+A5/2 - DL: 010001011001110010001000110000111000001010110111111111111011001110011000110100101111100101101110000011110001010010 => OK
+A5/2 - UL: 111100000011101010101100110111101110001101011011010111100110010110000000101110101010101111000000010110010010011001 => OK
diff --git a/src/shared/libosmocore/tests/auth/Makefile.am b/src/shared/libosmocore/tests/auth/Makefile.am
new file mode 100644
index 00000000..52976d02
--- /dev/null
+++ b/src/shared/libosmocore/tests/auth/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = milenage_test
+EXTRA_DIST = milenage_test.ok
+
+milenage_test_SOURCES = milenage_test.c
+milenage_test_LDADD = $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/gsm/libosmogsm.la
+
diff --git a/src/shared/libosmocore/tests/auth/milenage_test.c b/src/shared/libosmocore/tests/auth/milenage_test.c
new file mode 100644
index 00000000..da7c800a
--- /dev/null
+++ b/src/shared/libosmocore/tests/auth/milenage_test.c
@@ -0,0 +1,78 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/crypt/auth.h>
+#include <osmocom/core/utils.h>
+
+static void dump_auth_vec(struct osmo_auth_vector *vec)
+{
+ printf("RAND:\t%s\n", osmo_hexdump(vec->rand, sizeof(vec->rand)));
+
+ if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) {
+ printf("AUTN:\t%s\n", osmo_hexdump(vec->autn, sizeof(vec->autn)));
+ printf("IK:\t%s\n", osmo_hexdump(vec->ik, sizeof(vec->ik)));
+ printf("CK:\t%s\n", osmo_hexdump(vec->ck, sizeof(vec->ck)));
+ printf("RES:\t%s\n", osmo_hexdump(vec->res, vec->res_len));
+ }
+
+ if (vec->auth_types & OSMO_AUTH_TYPE_GSM) {
+ printf("SRES:\t%s\n", osmo_hexdump(vec->sres, sizeof(vec->sres)));
+ printf("Kc:\t%s\n", osmo_hexdump(vec->kc, sizeof(vec->kc)));
+ }
+}
+
+static struct osmo_sub_auth_data test_aud = {
+ .type = OSMO_AUTH_TYPE_UMTS,
+ .algo = OSMO_AUTH_ALG_MILENAGE,
+ .u.umts = {
+ .opc = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .k = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .amf = { 0x00, 0x00 },
+ .sqn = 0x22,
+ },
+};
+
+int main(int argc, char **argv)
+{
+ struct osmo_auth_vector _vec;
+ struct osmo_auth_vector *vec = &_vec;
+ uint8_t _rand[16];
+ int rc;
+
+#if 0
+ srand(time(NULL));
+ *(uint32_t *)&_rand[0] = rand();
+ *(uint32_t *)(&_rand[4]) = rand();
+ *(uint32_t *)(&_rand[8]) = rand();
+ *(uint32_t *)(&_rand[12]) = rand();
+#else
+ memset(_rand, 0, sizeof(_rand));
+#endif
+ memset(vec, 0, sizeof(*vec));
+
+ rc = osmo_auth_gen_vec(vec, &test_aud, _rand);
+ if (rc < 0) {
+ fprintf(stderr, "error generating auth vector\n");
+ exit(1);
+ }
+
+ dump_auth_vec(vec);
+
+ const uint8_t auts[14] = { 0x87, 0x11, 0xa0, 0xec, 0x9e, 0x16, 0x37, 0xdf,
+ 0x17, 0xf8, 0x0b, 0x38, 0x4e, 0xe4 };
+
+ rc = osmo_auth_gen_vec_auts(vec, &test_aud, auts, _rand, _rand);
+ if (rc < 0) {
+ printf("AUTS failed\n");
+ } else {
+ printf("AUTS success: SEQ.MS = %lu\n", test_aud.u.umts.sqn);
+ }
+
+ exit(0);
+
+}
diff --git a/src/shared/libosmocore/tests/auth/milenage_test.ok b/src/shared/libosmocore/tests/auth/milenage_test.ok
new file mode 100644
index 00000000..66337ca9
--- /dev/null
+++ b/src/shared/libosmocore/tests/auth/milenage_test.ok
@@ -0,0 +1,8 @@
+RAND: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+AUTN: ec 93 20 c2 c2 12 00 00 c8 b7 de 2a 34 49 f1 bd
+IK: 12 cb 2d d3 e0 ec 83 78 f6 fc 1d 60 6c 61 9f 47
+CK: 72 00 a1 84 d8 f2 c7 58 fb df 87 90 0d db f2 75
+RES: e9 fc 88 cc c8 a3 53 81
+SRES: 21 5f db 4d
+Kc: 6d e8 16 a7 59 a4 29 12
+AUTS success: SEQ.MS = 33
diff --git a/src/shared/libosmocore/tests/bits/Makefile.am b/src/shared/libosmocore/tests/bits/Makefile.am
new file mode 100644
index 00000000..d6fb2f2f
--- /dev/null
+++ b/src/shared/libosmocore/tests/bits/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = bitrev_test
+EXTRA_DIST = bitrev_test.ok
+
+bitrev_test_SOURCES = bitrev_test.c
+bitrev_test_LDADD = $(top_builddir)/src/libosmocore.la
+
diff --git a/src/shared/libosmocore/tests/bits/bitrev_test.c b/src/shared/libosmocore/tests/bits/bitrev_test.c
new file mode 100644
index 00000000..5eca990a
--- /dev/null
+++ b/src/shared/libosmocore/tests/bits/bitrev_test.c
@@ -0,0 +1,36 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+
+static const uint8_t input[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+static const uint8_t exp_out[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+
+int main(int argc, char **argv)
+{
+ uint8_t out[ARRAY_SIZE(input)];
+ unsigned int offs;
+
+ for (offs = 0; offs < sizeof(out); offs++) {
+ uint8_t *start = out + offs;
+ uint8_t len = sizeof(out) - offs;
+
+ memcpy(out, input, sizeof(out));
+
+ printf("INORDER: %s\n", osmo_hexdump(start, len));
+ osmo_revbytebits_buf(start, len);
+ printf("REVERSED: %s\n", osmo_hexdump(start, len));
+ if (memcmp(start, exp_out + offs, len)) {
+ printf("EXPECTED: %s\n", osmo_hexdump(exp_out+offs, len));
+ fprintf(stderr, "REVERSED != EXPECTED!\n");
+ exit(1);
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/bits/bitrev_test.ok b/src/shared/libosmocore/tests/bits/bitrev_test.ok
new file mode 100644
index 00000000..47f402f4
--- /dev/null
+++ b/src/shared/libosmocore/tests/bits/bitrev_test.ok
@@ -0,0 +1,24 @@
+INORDER: 01 02 04 08 10 20 40 80
+REVERSED: 80 40 20 10 08 04 02 01
+
+INORDER: 02 04 08 10 20 40 80
+REVERSED: 40 20 10 08 04 02 01
+
+INORDER: 04 08 10 20 40 80
+REVERSED: 20 10 08 04 02 01
+
+INORDER: 08 10 20 40 80
+REVERSED: 10 08 04 02 01
+
+INORDER: 10 20 40 80
+REVERSED: 08 04 02 01
+
+INORDER: 20 40 80
+REVERSED: 04 02 01
+
+INORDER: 40 80
+REVERSED: 02 01
+
+INORDER: 80
+REVERSED: 01
+
diff --git a/src/shared/libosmocore/tests/conv/Makefile.am b/src/shared/libosmocore/tests/conv/Makefile.am
new file mode 100644
index 00000000..75cfec8d
--- /dev/null
+++ b/src/shared/libosmocore/tests/conv/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = conv_test
+EXTRA_DIST = conv_test.ok
+
+conv_test_SOURCES = conv_test.c
+conv_test_LDADD = $(top_builddir)/src/libosmocore.la
diff --git a/src/shared/libosmocore/tests/conv/conv_test.c b/src/shared/libosmocore/tests/conv/conv_test.c
new file mode 100644
index 00000000..54e043e9
--- /dev/null
+++ b/src/shared/libosmocore/tests/conv/conv_test.c
@@ -0,0 +1,486 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/conv.h>
+#include <osmocom/core/utils.h>
+
+#define MAX_LEN_BITS 512
+#define MAX_LEN_BYTES (512/8)
+
+
+/* ------------------------------------------------------------------------ */
+/* Test codes */
+/* ------------------------------------------------------------------------ */
+
+/* GSM xCCH -> Non-recursive code, flushed, not punctured */
+static const uint8_t conv_gsm_xcch_next_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+static const uint8_t conv_gsm_xcch_next_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const struct osmo_conv_code conv_gsm_xcch = {
+ .N = 2,
+ .K = 5,
+ .len = 224,
+ .term = CONV_TERM_FLUSH,
+ .next_output = conv_gsm_xcch_next_output,
+ .next_state = conv_gsm_xcch_next_state,
+};
+
+
+/* GSM TCH/AFS 7.95 -> Recursive code, flushed, with puncturing */
+static const uint8_t conv_gsm_tch_afs_7_95_next_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+};
+
+static const uint8_t conv_gsm_tch_afs_7_95_next_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 },
+ { 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 },
+ { 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 },
+ { 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 },
+ { 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 },
+ { 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 },
+ { 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 },
+ { 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 },
+ { 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 },
+ { 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 },
+ { 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 },
+ { 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 },
+ { 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 },
+ { 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t conv_gsm_tch_afs_7_95_next_term_output[] = {
+ 0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0,
+ 4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4,
+ 7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7,
+ 3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3,
+};
+
+static const uint8_t conv_gsm_tch_afs_7_95_next_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
+};
+
+static int conv_gsm_tch_afs_7_95_puncture[] = {
+ 1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310,
+ 317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367,
+ 373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415,
+ 421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463,
+ 469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505,
+ 506, 508, 509, 511, 512,
+ -1, /* end */
+};
+
+static const struct osmo_conv_code conv_gsm_tch_afs_7_95 = {
+ .N = 3,
+ .K = 7,
+ .len = 165,
+ .term = CONV_TERM_FLUSH,
+ .next_output = conv_gsm_tch_afs_7_95_next_output,
+ .next_state = conv_gsm_tch_afs_7_95_next_state,
+ .next_term_output = conv_gsm_tch_afs_7_95_next_term_output,
+ .next_term_state = conv_gsm_tch_afs_7_95_next_term_state,
+ .puncture = conv_gsm_tch_afs_7_95_puncture,
+};
+
+
+/* GMR-1 TCH3 Speech -> Non recursive code, tail-biting, punctured */
+static const uint8_t conv_gmr1_tch3_speech_next_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 0, 3 }, { 1, 2 },
+ { 2, 1 }, { 3, 0 }, { 1, 2 }, { 0, 3 },
+ { 1, 2 }, { 0, 3 }, { 2, 1 }, { 3, 0 },
+ { 2, 1 }, { 3, 0 }, { 1, 2 }, { 0, 3 },
+ { 1, 2 }, { 0, 3 }, { 2, 1 }, { 3, 0 },
+ { 3, 0 }, { 2, 1 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 },
+ { 1, 2 }, { 0, 3 }, { 2, 1 }, { 3, 0 },
+ { 2, 1 }, { 3, 0 }, { 1, 2 }, { 0, 3 },
+ { 1, 2 }, { 0, 3 }, { 2, 1 }, { 3, 0 },
+ { 2, 1 }, { 3, 0 }, { 1, 2 }, { 0, 3 },
+};
+
+static const uint8_t conv_gmr1_tch3_speech_next_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 },
+ { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 },
+ { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 },
+ { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 },
+ { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 },
+ { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 },
+ { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 },
+ { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 },
+ { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const int conv_gmr1_tch3_speech_puncture[] = {
+ 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47,
+ 51, 55, 59, 63, 67, 71, 75, 79, 83, 87, 91, 95,
+ -1, /* end */
+};
+
+static const struct osmo_conv_code conv_gmr1_tch3_speech = {
+ .N = 2,
+ .K = 7,
+ .len = 48,
+ .term = CONV_TERM_TAIL_BITING,
+ .next_output = conv_gmr1_tch3_speech_next_output,
+ .next_state = conv_gmr1_tch3_speech_next_state,
+ .puncture = conv_gmr1_tch3_speech_puncture,
+};
+
+
+/* WiMax FCH -> Non recursive code, tail-biting, non-punctured */
+static const uint8_t conv_wimax_fch_next_output[][2] = {
+ { 0, 3 }, { 2, 1 }, { 3, 0 }, { 1, 2 },
+ { 3, 0 }, { 1, 2 }, { 0, 3 }, { 2, 1 },
+ { 0, 3 }, { 2, 1 }, { 3, 0 }, { 1, 2 },
+ { 3, 0 }, { 1, 2 }, { 0, 3 }, { 2, 1 },
+ { 1, 2 }, { 3, 0 }, { 2, 1 }, { 0, 3 },
+ { 2, 1 }, { 0, 3 }, { 1, 2 }, { 3, 0 },
+ { 1, 2 }, { 3, 0 }, { 2, 1 }, { 0, 3 },
+ { 2, 1 }, { 0, 3 }, { 1, 2 }, { 3, 0 },
+ { 3, 0 }, { 1, 2 }, { 0, 3 }, { 2, 1 },
+ { 0, 3 }, { 2, 1 }, { 3, 0 }, { 1, 2 },
+ { 3, 0 }, { 1, 2 }, { 0, 3 }, { 2, 1 },
+ { 0, 3 }, { 2, 1 }, { 3, 0 }, { 1, 2 },
+ { 2, 1 }, { 0, 3 }, { 1, 2 }, { 3, 0 },
+ { 1, 2 }, { 3, 0 }, { 2, 1 }, { 0, 3 },
+ { 2, 1 }, { 0, 3 }, { 1, 2 }, { 3, 0 },
+ { 1, 2 }, { 3, 0 }, { 2, 1 }, { 0, 3 },
+};
+
+static const uint8_t conv_wimax_fch_next_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 },
+ { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 },
+ { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 },
+ { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 },
+ { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 },
+ { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 },
+ { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 },
+ { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 },
+ { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const struct osmo_conv_code conv_wimax_fch = {
+ .N = 2,
+ .K = 7,
+ .len = 48,
+ .term = CONV_TERM_TAIL_BITING,
+ .next_output = conv_wimax_fch_next_output,
+ .next_state = conv_wimax_fch_next_state,
+};
+
+
+/* Random code -> Non recursive code, direct truncation, non-punctured */
+static const struct osmo_conv_code conv_trunc = {
+ .N = 2,
+ .K = 5,
+ .len = 224,
+ .term = CONV_TERM_TRUNCATION,
+ .next_output = conv_gsm_xcch_next_output,
+ .next_state = conv_gsm_xcch_next_state,
+};
+
+
+/* ------------------------------------------------------------------------ */
+/* Test vectors */
+/* ------------------------------------------------------------------------ */
+
+struct conv_test_vector {
+ const char *name;
+ const struct osmo_conv_code *code;
+ int in_len;
+ int out_len;
+ int has_vec;
+ pbit_t vec_in[MAX_LEN_BYTES];
+ pbit_t vec_out[MAX_LEN_BYTES];
+};
+
+static const struct conv_test_vector tests[] = {
+ {
+ .name = "GSM xCCH (non-recursive, flushed, not punctured)",
+ .code = &conv_gsm_xcch,
+ .in_len = 224,
+ .out_len = 456,
+ .has_vec = 1,
+ .vec_in = { 0xf3, 0x1d, 0xb4, 0x0c, 0x4d, 0x1d, 0x9d, 0xae,
+ 0xc0, 0x0a, 0x42, 0x57, 0x13, 0x60, 0x80, 0x96,
+ 0xef, 0x23, 0x7e, 0x4c, 0x1d, 0x96, 0x24, 0x19,
+ 0x17, 0xf2, 0x44, 0x99 },
+ .vec_out = { 0xe9, 0x4d, 0x70, 0xab, 0xa2, 0x87, 0xf0, 0xe7,
+ 0x04, 0x14, 0x7c, 0xab, 0xaf, 0x6b, 0xa1, 0x16,
+ 0xeb, 0x30, 0x00, 0xde, 0xc8, 0xfd, 0x0b, 0x85,
+ 0x80, 0x41, 0x4a, 0xcc, 0xd3, 0xc0, 0xd0, 0xb6,
+ 0x26, 0xe5, 0x4e, 0x32, 0x49, 0x69, 0x38, 0x17,
+ 0x33, 0xab, 0xaf, 0xb6, 0xc1, 0x08, 0xf3, 0x9f,
+ 0x8c, 0x75, 0x6a, 0x4e, 0x08, 0xc4, 0x20, 0x5f,
+ 0x8f },
+ },
+ {
+ .name = "GSM TCH/AFS 7.95 (recursive, flushed, punctured)",
+ .code = &conv_gsm_tch_afs_7_95,
+ .in_len = 165,
+ .out_len = 448,
+ .has_vec = 1,
+ .vec_in = { 0x87, 0x66, 0xc3, 0x58, 0x09, 0xd4, 0x06, 0x59,
+ 0x10, 0xbf, 0x6b, 0x7f, 0xc8, 0xed, 0x72, 0xaa,
+ 0xc1, 0x3d, 0xf3, 0x1e, 0xb0 },
+ .vec_out = { 0x92, 0xbc, 0xde, 0xa0, 0xde, 0xbe, 0x01, 0x2f,
+ 0xbe, 0xe4, 0x61, 0x32, 0x4d, 0x4f, 0xdc, 0x41,
+ 0x43, 0x0d, 0x15, 0xe0, 0x23, 0xdd, 0x18, 0x91,
+ 0xe5, 0x36, 0x2d, 0xb7, 0xd9, 0x78, 0xb8, 0xb1,
+ 0xb7, 0xcb, 0x2f, 0xc0, 0x52, 0x8f, 0xe2, 0x8c,
+ 0x6f, 0xa6, 0x79, 0x88, 0xed, 0x0c, 0x2e, 0x9e,
+ 0xa1, 0x5f, 0x45, 0x4a, 0xfb, 0xe6, 0x5a, 0x9c },
+ },
+ {
+ .name = "GMR-1 TCH3 Speech (non-recursive, tail-biting, punctured)",
+ .code = &conv_gmr1_tch3_speech,
+ .in_len = 48,
+ .out_len = 72,
+ .has_vec = 1,
+ .vec_in = { 0x4d, 0xcb, 0xfc, 0x72, 0xf4, 0x8c },
+ .vec_out = { 0xc0, 0x86, 0x63, 0x4b, 0x8b, 0xd4, 0x6a, 0x76, 0xb2 },
+ },
+ {
+ .name = "WiMax FCH (non-recursive, tail-biting, not punctured)",
+ .code = &conv_wimax_fch,
+ .in_len = 48,
+ .out_len = 96,
+ .has_vec = 1,
+ .vec_in = { 0xfc, 0xa0, 0xa0, 0xfc, 0xa0, 0xa0 },
+ .vec_out = { 0x19, 0x42, 0x8a, 0xed, 0x21, 0xed, 0x19, 0x42,
+ 0x8a, 0xed, 0x21, 0xed },
+ },
+ {
+ .name = "??? (non-recursive, direct truncation, not punctured)",
+ .code = &conv_trunc,
+ .in_len = 224,
+ .out_len = 448,
+ .has_vec = 1,
+ .vec_in = { 0xe5, 0xe0, 0x85, 0x7e, 0xf7, 0x08, 0x19, 0x5a,
+ 0xb9, 0xad, 0x82, 0x37, 0x98, 0x8b, 0x26, 0xb9,
+ 0x81, 0x26, 0x9c, 0x75, 0xaf, 0xf3, 0xcb, 0x07,
+ 0xac, 0x63, 0xe2, 0x9c,
+ },
+ .vec_out = { 0xea, 0x3b, 0x55, 0x0c, 0xd3, 0xf7, 0x85, 0x69,
+ 0xe5, 0x79, 0x83, 0xd3, 0xc3, 0x9f, 0xb8, 0x61,
+ 0x21, 0x63, 0x51, 0x18, 0xac, 0xcd, 0x32, 0x49,
+ 0x53, 0x5c, 0x13, 0x1d, 0xbe, 0x05, 0x11, 0x63,
+ 0x5c, 0xc3, 0x42, 0x05, 0x1c, 0x68, 0x0a, 0xb4,
+ 0x61, 0x15, 0xaa, 0x4d, 0x94, 0xed, 0xb3, 0x3a,
+ 0x5d, 0x1b, 0x09, 0xc2, 0x99, 0x01, 0xec, 0x68 },
+ },
+ { /* end */ },
+};
+
+
+
+
+/* ------------------------------------------------------------------------ */
+/* Main */
+/* ------------------------------------------------------------------------ */
+
+static void
+fill_random(ubit_t *b, int n)
+{
+ int i;
+ for (i=0; i<n; i++)
+ b[i] = random() & 1;
+}
+
+static void
+ubit_to_sbit(sbit_t *dst, ubit_t *src, int n)
+{
+ int i;
+ for (i=0; i<n; i++)
+ dst[i] = src[i] ? -127 : 127;
+}
+
+static void
+sbit_to_ubit(ubit_t *dst, sbit_t *src, int n)
+{
+ int i;
+ for (i=0; i<n; i++)
+ dst[i] = src[i] < 0;
+}
+
+
+int main(int argc, char argv[])
+{
+ const struct conv_test_vector *tst;
+ ubit_t *bu0, *bu1;
+ sbit_t *bs;
+
+ srandom(time(NULL));
+
+ bu0 = malloc(sizeof(ubit_t) * MAX_LEN_BITS);
+ bu1 = malloc(sizeof(ubit_t) * MAX_LEN_BITS);
+ bs = malloc(sizeof(sbit_t) * MAX_LEN_BITS);
+
+ for (tst=tests; tst->name; tst++)
+ {
+ int i,l;
+
+ /* Test name */
+ printf("[+] Testing: %s\n", tst->name);
+
+ /* Check length */
+ l = osmo_conv_get_input_length(tst->code, 0);
+ printf("[.] Input length : ret = %3d exp = %3d -> %s\n",
+ l, tst->in_len, l == tst->in_len ? "OK" : "Bad !");
+
+ if (l != tst->in_len) {
+ fprintf(stderr, "[!] Failure for input length computation\n");
+ return -1;
+ }
+
+ l = osmo_conv_get_output_length(tst->code, 0);
+ printf("[.] Output length : ret = %3d exp = %3d -> %s\n",
+ l, tst->out_len, l == tst->out_len ? "OK" : "Bad !");
+
+ if (l != tst->out_len) {
+ fprintf(stderr, "[!] Failure for output length computation\n");
+ return -1;
+ }
+
+ /* Check pre-computed vector */
+ if (tst->has_vec) {
+ printf("[.] Pre computed vector checks:\n");
+
+ printf("[..] Encoding: ");
+
+ osmo_pbit2ubit(bu0, tst->vec_in, tst->in_len);
+
+ l = osmo_conv_encode(tst->code, bu0, bu1);
+ if (l != tst->out_len) {
+ printf("ERROR !\n");
+ fprintf(stderr, "[!] Failed encoding length check\n");
+ return -1;
+ }
+
+ osmo_pbit2ubit(bu0, tst->vec_out, tst->out_len);
+
+ if (memcmp(bu0, bu1, tst->out_len)) {
+ printf("ERROR !\n");
+ fprintf(stderr, "[!] Failed encoding: Results don't match\n");
+ return -1;
+ };
+
+ printf("OK\n");
+
+
+ printf("[..] Decoding: ");
+
+ ubit_to_sbit(bs, bu0, l);
+
+ l = osmo_conv_decode(tst->code, bs, bu1);
+ if (l != 0) {
+ printf("ERROR !\n");
+ fprintf(stderr, "[!] Failed decoding: non-zero path (%d)\n", l);
+ return -1;
+ }
+
+ osmo_pbit2ubit(bu0, tst->vec_in, tst->in_len);
+
+ if (memcmp(bu0, bu1, tst->in_len)) {
+ printf("ERROR !\n");
+ fprintf(stderr, "[!] Failed decoding: Results don't match\n");
+ return -1;
+ }
+
+ printf("OK\n");
+ }
+
+ /* Check random vector */
+ printf("[.] Random vector checks:\n");
+
+ for (i=0; i<3; i++) {
+ printf("[..] Encoding / Decoding cycle : ");
+
+ fill_random(bu0, tst->in_len);
+
+ l = osmo_conv_encode(tst->code, bu0, bu1);
+ if (l != tst->out_len) {
+ printf("ERROR !\n");
+ fprintf(stderr, "[!] Failed encoding length check\n");
+ return -1;
+ }
+
+ ubit_to_sbit(bs, bu1, l);
+
+ l = osmo_conv_decode(tst->code, bs, bu1);
+ if (l != 0) {
+ printf("ERROR !\n");
+ fprintf(stderr, "[!] Failed decoding: non-zero path (%d)\n", l);
+ return -1;
+ }
+
+ if (memcmp(bu0, bu1, tst->in_len)) {
+ printf("ERROR !\n");
+ fprintf(stderr, "[!] Failed decoding: Results don't match\n");
+ return -1;
+ }
+
+ printf("OK\n");
+ }
+
+ /* Spacing */
+ printf("\n");
+ }
+
+ free(bs);
+ free(bu1);
+ free(bu0);
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/conv/conv_test.ok b/src/shared/libosmocore/tests/conv/conv_test.ok
new file mode 100644
index 00000000..21229619
--- /dev/null
+++ b/src/shared/libosmocore/tests/conv/conv_test.ok
@@ -0,0 +1,55 @@
+[+] Testing: GSM xCCH (non-recursive, flushed, not punctured)
+[.] Input length : ret = 224 exp = 224 -> OK
+[.] Output length : ret = 456 exp = 456 -> OK
+[.] Pre computed vector checks:
+[..] Encoding: OK
+[..] Decoding: OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: GSM TCH/AFS 7.95 (recursive, flushed, punctured)
+[.] Input length : ret = 165 exp = 165 -> OK
+[.] Output length : ret = 448 exp = 448 -> OK
+[.] Pre computed vector checks:
+[..] Encoding: OK
+[..] Decoding: OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: GMR-1 TCH3 Speech (non-recursive, tail-biting, punctured)
+[.] Input length : ret = 48 exp = 48 -> OK
+[.] Output length : ret = 72 exp = 72 -> OK
+[.] Pre computed vector checks:
+[..] Encoding: OK
+[..] Decoding: OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: WiMax FCH (non-recursive, tail-biting, not punctured)
+[.] Input length : ret = 48 exp = 48 -> OK
+[.] Output length : ret = 96 exp = 96 -> OK
+[.] Pre computed vector checks:
+[..] Encoding: OK
+[..] Decoding: OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: ??? (non-recursive, direct truncation, not punctured)
+[.] Input length : ret = 224 exp = 224 -> OK
+[.] Output length : ret = 448 exp = 448 -> OK
+[.] Pre computed vector checks:
+[..] Encoding: OK
+[..] Decoding: OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
diff --git a/src/shared/libosmocore/tests/gsm0808/Makefile.am b/src/shared/libosmocore/tests/gsm0808/Makefile.am
new file mode 100644
index 00000000..a238e7f3
--- /dev/null
+++ b/src/shared/libosmocore/tests/gsm0808/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = gsm0808_test
+EXTRA_DIST = gsm0808_test.ok
+
+gsm0808_test_SOURCES = gsm0808_test.c
+gsm0808_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
diff --git a/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c
new file mode 100644
index 00000000..7e5e97b5
--- /dev/null
+++ b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c
@@ -0,0 +1,269 @@
+/*
+ * (C) 2012 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 <osmocom/gsm/gsm0808.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define VERIFY(msg, data, len) \
+ if (msgb_l3len(msg) != len) { \
+ printf("%s:%d Length don't match: %d vs. %d. %s\n", \
+ __func__, __LINE__, msgb_l3len(msg), len, \
+ osmo_hexdump(msg->l3h, msgb_l3len(msg))); \
+ abort(); \
+ } else if (memcmp(msg->l3h, data, len) != 0) { \
+ printf("%s:%d didn't match: got: %s\n", \
+ __func__, __LINE__, \
+ osmo_hexdump(msg->l3h, msgb_l3len(msg))); \
+ abort(); \
+ }
+
+
+static void test_create_layer3(void)
+{
+ static const uint8_t res[] = {
+ 0x00, 0x0e, 0x57, 0x05, 0x08, 0x00, 0x77, 0x62,
+ 0x83, 0x33, 0x66, 0x44, 0x88, 0x17, 0x01, 0x23 };
+ struct msgb *msg, *in_msg;
+ printf("Testing creating Layer3\n");
+
+ in_msg = msgb_alloc_headroom(512, 128, "foo");
+ in_msg->l3h = in_msg->data;
+ msgb_v_put(in_msg, 0x23);
+
+ msg = gsm0808_create_layer3(in_msg, 0x1122, 0x2244, 0x3366, 0x4488);
+ VERIFY(msg, res, ARRAY_SIZE(res));
+ msgb_free(msg);
+ msgb_free(in_msg);
+}
+
+static void test_create_reset()
+{
+ static const uint8_t res[] = { 0x00, 0x04, 0x30, 0x04, 0x01, 0x20 };
+ struct msgb *msg;
+
+ printf("Testing creating Reset\n");
+ msg = gsm0808_create_reset();
+ VERIFY(msg, res, ARRAY_SIZE(res));
+ msgb_free(msg);
+}
+
+static void test_create_clear_command()
+{
+ static const uint8_t res[] = { 0x20, 0x04, 0x01, 0x23 };
+ struct msgb *msg;
+
+ printf("Testing creating Clear Command\n");
+ msg = gsm0808_create_clear_command(0x23);
+ VERIFY(msg, res, ARRAY_SIZE(res));
+ msgb_free(msg);
+}
+
+static void test_create_clear_complete()
+{
+ static const uint8_t res[] = { 0x00, 0x01, 0x21 };
+ struct msgb *msg;
+
+ printf("Testing creating Clear Complete\n");
+ msg = gsm0808_create_clear_complete();
+ VERIFY(msg, res, ARRAY_SIZE(res));
+ msgb_free(msg);
+}
+
+static void test_create_cipher_complete()
+{
+ static const uint8_t res1[] = {
+ 0x00, 0x08, 0x55, 0x20, 0x03, 0x23, 0x42, 0x21, 0x2c, 0x04 };
+ static const uint8_t res2[] = { 0x00, 0x03, 0x55, 0x2c, 0x04};
+ struct msgb *l3, *msg;
+
+ printf("Testing creating Cipher Complete\n");
+ l3 = msgb_alloc_headroom(512, 128, "l3h");
+ l3->l3h = l3->data;
+ msgb_v_put(l3, 0x23);
+ msgb_v_put(l3, 0x42);
+ msgb_v_put(l3, 0x21);
+
+ /* with l3 data */
+ msg = gsm0808_create_cipher_complete(l3, 4);
+ VERIFY(msg, res1, ARRAY_SIZE(res1));
+ msgb_free(msg);
+
+ /* with l3 data but short */
+ l3->len -= 1;
+ l3->tail -= 1;
+ msg = gsm0808_create_cipher_complete(l3, 4);
+ VERIFY(msg, res2, ARRAY_SIZE(res2));
+ msgb_free(msg);
+
+ /* without l3 data */
+ msg = gsm0808_create_cipher_complete(NULL, 4);
+ VERIFY(msg, res2, ARRAY_SIZE(res2));
+ msgb_free(msg);
+
+
+ msgb_free(l3);
+}
+
+static void test_create_cipher_reject()
+{
+ static const uint8_t res[] = { 0x00, 0x02, 0x59, 0x23 };
+ struct msgb *msg;
+
+ printf("Testing creating Cipher Reject\n");
+ msg = gsm0808_create_cipher_reject(0x23);
+ VERIFY(msg, res, ARRAY_SIZE(res));
+ msgb_free(msg);
+}
+
+static void test_create_cm_u()
+{
+ static const uint8_t res[] = {
+ 0x00, 0x07, 0x54, 0x12, 0x01, 0x23, 0x13, 0x01, 0x42 };
+ static const uint8_t res2o[] = {
+ 0x00, 0x04, 0x54, 0x12, 0x01, 0x23 };
+ struct msgb *msg;
+ const uint8_t cm2 = 0x23;
+ const uint8_t cm3 = 0x42;
+
+ printf("Testing creating CM U\n");
+ msg = gsm0808_create_classmark_update(&cm2, 1, &cm3, 1);
+ VERIFY(msg, res, ARRAY_SIZE(res));
+
+ msg = gsm0808_create_classmark_update(&cm2, 1, NULL, 0);
+ VERIFY(msg, res2o, ARRAY_SIZE(res2o));
+
+ msgb_free(msg);
+}
+
+static void test_create_sapi_reject()
+{
+ static const uint8_t res[] = { 0x00, 0x03, 0x25, 0x03, 0x25 };
+ struct msgb *msg;
+
+ printf("Testing creating SAPI Reject\n");
+ msg = gsm0808_create_sapi_reject(3);
+ VERIFY(msg, res, ARRAY_SIZE(res));
+ msgb_free(msg);
+}
+
+static void test_create_ass_compl()
+{
+ static const uint8_t res1[] = {
+ 0x00, 0x09, 0x02, 0x15, 0x23, 0x21, 0x42, 0x2c,
+ 0x11, 0x40, 0x22 };
+ static const uint8_t res2[] = {
+ 0x00, 0x07, 0x02, 0x15, 0x23, 0x21, 0x42, 0x2c, 0x11};
+ struct msgb *msg;
+
+ printf("Testing creating Assignment Complete\n");
+ msg = gsm0808_create_assignment_completed(0x23, 0x42, 0x11, 0x22);
+ VERIFY(msg, res1, ARRAY_SIZE(res1));
+ msgb_free(msg);
+
+ msg = gsm0808_create_assignment_completed(0x23, 0x42, 0x11, 0);
+ VERIFY(msg, res2, ARRAY_SIZE(res2));
+ msgb_free(msg);
+}
+
+static void test_create_ass_fail()
+{
+ static const uint8_t res1[] = { 0x00, 0x04, 0x03, 0x04, 0x01, 0x23 };
+ static const uint8_t res2[] = {
+ 0x00, 0x06, 0x03, 0x04, 0x01, 0x23, 0x15, 0x02};
+ uint8_t rr_res = 2;
+ struct msgb *msg;
+
+ printf("Testing creating Assignment Failure\n");
+ msg = gsm0808_create_assignment_failure(0x23, NULL);
+ VERIFY(msg, res1, ARRAY_SIZE(res1));
+ msgb_free(msg);
+
+ msg = gsm0808_create_assignment_failure(0x23, &rr_res);
+ VERIFY(msg, res2, ARRAY_SIZE(res2));
+ msgb_free(msg);
+}
+
+static void test_create_clear_rqst()
+{
+ static const uint8_t res[] = { 0x00, 0x04, 0x22, 0x04, 0x01, 0x23 };
+ struct msgb *msg;
+
+ printf("Testing creating Clear Request\n");
+ msg = gsm0808_create_clear_rqst(0x23);
+ VERIFY(msg, res, ARRAY_SIZE(res));
+ msgb_free(msg);
+}
+
+static void test_create_dtap()
+{
+ static const uint8_t res[] = { 0x01, 0x03, 0x02, 0x23, 0x42 };
+ struct msgb *msg, *l3;
+
+ printf("Testing creating DTAP\n");
+ l3 = msgb_alloc_headroom(512, 128, "test");
+ l3->l3h = l3->data;
+ msgb_v_put(l3, 0x23);
+ msgb_v_put(l3, 0x42);
+
+ msg = gsm0808_create_dtap(l3, 0x3);
+ VERIFY(msg, res, ARRAY_SIZE(res));
+ msgb_free(msg);
+ msgb_free(l3);
+}
+
+static void test_prepend_dtap()
+{
+ static const uint8_t res[] = { 0x01, 0x03, 0x02, 0x23, 0x42 };
+ struct msgb *in_msg;
+
+ printf("Testing prepend DTAP\n");
+
+ in_msg = msgb_alloc_headroom(512, 128, "test");
+ msgb_v_put(in_msg, 0x23);
+ msgb_v_put(in_msg, 0x42);
+
+ gsm0808_prepend_dtap_header(in_msg, 0x3);
+ in_msg->l3h = in_msg->data;
+ VERIFY(in_msg, res, ARRAY_SIZE(res));
+ msgb_free(in_msg);
+}
+
+int main(int argc, char **argv)
+{
+ printf("Testing generation of GSM0808 messages\n");
+ test_create_layer3();
+ test_create_reset();
+ test_create_clear_command();
+ test_create_clear_complete();
+ test_create_cipher_complete();
+ test_create_cipher_reject();
+ test_create_cm_u();
+ test_create_sapi_reject();
+ test_create_ass_compl();
+ test_create_ass_fail();
+ test_create_clear_rqst();
+ test_create_dtap();
+ test_prepend_dtap();
+
+ printf("Done\n");
+ return EXIT_SUCCESS;
+}
diff --git a/src/shared/libosmocore/tests/gsm0808/gsm0808_test.ok b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.ok
new file mode 100644
index 00000000..eb431267
--- /dev/null
+++ b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.ok
@@ -0,0 +1,15 @@
+Testing generation of GSM0808 messages
+Testing creating Layer3
+Testing creating Reset
+Testing creating Clear Command
+Testing creating Clear Complete
+Testing creating Cipher Complete
+Testing creating Cipher Reject
+Testing creating CM U
+Testing creating SAPI Reject
+Testing creating Assignment Complete
+Testing creating Assignment Failure
+Testing creating Clear Request
+Testing creating DTAP
+Testing prepend DTAP
+Done
diff --git a/src/shared/libosmocore/tests/lapd/Makefile.am b/src/shared/libosmocore/tests/lapd/Makefile.am
new file mode 100644
index 00000000..f7e2ab0e
--- /dev/null
+++ b/src/shared/libosmocore/tests/lapd/Makefile.am
@@ -0,0 +1,9 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_FLAGS = -Wall -O0
+noinst_PROGRAMS = lapd_test
+EXTRA_DIST = lapd_test.ok
+
+lapd_test_SOURCES = lapd_test.c
+lapd_test_LDADD = \
+ $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/gsm/libosmogsm.la
diff --git a/src/shared/libosmocore/tests/lapd/lapd_test.c b/src/shared/libosmocore/tests/lapd/lapd_test.c
new file mode 100644
index 00000000..d58bec65
--- /dev/null
+++ b/src/shared/libosmocore/tests/lapd/lapd_test.c
@@ -0,0 +1,319 @@
+/*
+ * (C) 2011 by Holger Hans Peter Freyther
+ * (C) 2011 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 <osmocom/core/logging.h>
+#include <osmocom/gsm/lapdm.h>
+#include <osmocom/gsm/rsl.h>
+
+#include <errno.h>
+
+#include <string.h>
+
+#define CHECK_RC(rc) \
+ if (rc != 0) { \
+ printf("Operation failed rc=%d on %s:%d\n", rc, __FILE__, __LINE__); \
+ abort(); \
+ }
+
+#define ASSERT(exp) \
+ if (!(exp)) { \
+ printf("Assert failed %s %s:%d\n", #exp, __FILE__, __LINE__); \
+ abort(); \
+ }
+
+
+static struct log_info info = {};
+
+struct lapdm_polling_state {
+ struct lapdm_channel *bts;
+ int bts_read;
+
+ struct lapdm_channel *ms;
+ int ms_read;
+};
+
+static struct msgb *msgb_from_array(const uint8_t *data, int len)
+{
+ struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
+ msg->l3h = msgb_put(msg, len);
+ memcpy(msg->l3h, data, len);
+ return msg;
+}
+
+/*
+ * Test data is below...
+ */
+static const uint8_t cm[] = {
+ 0x05, 0x24, 0x31, 0x03, 0x50, 0x18, 0x93, 0x08,
+ 0x29, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+};
+
+static const uint8_t cm_padded[] = {
+ 0x05, 0x24, 0x31, 0x03, 0x50, 0x18, 0x93, 0x08,
+ 0x29, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x2b, 0x2b, 0x2b, 0x2b
+};
+
+static const uint8_t mm[] = {
+ 0x00, 0x0c, 0x00, 0x03, 0x01, 0x01, 0x20, 0x02,
+ 0x00, 0x0b, 0x00, 0x03, 0x05, 0x04, 0x0d
+};
+
+static const uint8_t dummy1[] = {
+ 0xab, 0x03, 0x30, 0x60, 0x06,
+};
+
+static struct msgb *create_cm_serv_req(void)
+{
+ struct msgb *msg;
+
+ msg = msgb_from_array(cm, sizeof(cm));
+ rsl_rll_push_l3(msg, RSL_MT_EST_REQ, 0, 0, 1);
+ return msg;
+}
+
+static struct msgb *create_mm_id_req(void)
+{
+ struct msgb *msg;
+
+ msg = msgb_from_array(mm, sizeof(mm));
+ msg->l2h = msg->data + 3;
+ ASSERT(msgb_l2len(msg) == 12);
+ msg->l3h = msg->l2h + 6;
+ ASSERT(msgb_l3len(msg) == 6);
+
+ return msg;
+}
+
+static struct msgb *create_empty_msg(void)
+{
+ struct msgb *msg;
+
+ msg = msgb_from_array(NULL, 0);
+ ASSERT(msgb_l3len(msg) == 0);
+ rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, 0, 0, 1);
+ return msg;
+}
+
+static struct msgb *create_dummy_data_req(void)
+{
+ struct msgb *msg;
+
+ msg = msgb_from_array(dummy1, sizeof(dummy1));
+ rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, 0, 0, 1);
+ return msg;
+}
+
+static int send(struct msgb *in_msg, struct lapdm_channel *chan)
+{
+ struct osmo_phsap_prim pp;
+ struct msgb *msg;
+ int rc;
+
+ msg = msgb_alloc_headroom(128, 64, "PH-DATA.ind");
+ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_INDICATION, msg);
+ /* copy over actual MAC block */
+ msg->l2h = msgb_put(msg, msgb_l2len(in_msg));
+ memcpy(msg->l2h, in_msg->l2h, msgb_l2len(in_msg));
+
+ /* LAPDm requires those... */
+ pp.u.data.chan_nr = 0;
+ pp.u.data.link_id = 0;
+ /* feed into the LAPDm code of libosmogsm */
+ rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch);
+ ASSERT(rc == 0 || rc == -EBUSY);
+ return 0;
+}
+
+/*
+ * I get called from the LAPDm code when something was sent my way...
+ */
+static int bts_to_ms_tx_cb(struct msgb *in_msg, struct lapdm_entity *le, void *_ctx)
+{
+ struct lapdm_polling_state *state = _ctx;
+
+
+ printf("%s: MS->BTS(us) message %d\n", __func__, msgb_length(in_msg));
+
+
+ if (state->bts_read == 0) {
+ printf("BTS: Verifying CM request.\n");
+ ASSERT(msgb_l3len(in_msg) == ARRAY_SIZE(cm_padded));
+ ASSERT(memcmp(in_msg->l3h, cm_padded, ARRAY_SIZE(cm_padded)) == 0);
+ } else if (state->bts_read == 1) {
+ printf("BTS: Verifying dummy message.\n");
+ ASSERT(msgb_l3len(in_msg) == ARRAY_SIZE(dummy1));
+ ASSERT(memcmp(in_msg->l3h, dummy1, ARRAY_SIZE(dummy1)) == 0);
+ } else {
+ printf("BTS: Do not know to verify: %d\n", state->bts_read);
+ }
+
+ state->bts_read += 1;
+ msgb_free(in_msg);
+
+ return 0;
+}
+
+static int ms_to_bts_l1_cb(struct osmo_prim_hdr *oph, void *_ctx)
+{
+ int rc;
+ struct lapdm_polling_state *state = _ctx;
+ printf("%s: MS(us) -> BTS prim message\n", __func__);
+
+ /* i stuff it into the LAPDm channel of the BTS */
+ rc = send(oph->msg, state->bts);
+ msgb_free(oph->msg);
+}
+
+static int ms_to_bts_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *_ctx)
+{
+ struct lapdm_polling_state *state = _ctx;
+
+ printf("%s: BTS->MS(us) message %d\n", __func__, msgb_length(msg));
+
+ if (state->ms_read == 0) {
+ struct abis_rsl_rll_hdr hdr;
+
+ printf("MS: Verifying incoming primitive.\n");
+ ASSERT(msg->len == sizeof(struct abis_rsl_rll_hdr) + 3);
+
+ /* verify the header */
+ memset(&hdr, 0, sizeof(hdr));
+ rsl_init_rll_hdr(&hdr, RSL_MT_EST_CONF);
+ hdr.c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+ ASSERT(memcmp(msg->data, &hdr, sizeof(hdr)) == 0);
+
+ /* Verify the added RSL_IE_L3_INFO but we have a bug here */
+ ASSERT(msg->data[6] == RSL_IE_L3_INFO);
+ #warning "RSL_IE_L3_INFO 16 bit length is wrong"
+ /* ASSERT(msg->data[7] == 0x0 && msg->data[8] == 0x9c); */
+ /* this should be 0x0 and 0x0... but we have a bug */
+ } else if (state->ms_read == 1) {
+ printf("MS: Verifying incoming MM message: %d\n", msgb_l3len(msg));
+ ASSERT(msgb_l3len(msg) == 3);
+ ASSERT(memcmp(msg->l3h, &mm[12], msgb_l3len(msg)) == 0);
+ } else {
+ printf("MS: Do not know to verify: %d\n", state->ms_read);
+ }
+
+ state->ms_read += 1;
+ msgb_free(msg);
+ return 0;
+}
+
+static void test_lapdm_polling()
+{
+ printf("I do some very simple LAPDm test.\n");
+
+ int rc;
+ struct lapdm_polling_state test_state;
+ struct osmo_phsap_prim pp;
+
+ /* Configure LAPDm on both sides */
+ struct lapdm_channel bts_to_ms_channel;
+ struct lapdm_channel ms_to_bts_channel;
+ memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel));
+ memset(&ms_to_bts_channel, 0, sizeof(ms_to_bts_channel));
+
+ memset(&test_state, 0, sizeof(test_state));
+ test_state.bts = &bts_to_ms_channel;
+ test_state.ms = &ms_to_bts_channel;
+
+ /* BTS to MS in polling mode */
+ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS);
+ lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY);
+ lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state);
+ lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state);
+
+ /* MS to BTS in direct mode */
+ lapdm_channel_init(&ms_to_bts_channel, LAPDM_MODE_MS);
+ lapdm_channel_set_l1(&ms_to_bts_channel, ms_to_bts_l1_cb, &test_state);
+ lapdm_channel_set_l3(&ms_to_bts_channel, ms_to_bts_tx_cb, &test_state);
+
+ /*
+ * We try to send messages from the MS to the BTS to the MS..
+ */
+ /* 1. Start with MS -> BTS, BTS should have a pending message */
+ printf("Establishing link.\n");
+ lapdm_rslms_recvmsg(create_cm_serv_req(), &ms_to_bts_channel);
+
+ /* 2. Poll on the BTS for sending out a confirmation */
+ printf("\nConfirming\n");
+ ASSERT(test_state.bts_read == 1)
+ rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+ CHECK_RC(rc);
+ ASSERT(pp.oph.msg->data == pp.oph.msg->l2h);
+ send(pp.oph.msg, &ms_to_bts_channel);
+ msgb_free(pp.oph.msg);
+ ASSERT(test_state.ms_read == 1);
+
+ /* 3. Send some data to the MS */
+ printf("\nSending back to MS\n");
+ lapdm_rslms_recvmsg(create_mm_id_req(), &bts_to_ms_channel);
+ rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+ CHECK_RC(rc);
+ send(pp.oph.msg, &ms_to_bts_channel);
+ msgb_free(pp.oph.msg);
+ ASSERT(test_state.ms_read == 2);
+
+ /* verify that there is nothing more to poll */
+ rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+ ASSERT(rc < 0);
+
+ /* 3. And back to the BTS */
+ printf("\nSending back to BTS\n");
+ ASSERT(test_state.ms_read == 2);
+ lapdm_rslms_recvmsg(create_dummy_data_req(), &ms_to_bts_channel);
+
+
+ /* 4. And back to the MS, but let's move data/l2h apart */
+ ASSERT(test_state.bts_read == 2)
+ ASSERT(test_state.ms_read == 2);
+ rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+ CHECK_RC(rc);
+ send(pp.oph.msg, &ms_to_bts_channel);
+ ASSERT(test_state.ms_read == 2);
+ msgb_free(pp.oph.msg);
+
+ /* verify that there is nothing more to poll */
+ rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+ ASSERT(rc < 0);
+
+ /* check sending an empty L3 message fails */
+ rc = lapdm_rslms_recvmsg(create_empty_msg(), &bts_to_ms_channel);
+ ASSERT(rc == -1);
+ ASSERT(test_state.ms_read == 2);
+
+ /* clean up */
+ lapdm_channel_exit(&bts_to_ms_channel);
+ lapdm_channel_exit(&ms_to_bts_channel);
+}
+
+int main(int argc, char **argv)
+{
+ osmo_init_logging(&info);
+
+ test_lapdm_polling();
+ printf("Success.\n");
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/lapd/lapd_test.ok b/src/shared/libosmocore/tests/lapd/lapd_test.ok
new file mode 100644
index 00000000..d67a0a80
--- /dev/null
+++ b/src/shared/libosmocore/tests/lapd/lapd_test.ok
@@ -0,0 +1,20 @@
+I do some very simple LAPDm test.
+Establishing link.
+ms_to_bts_l1_cb: MS(us) -> BTS prim message
+bts_to_ms_tx_cb: MS->BTS(us) message 29
+BTS: Verifying CM request.
+
+Confirming
+ms_to_bts_tx_cb: BTS->MS(us) message 9
+MS: Verifying incoming primitive.
+
+Sending back to MS
+ms_to_bts_tx_cb: BTS->MS(us) message 12
+MS: Verifying incoming MM message: 3
+ms_to_bts_l1_cb: MS(us) -> BTS prim message
+
+Sending back to BTS
+ms_to_bts_l1_cb: MS(us) -> BTS prim message
+bts_to_ms_tx_cb: MS->BTS(us) message 14
+BTS: Verifying dummy message.
+Success.
diff --git a/src/shared/libosmocore/tests/msgfile/Makefile.am b/src/shared/libosmocore/tests/msgfile/Makefile.am
new file mode 100644
index 00000000..88c355db
--- /dev/null
+++ b/src/shared/libosmocore/tests/msgfile/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = msgfile_test
+EXTRA_DIST = msgfile_test.ok msgconfig.cfg
+
+msgfile_test_SOURCES = msgfile_test.c
+msgfile_test_LDADD = $(top_builddir)/src/libosmocore.la
diff --git a/src/shared/libosmocore/tests/msgfile/msgconfig.cfg b/src/shared/libosmocore/tests/msgfile/msgconfig.cfg
new file mode 100644
index 00000000..28d74326
--- /dev/null
+++ b/src/shared/libosmocore/tests/msgfile/msgconfig.cfg
@@ -0,0 +1,2 @@
+# This is a comment
+*:*::Hello Welt
diff --git a/src/shared/libosmocore/tests/msgfile/msgfile_test.c b/src/shared/libosmocore/tests/msgfile/msgfile_test.c
new file mode 100644
index 00000000..ed7aa978
--- /dev/null
+++ b/src/shared/libosmocore/tests/msgfile/msgfile_test.c
@@ -0,0 +1,50 @@
+/*
+ * (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 <osmocom/core/msgfile.h>
+
+#include <stdio.h>
+
+static void dump_entries(struct osmo_config_list *entries)
+{
+ struct osmo_config_entry *entry;
+
+ if (!entries) {
+ fprintf(stderr, "Failed to parse the file\n");
+ return;
+ }
+
+ llist_for_each_entry(entry, &entries->entry, list) {
+ printf("Entry '%s:%s:%s:%s'\n",
+ entry->mcc, entry->mnc, entry->option, entry->text);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct osmo_config_list *entries;
+
+ /* todo use msgfile_test.c.in and replace the path */
+ entries = osmo_config_list_parse(NULL, "msgconfig.cfg");
+ dump_entries(entries);
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/msgfile/msgfile_test.ok b/src/shared/libosmocore/tests/msgfile/msgfile_test.ok
new file mode 100644
index 00000000..c47cd747
--- /dev/null
+++ b/src/shared/libosmocore/tests/msgfile/msgfile_test.ok
@@ -0,0 +1 @@
+Entry '*:*::Hello Welt'
diff --git a/src/shared/libosmocore/tests/sms/Makefile.am b/src/shared/libosmocore/tests/sms/Makefile.am
new file mode 100644
index 00000000..02860afe
--- /dev/null
+++ b/src/shared/libosmocore/tests/sms/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = sms_test
+EXTRA_DIST = sms_test.ok
+
+sms_test_SOURCES = sms_test.c
+sms_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
diff --git a/src/shared/libosmocore/tests/sms/sms_test.c b/src/shared/libosmocore/tests/sms/sms_test.c
new file mode 100644
index 00000000..6df4b623
--- /dev/null
+++ b/src/shared/libosmocore/tests/sms/sms_test.c
@@ -0,0 +1,319 @@
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2010 by Nico Golde <nico@ngolde.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 <osmocom/core/msgb.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/core/utils.h>
+
+struct test_case {
+ const uint8_t *input;
+ const uint16_t input_length;
+
+ const uint8_t *expected;
+ const uint16_t expected_octet_length;
+ const uint16_t expected_septet_length;
+ const uint8_t ud_hdr_ind;
+};
+
+static const char simple_text[] = "test text";
+#define simple_septet_length 9
+static const uint8_t simple_enc[] = {
+ 0xf4, 0xf2, 0x9c, 0x0e, 0xa2, 0x97, 0xf1, 0x74
+};
+
+static const char escape_text[] = "!$ a more#^- complicated test@@?_%! case";
+#define escape_septet_length 41 /* note: the ^ counts as two, because it is a extension character */
+static const uint8_t escape_enc[] = {
+ 0x21, 0x01, 0x28, 0x0c, 0x6a, 0xbf, 0xe5, 0xe5, 0xd1,
+ 0x86, 0xd2, 0x02, 0x8d, 0xdf, 0x6d, 0x38, 0x3b, 0x3d,
+ 0x0e, 0xd3, 0xcb, 0x64, 0x10, 0xbd, 0x3c, 0xa7, 0x03,
+ 0x00, 0xbf, 0x48, 0x29, 0x04, 0x1a, 0x87, 0xe7, 0x65,
+};
+
+static const char enhanced_text[] = "enhanced ^ {][} test |+~ ^ test";
+#define enhanced_septet_length 39 /* note: the characters { } [ ] ^ | ~ count as two (each of them), because they are extension characters */
+static const uint8_t enhanced_enc[] = {
+ 0x65, 0x37, 0x3A, 0xEC, 0x1E, 0x97, 0xC9, 0xA0, 0x0D,
+ 0x05, 0xB4, 0x41, 0x6D, 0x7C, 0x1B, 0xDE, 0x26, 0x05,
+ 0xA2, 0x97, 0xE7, 0x74, 0xD0, 0x06, 0xB8, 0xDA, 0xF4,
+ 0x40, 0x1B, 0x0A, 0x88, 0x5E, 0x9E, 0xD3, 0x01,
+};
+
+static const char enhancedV2_text[] = "enhanced ^ {][} test |+~ ^ tests";
+#define enhancedV2_septet_length 40 /* note: number of octets are equal to the enhanced_text! */
+static const uint8_t enhancedV2_enc[] = {
+ 0x65, 0x37, 0x3A, 0xEC, 0x1E, 0x97, 0xC9, 0xA0, 0x0D,
+ 0x05, 0xB4, 0x41, 0x6D, 0x7C, 0x1B, 0xDE, 0x26, 0x05,
+ 0xA2, 0x97, 0xE7, 0x74, 0xD0, 0x06, 0xB8, 0xDA, 0xF4,
+ 0x40, 0x1B, 0x0A, 0x88, 0x5E, 0x9E, 0xD3, 0xE7,
+};
+
+
+
+static const char concatenated_text[] =
+ "this is a testmessage. this is a testmessage. this is a testmessage. this is a testmessage. "
+ "this is a testmessage. this is a testmessage. cut here .....: this is a second testmessage. end here.";
+
+static const char splitted_text_part1[] =
+ "this is a testmessage. this is a testmessage. this is a testmessage. this is a testmessage. "
+ "this is a testmessage. this is a testmessage. cut here .....:";
+#define concatenated_part1_septet_length_with_header 160
+#define concatenated_part1_septet_length 153
+static const uint8_t concatenated_part1_enc[] = {
+ 0x05, 0x00, 0x03, 0x6f, 0x02, 0x01,
+ 0xe8, 0xe8, 0xf4, 0x1c, 0x94, 0x9e, 0x83, 0xc2,
+ 0x20, 0x7a, 0x79, 0x4e, 0x6f, 0x97, 0xe7, 0xf3,
+ 0xf0, 0xb9, 0xec, 0x02, 0xd1, 0xd1, 0xe9, 0x39,
+ 0x28, 0x3d, 0x07, 0x85, 0x41, 0xf4, 0xf2, 0x9c,
+ 0xde, 0x2e, 0xcf, 0xe7, 0xe1, 0x73, 0xd9, 0x05,
+ 0xa2, 0xa3, 0xd3, 0x73, 0x50, 0x7a, 0x0e, 0x0a,
+ 0x83, 0xe8, 0xe5, 0x39, 0xbd, 0x5d, 0x9e, 0xcf,
+ 0xc3, 0xe7, 0xb2, 0x0b, 0x44, 0x47, 0xa7, 0xe7,
+ 0xa0, 0xf4, 0x1c, 0x14, 0x06, 0xd1, 0xcb, 0x73,
+ 0x7a, 0xbb, 0x3c, 0x9f, 0x87, 0xcf, 0x65, 0x17,
+ 0x88, 0x8e, 0x4e, 0xcf, 0x41, 0xe9, 0x39, 0x28,
+ 0x0c, 0xa2, 0x97, 0xe7, 0xf4, 0x76, 0x79, 0x3e,
+ 0x0f, 0x9f, 0xcb, 0x2e, 0x10, 0x1d, 0x9d, 0x9e,
+ 0x83, 0xd2, 0x73, 0x50, 0x18, 0x44, 0x2f, 0xcf,
+ 0xe9, 0xed, 0xf2, 0x7c, 0x1e, 0x3e, 0x97, 0x5d,
+ 0xa0, 0x71, 0x9d, 0x0e, 0x42, 0x97, 0xe5, 0x65,
+ 0x90, 0xcb, 0xe5, 0x72, 0xb9, 0x74,
+};
+
+static const char splitted_text_part2[] = " this is a second testmessage. end here.";
+#define concatenated_part2_septet_length_with_header 47
+#define concatenated_part2_septet_length 40
+static const uint8_t concatenated_part2_enc[] = {
+ 0x05, 0x00, 0x03, 0x6f, 0x02, 0x02,
+ 0x40, 0x74, 0x74, 0x7a, 0x0e, 0x4a, 0xcf, 0x41,
+ 0x61, 0xd0, 0xbc, 0x3c, 0x7e, 0xbb, 0xc9, 0x20,
+ 0x7a, 0x79, 0x4e, 0x6f, 0x97, 0xe7, 0xf3, 0xf0,
+ 0xb9, 0xec, 0x02, 0x95, 0xdd, 0x64, 0x10, 0xba,
+ 0x2c, 0x2f, 0xbb, 0x00,
+};
+
+static const struct test_case test_multiple_encode[] =
+{
+ {
+ .input = concatenated_text,
+ .expected = concatenated_part1_enc,
+ .expected_octet_length = sizeof(concatenated_part1_enc),
+ .expected_septet_length = concatenated_part1_septet_length,
+ .ud_hdr_ind = 1,
+ },
+ {
+ .input = concatenated_text,
+ .expected = concatenated_part2_enc,
+ .expected_octet_length = sizeof(concatenated_part2_enc),
+ .expected_septet_length = concatenated_part2_septet_length,
+ .ud_hdr_ind = 1,
+ },
+};
+
+static const struct test_case test_encode[] =
+{
+ {
+ .input = simple_text,
+ .expected = simple_enc,
+ .expected_octet_length = sizeof(simple_enc),
+ .expected_septet_length = simple_septet_length,
+ .ud_hdr_ind = 0,
+ },
+ {
+ .input = escape_text,
+ .expected = escape_enc,
+ .expected_octet_length = sizeof(escape_enc),
+ .expected_septet_length = escape_septet_length,
+ .ud_hdr_ind = 0,
+ },
+ {
+ .input = enhanced_text,
+ .expected = enhanced_enc,
+ .expected_octet_length = sizeof(enhanced_enc),
+ .expected_septet_length = enhanced_septet_length,
+ .ud_hdr_ind = 0,
+ },
+ {
+ .input = enhancedV2_text,
+ .expected = enhancedV2_enc,
+ .expected_octet_length = sizeof(enhancedV2_enc),
+ .expected_septet_length = enhancedV2_septet_length,
+ .ud_hdr_ind = 0,
+ },
+};
+
+static const struct test_case test_decode[] =
+{
+ {
+ .input = simple_enc,
+ .input_length = sizeof(simple_enc),
+ .expected = simple_text,
+ .expected_septet_length = simple_septet_length,
+ .ud_hdr_ind = 0,
+ },
+ {
+ .input = escape_enc,
+ .input_length = sizeof(escape_enc),
+ .expected = escape_text,
+ .expected_septet_length = escape_septet_length,
+ .ud_hdr_ind = 0,
+ },
+ {
+ .input = enhanced_enc,
+ .input_length = sizeof(enhanced_enc),
+ .expected = enhanced_text,
+ .expected_septet_length = enhanced_septet_length,
+ .ud_hdr_ind = 0,
+ },
+ {
+ .input = enhancedV2_enc,
+ .input_length = sizeof(enhancedV2_enc),
+ .expected = enhancedV2_text,
+ .expected_septet_length = enhancedV2_septet_length,
+ .ud_hdr_ind = 0,
+ },
+ {
+ .input = concatenated_part1_enc,
+ .input_length = sizeof(concatenated_part1_enc),
+ .expected = splitted_text_part1,
+ .expected_septet_length = concatenated_part1_septet_length_with_header,
+ .ud_hdr_ind = 1,
+ },
+ {
+ .input = concatenated_part2_enc,
+ .input_length = sizeof(concatenated_part2_enc),
+ .expected = splitted_text_part2,
+ .expected_septet_length = concatenated_part2_septet_length_with_header,
+ .ud_hdr_ind = 1,
+ },
+};
+
+int main(int argc, char** argv)
+{
+ printf("SMS testing\n");
+ struct msgb *msg;
+ uint8_t i;
+
+ uint8_t octet_length;
+ uint8_t septet_length;
+ uint8_t gsm_septet_length;
+ uint8_t coded[256];
+ uint8_t tmp[160];
+ uint8_t septet_data[256];
+ uint8_t ud_header[6];
+ char result[256];
+
+ /* test 7-bit encoding */
+ for (i = 0; i < ARRAY_SIZE(test_encode); ++i) {
+ memset(coded, 0x42, sizeof(coded));
+ septet_length = gsm_7bit_encode(coded, test_encode[i].input);
+ octet_length = gsm_get_octet_len(septet_length);
+ if (octet_length != test_encode[i].expected_octet_length) {
+ fprintf(stderr, "Encode case %d: Octet length failure. Got %d, expected %d\n",
+ i, octet_length, test_encode[i].expected_octet_length);
+ return -1;
+ }
+
+ if (septet_length != test_encode[i].expected_septet_length){
+ fprintf(stderr, "Encode case %d: Septet length failure. Got %d, expected %d\n",
+ i, septet_length, test_encode[i].expected_septet_length);
+ return -1;
+ }
+
+ if (memcmp(coded, test_encode[i].expected, octet_length) != 0) {
+ fprintf(stderr, "Encoded content does not match for case %d\n",
+ i);
+ return -1;
+ }
+ }
+
+
+ /* Test: encode multiple SMS */
+ int number_of_septets = gsm_septet_encode(septet_data, test_multiple_encode[0].input);
+
+ /* SMS part 1 */
+ memset(tmp, 0x42, sizeof(tmp));
+ memset(coded, 0x42, sizeof(coded));
+ memcpy(tmp, septet_data, concatenated_part1_septet_length);
+
+ /* In our case: test_multiple_decode[0].ud_hdr_ind equals number of padding bits*/
+ octet_length = gsm_septets2octets(coded, tmp, concatenated_part1_septet_length, test_multiple_encode[0].ud_hdr_ind);
+
+ /* copy header */
+ memset(tmp, 0x42, sizeof(tmp));
+ int udh_length = test_multiple_encode[0].expected[0] + 1;
+ memcpy(tmp, test_multiple_encode[0].expected, udh_length);
+ memcpy(tmp + udh_length, coded, octet_length);
+ memset(coded, 0x42, sizeof(coded));
+ memcpy(coded, tmp, octet_length + 6);
+
+ if (memcmp(coded, test_multiple_encode[0].expected, octet_length) != 0) {
+ fprintf(stderr, "Multiple-SMS encoded content does not match for part 1\n");
+ return -1;
+ }
+
+
+ /* SMS part 2 */
+ memset(tmp, 0x42, sizeof(tmp));
+ memset(coded, 0x42, sizeof(coded));
+ memcpy(tmp, septet_data + concatenated_part1_septet_length, concatenated_part2_septet_length);
+
+ /* In our case: test_multiple_decode[1].ud_hdr_ind equals number of padding bits*/
+ octet_length = gsm_septets2octets(coded, tmp, concatenated_part2_septet_length, test_multiple_encode[1].ud_hdr_ind);
+
+ /* copy header */
+ memset(tmp, 0x42, sizeof(tmp));
+ udh_length = test_multiple_encode[1].expected[0] + 1;
+ memcpy(tmp, test_multiple_encode[1].expected, udh_length);
+ memcpy(tmp + udh_length, coded, octet_length);
+ memset(coded, 0x42, sizeof(coded));
+ memcpy(coded, tmp, octet_length + 6);
+
+ if (memcmp(coded, test_multiple_encode[1].expected, octet_length) != 0) {
+ fprintf(stderr, "Multiple-SMS encoded content does not match for part 2\n");
+ return -1;
+ }
+
+
+
+ /* test 7-bit decoding */
+ for (i = 0; i < ARRAY_SIZE(test_decode); ++i) {
+ memset(result, 0x42, sizeof(coded));
+ septet_length = gsm_7bit_decode_hdr(result, test_decode[i].input,
+ test_decode[i].expected_septet_length, test_decode[i].ud_hdr_ind);
+
+ if (strcmp(result, test_decode[i].expected) != 0) {
+ fprintf(stderr, "Test case %d failed to decode.\n", i);
+ return -1;
+ }
+ if (septet_length != test_decode[i].expected_septet_length) {
+ fprintf(stderr, "Decode case %d: Septet length failure. Got %d, expected %d\n",
+ i, septet_length, test_decode[i].expected_septet_length);
+ return -1;
+ }
+ }
+
+ printf("OK\n");
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/sms/sms_test.ok b/src/shared/libosmocore/tests/sms/sms_test.ok
new file mode 100644
index 00000000..d0e09838
--- /dev/null
+++ b/src/shared/libosmocore/tests/sms/sms_test.ok
@@ -0,0 +1,2 @@
+SMS testing
+OK
diff --git a/src/shared/libosmocore/tests/smscb/Makefile.am b/src/shared/libosmocore/tests/smscb/Makefile.am
new file mode 100644
index 00000000..7045ea76
--- /dev/null
+++ b/src/shared/libosmocore/tests/smscb/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = smscb_test
+EXTRA_DIST = smscb_test.ok
+
+smscb_test_SOURCES = smscb_test.c
+smscb_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
diff --git a/src/shared/libosmocore/tests/smscb/smscb_test.c b/src/shared/libosmocore/tests/smscb/smscb_test.c
new file mode 100644
index 00000000..e10e12d8
--- /dev/null
+++ b/src/shared/libosmocore/tests/smscb/smscb_test.c
@@ -0,0 +1,41 @@
+/*
+ * (C) 2010 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 <osmocom/gsm/protocol/gsm_03_41.h>
+
+#include <stdio.h>
+
+static uint8_t smscb_msg[] = { 0x40, 0x10, 0x05, 0x0d, 0x01, 0x11 };
+
+int main(int argc, char **argv)
+{
+ struct gsm341_ms_message *msg;
+
+ msg = (struct gsm341_ms_message *) smscb_msg;
+ printf("(srl) GS: %d MSG_CODE: %d UPDATE: %d\n",
+ msg->serial.gs, GSM341_MSG_CODE(msg), msg->serial.update);
+ printf("(msg) msg_id: %d\n", htons(msg->msg_id));
+ printf("(dcs) group: %d language: %d\n",
+ msg->dcs.language, msg->dcs.group);
+ printf("(pge) page total: %d current: %d\n",
+ msg->page.total, msg->page.current);
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/smscb/smscb_test.ok b/src/shared/libosmocore/tests/smscb/smscb_test.ok
new file mode 100644
index 00000000..347037f8
--- /dev/null
+++ b/src/shared/libosmocore/tests/smscb/smscb_test.ok
@@ -0,0 +1,4 @@
+(srl) GS: 1 MSG_CODE: 1 UPDATE: 0
+(msg) msg_id: 1293
+(dcs) group: 1 language: 0
+(pge) page total: 1 current: 1
diff --git a/src/shared/libosmocore/tests/testsuite.at b/src/shared/libosmocore/tests/testsuite.at
new file mode 100644
index 00000000..69624c12
--- /dev/null
+++ b/src/shared/libosmocore/tests/testsuite.at
@@ -0,0 +1,73 @@
+AT_INIT
+AT_BANNER([Regression tests.])
+
+
+# todo.. create one macro for it
+AT_SETUP([a5])
+AT_KEYWORDS([a5])
+cat $abs_srcdir/a5/a5_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/a5/a5_test], [], [expout])
+AT_CLEANUP
+
+AT_SETUP([bits])
+AT_KEYWORDS([bits])
+cat $abs_srcdir/bits/bitrev_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/bits/bitrev_test], [], [expout])
+AT_CLEANUP
+
+AT_SETUP([conv])
+AT_KEYWORDS([conv])
+cat $abs_srcdir/conv/conv_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/conv/conv_test], [], [expout])
+AT_CLEANUP
+
+if ENABLE_MSGFILE
+AT_SETUP([msgfile])
+AT_KEYWORDS([msgfile])
+cp $abs_srcdir/msgfile/msgconfig.cfg .
+cat $abs_srcdir/msgfile/msgfile_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/msgfile/msgfile_test], [], [expout])
+AT_CLEANUP
+endif
+
+AT_SETUP([sms])
+AT_KEYWORDS([sms])
+cat $abs_srcdir/sms/sms_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/sms/sms_test], [], [expout])
+AT_CLEANUP
+
+AT_SETUP([smscb])
+AT_KEYWORDS([smscb])
+cat $abs_srcdir/smscb/smscb_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/smscb/smscb_test], [], [expout])
+AT_CLEANUP
+
+AT_SETUP([timer])
+AT_KEYWORDS([timer])
+cat $abs_srcdir/timer/timer_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/timer/timer_test -s 5], [], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([ussd])
+AT_KEYWORDS([ussd])
+cat $abs_srcdir/ussd/ussd_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/ussd/ussd_test], [], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([auth])
+AT_KEYWORDS([auth])
+cat $abs_srcdir/auth/milenage_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/auth/milenage_test], [], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([lapd])
+AT_KEYWORDS([lapd])
+cat $abs_srcdir/lapd/lapd_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/lapd/lapd_test], [], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([gsm0808])
+AT_KEYWORDS([gsm0808])
+cat $abs_srcdir/gsm0808/gsm0808_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gsm0808/gsm0808_test], [], [expout], [ignore])
+AT_CLEANUP
diff --git a/src/shared/libosmocore/tests/timer/Makefile.am b/src/shared/libosmocore/tests/timer/Makefile.am
new file mode 100644
index 00000000..062d81b3
--- /dev/null
+++ b/src/shared/libosmocore/tests/timer/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = timer_test
+EXTRA_DIST = timer_test.ok
+
+timer_test_SOURCES = timer_test.c
+timer_test_LDADD = $(top_builddir)/src/libosmocore.la
+
diff --git a/src/shared/libosmocore/tests/timer/timer_test.c b/src/shared/libosmocore/tests/timer/timer_test.c
new file mode 100644
index 00000000..3775151c
--- /dev/null
+++ b/src/shared/libosmocore/tests/timer/timer_test.c
@@ -0,0 +1,191 @@
+/*
+ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2011 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * Authors: Holger Hans Peter Freyther <zecke@selfish.org>
+ * Pablo Neira Ayuso <pablo@gnumonks.org>
+ *
+ * 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 <signal.h>
+#include <getopt.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+
+#include "../../config.h"
+
+static void main_timer_fired(void *data);
+static void secondary_timer_fired(void *data);
+
+static unsigned int main_timer_step = 0;
+static struct osmo_timer_list main_timer = {
+ .cb = main_timer_fired,
+ .data = &main_timer_step,
+};
+
+static LLIST_HEAD(timer_test_list);
+
+struct test_timer {
+ struct llist_head head;
+ struct osmo_timer_list timer;
+ struct timeval start;
+ struct timeval stop;
+};
+
+/* number of test steps. We add fact(steps) timers in the whole test. */
+#define MAIN_TIMER_NSTEPS 16
+
+/* time between two steps, in secs. */
+#define TIME_BETWEEN_STEPS 1
+
+/* timer imprecision that we accept for this test: 10 milliseconds. */
+#define TIMER_PRES_SECS 0
+#define TIMER_PRES_USECS 10000
+
+static int timer_nsteps = MAIN_TIMER_NSTEPS;
+static unsigned int expired_timers = 0;
+static unsigned int total_timers = 0;
+static unsigned int too_late = 0;
+
+static void main_timer_fired(void *data)
+{
+ unsigned int *step = data;
+ unsigned int add_in_this_step;
+ int i;
+
+ if (*step == timer_nsteps) {
+ fprintf(stderr, "Main timer has finished, please, "
+ "wait a bit for the final report.\n");
+ return;
+ }
+ /* add 2^step pair of timers per step. */
+ add_in_this_step = (1 << *step);
+
+ for (i=0; i<add_in_this_step; i++) {
+ struct test_timer *v;
+
+ v = talloc_zero(NULL, struct test_timer);
+ if (v == NULL) {
+ fprintf(stderr, "timer_test: OOM!\n");
+ return;
+ }
+ gettimeofday(&v->start, NULL);
+ v->timer.cb = secondary_timer_fired;
+ v->timer.data = v;
+ unsigned int seconds = (random() % 10) + 1;
+ v->stop.tv_sec = v->start.tv_sec + seconds;
+ osmo_timer_schedule(&v->timer, seconds, 0);
+ llist_add(&v->head, &timer_test_list);
+ }
+ fprintf(stderr, "added %d timers in step %u (expired=%u)\n",
+ add_in_this_step, *step, expired_timers);
+ total_timers += add_in_this_step;
+ osmo_timer_schedule(&main_timer, TIME_BETWEEN_STEPS, 0);
+ (*step)++;
+}
+
+static void secondary_timer_fired(void *data)
+{
+ struct test_timer *v = data, *this, *tmp;
+ struct timeval current, res, precision = { 1, 0 };
+
+ gettimeofday(&current, NULL);
+
+ timersub(&current, &v->stop, &res);
+ if (timercmp(&res, &precision, >)) {
+ fprintf(stderr, "ERROR: timer %p has expired too late!\n",
+ v->timer);
+ too_late++;
+ }
+
+ llist_del(&v->head);
+ talloc_free(data);
+ expired_timers++;
+ if (expired_timers == total_timers) {
+ fprintf(stdout, "test over: added=%u expired=%u too_late=%u \n",
+ total_timers, expired_timers, too_late);
+ exit(EXIT_SUCCESS);
+ }
+
+ /* randomly (10%) deletion of timers. */
+ llist_for_each_entry_safe(this, tmp, &timer_test_list, head) {
+ if ((random() % 100) < 10) {
+ osmo_timer_del(&this->timer);
+ llist_del(&this->head);
+ talloc_free(this);
+ expired_timers++;
+ }
+ }
+}
+
+static void alarm_handler(int signum)
+{
+ fprintf(stderr, "ERROR: We took too long to run the timer test, "
+ "something seems broken, aborting.\n");
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+
+ if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
+ perror("cannot register signal handler");
+ exit(EXIT_FAILURE);
+ }
+
+ while ((c = getopt_long(argc, argv, "s:", NULL, NULL)) != -1) {
+ switch(c) {
+ case 's':
+ timer_nsteps = atoi(optarg);
+ if (timer_nsteps <= 0) {
+ fprintf(stderr, "%s: steps must be > 0\n",
+ argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ default:
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ fprintf(stdout, "Running timer test for %u steps, accepting "
+ "imprecision of %u.%.6u seconds\n",
+ timer_nsteps, TIMER_PRES_SECS, TIMER_PRES_USECS);
+
+ osmo_timer_schedule(&main_timer, 1, 0);
+
+ /* if the test takes too long, we may consider that the timer scheduler
+ * has hung. We set some maximum wait time which is the double of the
+ * maximum timeout randomly set (10 seconds, worst case) plus the
+ * number of steps (since some of them are reset each step). */
+ alarm(2 * (10 + timer_nsteps));
+
+#ifdef HAVE_SYS_SELECT_H
+ while (1) {
+ osmo_select_main(0);
+ }
+#else
+ fprintf(stdout, "Select not supported on this platform!\n");
+#endif
+}
diff --git a/src/shared/libosmocore/tests/timer/timer_test.ok b/src/shared/libosmocore/tests/timer/timer_test.ok
new file mode 100644
index 00000000..b4e0e118
--- /dev/null
+++ b/src/shared/libosmocore/tests/timer/timer_test.ok
@@ -0,0 +1,2 @@
+Running timer test for 5 steps, accepting imprecision of 0.010000 seconds
+test over: added=31 expired=31 too_late=0
diff --git a/src/shared/libosmocore/tests/ussd/Makefile.am b/src/shared/libosmocore/tests/ussd/Makefile.am
new file mode 100644
index 00000000..de9ff892
--- /dev/null
+++ b/src/shared/libosmocore/tests/ussd/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = ussd_test
+EXTRA_DIST = ussd_test.ok
+
+ussd_test_SOURCES = ussd_test.c
+ussd_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
diff --git a/src/shared/libosmocore/tests/ussd/ussd_test.c b/src/shared/libosmocore/tests/ussd/ussd_test.c
new file mode 100644
index 00000000..55384f10
--- /dev/null
+++ b/src/shared/libosmocore/tests/ussd/ussd_test.c
@@ -0,0 +1,97 @@
+/*
+ * (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 <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/gsm0480.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const uint8_t ussd_request[] = {
+ 0x0b, 0x7b, 0x1c, 0x15, 0xa1, 0x13, 0x02, 0x01,
+ 0x03, 0x02, 0x01, 0x3b, 0x30, 0x0b, 0x04, 0x01,
+ 0x0f, 0x04, 0x06, 0x2a, 0xd5, 0x4c, 0x16, 0x1b,
+ 0x01, 0x7f, 0x01, 0x00
+};
+
+static int parse_ussd(const uint8_t *_data, int len)
+{
+ uint8_t *data;
+ int rc;
+ struct ussd_request req;
+ struct gsm48_hdr *hdr;
+
+ data = malloc(len);
+ memcpy(data, _data, len);
+ hdr = (struct gsm48_hdr *) &data[0];
+ rc = gsm0480_decode_ussd_request(hdr, len, &req);
+ free(data);
+
+ return rc;
+}
+
+static int parse_mangle_ussd(const uint8_t *_data, int len)
+{
+ uint8_t *data;
+ int rc;
+ struct ussd_request req;
+ struct gsm48_hdr *hdr;
+
+ data = malloc(len);
+ memcpy(data, _data, len);
+ hdr = (struct gsm48_hdr *) &data[0];
+ hdr->data[1] = len - sizeof(*hdr) - 2;
+ rc = gsm0480_decode_ussd_request(hdr, len, &req);
+ free(data);
+
+ return rc;
+}
+
+struct log_info info = {};
+
+int main(int argc, char **argv)
+{
+ struct ussd_request req;
+ const int size = sizeof(ussd_request);
+ int i;
+
+ osmo_init_logging(&info);
+
+ gsm0480_decode_ussd_request((struct gsm48_hdr *) ussd_request, size, &req);
+ printf("Tested if it still works. Text was: %s\n", req.text);
+
+
+ printf("Testing parsing a USSD request and truncated versions\n");
+
+ for (i = size; i > sizeof(struct gsm48_hdr); --i) {
+ int rc = parse_ussd(&ussd_request[0], i);
+ printf("Result for %d is %d\n", rc, i);
+ }
+
+ printf("Mangling the container now\n");
+ for (i = size; i > sizeof(struct gsm48_hdr) + 2; --i) {
+ int rc = parse_mangle_ussd(&ussd_request[0], i);
+ printf("Result for %d is %d\n", rc, i);
+ }
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/tests/ussd/ussd_test.ok b/src/shared/libosmocore/tests/ussd/ussd_test.ok
new file mode 100644
index 00000000..1b6316e8
--- /dev/null
+++ b/src/shared/libosmocore/tests/ussd/ussd_test.ok
@@ -0,0 +1,53 @@
+Tested if it still works. Text was: **321#
+Testing parsing a USSD request and truncated versions
+Result for 1 is 28
+Result for 1 is 27
+Result for 1 is 26
+Result for 1 is 25
+Result for 0 is 24
+Result for 0 is 23
+Result for 0 is 22
+Result for 0 is 21
+Result for 0 is 20
+Result for 0 is 19
+Result for 0 is 18
+Result for 0 is 17
+Result for 0 is 16
+Result for 0 is 15
+Result for 0 is 14
+Result for 0 is 13
+Result for 0 is 12
+Result for 0 is 11
+Result for 0 is 10
+Result for 0 is 9
+Result for 0 is 8
+Result for 0 is 7
+Result for 0 is 6
+Result for 0 is 5
+Result for 0 is 4
+Result for 0 is 3
+Mangling the container now
+Result for 0 is 28
+Result for 0 is 27
+Result for 1 is 26
+Result for 1 is 25
+Result for 0 is 24
+Result for 0 is 23
+Result for 0 is 22
+Result for 0 is 21
+Result for 0 is 20
+Result for 0 is 19
+Result for 0 is 18
+Result for 0 is 17
+Result for 0 is 16
+Result for 0 is 15
+Result for 0 is 14
+Result for 0 is 13
+Result for 0 is 12
+Result for 0 is 11
+Result for 0 is 10
+Result for 0 is 9
+Result for 0 is 8
+Result for 0 is 7
+Result for 0 is 6
+Result for 1 is 5
diff --git a/src/shared/libosmocore/utils/Makefile.am b/src/shared/libosmocore/utils/Makefile.am
new file mode 100644
index 00000000..4e7869e4
--- /dev/null
+++ b/src/shared/libosmocore/utils/Makefile.am
@@ -0,0 +1,10 @@
+if ENABLE_UTILITIES
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = osmo-arfcn osmo-auc-gen
+
+osmo_arfcn_SOURCES = osmo-arfcn.c
+osmo_arfcn_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+
+osmo_auc_gen_SOURCES = osmo-auc-gen.c
+osmo_auc_gen_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+endif
diff --git a/src/shared/libosmocore/utils/gen_website_doc_tree.sh b/src/shared/libosmocore/utils/gen_website_doc_tree.sh
new file mode 100755
index 00000000..622db56d
--- /dev/null
+++ b/src/shared/libosmocore/utils/gen_website_doc_tree.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+TOPDIR=`pwd`
+INDIR="$TOPDIR/doc"
+OUTDIR=/tmp/doxywww
+GITREV=`./git-version-gen .tarball-version`
+
+[ -f "$OUTDIR" ] || mkdir "$OUTDIR"
+
+for MOD in core gsm vty codec; do
+ TGTDIR="$OUTDIR/libosmo$MOD/$GITREV"
+ mkdir -p "$TGTDIR"
+ cp -R "$INDIR/$MOD/"* "$TGTDIR/"
+done
diff --git a/src/shared/libosmocore/utils/osmo-arfcn.c b/src/shared/libosmocore/utils/osmo-arfcn.c
new file mode 100644
index 00000000..15adbca2
--- /dev/null
+++ b/src/shared/libosmocore/utils/osmo-arfcn.c
@@ -0,0 +1,103 @@
+/* Utility program for ARFCN / frequency calculations */
+/*
+ * (C) 2011 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 <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+
+enum program_mode {
+ MODE_NONE,
+ MODE_A2F,
+ MODE_F2A,
+};
+
+static int arfcn2freq(char *arfcn_str)
+{
+ int arfcn = atoi(arfcn_str);
+ uint16_t freq10u, freq10d;
+
+ if (arfcn < 0 || arfcn > 0xffff) {
+ fprintf(stderr, "Invalid ARFCN %d\n", arfcn);
+ return -EINVAL;
+ }
+
+ freq10u = gsm_arfcn2freq10(arfcn, 1);
+ freq10d = gsm_arfcn2freq10(arfcn, 0);
+ if (freq10u == 0xffff || freq10d == 0xffff) {
+ fprintf(stderr, "Error during conversion of ARFCN %d\n",
+ arfcn);
+ return -EINVAL;
+ }
+
+ printf("ARFCN %4d: Uplink %4u.%1u MHz / Downlink %4u.%1u MHz\n",
+ arfcn, freq10u/10, freq10u%10, freq10d/10, freq10d%10);
+
+ return 0;
+}
+
+static void help(const char *progname)
+{
+ printf("Usage: %s [-h] [-a arfcn] [-f freq] [-u|-d]\n",
+ progname);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ char *param;
+ enum program_mode mode = MODE_NONE;
+
+ while ((opt = getopt(argc, argv, "a:f:ud")) != -1) {
+ switch (opt) {
+ case 'a':
+ mode = MODE_A2F;
+ param = optarg;
+ break;
+ case 'f':
+ mode = MODE_F2A;
+ param = optarg;
+ break;
+ case 'h':
+ help(argv[0]);
+ exit(0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (mode) {
+ case MODE_NONE:
+ help(argv[0]);
+ exit(2);
+ break;
+ case MODE_A2F:
+ arfcn2freq(param);
+ break;
+ }
+
+ exit(0);
+}
diff --git a/src/shared/libosmocore/utils/osmo-auc-gen.c b/src/shared/libosmocore/utils/osmo-auc-gen.c
new file mode 100644
index 00000000..62b51289
--- /dev/null
+++ b/src/shared/libosmocore/utils/osmo-auc-gen.c
@@ -0,0 +1,183 @@
+/* GSM/GPRS/3G authentication testing tool */
+
+/* (C) 2010-2011 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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <osmocom/crypt/auth.h>
+#include <osmocom/core/utils.h>
+
+static void dump_auth_vec(struct osmo_auth_vector *vec)
+{
+ printf("RAND:\t%s\n", osmo_hexdump(vec->rand, sizeof(vec->rand)));
+
+ if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) {
+ printf("AUTN:\t%s\n", osmo_hexdump(vec->autn, sizeof(vec->autn)));
+ printf("IK:\t%s\n", osmo_hexdump(vec->ik, sizeof(vec->ik)));
+ printf("CK:\t%s\n", osmo_hexdump(vec->ck, sizeof(vec->ck)));
+ printf("RES:\t%s\n", osmo_hexdump(vec->res, vec->res_len));
+ }
+
+ if (vec->auth_types & OSMO_AUTH_TYPE_GSM) {
+ printf("SRES:\t%s\n", osmo_hexdump(vec->sres, sizeof(vec->sres)));
+ printf("Kc:\t%s\n", osmo_hexdump(vec->kc, sizeof(vec->kc)));
+ }
+}
+
+static struct osmo_sub_auth_data test_aud = {
+ .type = OSMO_AUTH_TYPE_NONE,
+ .algo = OSMO_AUTH_ALG_NONE,
+};
+
+int main(int argc, char **argv)
+{
+ struct osmo_auth_vector _vec;
+ struct osmo_auth_vector *vec = &_vec;
+ uint8_t _rand[16];
+ int rc, option_index;
+ int rand_is_set = 0;
+
+ printf("osmo-auc-gen (C) 2011 by Harald Welte\n");
+ printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
+
+ while (1) {
+ int c;
+ unsigned long ul;
+ static struct option long_options[] = {
+ { "2g", 0, 0, '2' },
+ { "3g", 0, 0, '3' },
+ { "algorithm", 1, 0, 'a' },
+ { "key", 1, 0, 'k' },
+ { "opc", 1, 0, 'o' },
+ { "amf", 1, 0, 'f' },
+ { "sqn", 1, 0, 's' },
+ { "rand", 1, 0, 'r' },
+ { 0, 0, 0, 0 }
+ };
+
+ rc = 0;
+
+ c = getopt_long(argc, argv, "23a:k:o:f:s:r:", long_options,
+ &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '2':
+ test_aud.type = OSMO_AUTH_TYPE_GSM;
+ break;
+ case '3':
+ test_aud.type = OSMO_AUTH_TYPE_UMTS;
+ break;
+ case 'a':
+ rc = osmo_auth_alg_parse(optarg);
+ if (rc < 0)
+ break;
+ test_aud.algo = rc;
+ break;
+ case 'k':
+ switch (test_aud.type) {
+ case OSMO_AUTH_TYPE_GSM:
+ rc = osmo_hexparse(optarg, test_aud.u.gsm.ki,
+ sizeof(test_aud.u.gsm.ki));
+ break;
+ case OSMO_AUTH_TYPE_UMTS:
+ rc = osmo_hexparse(optarg, test_aud.u.umts.k,
+ sizeof(test_aud.u.umts.k));
+ break;
+ default:
+ fprintf(stderr, "please specify 2g/3g first!\n");
+ }
+ break;
+ case 'o':
+ if (test_aud.type != OSMO_AUTH_TYPE_UMTS) {
+ fprintf(stderr, "Only UMTS has OPC\n");
+ exit(2);
+ }
+ rc = osmo_hexparse(optarg, test_aud.u.umts.opc,
+ sizeof(test_aud.u.umts.opc));
+ break;
+ case 'f':
+ if (test_aud.type != OSMO_AUTH_TYPE_UMTS) {
+ fprintf(stderr, "Only UMTS has AMF\n");
+ exit(2);
+ }
+ rc = osmo_hexparse(optarg, test_aud.u.umts.amf,
+ sizeof(test_aud.u.umts.amf));
+ break;
+ case 's':
+ if (test_aud.type != OSMO_AUTH_TYPE_UMTS) {
+ fprintf(stderr, "Only UMTS has SQN\n");
+ exit(2);
+ }
+ ul = strtoul(optarg, 0, 10);
+ test_aud.u.umts.sqn = ul;
+ break;
+ case 'r':
+ rc = osmo_hexparse(optarg, _rand, sizeof(_rand));
+ rand_is_set = 1;
+ break;
+ }
+
+ if (rc < 0) {
+ fprintf(stderr, "Error parsing argument of option `%c'\n", c);
+ exit(2);
+ }
+ }
+
+ if (!rand_is_set) {
+ printf("WARNING: We're using really weak random numbers!\n\n");
+ srand(time(NULL));
+ *(uint32_t *)&_rand[0] = rand();
+ *(uint32_t *)(&_rand[4]) = rand();
+ *(uint32_t *)(&_rand[8]) = rand();
+ *(uint32_t *)(&_rand[12]) = rand();
+ }
+
+ memset(vec, 0, sizeof(*vec));
+
+ rc = osmo_auth_gen_vec(vec, &test_aud, _rand);
+ if (rc < 0) {
+ fprintf(stderr, "error generating auth vector\n");
+ exit(1);
+ }
+
+ dump_auth_vec(vec);
+#if 0
+ const uint8_t auts[14] = { 0x87, 0x11, 0xa0, 0xec, 0x9e, 0x16, 0x37, 0xdf,
+ 0x17, 0xf8, 0x0b, 0x38, 0x4e, 0xe4 };
+
+ rc = osmo_auth_gen_vec_auts(vec, &test_aud, auts, _rand, _rand);
+ if (rc < 0) {
+ printf("AUTS failed\n");
+ } else {
+ printf("AUTS success: SEQ.MS = %lu\n", test_aud.u.umts.sqn);
+ }
+#endif
+ exit(0);
+
+}
diff --git a/src/shared/update-libosmocore.sh b/src/shared/update-libosmocore.sh
new file mode 100755
index 00000000..69dfbe11
--- /dev/null
+++ b/src/shared/update-libosmocore.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+(cd ../.. && git subtree pull --prefix=src/shared/libosmocore git://git.osmocom.org/libosmocore.git master)