Portability fix: Adding local partial copy of libosmocore (TODO: minimize it)
diff --git a/lib/decoding/osmocom/gsm/CMakeLists.txt b/lib/decoding/osmocom/gsm/CMakeLists.txt
new file mode 100644
index 0000000..945d1c2
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_sources(
+a5.c
+auth_core.c
+gsm48_ie.c
+kasumi.c
+)
diff --git a/lib/decoding/osmocom/gsm/a5.c b/lib/decoding/osmocom/gsm/a5.c
new file mode 100644
index 0000000..9f4ede1
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/a5.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2011  Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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
+ *  @{
+ *  Osmocom GSM ciphering algorithm implementation
+ *
+ *  Full reimplementation of A5/1,2,3,4 (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.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <osmocom/gsm/a5.h>
+#include <osmocom/gsm/kasumi.h>
+//#include <osmocom/crypt/auth.h>
+
+/* Somme OS (like Nuttx) don't have ENOTSUP */
+#ifndef ENOTSUP
+#define ENOTSUP EINVAL
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* A5/3&4                                                                   */
+/* ------------------------------------------------------------------------ */
+
+/*! Generate a GSM A5/4 cipher stream
+ *  \param[in] key 16 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
+ *  \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
+ *
+ * Either (or both) of dl/ul should be NULL if not needed.
+ *
+ * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
+ * with slight simplifications (CE hardcoded to 0).
+ */
+void
+_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
+{
+       uint8_t i, gamma[32], uplink[15];
+       uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn;
+
+       if (ul) {
+               _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228);
+               for(i = 0; i < 15; i++) uplink[i] = (gamma[i + 14] << 2) + (gamma[i + 15] >> 6);
+               osmo_pbit2ubit(ul, uplink, 114);
+       }
+       if (dl) {
+               _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 114);
+               osmo_pbit2ubit(dl, gamma, 114);
+       }
+}
+
+/*! Generate a GSM A5/3 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
+ *  \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
+ *
+ * Either (or both) of dl/ul should be NULL if not needed.
+ *
+ * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
+ * with slight simplifications (CE hardcoded to 0).
+ */
+void
+_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
+{
+       uint8_t ck[16];
+       osmo_c4(ck, key);
+       /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */
+       _a5_4(ck, fn, dl, ul, fn_correct);
+}
+
+/* ------------------------------------------------------------------------ */
+/* 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^18 + x^17 + x^14 + 1 */
+#define A5_R2_TAPS	0x300000 /* x^22 + x^21 + 1 */
+#define A5_R3_TAPS	0x700080 /* x^23 + x^22 + x^21 + x^8 + 1 */
+#define A5_R4_TAPS	0x010800 /* x^17 + x^12 + 1 */
+
+/*! 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;
+}
+
+/*! 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;
+}
+
+/*! 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
+
+/*! 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);
+}
+
+/*! 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));
+}
+
+/*! 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
+_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);
+	}
+}
+
+void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+	osmo_a5(1, key, fn, dl, ul);
+}
+
+/* ------------------------------------------------------------------------ */
+/* A5/2                                                                     */
+/* ------------------------------------------------------------------------ */
+
+#define A52_R4_CLKBIT0	0x000400
+#define A52_R4_CLKBIT1	0x000008
+#define A52_R4_CLKBIT2	0x000080
+
+/*! 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);
+}
+
+/*! 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;
+}
+
+/*! 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
+_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);
+	}
+}
+
+void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+	osmo_a5(2, key, fn, dl, ul);
+}
+
+/*! Main method to generate a A5/x cipher stream
+ *  \param[in] n Which A5/x method to use
+ *  \param[in] key 8 or 16 (for a5/4) 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
+ *  \returns 0 for success, -ENOTSUP for invalid cipher selection.
+ *
+ * Currently A5/[0-4] are supported.
+ * Either (or both) of dl/ul can be NULL if not needed.
+ */
+int
+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:
+		_a5_1(key, fn, dl, ul);
+		break;
+
+	case 2:
+		_a5_2(key, fn, dl, ul);
+		break;
+
+	case 3:
+		_a5_3(key, fn, dl, ul, true);
+		break;
+
+	case 4:
+		_a5_4(key, fn, dl, ul, true);
+		break;
+
+	default:
+		/* a5/[5..7] not supported here/yet */
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/gsm/a5.h b/lib/decoding/osmocom/gsm/a5.h
new file mode 100644
index 0000000..fa63246
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/a5.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/defs.h>
+#include <osmocom/core/bits.h>
+
+/*! \defgroup a5 GSM A5 ciphering algorithm
+ *  @{
+ * \file a5.h */
+
+/*! 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 or 16 (for a5/4) 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)
+	 */
+int 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) OSMO_DEPRECATED("Use generic osmo_a5() instead");
+void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) OSMO_DEPRECATED("Use generic osmo_a5() instead");
+
+/*! @} */
diff --git a/lib/decoding/osmocom/gsm/auth_core.c b/lib/decoding/osmocom/gsm/auth_core.c
new file mode 100644
index 0000000..230500e
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/auth_core.c
@@ -0,0 +1,252 @@
+/* (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 <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>
+
+/*! \addtogroup auth
+ *  @{
+ *  GSM/GPRS/3G authentication core infrastructure
+ *
+ * \file auth_core.c */
+
+static LLIST_HEAD(osmo_auths);
+
+static struct osmo_auth_impl *selected_auths[_OSMO_AUTH_ALG_NUM];
+
+/*! Register an authentication algorithm implementation with the core
+ *  \param[in] impl Structure describing implementation and it's callbacks
+ *  \returns 0 on success, or a negative error code on failure
+ *
+ * This function is called by an authentication implementation plugin to
+ * register itself with the authentication 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 authentication plugins from the given path
+ *  \param[in] path Path name of the directory containing the plugins
+ *  \returns number of plugins loaded in case of success, negative in case of error
+ *
+ * This function will load all plugins contained in the specified path.
+ */
+int osmo_auth_load(const char *path)
+{
+	/* load all plugins available from path */
+/*#if !defined(EMBEDDED)
+	return osmo_plugin_load_all(path);
+#else*/
+	return -1;
+/*#endif*/
+}
+
+/*! Determine if a given authentication algorithm is supported
+ *  \param[in] algo Algorithm which should be checked
+ *  \returns 1 if algo is supported, 0 if not, negative error on failure
+ *
+ * This function is used by an application to determine at runtime if a
+ * given authentication algorithm is supported or not.
+ */
+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;
+}
+
+/* C5 function to derive UMTS IK from GSM Kc */
+static inline void c5_function(uint8_t *ik, const uint8_t *kc)
+{
+	unsigned int i;
+
+	for (i = 0; i < 4; i++)
+		ik[i] = kc[i] ^ kc[i+4];
+	memcpy(ik+4, kc, 8);
+	for (i = 12; i < 16; i++)
+		ik[i] = ik[i-12];
+}
+
+/* C4 function to derive UMTS CK from GSM Kc */
+void osmo_c4(uint8_t *ck, const uint8_t *kc)
+{
+	memcpy(ck, kc, 8);
+	memcpy(ck+8, kc, 8);
+}
+
+/*! Generate 3G CK + IK from 2G authentication vector
+ *  \param vec Authentication Vector to be modified
+ *  \returns 1 if the vector was changed, 0 otherwise
+ *
+ * This function performs the C5 and C4 functions to derive the UMTS key
+ * material from the GSM key material in the supplied vector, _if_ the input
+ * vector doesn't yet have UMTS authentication capability.
+ */
+int osmo_auth_3g_from_2g(struct osmo_auth_vector *vec)
+{
+	if ((vec->auth_types & OSMO_AUTH_TYPE_GSM) &&
+	    !(vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
+		c5_function(vec->ik, vec->kc);
+		osmo_c4(vec->ck, vec->kc);
+		/* We cannot actually set OSMO_AUTH_TYPE_UMTS as we have no
+		 * AUTN and no RES, and thus can only perform GSM
+		 * authentication with this tuple.
+		 */
+		return 1;
+	}
+
+	return 0;
+}
+
+/*! Generate authentication vector
+ *  \param[out] vec Generated authentication vector
+ *  \param[in] aud Subscriber-specific key material
+ *  \param[in] _rand Random challenge to be used
+ *  \returns 0 on success, negative error on failure
+ *
+ * This function performs the core cryptographic function of the AUC,
+ * computing authentication triples/quintuples based on the permanent
+ * subscriber data and a random value.  The result is what is forwarded
+ * by the AUC via HLR and VLR to the MSC which will then be able to
+ * invoke authentication with the MS
+ */
+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;
+}
+
+/*! Generate authentication vector and re-sync sequence
+ *  \param[out] vec Generated authentication vector
+ *  \param[in] aud Subscriber-specific key material
+ *  \param[in] auts AUTS value sent by the SIM/MS
+ *  \param[in] rand_auts RAND value sent by the SIM/MS
+ *  \param[in] _rand Random challenge to be used to generate vector
+ *  \returns 0 on success, negative error on failure
+ *
+ * This function performs a special variant of the  core cryptographic
+ * function of the AUC: computing authentication triples/quintuples
+ * based on the permanent subscriber data, a random value as well as the
+ * AUTS and RAND values returned by the SIM/MS.  This special variant is
+ * needed if the sequence numbers between MS and AUC have for some
+ * reason become different.
+ */
+int osmo_auth_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)
+{
+	struct osmo_auth_impl *impl = selected_auths[aud->algo];
+	int rc;
+
+	if (!impl || !impl->gen_vec_auts)
+		return -ENOENT;
+
+	rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
+	if (rc < 0)
+		return rc;
+
+	memcpy(vec->rand, _rand, sizeof(vec->rand));
+
+	return 0;
+}
+
+static 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 }
+};
+
+/*! Get human-readable name of authentication algorithm *
+const char *osmo_auth_alg_name(enum osmo_auth_algo alg)
+{
+	return get_value_string(auth_alg_vals, alg);
+}
+
+/*! Parse human-readable name of authentication algorithm */
+/*enum osmo_auth_algo osmo_auth_alg_parse(const char *name)
+{
+	return get_string_value(auth_alg_vals, name);
+}
+*/
+const struct value_string osmo_sub_auth_type_names[] = {
+	{ OSMO_AUTH_TYPE_NONE, "None" },
+	{ OSMO_AUTH_TYPE_GSM, "GSM" },
+	{ OSMO_AUTH_TYPE_UMTS, "UMTS" },
+	{ 0, NULL }
+};
+
+/* Derive GSM AKA ciphering key Kc from UMTS AKA CK and IK (auth function c3 from 3GPP TS 33.103 §
+ * 4.6.1).
+ * \param[out] kc  GSM AKA Kc, 8 byte target buffer.
+ * \param[in] ck  UMTS AKA CK, 16 byte input buffer.
+ * \param[in] ik  UMTS AKA IK, 16 byte input buffer.
+ */
+void osmo_auth_c3(uint8_t kc[], const uint8_t ck[], const uint8_t ik[])
+{
+	int i;
+	for (i = 0; i < 8; i++)
+		kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/gsm/gsm48_ie.c b/lib/decoding/osmocom/gsm/gsm48_ie.c
new file mode 100644
index 0000000..3881b18
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/gsm48_ie.c
@@ -0,0 +1,1247 @@
+/*! \file gsm48_ie.c
+ * 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
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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>
+
+/*! \addtogroup gsm0408
+ *  @{
+ */
+
+//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
+// *  \param[out] Caller-provided output buffer
+// *  \param[in] bcd_lv Length-Value portion of to-be-decoded IE
+// *  \param[in] h_len Length of an optional heder between L and V portion
+// *  \returns - in case of success; negative on error */
+//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'
+// *  \param[out] bcd_lv Caller-provided output buffer
+// *  \param[in] max_len Maximum Length of \a bcd_lv
+// *  \param[in] h_len Length of an optional heder between L and V portion
+// *  \param[in] input phone number as 0-terminated ASCII
+// *  \returns number of bytes used in \a bcd_lv */
+//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 TS 04.08 Bearer Capability IE (10.5.4.5)
+// *  \param[out] Caller-provided memory for decoded output
+// *  \[aram[in] LV portion of TS 04.08 Bearer Capability
+// *  \returns 0 on success; negative on error */
+//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;
+
+//	switch (bcap->transfer) {
+//	case 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;
+//		}
+//		break;
+//	case GSM_MNCC_BCAP_UNR_DIG:
+//	case GSM_MNCC_BCAP_FAX_G3:
+//		i = 1;
+//		while(!(lv[i] & 0x80)) {
+//			i++; /* octet 3a etc */
+//			if (in_len < i)
+//				return 0;
+//			/* ignore them */
+//		}
+//		/* octet 4: skip */
+//		i++;
+//		/* octet 5 */
+//		i++;
+//		if (in_len < i)
+//			return 0;
+//		bcap->data.rate_adaption = (lv[i] >> 3) & 3;
+//		bcap->data.sig_access = lv[i] & 7;
+//		while(!(lv[i] & 0x80)) {
+//			i++; /* octet 5a etc */
+//			if (in_len < i)
+//				return 0;
+//			/* ignore them */
+//		}
+//		/* octet 6 */
+//		i++;
+//		if (in_len < i)
+//			return 0;
+//		bcap->data.async = lv[i] & 1;
+//		if (!(lv[i] & 0x80)) {
+//			i++;
+//			if (in_len < i)
+//				return 0;
+//			/* octet 6a */
+//			bcap->data.nr_stop_bits = ((lv[i] >> 7) & 1) + 1;
+//			if (lv[i] & 0x10)
+//				bcap->data.nr_data_bits = 8;
+//			else
+//				bcap->data.nr_data_bits = 7;
+//			bcap->data.user_rate = lv[i]  & 0xf;
+
+//			if (!(lv[i] & 0x80)) {
+//				i++;
+//				if (in_len < i)
+//					return 0;
+//				/* octet 6b */
+//				bcap->data.parity = lv[i] & 7;
+//				bcap->data.interm_rate = (lv[i] >> 5) & 3;
+
+//				/* octet 6c */
+//				if (!(lv[i] & 0x80)) {
+//					i++;
+//					if (in_len < i)
+//						return 0;
+//					bcap->data.transp = (lv[i] >> 5) & 3;
+//					bcap->data.modem_type = lv[i] & 0x1F;
+//				}
+//			}
+
+//		}
+//		break;
+//	default:
+//		i = 1;
+//		while (!(lv[i] & 0x80)) {
+//			i++; /* octet 3a etc */
+//			if (in_len < i)
+//				return 0;
+//			/* ignore them */
+//		}
+//		/* FIXME: implement OCTET 4+ parsing */
+//		break;
+//	}
+
+//	return 0;
+//}
+
+///*! Encode TS 04.08 Bearer Capability IE (10.5.4.5)
+// *  \param[out] msg Message Buffer to which IE is to be appended
+// *  \param[in] lv_only Write only LV portion (1) or TLV (0)
+// *  \param[in] bcap Decoded Bearer Capability to be encoded
+// *  \returns 0 on success; negative on error */
+//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;
+
+//	switch (bcap->transfer) {
+//	case 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 */
+//		break;
+//	case GSM48_BCAP_ITCAP_UNR_DIG_INF:
+//	case GSM48_BCAP_ITCAP_FAX_G3:
+//		lv[i++] |= 0x80; /* last IE of octet 3 etc */
+//		/* octet 4 */
+//		lv[i++] = 0xb8;
+//		/* octet 5 */
+//		lv[i++] = 0x80 | ((bcap->data.rate_adaption & 3) << 3)
+//			  | (bcap->data.sig_access & 7);
+//		/* octet 6 */
+//		lv[i++] = 0x20 | (bcap->data.async & 1);
+//		/* octet 6a */
+//		lv[i++] = (bcap->data.user_rate & 0xf) |
+//			  (bcap->data.nr_data_bits == 8 ? 0x10 : 0x00) |
+//			  (bcap->data.nr_stop_bits == 2 ? 0x40 : 0x00);
+//		/* octet 6b */
+//		lv[i++] = (bcap->data.parity & 7) |
+//			  ((bcap->data.interm_rate & 3) << 5);
+//		/* octet 6c */
+//		lv[i] = 0x80 | (bcap->data.modem_type & 0x1f);
+//		break;
+//	default:
+//		return -EINVAL;
+//	}
+
+//	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 TS 04.08 Call Control Capabilities IE (10.5.4.5a)
+// *  \param[out] Caller-provided memory for decoded CC capabilities
+// *  \param[in] lv Length-Value of IE
+// *  \retursns 0 on success; negative on error */
+//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;
+//}
+
+///*! Encodoe TS 04.08 Call Control Capabilities (10.5.4.5a)
+// *  \param[out] msg Message Buffer to which to append IE (as TLV)
+// *  \param[in] ccap Decoded CC Capabilities to be encoded
+// *  \returns 0 on success; negative on error */
+//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 TS 04.08 Called Party BCD Number IE (10.5.4.7)
+// *  \param[out] called Caller-provided memory for decoded number
+// *  \param[in] lv Length-Value portion of IE
+// *  \returns 0 on success; negative on error */
+//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 TS 04.08 Called Party IE (10.5.4.7)
+// *  \param[out] msg Mesage Buffer to which to append IE (as TLV)
+// *  \param[in] called MNCC Number to encode/append
+// *  \returns 0 on success; negative on error */
+//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 TS 04.08 Caller ID
+// *  \param[out] called Caller-provided memory for decoded number
+// *  \param[in] lv Length-Value portion of IE
+// *  \returns 0 on success; negative on error */
+//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 TS 04.08 Caller ID IE
+// *  \param[out] msg Mesage Buffer to which to append IE (as TLV)
+// *  \param[in] ie IE Identifier (tag)
+// *  \param[in] max_len maximum generated output in bytes
+// *  \param[in] callerid MNCC Number to encode/append
+// *  \returns 0 on success; negative on error */
+//int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len,
+//			   const struct gsm_mncc_number *callerid)
+//{
+//	uint8_t * lv = malloc(sizeof(uint8_t)*(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);
+//	free(lv);
+//	return 0;
+//}
+
+///*! Decode TS 04.08 Cause IE (10.5.4.11)
+// *  \param[out] cause Caller-provided memory for output
+// *  \param[in] lv LV portion of Cause IE
+// *  \returns 0 on success; negative on error */
+//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 TS 04.08 Cause IE (10.5.4.11)
+// *  \param[out] msg Message Buffer to which to append IE
+// *  \param[in] lv_only Encode as LV (1) or TLV (0)
+// *  \param[in] cause Cause value to be encoded
+// *  \returns 0 on success; negative on error */
+//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 TS 04.08 Calling Number IE (10.5.4.9) */
+//int gsm48_decode_calling(struct gsm_mncc_number *calling,
+//			 const uint8_t *lv)
+//{
+//	return gsm48_decode_callerid(calling, lv);
+//}
+
+///*! Encode TS 04.08 Calling Number IE (10.5.4.9) */
+//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 TS 04.08 Connected Number IE (10.5.4.13) */
+//int gsm48_decode_connected(struct gsm_mncc_number *connected,
+//			 const uint8_t *lv)
+//{
+//	return gsm48_decode_callerid(connected, lv);
+//}
+
+///*! Encode TS 04.08 Connected Number IE (10.5.4.13) */
+//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 TS 04.08 Redirecting Number IE (10.5.4.21b) */
+//int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting,
+//			 const uint8_t *lv)
+//{
+//	return gsm48_decode_callerid(redirecting, lv);
+//}
+
+///*! Encode TS 04.08 Redirecting Number IE (10.5.4.21b) */
+//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 TS 04.08 Facility IE (10.5.4.15) */
+//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 TS 04.08 Facility IE (10.5.4.15) */
+//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 TS 04.08 Notify IE (10.5.4.20) */
+//int gsm48_decode_notify(int *notify, const uint8_t *v)
+//{
+//	*notify = v[0] & 0x7f;
+
+//	return 0;
+//}
+
+///*! Encode TS 04.08 Notify IE (10.5.4.20) */
+//int gsm48_encode_notify(struct msgb *msg, int notify)
+//{
+//	msgb_v_put(msg, notify | 0x80);
+
+//	return 0;
+//}
+
+///*! Decode TS 04.08 Signal IE (10.5.4.23) */
+//int gsm48_decode_signal(int *signal, const uint8_t *v)
+//{
+//	*signal = v[0];
+
+//	return 0;
+//}
+
+///*! Encode TS 04.08 Signal IE (10.5.4.23) */
+//int gsm48_encode_signal(struct msgb *msg, int signal)
+//{
+//	msgb_tv_put(msg, GSM48_IE_SIGNAL, signal);
+
+//	return 0;
+//}
+
+///*! Decode TS 04.08 Keypad IE (10.5.4.17) */
+//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 TS 04.08 Keypad IE (10.5.4.17) */
+//int gsm48_encode_keypad(struct msgb *msg, int keypad)
+//{
+//	msgb_tv_put(msg, GSM48_IE_KPD_FACILITY, keypad);
+
+//	return 0;
+//}
+
+///*! Decode TS 04.08 Progress IE (10.5.4.21) */
+//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 TS 04.08 Progress IE (10.5.4.21) */
+//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 TS 04.08 User-User IE (10.5.4.25) */
+//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 TS 04.08 User-User IE (10.5.4.25) */
+//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 TS 04.08 SS Version IE (10.5.4.24) */
+//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 TS 04.08 SS Version IE (10.5.4.24) */
+//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 TS 04.08 More Data IE (10.5.4.19) */
+//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 TS 04.08 Cell Channel Description IE (10.5.2.1b) and other frequency lists
+ *  \param[out] f Caller-provided output memory
+ *  \param[in] cd Cell Channel Description IE
+ *  \param[in] len Length of \a cd in bytes
+ *  \returns 0 on success; negative on error */
+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->w14_hi << 2) | r->w14_lo;
+		if (len >= 13)
+			w[15] = r->w15;
+		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->w18_hi << 3) | r->w18_lo;
+		if (len >= 15)
+			w[19] = r->w19;
+		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/lib/decoding/osmocom/gsm/gsm48_ie.h b/lib/decoding/osmocom/gsm/gsm48_ie.h
new file mode 100644
index 0000000..19e0b25
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/gsm48_ie.h
@@ -0,0 +1,116 @@
+/*! \file gsm48_ie.h */
+
+#pragma once
+
+#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;
+};
+
+/* 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);
diff --git a/lib/decoding/osmocom/gsm/kasumi.c b/lib/decoding/osmocom/gsm/kasumi.c
new file mode 100644
index 0000000..7de5cd0
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/kasumi.c
@@ -0,0 +1,188 @@
+/*! \file kasumi.c
+ * Kasumi cipher and KGcore functions. */
+/*
+ * (C) 2013 by Max <Max.Suraev@fairwaves.ru>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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/bits.h>
+#include <osmocom/gsm/kasumi.h>
+
+/* See TS 135 202 for constants and full Kasumi spec. */
+inline static uint16_t kasumi_FI(uint16_t I, uint16_t skey)
+{
+	static const uint16_t S7[] = {
+		54, 50, 62, 56, 22, 34, 94, 96, 38, 6, 63, 93, 2, 18, 123, 33,
+		55, 113, 39, 114, 21, 67, 65, 12, 47, 73, 46, 27, 25, 111, 124, 81,
+		53, 9, 121, 79, 52, 60, 58, 48, 101, 127, 40, 120, 104, 70, 71, 43,
+		20, 122, 72, 61, 23, 109, 13, 100, 77, 1, 16, 7, 82, 10, 105, 98,
+		117, 116, 76, 11, 89, 106, 0,125,118, 99, 86, 69, 30, 57, 126, 87,
+		112, 51, 17, 5, 95, 14, 90, 84, 91, 8, 35,103, 32, 97, 28, 66,
+		102, 31, 26, 45, 75, 4, 85, 92, 37, 74, 80, 49, 68, 29, 115, 44,
+		64, 107, 108, 24, 110, 83, 36, 78, 42, 19, 15, 41, 88, 119, 59, 3
+	};
+	static const uint16_t S9[] = {
+		167, 239, 161, 379, 391, 334,  9, 338, 38, 226, 48, 358, 452, 385, 90, 397,
+		183, 253, 147, 331, 415, 340, 51, 362, 306, 500, 262, 82, 216, 159, 356, 177,
+		175, 241, 489, 37, 206, 17, 0, 333, 44, 254, 378, 58, 143, 220, 81, 400,
+		95, 3, 315, 245, 54, 235, 218, 405, 472, 264, 172, 494, 371, 290, 399, 76,
+		165, 197, 395, 121, 257, 480, 423, 212, 240, 28, 462, 176, 406, 507, 288, 223,
+		501, 407, 249, 265, 89, 186, 221, 428,164, 74, 440, 196, 458, 421, 350, 163,
+		232, 158, 134, 354, 13, 250, 491, 142,191, 69, 193, 425, 152, 227, 366, 135,
+		344, 300, 276, 242, 437, 320, 113, 278, 11, 243, 87, 317, 36, 93, 496, 27,
+		487, 446, 482, 41, 68, 156, 457, 131, 326, 403, 339, 20, 39, 115, 442, 124,
+		475, 384, 508, 53, 112, 170, 479, 151, 126, 169, 73, 268, 279, 321, 168, 364,
+		363, 292, 46, 499, 393, 327, 324, 24, 456, 267, 157, 460, 488, 426, 309, 229,
+		439, 506, 208, 271, 349, 401, 434, 236, 16, 209, 359, 52, 56, 120, 199, 277,
+		465, 416, 252, 287, 246,  6, 83, 305, 420, 345, 153,502, 65, 61, 244, 282,
+		173, 222, 418, 67, 386, 368, 261, 101, 476, 291, 195,430, 49, 79, 166, 330,
+		280, 383, 373, 128, 382, 408, 155, 495, 367, 388, 274, 107, 459, 417, 62, 454,
+		132, 225, 203, 316, 234, 14, 301, 91, 503, 286, 424, 211, 347, 307, 140, 374,
+		35, 103, 125, 427, 19, 214, 453, 146, 498, 314, 444, 230, 256, 329, 198, 285,
+		50, 116, 78, 410, 10, 205, 510, 171, 231, 45, 139, 467, 29, 86, 505, 32,
+		72, 26, 342, 150, 313, 490, 431, 238, 411, 325, 149, 473, 40, 119, 174, 355,
+		185, 233, 389, 71, 448, 273, 372, 55, 110, 178, 322, 12, 469, 392, 369, 190,
+		1, 109, 375, 137, 181, 88, 75, 308, 260, 484, 98, 272, 370, 275, 412, 111,
+		336, 318, 4, 504, 492, 259, 304, 77, 337, 435, 21, 357, 303, 332, 483, 18,
+		47, 85, 25, 497, 474, 289, 100, 269, 296, 478, 270, 106, 31, 104, 433, 84,
+		414, 486, 394, 96, 99, 154, 511, 148, 413, 361, 409, 255, 162, 215, 302, 201,
+		266, 351, 343, 144, 441, 365, 108, 298, 251, 34, 182, 509, 138, 210, 335, 133,
+		311, 352, 328, 141, 396, 346, 123, 319, 450, 281, 429, 228, 443, 481, 92, 404,
+		485, 422, 248, 297, 23, 213, 130, 466, 22, 217, 283, 70, 294, 360, 419, 127,
+		312, 377, 7, 468, 194, 2, 117, 295, 463, 258, 224, 447, 247, 187, 80, 398,
+		284, 353, 105, 390, 299, 471, 470, 184, 57, 200, 348, 63, 204, 188, 33, 451,
+		97, 30, 310, 219, 94, 160, 129, 493, 64, 179, 263, 102, 189, 207, 114, 402,
+		438, 477, 387, 122, 192, 42, 381, 5, 145, 118, 180, 449, 293, 323, 136, 380,
+		43, 66, 60, 455, 341, 445, 202, 432, 8, 237, 15, 376, 436, 464, 59, 461
+	};
+	uint16_t L, R;
+
+	/* Split 16 bit input into two unequal halves: 9 and 7 bits, same for subkey */
+	L = I >> 7; /* take 9 bits */
+	R = I & 0x7F; /* take 7 bits */
+
+	L = S9[L]  ^ R;
+	R = S7[R] ^ (L & 0x7F);
+
+	L ^= (skey & 0x1FF);
+	R ^= (skey >> 9);
+
+	L = S9[L]  ^ R;
+	R = S7[R] ^ (L & 0x7F);
+
+	return (R << 9) + L;
+}
+
+inline static uint32_t kasumi_FO(uint32_t I, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3, unsigned i)
+{
+	uint16_t L = I >> 16, R = I; /* Split 32 bit input into Left and Right parts */
+
+	L ^= KOi1[i];
+	L = kasumi_FI(L, KIi1[i]);
+	L ^= R;
+
+	R ^= KOi2[i];
+	R = kasumi_FI(R, KIi2[i]);
+	R ^= L;
+
+	L ^= KOi3[i];
+	L = kasumi_FI(L, KIi3[i]);
+	L ^= R;
+
+	return (((uint32_t)R) << 16) + L;
+}
+
+inline static uint32_t kasumi_FL(uint32_t I, const uint16_t *KLi1, const uint16_t *KLi2, unsigned i)
+{
+	uint16_t L = I >> 16, R = I, tmp; /* Split 32 bit input into Left and Right parts */
+
+	tmp = L & KLi1[i];
+	R ^= osmo_rol16(tmp, 1);
+
+	tmp = R | KLi2[i];
+	L ^= osmo_rol16(tmp, 1);
+
+	return (((uint32_t)L) << 16) + R;
+}
+
+uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3)
+{
+	uint32_t i, L = P >> 32, R = P; /* Split 64 bit input into Left and Right parts */
+
+	for (i = 0; i < 8; i++) {
+		R ^= kasumi_FO(kasumi_FL(L, KLi1, KLi2, i), KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i); /* odd round */
+		i++;
+		L ^= kasumi_FL(kasumi_FO(R, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i), KLi1, KLi2, i); /* even round */
+	}
+	return (((uint64_t)L) << 32) + R; /* Concatenate Left and Right 32 bits into 64 bit ciphertext */
+}
+
+void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3)
+{
+	uint16_t i, C[] = { 0x0123, 0x4567, 0x89AB, 0xCDEF, 0xFEDC, 0xBA98, 0x7654, 0x3210 };
+
+	/* Work with 16 bit subkeys and create prime subkeys */
+	for (i = 0; i < 8; i++)
+		C[i] ^= osmo_load16be(key + i * 2);
+	/* C[] now stores K-prime[] */
+
+	/* Create round-specific subkeys */
+	for (i = 0; i < 8; i++) {
+		KLi1[i] = osmo_rol16(osmo_load16be(key + i * 2), 1);
+		KLi2[i] = C[(i + 2) & 0x7];
+
+		KOi1[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 1)) & 0xE)), 5);
+		KOi2[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 5)) & 0xE)), 8);
+		KOi3[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 6)) & 0xE)), 13);
+
+		KIi1[i] = C[(i + 4) & 0x7];
+		KIi2[i] = C[(i + 3) & 0x7];
+		KIi3[i] = C[(i + 7) & 0x7];
+	}
+}
+
+void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl)
+{
+	uint16_t KLi1[8], KLi2[8], KOi1[8], KOi2[8], KOi3[8], KIi1[8], KIi2[8], KIi3[8], i;
+	uint64_t A = ((uint64_t)cc) << 32, BLK = 0, _ca = ((uint64_t)CA << 16) ;
+	A |= _ca;
+	_ca = (uint64_t)((cb << 3) | (cd << 2)) << 24;
+	A |= _ca;
+	/* Register loading complete: see TR 55.919 8.2 and TS 55.216 3.2 */
+
+	uint8_t ck_km[16];
+	for (i = 0; i < 16; i++)
+		ck_km[i] = ck[i] ^ 0x55;
+	/* Modified key established */
+
+	/* preliminary round with modified key */
+	_kasumi_key_expand(ck_km, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+	A = _kasumi(A, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+
+	/* Run Kasumi in OFB to obtain enough data for gamma. */
+	_kasumi_key_expand(ck, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+
+	/* i is a block counter */
+	for (i = 0; i < cl / 64 + 1; i++) {
+		BLK = _kasumi(A ^ i ^ BLK, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+		osmo_store64be(BLK, co + (i * 8));
+	}
+}
diff --git a/lib/decoding/osmocom/gsm/kasumi.h b/lib/decoding/osmocom/gsm/kasumi.h
new file mode 100644
index 0000000..d9de10b
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/kasumi.h
@@ -0,0 +1,48 @@
+/*! \file kasumi.h
+ * KASUMI header.
+ *
+ * See kasumi.c for details
+ * The parameters are described in TS 135 202.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*! Single iteration of KASUMI cipher
+ *  \param[in] P Block, 64 bits to be processed in this round
+ *  \param[in] KLi1 Expanded subkeys
+ *  \param[in] KLi2 Expanded subkeys
+ *  \param[in] KOi1 Expanded subkeys
+ *  \param[in] KOi2 Expanded subkeys
+ *  \param[in] KOi3 Expanded subkeys
+ *  \param[in] KIi1 Expanded subkeys
+ *  \param[in] KIi2 Expanded subkeys
+ *  \param[in] KIi3 Expanded subkeys
+ *  \returns processed block of 64 bits
+ */
+uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3);
+
+/*! Implementation of the KGCORE algorithm (used by A5/3, A5/4, GEA3, GEA4 and ECSD)
+ *  \param[in] CA
+ *  \param[in] cb
+ *  \param[in] cc
+ *  \param[in] cd
+ *  \param[in] ck 8-bytes long key
+ *  \param[out] co cl-dependent
+ *  \param[in] cl
+ */
+void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl);
+
+/*! Expand key into set of subkeys - see TS 135 202 for details
+ *  \param[in] key (128 bits) as array of bytes
+ *  \param[out] KLi1 Expanded subkeys
+ *  \param[out] KLi2 Expanded subkeys
+ *  \param[out] KOi1 Expanded subkeys
+ *  \param[out] KOi2 Expanded subkeys
+ *  \param[out] KOi3 Expanded subkeys
+ *  \param[out] KIi1 Expanded subkeys
+ *  \param[out] KIi2 Expanded subkeys
+ *  \param[out] KIi3 Expanded subkeys
+ */
+void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3);
diff --git a/lib/decoding/osmocom/gsm/protocol/gsm_04_08.h b/lib/decoding/osmocom/gsm/protocol/gsm_04_08.h
new file mode 100644
index 0000000..10763f9
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/protocol/gsm_04_08.h
@@ -0,0 +1,1683 @@
+/*! \file gsm_04_08.h
+ * GSM TS 04.08  definitions. */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/endian.h>
+
+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 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
+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));
+#else
+struct gsm48_range_1024 {
+	uint8_t	 form_id:5,
+		f0:1,
+		w1_hi:2;
+	uint8_t	w1_lo;
+	uint8_t	w2_hi;
+	uint8_t	 w2_lo:1,
+		w3_hi:7;
+	uint8_t	 w3_lo:2,
+		w4_hi:6;
+	uint8_t	 w4_lo:2,
+		w5_hi:6;
+	uint8_t	 w5_lo:2,
+		w6_hi:6;
+	uint8_t	 w6_lo:2,
+		w7_hi:6;
+	uint8_t	 w7_lo:2,
+		w8_hi:6;
+	uint8_t	 w8_lo:1,
+		w9:7;
+	uint8_t	 w10:7,
+		w11_hi:1;
+	uint8_t	 w11_lo:6,
+		w12_hi:2;
+	uint8_t	 w12_lo:5,
+		w13_hi:3;
+	uint8_t	 w13_lo:4,
+		w14_hi:4;
+	uint8_t	 w14_lo:3,
+		w15_hi:5;
+	uint8_t	 w15_lo:2,
+		w16:6;
+} __attribute__ ((packed));
+#endif
+
+/* Chapter 10.5.2.1b.4 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
+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));
+#else
+struct gsm48_range_512 {
+	uint8_t	 form_id:7,
+		orig_arfcn_hi:1;
+	uint8_t	orig_arfcn_mid;
+	uint8_t	 orig_arfcn_lo:1,
+		w1_hi:7;
+	uint8_t	 w1_lo:2,
+		w2_hi:6;
+	uint8_t	 w2_lo:2,
+		w3_hi:6;
+	uint8_t	 w3_lo:2,
+		w4_hi:6;
+	uint8_t	 w4_lo:1,
+		w5:7;
+	uint8_t	 w6:7,
+		w7_hi:1;
+	uint8_t	 w7_lo:6,
+		w8_hi:2;
+	uint8_t	 w8_lo:4,
+		w9_hi:4;
+	uint8_t	 w9_lo:2,
+		w10:6;
+	uint8_t	 w11:6,
+		w12_hi:2;
+	uint8_t	 w12_lo:4,
+		w13_hi:4;
+	uint8_t	 w13_lo:2,
+		w14:6;
+	uint8_t	 w15:6,
+		w16_hi:2;
+	uint8_t	 w16_lo:3,
+		w17:5;
+} __attribute__ ((packed));
+#endif
+
+/* Chapter 10.5.2.1b.5 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
+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));
+#else
+struct gsm48_range_256 {
+	uint8_t	 form_id:7,
+		orig_arfcn_hi:1;
+	uint8_t	orig_arfcn_mid;
+	uint8_t	 orig_arfcn_lo:1,
+		w1_hi:7;
+	uint8_t	 w1_lo:1,
+		w2:7;
+	uint8_t	 w3:7,
+		w4_hi:1;
+	uint8_t	 w4_lo:5,
+		w5_hi:3;
+	uint8_t	 w5_lo:3,
+		w6_hi:5;
+	uint8_t	 w6_lo:1,
+		 w7:6,
+		w8_hi:1;
+	uint8_t	 w8_lo:4,
+		w9_hi:4;
+	uint8_t	 w9_lo:1,
+		 w10:5,
+		w11_hi:2;
+	uint8_t	 w11_lo:3,
+		w12:5;
+	uint8_t	 w13:5,
+		w14_hi:3;
+	uint8_t	 w14_lo:2,
+		 w15:5,
+		w16_hi:1;
+	uint8_t	 w16_lo:3,
+		 w17:4,
+		w18_hi:1;
+	uint8_t	 w18_lo:3,
+		 w19:4,
+		w20_hi:1;
+	uint8_t	 w20_lo:3,
+		 w21:4,
+		spare:1;
+} __attribute__ ((packed));
+#endif
+
+/* Chapter 10.5.2.1b.6 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
+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));
+#else
+struct gsm48_range_128 {
+	uint8_t	 form_id:7,
+		orig_arfcn_hi:1;
+	uint8_t	orig_arfcn_mid;
+	uint8_t	 orig_arfcn_lo:1,
+		w1:7;
+	uint8_t	 w2:6,
+		w3_hi:2;
+	uint8_t	 w3_lo:4,
+		w4_hi:4;
+	uint8_t	 w4_lo:1,
+		 w5:5,
+		w6_hi:2;
+	uint8_t	 w6_lo:3,
+		w7:5;
+	uint8_t	 w8:4,
+		w9:4;
+	uint8_t	 w10:4,
+		w11:4;
+	uint8_t	 w12:4,
+		w13:4;
+	uint8_t	 w14:4,
+		w15:4;
+	uint8_t	 w16:3,
+		 w17:3,
+		w18_hi:2;
+	uint8_t	 w18_lo:1,
+		 w19:3,
+		 w20:3,
+		w21_hi:1;
+	uint8_t	 w21_lo:2,
+		 w22:3,
+		w23:3;
+	uint8_t	 w24:3,
+		 w25:3,
+		w26_hi:2;
+	uint8_t	 w26_lo:1,
+		 w27:3,
+		 w28:3,
+		spare:1;
+} __attribute__ ((packed));
+#endif
+
+/* 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; /* ACC 8-15 barred flags */
+	uint8_t t3; /* ACC 0-7 barred flags */
+} __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	= 0x13,
+};
+
+extern const struct value_string gsm48_chan_mode_names[];
+
+/* 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 9.1.13b GPRS suspension request */
+struct gsm48_gprs_susp_req {
+	uint32_t tlli;
+	uint8_t ra_id[6];
+	uint8_t cause;
+	uint8_t options[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));
+
+/* 3GPP TS 44.018 Section 10.5.2.11 Control Channel Description */
+struct gsm48_control_channel_descr {
+	uint8_t ccch_conf :3,
+		bs_ag_blks_res :3,
+		att :1,
+		mscr :1;
+	uint8_t bs_pa_mfrms : 3,
+		spare_1 :2,
+		cbq3 :2,
+		spare_2 :1;
+	uint8_t t3212;
+} __attribute__ ((packed));
+
+enum gsm48_dtx_mode {
+	GSM48_DTX_MAY_BE_USED,
+	GSM48_DTX_SHALL_BE_USED,
+	GSM48_DTX_SHALL_NOT_BE_USED
+};
+
+/* Cell Options for SI6, SACCH (10.5.2.3a.2) or SI3, BCCH (Table 10.5.2.3.1),
+   3GPP TS 44.018 */
+struct gsm48_cell_options {
+	uint8_t radio_link_timeout:4,
+		 dtx:2,
+		 pwrc:1,
+	/* either DN-IND or top bit of DTX IND */
+		 d: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.34a System information Type 2quater */
+struct gsm48_system_information_type_2quater {
+	struct gsm48_system_information_type_header header;
+	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 + 3GPP TS 24.007 11.2.3.1.1 */
+#define GSM48_PDISC_GROUP_CC	0x00
+#define GSM48_PDISC_BCAST_CC	0x01
+#define GSM48_PDISC_PDSS1	0x02	/* 04.07 only */
+#define GSM48_PDISC_CC		0x03
+#define GSM48_PDISC_PDSS2	0x04	/* 04.07 only */
+#define GSM48_PDISC_GTTP	0x04	/* 24.007 only */
+#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_EXTEND	0x0e
+#define GSM48_PDISC_TEST	0x0f	/* as per 11.10, 04.14 */
+#define GSM48_PDISC_MASK	0x0f
+#define GSM48_PDISC_USSD	0x11
+
+extern const struct value_string gsm48_pdisc_names[];
+static inline const char *gsm48_pdisc_name(uint8_t val)
+{ return get_value_string(gsm48_pdisc_names, val); }
+
+bool gsm48_hdr_gmm_cipherable(const struct gsm48_hdr *hdr);
+
+static inline uint8_t gsm48_hdr_pdisc(const struct gsm48_hdr *hdr)
+{
+	/*
+	 * 3GPP TS 24.007 version 12.0.0 Release 12,
+	 * 11.2.3.1.1 Protocol discriminator
+	 */
+	uint8_t pdisc = hdr->proto_discr & GSM48_PDISC_MASK;
+	if (pdisc == GSM48_PDISC_EXTEND)
+		return hdr->proto_discr;
+	return pdisc;
+}
+
+static inline uint8_t gsm48_hdr_trans_id(const struct gsm48_hdr *hdr)
+{
+	/*
+	 * 3GPP TS 24.007 version 12.0.0 Release 12,
+	 * 11.2.3.1.3 Transaction identifier
+	 */
+	return (hdr->proto_discr & 0xf0) >> 4;
+}
+
+#define GSM48_TA_INVALID 220
+
+/*! Check if TA is valid according to 3GPP TS 44.018 § 10.5.2.40
+ *  \param[in] ta Timing Advance value
+ *  \returns true if ta is valid, false otherwise
+ *  Note: Rules for GSM400 band are ignored as it's not implemented in practice.
+ */
+static inline bool gsm48_ta_is_valid(uint8_t ta)
+{
+	return (ta < 64);
+}
+
+static inline uint8_t gsm48_hdr_trans_id_flip_ti(const struct gsm48_hdr *hdr)
+{
+	return gsm48_hdr_trans_id(hdr) ^ 0x08;
+}
+
+static inline uint8_t gsm48_hdr_trans_id_no_ti(const struct gsm48_hdr *hdr)
+{
+	return gsm48_hdr_trans_id(hdr) & 0x07;
+}
+
+static inline uint8_t gsm48_hdr_msg_type_r98(const struct gsm48_hdr *hdr)
+{
+	/*
+	 * 3GPP TS 24.007 version 12.0.0 Release 12,
+	 * 11.2.3.2.1 Message type octet (when accessing Release 98 and older
+	 * networks only)
+	 */
+	switch (gsm48_hdr_pdisc(hdr)) {
+	case GSM48_PDISC_MM:
+	case GSM48_PDISC_CC:
+	case GSM48_PDISC_NC_SS:
+	case GSM48_PDISC_GROUP_CC:
+	case GSM48_PDISC_BCAST_CC:
+	case GSM48_PDISC_LOC:
+		return hdr->msg_type & 0x3f;
+	default:
+		return hdr->msg_type;
+	}
+}
+
+static inline uint8_t gsm48_hdr_msg_type_r99(const struct gsm48_hdr *hdr)
+{
+	/*
+	 * 3GPP TS 24.007 version 12.0.0 Release 12,
+	 * 11.2.3.2.2 Message type octet (when accessing Release 99 and newer
+	 * networks)
+	 */
+	switch (gsm48_hdr_pdisc(hdr)) {
+	case GSM48_PDISC_MM:
+	case GSM48_PDISC_CC:
+	case GSM48_PDISC_NC_SS:
+		return hdr->msg_type & 0x3f;
+	case GSM48_PDISC_GROUP_CC:
+	case GSM48_PDISC_BCAST_CC:
+	case GSM48_PDISC_LOC:
+		return hdr->msg_type & 0x3f;
+	default:
+		return hdr->msg_type;
+	}
+}
+
+void gsm48_set_dtx(struct gsm48_cell_options *op, enum gsm48_dtx_mode full,
+		   enum gsm48_dtx_mode half, bool is_bcch);
+
+#define gsm48_hdr_msg_type gsm48_hdr_msg_type_r99
+
+/* 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_DTM_ASS_FAIL	0x48
+#define GSM48_MT_RR_DTM_REJECT		0x49
+#define GSM48_MT_RR_DTM_REQUEST		0x4A
+#define GSM48_MT_RR_PACKET_ASS		0x4B
+
+#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_HANDO_INFO		0x2d
+#define GSM48_MT_RR_DTM_ASS_CMD		0x4c
+
+#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 /* (Reserved) */
+#define GSM48_MT_RR_NOTIF_RESP		0x26
+#define GSM48_MT_RR_PACKET_NOTIF	0x4e
+#define GSM48_MT_RR_UTRAN_CLSM_CHG	0x60
+#define GSM48_MT_RR_CDMA2K_CLSM_CHG	0x62
+#define GSM48_MT_RR_IS_TO_UTRAN_HANDO	0x63
+#define GSM48_MT_RR_IS_TO_CDMA2K_HANDO	0x64
+
+#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_2quater	0x07
+#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_SYSINFO_18		0x40
+#define GSM48_MT_RR_SYSINFO_19		0x41
+#define GSM48_MT_RR_SYSINFO_20		0x42
+
+#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_DTM_INFO		0x4d
+
+#define GSM48_MT_RR_VGCS_UPL_GRANT	0x09
+#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_AUTH_FAIL		0x1c
+#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
+
+extern const struct value_string gsm48_rr_msgtype_names[];
+extern const struct value_string gsm48_mm_msgtype_names[];
+extern const struct value_string gsm48_cc_msgtype_names[];
+const char *gsm48_pdisc_msgtype_name(uint8_t pdisc, uint8_t msg_type);
+
+/* 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_NET_DST	0x49	/* 10.5.3.12 [24.008] */
+
+#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_AUTN		0x20
+#define GSM48_IE_AUTH_RES_EXT	0x21
+#define GSM48_IE_AUTS		0x22
+#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	= 0x08,
+	GSM48_RR_CAUSE_CHAN_MODE_UNACCT	= 0x09,
+	GSM48_RR_CAUSE_FREQ_NOT_IMPL	= 0x0a,
+	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_SYNCH_FAILURE		= 21,
+	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,
+	CHREQ_T_PDCH_ONE_PHASE,
+	CHREQ_T_PDCH_TWO_PHASE,
+	_NUM_CHREQ_T,
+};
+
+/* 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		/* no spec default */
+#define GSM48_T310	30, 0		/* no spec default */
+#define GSM48_T313	30, 0		/* no spec default */
+#define GSM48_T323	30, 0
+#define GSM48_T331	30, 0		/* no spec default */
+#define GSM48_T333	30, 0		/* no spec default */
+#define GSM48_T334	25, 0 /* min 15s */
+#define GSM48_T338	30, 0		/* no spec default */
+#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,
+};
+
+/* GSM 04.08 Bearer Capability: Rate Adaption */
+enum gsm48_bcap_ra {
+	GSM48_BCAP_RA_NONE	= 0,
+	GSM48_BCAP_RA_V110_X30	= 1,
+	GSM48_BCAP_RA_X31	= 2,
+	GSM48_BCAP_RA_OTHER	= 3,
+};
+
+/* GSM 04.08 Bearer Capability: Signalling access protocol */
+enum gsm48_bcap_sig_access {
+	GSM48_BCAP_SA_I440_I450	= 1,
+	GSM48_BCAP_SA_X21	= 2,
+	GSM48_BCAP_SA_X28_DP_IN	= 3,
+	GSM48_BCAP_SA_X28_DP_UN	= 4,
+	GSM48_BCAP_SA_X28_NDP	= 5,
+	GSM48_BCAP_SA_X32	= 6,
+};
+
+/* GSM 04.08 Bearer Capability: User Rate */
+enum gsm48_bcap_user_rate {
+	GSM48_BCAP_UR_300	= 1,
+	GSM48_BCAP_UR_1200	= 2,
+	GSM48_BCAP_UR_2400	= 3,
+	GSM48_BCAP_UR_4800	= 4,
+	GSM48_BCAP_UR_9600	= 5,
+	GSM48_BCAP_UR_12000	= 6,
+	GSM48_BCAP_UR_1200_75	= 7,
+};
+
+/* GSM 04.08 Bearer Capability: Parity */
+enum gsm48_bcap_parity {
+	GSM48_BCAP_PAR_ODD	= 0,
+	GSM48_BCAP_PAR_EVEN	= 2,
+	GSM48_BCAP_PAR_NONE	= 3,
+	GSM48_BCAP_PAR_ZERO	= 4,
+	GSM48_BCAP_PAR_ONE	= 5,
+};
+
+/* GSM 04.08 Bearer Capability: Intermediate Rate */
+enum gsm48_bcap_interm_rate {
+	GSM48_BCAP_IR_8k	= 2,
+	GSM48_BCAP_IR_16k	= 3,
+};
+
+/* GSM 04.08 Bearer Capability: Transparency */
+enum gsm48_bcap_transp {
+	GSM48_BCAP_TR_TRANSP	= 0,
+	GSM48_BCAP_TR_RLP	= 1,
+	GSM48_BCAP_TR_TR_PREF	= 2,
+	GSM48_BCAP_TR_RLP_PREF	= 3,
+};
+
+/* GSM 04.08 Bearer Capability: Modem Type */
+enum gsm48_bcap_modem_type {
+	GSM48_BCAP_MT_NONE	= 0,
+	GSM48_BCAP_MT_V21	= 1,
+	GSM48_BCAP_MT_V22	= 2,
+	GSM48_BCAP_MT_V22bis	= 3,
+	GSM48_BCAP_MT_V23	= 4,
+	GSM48_BCAP_MT_V26ter	= 5,
+	GSM48_BCAP_MT_V32	= 6,
+	GSM48_BCAP_MT_UNDEF	= 7,
+	GSM48_BCAP_MT_AUTO_1	= 8,
+};
+
+/*! GSM 04.08 Bearer Capability: Speech Version Indication
+ *  (See also 3GPP TS 24.008, Table 10.5.103) */
+enum gsm48_bcap_speech_ver {
+	GSM48_BCAP_SV_FR	= 0,	/*!< GSM FR V1 (GSM FR) */
+	GSM48_BCAP_SV_HR	= 1,	/*!< GSM HR V1 (GSM HR) */
+	GSM48_BCAP_SV_EFR	= 2,	/*!< GSM FR V2 (GSM EFR) */
+	GSM48_BCAP_SV_AMR_F	= 4,	/*!< GSM FR V3 (FR AMR) */
+	GSM48_BCAP_SV_AMR_H	= 5,	/*!< GSM HR V3 (HR_AMR) */
+	GSM48_BCAP_SV_AMR_OFW	= 6,	/*!< GSM FR V4 (OFR AMR-WB) */
+	GSM48_BCAP_SV_AMR_OHW	= 7,	/*!< GSM HR V4 (OHR AMR-WB) */
+	GSM48_BCAP_SV_AMR_FW	= 8,	/*!< GSM FR V5 (FR AMR-WB) */
+	GSM48_BCAP_SV_AMR_OH	= 11,	/*!< GSM HR V6 (OHR AMR) */
+};
+
+#define GSM48_TMSI_LEN	5
+#define GSM48_MID_TMSI_LEN	(GSM48_TMSI_LEN + 2)
+#define GSM48_MI_SIZE 32
+
+/* 3GPP TS 24.008 § 10.5.5.15 Routing area identification */
+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
+
+/* Table 10.5.118 / 3GPP TS 24.008 Section 10.5.4.7 */
+enum gsm48_type_of_number {
+	GSM48_TON_UNKNOWN	= 0,
+	GSM48_TON_INTERNATIONAL	= 1,
+	GSM48_TON_NATIONAL	= 2,
+	GSM48_TON_NET_SPEC	= 3,
+	GSM48_TON_SHORT_CODE	= 4,
+	/* reserved */
+};
+
+/* Table 10.5.118 / 3GPP TS 24.008 Section 10.5.4.7 */
+enum gsm48_numbering_plan {
+	GSM48_NPI_UNKNOWN	= 0,
+	GSM48_NPI_ISDN_E164	= 1,
+	GSM48_NPI_DATA_X121	= 3,
+	GSM48_NPI_TELEX_F69	= 4,
+	GSM48_NPI_NATIONAL	= 8,
+	GSM48_NPI_PRIVATE	= 9,
+	GSM48_NPI_CTS		= 11,
+	/* reserved */
+};