From 9e2e8358a3cd5ec0a34906e672b227a90e558306 Mon Sep 17 00:00:00 2001 From: Piotr Krysik Date: Tue, 27 Feb 2018 12:16:25 +0100 Subject: Portability fix: Adding local partial copy of libosmocore (TODO: minimize it) --- lib/decoding/CMakeLists.txt | 4 + lib/decoding/osmocom/codec/CMakeLists.txt | 6 + lib/decoding/osmocom/codec/codec.h | 81 + lib/decoding/osmocom/codec/gsm610.c | 336 ++++ lib/decoding/osmocom/codec/gsm610_bits.h | 272 ++++ lib/decoding/osmocom/codec/gsm620.c | 297 ++++ lib/decoding/osmocom/codec/gsm660.c | 259 +++ lib/decoding/osmocom/codec/gsm690.c | 318 ++++ lib/decoding/osmocom/coding/CMakeLists.txt | 1 - lib/decoding/osmocom/coding/gsm0503.h | 212 ++- lib/decoding/osmocom/coding/gsm0503_coding.c | 492 ++++-- lib/decoding/osmocom/coding/gsm0503_coding.h | 91 +- lib/decoding/osmocom/coding/gsm0503_interleaving.c | 182 ++- lib/decoding/osmocom/coding/gsm0503_interleaving.h | 38 +- lib/decoding/osmocom/coding/gsm0503_mapping.c | 31 +- lib/decoding/osmocom/coding/gsm0503_mapping.h | 30 +- lib/decoding/osmocom/coding/gsm0503_parity.c | 43 +- lib/decoding/osmocom/coding/gsm0503_parity.h | 29 +- lib/decoding/osmocom/coding/gsm0503_tables.c | 19 +- lib/decoding/osmocom/coding/gsm0503_tables.h | 28 +- lib/decoding/osmocom/core/CMakeLists.txt | 11 + lib/decoding/osmocom/core/bit16gen.h | 105 ++ lib/decoding/osmocom/core/bit32gen.h | 105 ++ lib/decoding/osmocom/core/bit64gen.h | 105 ++ lib/decoding/osmocom/core/bits.c | 311 ++++ lib/decoding/osmocom/core/bits.h | 122 ++ lib/decoding/osmocom/core/bitvec.c | 708 ++++++++ lib/decoding/osmocom/core/bitvec.h | 87 + lib/decoding/osmocom/core/conv.c | 644 ++++++++ lib/decoding/osmocom/core/conv.h | 139 ++ lib/decoding/osmocom/core/conv_acc.c | 720 +++++++++ lib/decoding/osmocom/core/conv_acc_generic.c | 213 +++ lib/decoding/osmocom/core/crc16gen.c | 120 ++ lib/decoding/osmocom/core/crc16gen.h | 56 + lib/decoding/osmocom/core/crc32gen.h | 56 + lib/decoding/osmocom/core/crc64gen.c | 120 ++ lib/decoding/osmocom/core/crc64gen.h | 56 + lib/decoding/osmocom/core/crc8gen.c | 120 ++ lib/decoding/osmocom/core/crc8gen.h | 56 + lib/decoding/osmocom/core/crcgen.h | 34 + lib/decoding/osmocom/core/defs.h | 53 + lib/decoding/osmocom/core/endian.h | 62 + lib/decoding/osmocom/core/linuxlist.h | 409 +++++ lib/decoding/osmocom/core/panic.c | 100 ++ lib/decoding/osmocom/core/panic.h | 15 + lib/decoding/osmocom/core/utils.h | 129 ++ lib/decoding/osmocom/crypt/auth.h | 111 ++ lib/decoding/osmocom/gsm/CMakeLists.txt | 6 + lib/decoding/osmocom/gsm/a5.c | 446 ++++++ lib/decoding/osmocom/gsm/a5.h | 55 + lib/decoding/osmocom/gsm/auth_core.c | 252 +++ lib/decoding/osmocom/gsm/gsm48_ie.c | 1247 +++++++++++++++ lib/decoding/osmocom/gsm/gsm48_ie.h | 116 ++ lib/decoding/osmocom/gsm/kasumi.c | 188 +++ lib/decoding/osmocom/gsm/kasumi.h | 48 + lib/decoding/osmocom/gsm/protocol/gsm_04_08.h | 1683 ++++++++++++++++++++ 56 files changed, 11241 insertions(+), 336 deletions(-) create mode 100644 lib/decoding/osmocom/codec/CMakeLists.txt create mode 100644 lib/decoding/osmocom/codec/codec.h create mode 100644 lib/decoding/osmocom/codec/gsm610.c create mode 100644 lib/decoding/osmocom/codec/gsm610_bits.h create mode 100644 lib/decoding/osmocom/codec/gsm620.c create mode 100644 lib/decoding/osmocom/codec/gsm660.c create mode 100644 lib/decoding/osmocom/codec/gsm690.c create mode 100644 lib/decoding/osmocom/core/CMakeLists.txt create mode 100644 lib/decoding/osmocom/core/bit16gen.h create mode 100644 lib/decoding/osmocom/core/bit32gen.h create mode 100644 lib/decoding/osmocom/core/bit64gen.h create mode 100644 lib/decoding/osmocom/core/bits.c create mode 100644 lib/decoding/osmocom/core/bits.h create mode 100644 lib/decoding/osmocom/core/bitvec.c create mode 100644 lib/decoding/osmocom/core/bitvec.h create mode 100644 lib/decoding/osmocom/core/conv.c create mode 100644 lib/decoding/osmocom/core/conv.h create mode 100644 lib/decoding/osmocom/core/conv_acc.c create mode 100644 lib/decoding/osmocom/core/conv_acc_generic.c create mode 100644 lib/decoding/osmocom/core/crc16gen.c create mode 100644 lib/decoding/osmocom/core/crc16gen.h create mode 100644 lib/decoding/osmocom/core/crc32gen.h create mode 100644 lib/decoding/osmocom/core/crc64gen.c create mode 100644 lib/decoding/osmocom/core/crc64gen.h create mode 100644 lib/decoding/osmocom/core/crc8gen.c create mode 100644 lib/decoding/osmocom/core/crc8gen.h create mode 100644 lib/decoding/osmocom/core/crcgen.h create mode 100644 lib/decoding/osmocom/core/defs.h create mode 100644 lib/decoding/osmocom/core/endian.h create mode 100644 lib/decoding/osmocom/core/linuxlist.h create mode 100644 lib/decoding/osmocom/core/panic.c create mode 100644 lib/decoding/osmocom/core/panic.h create mode 100644 lib/decoding/osmocom/core/utils.h create mode 100644 lib/decoding/osmocom/crypt/auth.h create mode 100644 lib/decoding/osmocom/gsm/CMakeLists.txt create mode 100644 lib/decoding/osmocom/gsm/a5.c create mode 100644 lib/decoding/osmocom/gsm/a5.h create mode 100644 lib/decoding/osmocom/gsm/auth_core.c create mode 100644 lib/decoding/osmocom/gsm/gsm48_ie.c create mode 100644 lib/decoding/osmocom/gsm/gsm48_ie.h create mode 100644 lib/decoding/osmocom/gsm/kasumi.c create mode 100644 lib/decoding/osmocom/gsm/kasumi.h create mode 100644 lib/decoding/osmocom/gsm/protocol/gsm_04_08.h diff --git a/lib/decoding/CMakeLists.txt b/lib/decoding/CMakeLists.txt index b1c49e7..cbf2a8e 100644 --- a/lib/decoding/CMakeLists.txt +++ b/lib/decoding/CMakeLists.txt @@ -18,6 +18,10 @@ # Boston, MA 02110-1301, USA. add_subdirectory(osmocom/coding) +add_subdirectory(osmocom/core) +add_subdirectory(osmocom/codec) +add_subdirectory(osmocom/gsm) +add_subdirectory(osmocom/crypt) add_subdirectory(openbts) add_sources( diff --git a/lib/decoding/osmocom/codec/CMakeLists.txt b/lib/decoding/osmocom/codec/CMakeLists.txt new file mode 100644 index 0000000..d5c3997 --- /dev/null +++ b/lib/decoding/osmocom/codec/CMakeLists.txt @@ -0,0 +1,6 @@ +add_sources( +gsm610.c +gsm620.c +gsm660.c +gsm690.c +) diff --git a/lib/decoding/osmocom/codec/codec.h b/lib/decoding/osmocom/codec/codec.h new file mode 100644 index 0000000..6a1bf9f --- /dev/null +++ b/lib/decoding/osmocom/codec/codec.h @@ -0,0 +1,81 @@ +/*! \file codec.h */ + +#pragma once + +#include +#include + +#include + +/* TS 101318 Chapter 5.1: 260 bits + 4bit sig */ +#define GSM_FR_BYTES 33 +/* TS 101318 Chapter 5.2: 112 bits, no sig */ +#define GSM_HR_BYTES 14 +/* TS 101318 Chapter 5.3: 244 bits + 4bit sig */ +#define GSM_EFR_BYTES 31 + +extern const uint16_t gsm610_bitorder[]; /* FR */ +extern const uint16_t gsm620_unvoiced_bitorder[]; /* HR unvoiced */ +extern const uint16_t gsm620_voiced_bitorder[]; /* HR voiced */ +extern const uint16_t gsm660_bitorder[]; /* EFR */ + +extern const uint16_t gsm690_12_2_bitorder[]; /* AMR 12.2 kbits */ +extern const uint16_t gsm690_10_2_bitorder[]; /* AMR 10.2 kbits */ +extern const uint16_t gsm690_7_95_bitorder[]; /* AMR 7.95 kbits */ +extern const uint16_t gsm690_7_4_bitorder[]; /* AMR 7.4 kbits */ +extern const uint16_t gsm690_6_7_bitorder[]; /* AMR 6.7 kbits */ +extern const uint16_t gsm690_5_9_bitorder[]; /* AMR 5.9 kbits */ +extern const uint16_t gsm690_5_15_bitorder[]; /* AMR 5.15 kbits */ +extern const uint16_t gsm690_4_75_bitorder[]; /* AMR 4.75 kbits */ + +extern const struct value_string osmo_amr_type_names[]; + +enum osmo_amr_type { + AMR_4_75 = 0, + AMR_5_15 = 1, + AMR_5_90 = 2, + AMR_6_70 = 3, + AMR_7_40 = 4, + AMR_7_95 = 5, + AMR_10_2 = 6, + AMR_12_2 = 7, + AMR_SID = 8, + AMR_GSM_EFR_SID = 9, + AMR_TDMA_EFR_SID = 10, + AMR_PDC_EFR_SID = 11, + AMR_NO_DATA = 15, +}; + +enum osmo_amr_quality { + AMR_BAD = 0, + AMR_GOOD = 1 +}; + +/*! Check if given AMR Frame Type is a speech frame + * \param[in] ft AMR Frame Type + * \returns true if AMR with given Frame Type contains voice, false otherwise + */ +static inline bool osmo_amr_is_speech(enum osmo_amr_type ft) +{ + switch (ft) { + case AMR_4_75: + case AMR_5_15: + case AMR_5_90: + case AMR_6_70: + case AMR_7_40: + case AMR_7_95: + case AMR_10_2: + case AMR_12_2: + return true; + default: + return false; + } +} + +bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len); +bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len); +int osmo_amr_rtp_enc(uint8_t *payload, uint8_t cmr, enum osmo_amr_type ft, + enum osmo_amr_quality bfi); +int osmo_amr_rtp_dec(const uint8_t *payload, int payload_len, uint8_t *cmr, + int8_t *cmi, enum osmo_amr_type *ft, + enum osmo_amr_quality *bfi, int8_t *sti); diff --git a/lib/decoding/osmocom/codec/gsm610.c b/lib/decoding/osmocom/codec/gsm610.c new file mode 100644 index 0000000..a05eaba --- /dev/null +++ b/lib/decoding/osmocom/codec/gsm610.c @@ -0,0 +1,336 @@ +/*! \file gsm610.c + * GSM 06.10 - GSM FR codec. */ +/* + * (C) 2010 Sylvain Munaut + * + * 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 +#include + +#include +#include +#include + +/* GSM FR - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 2. + * It's also GSM 06.10 Table A.2.1a + * + * It converts between serial parameter output by the encoder and the + * order needed before channel encoding. + */ +const uint16_t gsm610_bitorder[260] = { + 0, /* LARc0:5 */ + 47, /* Xmaxc0:5 */ + 103, /* Xmaxc1:5 */ + 159, /* Xmaxc2:5 */ + 215, /* Xmaxc3:5 */ + 1, /* LARc0:4 */ + 6, /* LARc1:5 */ + 12, /* LARc2:4 */ + 2, /* LARc0:3 */ + 7, /* LARc1:4 */ + 13, /* LARc2:3 */ + 17, /* LARc3:4 */ + 36, /* Nc0:6 */ + 92, /* Nc1:6 */ + 148, /* Nc2:6 */ + 204, /* Nc3:6 */ + 48, /* Xmaxc0:4 */ + 104, /* Xmaxc1:4 */ + 160, /* Xmaxc2:4 */ + 216, /* Xmaxc3:4 */ + 8, /* LARc1:3 */ + 22, /* LARc4:3 */ + 26, /* LARc5:3 */ + 37, /* Nc0:5 */ + 93, /* Nc1:5 */ + 149, /* Nc2:5 */ + 205, /* Nc3:5 */ + 38, /* Nc0:4 */ + 94, /* Nc1:4 */ + 150, /* Nc2:4 */ + 206, /* Nc3:4 */ + 39, /* Nc0:3 */ + 95, /* Nc1:3 */ + 151, /* Nc2:3 */ + 207, /* Nc3:3 */ + 40, /* Nc0:2 */ + 96, /* Nc1:2 */ + 152, /* Nc2:2 */ + 208, /* Nc3:2 */ + 49, /* Xmaxc0:3 */ + 105, /* Xmaxc1:3 */ + 161, /* Xmaxc2:3 */ + 217, /* Xmaxc3:3 */ + 3, /* LARc0:2 */ + 18, /* LARc3:3 */ + 30, /* LARc6:2 */ + 41, /* Nc0:1 */ + 97, /* Nc1:1 */ + 153, /* Nc2:1 */ + 209, /* Nc3:1 */ + 23, /* LARc4:2 */ + 27, /* LARc5:2 */ + 43, /* bc0:1 */ + 99, /* bc1:1 */ + 155, /* bc2:1 */ + 211, /* bc3:1 */ + 42, /* Nc0:0 */ + 98, /* Nc1:0 */ + 154, /* Nc2:0 */ + 210, /* Nc3:0 */ + 45, /* Mc0:1 */ + 101, /* Mc1:1 */ + 157, /* Mc2:1 */ + 213, /* Mc3:1 */ + 4, /* LARc0:1 */ + 9, /* LARc1:2 */ + 14, /* LARc2:2 */ + 33, /* LARc7:2 */ + 19, /* LARc3:2 */ + 24, /* LARc4:1 */ + 31, /* LARc6:1 */ + 44, /* bc0:0 */ + 100, /* bc1:0 */ + 156, /* bc2:0 */ + 212, /* bc3:0 */ + 50, /* Xmaxc0:2 */ + 106, /* Xmaxc1:2 */ + 162, /* Xmaxc2:2 */ + 218, /* Xmaxc3:2 */ + 53, /* xmc0_0:2 */ + 56, /* xmc0_1:2 */ + 59, /* xmc0_2:2 */ + 62, /* xmc0_3:2 */ + 65, /* xmc0_4:2 */ + 68, /* xmc0_5:2 */ + 71, /* xmc0_6:2 */ + 74, /* xmc0_7:2 */ + 77, /* xmc0_8:2 */ + 80, /* xmc0_9:2 */ + 83, /* xmc0_10:2 */ + 86, /* xmc0_11:2 */ + 89, /* xmc0_12:2 */ + 109, /* xmc1_0:2 */ + 112, /* xmc1_1:2 */ + 115, /* xmc1_2:2 */ + 118, /* xmc1_3:2 */ + 121, /* xmc1_4:2 */ + 124, /* xmc1_5:2 */ + 127, /* xmc1_6:2 */ + 130, /* xmc1_7:2 */ + 133, /* xmc1_8:2 */ + 136, /* xmc1_9:2 */ + 139, /* xmc1_10:2 */ + 142, /* xmc1_11:2 */ + 145, /* xmc1_12:2 */ + 165, /* xmc2_0:2 */ + 168, /* xmc2_1:2 */ + 171, /* xmc2_2:2 */ + 174, /* xmc2_3:2 */ + 177, /* xmc2_4:2 */ + 180, /* xmc2_5:2 */ + 183, /* xmc2_6:2 */ + 186, /* xmc2_7:2 */ + 189, /* xmc2_8:2 */ + 192, /* xmc2_9:2 */ + 195, /* xmc2_10:2 */ + 198, /* xmc2_11:2 */ + 201, /* xmc2_12:2 */ + 221, /* xmc3_0:2 */ + 224, /* xmc3_1:2 */ + 227, /* xmc3_2:2 */ + 230, /* xmc3_3:2 */ + 233, /* xmc3_4:2 */ + 236, /* xmc3_5:2 */ + 239, /* xmc3_6:2 */ + 242, /* xmc3_7:2 */ + 245, /* xmc3_8:2 */ + 248, /* xmc3_9:2 */ + 251, /* xmc3_10:2 */ + 254, /* xmc3_11:2 */ + 257, /* xmc3_12:2 */ + 46, /* Mc0:0 */ + 102, /* Mc1:0 */ + 158, /* Mc2:0 */ + 214, /* Mc3:0 */ + 51, /* Xmaxc0:1 */ + 107, /* Xmaxc1:1 */ + 163, /* Xmaxc2:1 */ + 219, /* Xmaxc3:1 */ + 54, /* xmc0_0:1 */ + 57, /* xmc0_1:1 */ + 60, /* xmc0_2:1 */ + 63, /* xmc0_3:1 */ + 66, /* xmc0_4:1 */ + 69, /* xmc0_5:1 */ + 72, /* xmc0_6:1 */ + 75, /* xmc0_7:1 */ + 78, /* xmc0_8:1 */ + 81, /* xmc0_9:1 */ + 84, /* xmc0_10:1 */ + 87, /* xmc0_11:1 */ + 90, /* xmc0_12:1 */ + 110, /* xmc1_0:1 */ + 113, /* xmc1_1:1 */ + 116, /* xmc1_2:1 */ + 119, /* xmc1_3:1 */ + 122, /* xmc1_4:1 */ + 125, /* xmc1_5:1 */ + 128, /* xmc1_6:1 */ + 131, /* xmc1_7:1 */ + 134, /* xmc1_8:1 */ + 137, /* xmc1_9:1 */ + 140, /* xmc1_10:1 */ + 143, /* xmc1_11:1 */ + 146, /* xmc1_12:1 */ + 166, /* xmc2_0:1 */ + 169, /* xmc2_1:1 */ + 172, /* xmc2_2:1 */ + 175, /* xmc2_3:1 */ + 178, /* xmc2_4:1 */ + 181, /* xmc2_5:1 */ + 184, /* xmc2_6:1 */ + 187, /* xmc2_7:1 */ + 190, /* xmc2_8:1 */ + 193, /* xmc2_9:1 */ + 196, /* xmc2_10:1 */ + 199, /* xmc2_11:1 */ + 202, /* xmc2_12:1 */ + 222, /* xmc3_0:1 */ + 225, /* xmc3_1:1 */ + 228, /* xmc3_2:1 */ + 231, /* xmc3_3:1 */ + 234, /* xmc3_4:1 */ + 237, /* xmc3_5:1 */ + 240, /* xmc3_6:1 */ + 243, /* xmc3_7:1 */ + 246, /* xmc3_8:1 */ + 249, /* xmc3_9:1 */ + 252, /* xmc3_10:1 */ + 255, /* xmc3_11:1 */ + 258, /* xmc3_12:1 */ + 5, /* LARc0:0 */ + 10, /* LARc1:1 */ + 15, /* LARc2:1 */ + 28, /* LARc5:1 */ + 32, /* LARc6:0 */ + 34, /* LARc7:1 */ + 35, /* LARc7:0 */ + 16, /* LARc2:0 */ + 20, /* LARc3:1 */ + 21, /* LARc3:0 */ + 25, /* LARc4:0 */ + 52, /* Xmaxc0:0 */ + 108, /* Xmaxc1:0 */ + 164, /* Xmaxc2:0 */ + 220, /* Xmaxc3:0 */ + 55, /* xmc0_0:0 */ + 58, /* xmc0_1:0 */ + 61, /* xmc0_2:0 */ + 64, /* xmc0_3:0 */ + 67, /* xmc0_4:0 */ + 70, /* xmc0_5:0 */ + 73, /* xmc0_6:0 */ + 76, /* xmc0_7:0 */ + 79, /* xmc0_8:0 */ + 82, /* xmc0_9:0 */ + 85, /* xmc0_10:0 */ + 88, /* xmc0_11:0 */ + 91, /* xmc0_12:0 */ + 111, /* xmc1_0:0 */ + 114, /* xmc1_1:0 */ + 117, /* xmc1_2:0 */ + 120, /* xmc1_3:0 */ + 123, /* xmc1_4:0 */ + 126, /* xmc1_5:0 */ + 129, /* xmc1_6:0 */ + 132, /* xmc1_7:0 */ + 135, /* xmc1_8:0 */ + 138, /* xmc1_9:0 */ + 141, /* xmc1_10:0 */ + 144, /* xmc1_11:0 */ + 147, /* xmc1_12:0 */ + 167, /* xmc2_0:0 */ + 170, /* xmc2_1:0 */ + 173, /* xmc2_2:0 */ + 176, /* xmc2_3:0 */ + 179, /* xmc2_4:0 */ + 182, /* xmc2_5:0 */ + 185, /* xmc2_6:0 */ + 188, /* xmc2_7:0 */ + 191, /* xmc2_8:0 */ + 194, /* xmc2_9:0 */ + 197, /* xmc2_10:0 */ + 200, /* xmc2_11:0 */ + 203, /* xmc2_12:0 */ + 223, /* xmc3_0:0 */ + 226, /* xmc3_1:0 */ + 229, /* xmc3_2:0 */ + 232, /* xmc3_3:0 */ + 235, /* xmc3_4:0 */ + 238, /* xmc3_5:0 */ + 241, /* xmc3_6:0 */ + 244, /* xmc3_7:0 */ + 247, /* xmc3_8:0 */ + 250, /* xmc3_9:0 */ + 253, /* xmc3_10:0 */ + 256, /* xmc3_11:0 */ + 259, /* xmc3_12:0 */ + 11, /* LARc1:0 */ + 29, /* LARc5:0 */ +}; + +/*! Check whether RTP frame contains FR SID code word according to + * TS 101 318 §5.1.2 + * \param[in] rtp_payload Buffer with RTP payload + * \param[in] payload_len Length of payload + * \returns true if code word is found, false otherwise + */ +bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len) +{ + struct bitvec bv; + uint16_t i, z_bits[] = { 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, + 75, 76, 78, 79, 81, 82, 84, 85, 87, 88, 90, 91, + 93, 94, 113, 114, 116, 117, 119, 120, 122, 123, + 125, 126, 128, 129, 131, 132, 134, 135, 137, + 138, 140, 141, 143, 144, 146, 147, 149, 150, + 169, 170, 172, 173, 175, 176, 178, 179, 181, + 182, 184, 185, 187, 188, 190, 191, 193, 194, + 196, 197, 199, 200, 202, 203, 205, 206, 225, + 226, 228, 229, 231, 232, 234, 235, 237, 240, + 243, 246, 249, 252, 255, 258, 261 }; + + /* signature does not match Full Rate SID */ + if ((rtp_payload[0] >> 4) != 0xD) + return false; + + bv.data = (uint8_t *) rtp_payload; + bv.data_len = payload_len; + + /* code word is all 0 at given bits, numbered from 1 */ + for (i = 0; i < ARRAY_SIZE(z_bits); i++) + if (bitvec_get_bit_pos(&bv, z_bits[i]) != ZERO) + return false; + + return true; +} diff --git a/lib/decoding/osmocom/codec/gsm610_bits.h b/lib/decoding/osmocom/codec/gsm610_bits.h new file mode 100644 index 0000000..cef4cf4 --- /dev/null +++ b/lib/decoding/osmocom/codec/gsm610_bits.h @@ -0,0 +1,272 @@ +/*! \file gsm610_bits.h */ + +#pragma once + +/* This enumeration describs a GSM-FR (GSM 06.10) frame in ints RTP bit order + * representation. See also RFC 3551 Table 3: GSM payload format */ +enum gsm610_rtp_bit_offsets { + GSM610_RTP_SIGNATURE_0, + GSM610_RTP_SIGNATURE_1, + GSM610_RTP_SIGNATURE_2, + GSM610_RTP_SIGNATURE_3, + GSM610_RTP_LARC0_0, + GSM610_RTP_LARC0_1, + GSM610_RTP_LARC0_2, + GSM610_RTP_LARC0_3, + GSM610_RTP_LARC0_4, + GSM610_RTP_LARC0_5, + GSM610_RTP_LARC1_0, + GSM610_RTP_LARC1_1, + GSM610_RTP_LARC1_2, + GSM610_RTP_LARC1_3, + GSM610_RTP_LARC1_4, + GSM610_RTP_LARC1_5, + GSM610_RTP_LARC2_0, + GSM610_RTP_LARC2_1, + GSM610_RTP_LARC2_2, + GSM610_RTP_LARC2_3, + GSM610_RTP_LARC2_4, + GSM610_RTP_LARC3_0, + GSM610_RTP_LARC3_1, + GSM610_RTP_LARC3_2, + GSM610_RTP_LARC3_3, + GSM610_RTP_LARC3_4, + GSM610_RTP_LARC4_0, + GSM610_RTP_LARC4_1, + GSM610_RTP_LARC4_2, + GSM610_RTP_LARC4_3, + GSM610_RTP_LARC5_0, + GSM610_RTP_LARC5_1, + GSM610_RTP_LARC5_2, + GSM610_RTP_LARC5_3, + GSM610_RTP_LARC6_0, + GSM610_RTP_LARC6_1, + GSM610_RTP_LARC6_2, + GSM610_RTP_LARC7_0, + GSM610_RTP_LARC7_1, + GSM610_RTP_LARC7_2, + GSM610_RTP_NC0_0, + GSM610_RTP_NC0_1, + GSM610_RTP_NC0_2, + GSM610_RTP_NC0_3, + GSM610_RTP_NC0_4, + GSM610_RTP_NC0_5, + GSM610_RTP_NC0_6, + GSM610_RTP_BC0_0, + GSM610_RTP_BC0_1, + GSM610_RTP_MC0_0, + GSM610_RTP_MC0_1, + GSM610_RTP_XMAXC00, + GSM610_RTP_XMAXC01, + GSM610_RTP_XMAXC02, + GSM610_RTP_XMAXC03, + GSM610_RTP_XMAXC04, + GSM610_RTP_XMAXC05, + GSM610_RTP_XMC0_0, + GSM610_RTP_XMC0_1, + GSM610_RTP_XMC0_2, + GSM610_RTP_XMC1_0, + GSM610_RTP_XMC1_1, + GSM610_RTP_XMC1_2, + GSM610_RTP_XMC2_0, + GSM610_RTP_XMC2_1, + GSM610_RTP_XMC2_2, + GSM610_RTP_XMC3_0, + GSM610_RTP_XMC3_1, + GSM610_RTP_XMC3_2, + GSM610_RTP_XMC4_0, + GSM610_RTP_XMC4_1, + GSM610_RTP_XMC4_2, + GSM610_RTP_XMC5_0, + GSM610_RTP_XMC5_1, + GSM610_RTP_XMC5_2, + GSM610_RTP_XMC6_0, + GSM610_RTP_XMC6_1, + GSM610_RTP_XMC6_2, + GSM610_RTP_XMC7_0, + GSM610_RTP_XMC7_1, + GSM610_RTP_XMC7_2, + GSM610_RTP_XMC8_0, + GSM610_RTP_XMC8_1, + GSM610_RTP_XMC8_2, + GSM610_RTP_XMC9_0, + GSM610_RTP_XMC9_1, + GSM610_RTP_XMC9_2, + GSM610_RTP_XMC10_0, + GSM610_RTP_XMC10_1, + GSM610_RTP_XMC10_2, + GSM610_RTP_XMC11_0, + GSM610_RTP_XMC11_1, + GSM610_RTP_XMC11_2, + GSM610_RTP_XMC12_0, + GSM610_RTP_XMC12_1, + GSM610_RTP_XCM12_2, + GSM610_RTP_NC1_0, + GSM610_RTP_NC1_1, + GSM610_RTP_NC1_2, + GSM610_RTP_NC1_3, + GSM610_RTP_NC1_4, + GSM610_RTP_NC1_5, + GSM610_RTP_NC1_6, + GSM610_RTP_BC1_0, + GSM610_RTP_BC1_1, + GSM610_RTP_MC1_0, + GSM610_RTP_MC1_1, + GSM610_RTP_XMAXC10, + GSM610_RTP_XMAXC11, + GSM610_RTP_XMAXC12, + GSM610_RTP_XMAXC13, + GSM610_RTP_XMAXC14, + GSM610_RTP_XMAX15, + GSM610_RTP_XMC13_0, + GSM610_RTP_XMC13_1, + GSM610_RTP_XMC13_2, + GSM610_RTP_XMC14_0, + GSM610_RTP_XMC14_1, + GSM610_RTP_XMC14_2, + GSM610_RTP_XMC15_0, + GSM610_RTP_XMC15_1, + GSM610_RTP_XMC15_2, + GSM610_RTP_XMC16_0, + GSM610_RTP_XMC16_1, + GSM610_RTP_XMC16_2, + GSM610_RTP_XMC17_0, + GSM610_RTP_XMC17_1, + GSM610_RTP_XMC17_2, + GSM610_RTP_XMC18_0, + GSM610_RTP_XMC18_1, + GSM610_RTP_XMC18_2, + GSM610_RTP_XMC19_0, + GSM610_RTP_XMC19_1, + GSM610_RTP_XMC19_2, + GSM610_RTP_XMC20_0, + GSM610_RTP_XMC20_1, + GSM610_RTP_XMC20_2, + GSM610_RTP_XMC21_0, + GSM610_RTP_XMC21_1, + GSM610_RTP_XMC21_2, + GSM610_RTP_XMC22_0, + GSM610_RTP_XMC22_1, + GSM610_RTP_XMC22_2, + GSM610_RTP_XMC23_0, + GSM610_RTP_XMC23_1, + GSM610_RTP_XMC23_2, + GSM610_RTP_XMC24_0, + GSM610_RTP_XMC24_1, + GSM610_RTP_XMC24_2, + GSM610_RTP_XMC25_0, + GSM610_RTP_XMC25_1, + GSM610_RTP_XMC25_2, + GSM610_RTP_NC2_0, + GSM610_RTP_NC2_1, + GSM610_RTP_NC2_2, + GSM610_RTP_NC2_3, + GSM610_RTP_NC2_4, + GSM610_RTP_NC2_5, + GSM610_RTP_NC2_6, + GSM610_RTP_BC2_0, + GSM610_RTP_BC2_1, + GSM610_RTP_MC2_0, + GSM610_RTP_MC2_1, + GSM610_RTP_XMAXC20, + GSM610_RTP_XMAXC21, + GSM610_RTP_XMAXC22, + GSM610_RTP_XMAXC23, + GSM610_RTP_XMAXC24, + GSM610_RTP_XMAXC25, + GSM610_RTP_XMC26_0, + GSM610_RTP_XMC26_1, + GSM610_RTP_XMC26_2, + GSM610_RTP_XMC27_0, + GSM610_RTP_XMC27_1, + GSM610_RTP_XMC27_2, + GSM610_RTP_XMC28_0, + GSM610_RTP_XMC28_1, + GSM610_RTP_XMC28_2, + GSM610_RTP_XMC29_0, + GSM610_RTP_XMC29_1, + GSM610_RTP_XMC29_2, + GSM610_RTP_XMC30_0, + GSM610_RTP_XMC30_1, + GSM610_RTP_XMC30_2, + GSM610_RTP_XMC31_0, + GSM610_RTP_XMC31_1, + GSM610_RTP_XMC31_2, + GSM610_RTP_XMC32_0, + GSM610_RTP_XMC32_1, + GSM610_RTP_XMC32_2, + GSM610_RTP_XMC33_0, + GSM610_RTP_XMC33_1, + GSM610_RTP_XMC33_2, + GSM610_RTP_XMC34_0, + GSM610_RTP_XMC34_1, + GSM610_RTP_XMC34_2, + GSM610_RTP_XMC35_0, + GSM610_RTP_XMC35_1, + GSM610_RTP_XMC35_2, + GSM610_RTP_XMC36_0, + GSM610_RTP_XMC36_1, + GSM610_RTP_XMC36_2, + GSM610_RTP_XMC37_0, + GSM610_RTP_XMC37_1, + GSM610_RTP_XMC37_2, + GSM610_RTP_XMC38_0, + GSM610_RTP_XMC38_1, + GSM610_RTP_XMC38_2, + GSM610_RTP_NC3_0, + GSM610_RTP_NC3_1, + GSM610_RTP_NC3_2, + GSM610_RTP_NC3_3, + GSM610_RTP_NC3_4, + GSM610_RTP_NC3_5, + GSM610_RTP_NC3_6, + GSM610_RTP_BC3_0, + GSM610_RTP_BC3_1, + GSM610_RTP_MC3_0, + GSM610_RTP_MC3_1, + GSM610_RTP_XMAXC30, + GSM610_RTP_XMAXC31, + GSM610_RTP_XMAXC32, + GSM610_RTP_XMAXC33, + GSM610_RTP_XMAXC34, + GSM610_RTP_XMAXC35, + GSM610_RTP_XMC39_0, + GSM610_RTP_XMC39_1, + GSM610_RTP_XMC39_2, + GSM610_RTP_XMC40_0, + GSM610_RTP_XMC40_1, + GSM610_RTP_XMC40_2, + GSM610_RTP_XMC41_0, + GSM610_RTP_XMC41_1, + GSM610_RTP_XMC41_2, + GSM610_RTP_XMC42_0, + GSM610_RTP_XMC42_1, + GSM610_RTP_XMC42_2, + GSM610_RTP_XMC43_0, + GSM610_RTP_XMC43_1, + GSM610_RTP_XMC43_2, + GSM610_RTP_XMC44_0, + GSM610_RTP_XMC44_1, + GSM610_RTP_XMC44_2, + GSM610_RTP_XMC45_0, + GSM610_RTP_XMC45_1, + GSM610_RTP_XMC45_2, + GSM610_RTP_XMC46_0, + GSM610_RTP_XMC46_1, + GSM610_RTP_XMC46_2, + GSM610_RTP_XMC47_0, + GSM610_RTP_XMC47_1, + GSM610_RTP_XMC47_2, + GSM610_RTP_XMC48_0, + GSM610_RTP_XMC48_1, + GSM610_RTP_XMC48_2, + GSM610_RTP_XMC49_0, + GSM610_RTP_XMC49_1, + GSM610_RTP_XMC49_2, + GSM610_RTP_XMC50_0, + GSM610_RTP_XMC50_1, + GSM610_RTP_XMC50_2, + GSM610_RTP_XMC51_0, + GSM610_RTP_XMC51_1, + GSM610_RTP_XMC51_2 +}; diff --git a/lib/decoding/osmocom/codec/gsm620.c b/lib/decoding/osmocom/codec/gsm620.c new file mode 100644 index 0000000..282781f --- /dev/null +++ b/lib/decoding/osmocom/codec/gsm620.c @@ -0,0 +1,297 @@ +/*! \file gsm620.c + * GSM 06.20 - GSM HR codec. */ +/* + * (C) 2010 Sylvain Munaut + * + * 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 +#include + +#include +#include +#include + +/* GSM HR unvoiced (mode=0) frames - subjective importance bit ordering */ + /* This array encode mapping between GSM 05.03 Table 3a (bits + * ordering before channel coding on TCH) and GSM 06.20 Table B.1 + * (bit ordering on A-bis */ +const uint16_t gsm620_unvoiced_bitorder[112] = { + 3, /* R0:1 */ + 25, /* LPC 3:7 */ + 52, /* GSP 0-1:2 */ + 71, /* GSP 0-2:2 */ + 90, /* GSP 0-3:2 */ + 109, /* GSP 0-4:2 */ + 15, /* LPC 1:0 */ + 19, /* LPC 2:5 */ + 20, /* LPC 2:4 */ + 21, /* LPC 2:3 */ + 22, /* LPC 2:2 */ + 23, /* LPC 2:1 */ + 26, /* LPC 3:6 */ + 27, /* LPC 3:5 */ + 28, /* LPC 3:4 */ + 29, /* LPC 3:3 */ + 30, /* LPC 3:2 */ + 31, /* LPC 3:1 */ + 61, /* Code 1-2:0 */ + 62, /* Code 2-2:6 */ + 63, /* Code 2-2:5 */ + 64, /* Code 2-2:4 */ + 65, /* Code 2-2:3 */ + 66, /* Code 2-2:2 */ + 67, /* Code 2-2:1 */ + 68, /* Code 2-2:0 */ + 74, /* Code 1-3:6 */ + 75, /* Code 1-3:5 */ + 76, /* Code 1-3:4 */ + 77, /* Code 1-3:3 */ + 78, /* Code 1-3:2 */ + 79, /* Code 1-3:1 */ + 80, /* Code 1-3:0 */ + 81, /* Code 2-3:6 */ + 82, /* Code 2-3:5 */ + 83, /* Code 2-3:4 */ + 84, /* Code 2-3:3 */ + 32, /* LPC 3:0 */ + 4, /* R0:0 */ + 33, /* INT-LPC:0 */ + 60, /* Code 1-2:1 */ + 59, /* Code 1-2:2 */ + 58, /* Code 1-2:3 */ + 57, /* Code 1-2:4 */ + 56, /* Code 1-2:5 */ + 55, /* Code 1-2:6 */ + 49, /* Code 2-1:0 */ + 48, /* Code 2-1:1 */ + 47, /* Code 2-1:2 */ + 46, /* Code 2-1:3 */ + 45, /* Code 2-1:4 */ + 44, /* Code 2-1:5 */ + 43, /* Code 2-1:6 */ + 42, /* Code 1-1:0 */ + 41, /* Code 1-1:1 */ + 40, /* Code 1-1:2 */ + 39, /* Code 1-1:3 */ + 38, /* Code 1-1:4 */ + 37, /* Code 1-1:5 */ + 36, /* Code 1-1:6 */ + 111, /* GSP 0-4:0 */ + 92, /* GSP 0-3:0 */ + 73, /* GSP 0-2:0 */ + 54, /* GSP 0-1:0 */ + 24, /* LPC 2:0 */ + 110, /* GSP 0-4:1 */ + 91, /* GSP 0-3:1 */ + 72, /* GSP 0-2:1 */ + 53, /* GSP 0-1:1 */ + 14, /* LPC 1:1 */ + 13, /* LPC 1:2 */ + 12, /* LPC 1:3 */ + 11, /* LPC 1:4 */ + 10, /* LPC 1:5 */ + 108, /* GSP 0-4:3 */ + 89, /* GSP 0-3:3 */ + 70, /* GSP 0-2:3 */ + 51, /* GSP 0-1:3 */ + 16, /* LPC 2:8 */ + 17, /* LPC 2:7 */ + 18, /* LPC 2:6 */ + 107, /* GSP 0-4:4 */ + 88, /* GSP 0-3:4 */ + 69, /* GSP 0-2:4 */ + 50, /* GSP 0-1:4 */ + 9, /* LPC 1:6 */ + 8, /* LPC 1:7 */ + 7, /* LPC 1:8 */ + 6, /* LPC 1:9 */ + 2, /* R0:2 */ + 5, /* LPC 1:10 */ + 1, /* R0:3 */ + 0, /* R0:4 */ + 35, /* Mode:0 */ + 34, /* Mode:1 */ + 106, /* Code 2-4:0 */ + 105, /* Code 2-4:1 */ + 104, /* Code 2-4:2 */ + 103, /* Code 2-4:3 */ + 102, /* Code 2-4:4 */ + 101, /* Code 2-4:5 */ + 100, /* Code 2-4:6 */ + 99, /* Code 1-4:0 */ + 98, /* Code 1-4:1 */ + 97, /* Code 1-4:2 */ + 96, /* Code 1-4:3 */ + 95, /* Code 1-4:4 */ + 94, /* Code 1-4:5 */ + 93, /* Code 1-4:6 */ + 87, /* Code 2-3:0 */ + 86, /* Code 2-3:1 */ + 85, /* Code 2-3:2 */ +}; + +/* GSM HR voiced (mode=1,2,3) frames - subjective importance bit ordering */ + /* This array encode mapping between GSM 05.03 Table 3b (bits + * ordering before channel coding on TCH) and GSM 06.20 Table B.2 + * (bit ordering on A-bis */ +const uint16_t gsm620_voiced_bitorder[112] = { + 13, /* LPC 1:2 */ + 14, /* LPC 1:1 */ + 18, /* LPC 2:6 */ + 19, /* LPC 2:5 */ + 20, /* LPC 2:4 */ + 53, /* GSP 0-1:4 */ + 71, /* GSP 0-2:4 */ + 89, /* GSP 0-3:4 */ + 107, /* GSP 0-4:4 */ + 54, /* GSP 0-1:3 */ + 72, /* GSP 0-2:3 */ + 90, /* GSP 0-3:3 */ + 108, /* GSP 0-4:3 */ + 55, /* GSP 0-1:2 */ + 73, /* GSP 0-2:2 */ + 91, /* GSP 0-3:2 */ + 109, /* GSP 0-4:2 */ + 44, /* Code 1:8 */ + 45, /* Code 1:7 */ + 46, /* Code 1:6 */ + 47, /* Code 1:5 */ + 48, /* Code 1:4 */ + 49, /* Code 1:3 */ + 50, /* Code 1:2 */ + 51, /* Code 1:1 */ + 52, /* Code 1:0 */ + 62, /* Code 2:8 */ + 63, /* Code 2:7 */ + 64, /* Code 2:6 */ + 65, /* Code 2:5 */ + 68, /* Code 2:2 */ + 69, /* Code 2:1 */ + 70, /* Code 2:0 */ + 80, /* Code 3:8 */ + 66, /* Code 2:4 */ + 67, /* Code 2:3 */ + 56, /* GSP 0-1:1 */ + 74, /* GSP 0-2:1 */ + 92, /* GSP 0-3:1 */ + 110, /* GSP 0-4:1 */ + 57, /* GSP 0-1:0 */ + 75, /* GSP 0-2:0 */ + 93, /* GSP 0-3:0 */ + 111, /* GSP 0-4:0 */ + 33, /* INT-LPC:0 */ + 24, /* LPC 2:0 */ + 32, /* LPC 3:0 */ + 97, /* LAG 4:0 */ + 31, /* LPC 3:1 */ + 23, /* LPC 2:1 */ + 96, /* LAG 4:1 */ + 79, /* LAG 3:0 */ + 61, /* LAG 2:0 */ + 43, /* LAG 1:0 */ + 95, /* LAG 4:2 */ + 78, /* LAG 3:1 */ + 60, /* LAG 2:1 */ + 42, /* LAG 1:1 */ + 30, /* LPC 3:2 */ + 29, /* LPC 3:3 */ + 28, /* LPC 3:4 */ + 22, /* LPC 2:2 */ + 27, /* LPC 3:5 */ + 26, /* LPC 3:6 */ + 21, /* LPC 2:3 */ + 4, /* R0:0 */ + 25, /* LPC 3:7 */ + 15, /* LPC 1:0 */ + 94, /* LAG 4:3 */ + 77, /* LAG 3:2 */ + 59, /* LAG 2:2 */ + 41, /* LAG 1:2 */ + 3, /* R0:1 */ + 76, /* LAG 3:3 */ + 58, /* LAG 2:3 */ + 40, /* LAG 1:3 */ + 39, /* LAG 1:4 */ + 17, /* LPC 2:7 */ + 16, /* LPC 2:8 */ + 12, /* LPC 1:3 */ + 11, /* LPC 1:4 */ + 10, /* LPC 1:5 */ + 9, /* LPC 1:6 */ + 2, /* R0:2 */ + 38, /* LAG 1:5 */ + 37, /* LAG 1:6 */ + 36, /* LAG 1:7 */ + 8, /* LPC 1:7 */ + 7, /* LPC 1:8 */ + 6, /* LPC 1:9 */ + 5, /* LPC 1:10 */ + 1, /* R0:3 */ + 0, /* R0:4 */ + 35, /* Mode:0 */ + 34, /* Mode:1 */ + 106, /* Code 4:0 */ + 105, /* Code 4:1 */ + 104, /* Code 4:2 */ + 103, /* Code 4:3 */ + 102, /* Code 4:4 */ + 101, /* Code 4:5 */ + 100, /* Code 4:6 */ + 99, /* Code 4:7 */ + 98, /* Code 4:8 */ + 88, /* Code 3:0 */ + 87, /* Code 3:1 */ + 86, /* Code 3:2 */ + 85, /* Code 3:3 */ + 84, /* Code 3:4 */ + 83, /* Code 3:5 */ + 82, /* Code 3:6 */ + 81, /* Code 3:7 */ +}; + +static inline uint16_t mask(const uint8_t msb) +{ + const uint16_t m = (uint16_t)1 << (msb - 1); + return (m - 1) ^ m; +} + +/*! Check whether RTP frame contains HR SID code word according to + * TS 101 318 §5.2.2 + * \param[in] rtp_payload Buffer with RTP payload + * \param[in] payload_len Length of payload + * \returns true if code word is found, false otherwise + */ +bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len) +{ + uint8_t i, bits[] = { 1, 2, 8, 9, 5, 4, 9, 5, 4, 9, 5, 4, 9, 5 }; + struct bitvec bv; + bv.data = (uint8_t *) rtp_payload; + bv.data_len = payload_len; + bv.cur_bit = 33; + + /* code word is all 1 at given bits, numbered from 1, MODE is always 3 */ + for (i = 0; i < ARRAY_SIZE(bits); i++) + if (bitvec_get_uint(&bv, bits[i]) != mask(bits[i])) + return false; + + return true; +} diff --git a/lib/decoding/osmocom/codec/gsm660.c b/lib/decoding/osmocom/codec/gsm660.c new file mode 100644 index 0000000..4f7bb09 --- /dev/null +++ b/lib/decoding/osmocom/codec/gsm660.c @@ -0,0 +1,259 @@ +/*! \file gsm660.c + * GSM 06.60 - GSM EFR Codec. */ +/* + * (C) 2010 Sylvain Munaut + * + * 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 +#include + +/* GSM EFR - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 6. + * + * It converts between serial parameter output (as described in + * GSM 06.60 Table 6 and GSM 05.03 Table 5) and the order needed + * before channel encoding. CRC poly and bit repetition must be + * applied prior to this table, as in GSM 05.03 3.1.1, to get 260 + * bits from a 244 bits raw EFR frame. + */ +const uint16_t gsm660_bitorder[260] = { + 38, 39, 40, 41, 42, 43, /* 0 -> LTP-LAG 1: b8..b3 */ + 145, 146, 147, 148, 149, 150, /* 6 -> LTP-LAG 3: b8..b3 */ + 93, 94, /* 12 -> LTP-LAG 2: b5..b4 */ + 200, 201, /* 14 -> LTP-LAG 4: b5..b4 */ + 47, /* 16 -> LTP-GAIN 1: b3 */ + 88, /* 17 -> FCB-GAIN 1: b4 */ + 99, /* 18 -> LTP-GAIN 2: b3 */ + 140, /* 19 -> FCB-GAIN 2: b4 */ + 44, /* 20 -> LTP-LAG 1: b2 */ + 151, /* 21 -> LTP-LAG 3: b2 */ + 95, /* 22 -> LTP-LAG 2: b3 */ + 202, /* 23 -> LTP-LAG 4: b3 */ + 1, 2, /* 24 -> LPC 1: b5..b4 */ + 7, /* 26 -> LPC 2: b7 */ + 9, /* 27 -> LPC 2: b5 */ + 17, 18, /* 28 -> LPC 3: b6..b5 */ + 23, /* 30 -> LPC 3: b0 */ + 45, 46, /* 31 -> LTP-LAG 1: b1..b0 */ + 152, 153, /* 33 -> LTP-LAG 3: b1..b0 */ + 96, /* 35 -> LTP-LAG 2: b2 */ + 203, /* 36 -> LTP-LAG 4: b2 */ + 3, 4, /* 37 -> LPC 1: b3..b2 */ + 10, 11, /* 39 -> LPC 2: b4..b3 */ + 15, /* 41 -> LPC 3: b8 */ + 8, /* 42 -> LPC 2: b6 */ + 5, 6, /* 43 -> LPC 1: b1..b0 */ + 12, /* 45 -> LPC 2: b2 */ + 16, /* 46 -> LPC 3: b7 */ + 19, /* 47 -> LPC 3: b4 */ + 97, /* 48 -> LTP-LAG 2: b1 */ + 204, /* 49 -> LTP-LAG 4: b1 */ + 0, /* 50 -> LPC 1: b6 */ + 13, 14, /* 51 -> LPC 2: b1..b0 */ + 20, /* 53 -> LPC 3: b3 */ + 24, 25, /* 54 -> LPC 4: b7..b6 */ + 27, /* 56 -> LPC 4: b4 */ + 154, /* 57 -> LTP-GAIN 3: b3 */ + 206, /* 58 -> LTP-GAIN 4: b3 */ + 195, /* 59 -> FCB-GAIN 3: b4 */ + 247, /* 60 -> FCB-GAIN 4: b4 */ + 89, /* 61 -> FCB-GAIN 1: b3 */ + 141, /* 62 -> FCB-GAIN 2: b3 */ + 196, /* 63 -> FCB-GAIN 3: b3 */ + 248, /* 64 -> FCB-GAIN 4: b3 */ + 252, 253, 254, 255, 256, 257, 258, 259, /* 65 -> CRC-POLY: b7..b0 */ + 48, /* 73 -> LTP-GAIN 1: b2 */ + 100, /* 74 -> LTP-GAIN 2: b2 */ + 155, /* 75 -> LTP-GAIN 3: b2 */ + 207, /* 76 -> LTP-GAIN 4: b2 */ + 21, 22, /* 77 -> LPC 3: b2..b1 */ + 26, /* 79 -> LPC 4: b5 */ + 28, /* 80 -> LPC 4: b3 */ + 51, /* 81 -> PULSE 1_1: b3 */ + 55, /* 82 -> PULSE 1_2: b3 */ + 59, /* 83 -> PULSE 1_3: b3 */ + 63, /* 84 -> PULSE 1_4: b3 */ + 67, /* 85 -> PULSE 1_5: b3 */ + 103, /* 86 -> PULSE 2_1: b3 */ + 107, /* 87 -> PULSE 2_2: b3 */ + 111, /* 88 -> PULSE 2_3: b3 */ + 115, /* 89 -> PULSE 2_4: b3 */ + 119, /* 90 -> PULSE 2_5: b3 */ + 158, /* 91 -> PULSE 3_1: b3 */ + 162, /* 92 -> PULSE 3_2: b3 */ + 166, /* 93 -> PULSE 3_3: b3 */ + 170, /* 94 -> PULSE 3_4: b3 */ + 174, /* 95 -> PULSE 3_5: b3 */ + 210, /* 96 -> PULSE 4_1: b3 */ + 214, /* 97 -> PULSE 4_2: b3 */ + 218, /* 98 -> PULSE 4_3: b3 */ + 222, /* 99 -> PULSE 4_4: b3 */ + 226, /* 100 -> PULSE 4_5: b3 */ + 90, /* 101 -> FCB-GAIN 1: b2 */ + 142, /* 102 -> FCB-GAIN 2: b2 */ + 197, /* 103 -> FCB-GAIN 3: b2 */ + 249, /* 104 -> FCB-GAIN 4: b2 */ + 49, /* 105 -> LTP-GAIN 1: b1 */ + 101, /* 106 -> LTP-GAIN 2: b1 */ + 156, /* 107 -> LTP-GAIN 3: b1 */ + 208, /* 108 -> LTP-GAIN 4: b1 */ + 29, 30, 31, /* 109 -> LPC 4: b2..b0 */ + 32, 33, 34, 35, /* 112 -> LPC 5: b5..b2 */ + 98, /* 116 -> LTP-LAG 2: b0 */ + 205, /* 117 -> LTP-LAG 4: b0 */ + 52, /* 118 -> PULSE 1_1: b2 */ + 56, /* 119 -> PULSE 1_2: b2 */ + 60, /* 120 -> PULSE 1_3: b2 */ + 64, /* 121 -> PULSE 1_4: b2 */ + 68, /* 122 -> PULSE 1_5: b2 */ + 104, /* 123 -> PULSE 2_1: b2 */ + 108, /* 124 -> PULSE 2_2: b2 */ + 112, /* 125 -> PULSE 2_3: b2 */ + 116, /* 126 -> PULSE 2_4: b2 */ + 120, /* 127 -> PULSE 2_5: b2 */ + 159, /* 128 -> PULSE 3_1: b2 */ + 163, /* 129 -> PULSE 3_2: b2 */ + 167, /* 130 -> PULSE 3_3: b2 */ + 171, /* 131 -> PULSE 3_4: b2 */ + 175, /* 132 -> PULSE 3_5: b2 */ + 211, /* 133 -> PULSE 4_1: b2 */ + 215, /* 134 -> PULSE 4_2: b2 */ + 219, /* 135 -> PULSE 4_3: b2 */ + 223, /* 136 -> PULSE 4_4: b2 */ + 227, /* 137 -> PULSE 4_5: b2 */ + 53, /* 138 -> PULSE 1_1: b1 */ + 57, /* 139 -> PULSE 1_2: b1 */ + 61, /* 140 -> PULSE 1_3: b1 */ + 65, /* 141 -> PULSE 1_4: b1 */ + 105, /* 142 -> PULSE 2_1: b1 */ + 109, /* 143 -> PULSE 2_2: b1 */ + 113, /* 144 -> PULSE 2_3: b1 */ + 117, /* 145 -> PULSE 2_4: b1 */ + 160, /* 146 -> PULSE 3_1: b1 */ + 164, /* 147 -> PULSE 3_2: b1 */ + 168, /* 148 -> PULSE 3_3: b1 */ + 172, /* 149 -> PULSE 3_4: b1 */ + 212, /* 150 -> PULSE 4_1: b1 */ + 220, /* 151 -> PULSE 4_3: b1 */ + 224, /* 152 -> PULSE 4_4: b1 */ + 91, /* 153 -> FCB-GAIN 1: b1 */ + 143, /* 154 -> FCB-GAIN 2: b1 */ + 198, /* 155 -> FCB-GAIN 3: b1 */ + 250, /* 156 -> FCB-GAIN 4: b1 */ + 50, /* 157 -> LTP-GAIN 1: b0 */ + 102, /* 158 -> LTP-GAIN 2: b0 */ + 157, /* 159 -> LTP-GAIN 3: b0 */ + 209, /* 160 -> LTP-GAIN 4: b0 */ + 92, /* 161 -> FCB-GAIN 1: b0 */ + 144, /* 162 -> FCB-GAIN 2: b0 */ + 199, /* 163 -> FCB-GAIN 3: b0 */ + 251, /* 164 -> FCB-GAIN 4: b0 */ + 54, /* 165 -> PULSE 1_1: b0 */ + 58, /* 166 -> PULSE 1_2: b0 */ + 62, /* 167 -> PULSE 1_3: b0 */ + 66, /* 168 -> PULSE 1_4: b0 */ + 106, /* 169 -> PULSE 2_1: b0 */ + 110, /* 170 -> PULSE 2_2: b0 */ + 114, /* 171 -> PULSE 2_3: b0 */ + 118, /* 172 -> PULSE 2_4: b0 */ + 161, /* 173 -> PULSE 3_1: b0 */ + 165, /* 174 -> PULSE 3_2: b0 */ + 169, /* 175 -> PULSE 3_3: b0 */ + 173, /* 176 -> PULSE 3_4: b0 */ + 213, /* 177 -> PULSE 4_1: b0 */ + 221, /* 178 -> PULSE 4_3: b0 */ + 225, /* 179 -> PULSE 4_4: b0 */ + 36, 37, /* 180 -> LPC 5: b1..b0 */ + 69, /* 182 -> PULSE 1_5: b1 */ + 71, 72, /* 183 -> PULSE 1_5: b1..b1 */ + 121, /* 185 -> PULSE 2_5: b1 */ + 123, 124, /* 186 -> PULSE 2_5: b1..b1 */ + 176, /* 188 -> PULSE 3_5: b1 */ + 178, 179, /* 189 -> PULSE 3_5: b1..b1 */ + 228, /* 191 -> PULSE 4_5: b1 */ + 230, 231, /* 192 -> PULSE 4_5: b1..b1 */ + 216, 217, /* 194 -> PULSE 4_2: b1..b0 */ + 70, /* 196 -> PULSE 1_5: b0 */ + 122, /* 197 -> PULSE 2_5: b0 */ + 177, /* 198 -> PULSE 3_5: b0 */ + 229, /* 199 -> PULSE 4_5: b0 */ + 73, /* 200 -> PULSE 1_6: b2 */ + 76, /* 201 -> PULSE 1_7: b2 */ + 79, /* 202 -> PULSE 1_8: b2 */ + 82, /* 203 -> PULSE 1_9: b2 */ + 85, /* 204 -> PULSE 1_10: b2 */ + 125, /* 205 -> PULSE 2_6: b2 */ + 128, /* 206 -> PULSE 2_7: b2 */ + 131, /* 207 -> PULSE 2_8: b2 */ + 134, /* 208 -> PULSE 2_9: b2 */ + 137, /* 209 -> PULSE 2_10: b2 */ + 180, /* 210 -> PULSE 3_6: b2 */ + 183, /* 211 -> PULSE 3_7: b2 */ + 186, /* 212 -> PULSE 3_8: b2 */ + 189, /* 213 -> PULSE 3_9: b2 */ + 192, /* 214 -> PULSE 3_10: b2 */ + 232, /* 215 -> PULSE 4_6: b2 */ + 235, /* 216 -> PULSE 4_7: b2 */ + 238, /* 217 -> PULSE 4_8: b2 */ + 241, /* 218 -> PULSE 4_9: b2 */ + 244, /* 219 -> PULSE 4_10: b2 */ + 74, /* 220 -> PULSE 1_6: b1 */ + 77, /* 221 -> PULSE 1_7: b1 */ + 80, /* 222 -> PULSE 1_8: b1 */ + 83, /* 223 -> PULSE 1_9: b1 */ + 86, /* 224 -> PULSE 1_10: b1 */ + 126, /* 225 -> PULSE 2_6: b1 */ + 129, /* 226 -> PULSE 2_7: b1 */ + 132, /* 227 -> PULSE 2_8: b1 */ + 135, /* 228 -> PULSE 2_9: b1 */ + 138, /* 229 -> PULSE 2_10: b1 */ + 181, /* 230 -> PULSE 3_6: b1 */ + 184, /* 231 -> PULSE 3_7: b1 */ + 187, /* 232 -> PULSE 3_8: b1 */ + 190, /* 233 -> PULSE 3_9: b1 */ + 193, /* 234 -> PULSE 3_10: b1 */ + 233, /* 235 -> PULSE 4_6: b1 */ + 236, /* 236 -> PULSE 4_7: b1 */ + 239, /* 237 -> PULSE 4_8: b1 */ + 242, /* 238 -> PULSE 4_9: b1 */ + 245, /* 239 -> PULSE 4_10: b1 */ + 75, /* 240 -> PULSE 1_6: b0 */ + 78, /* 241 -> PULSE 1_7: b0 */ + 81, /* 242 -> PULSE 1_8: b0 */ + 84, /* 243 -> PULSE 1_9: b0 */ + 87, /* 244 -> PULSE 1_10: b0 */ + 127, /* 245 -> PULSE 2_6: b0 */ + 130, /* 246 -> PULSE 2_7: b0 */ + 133, /* 247 -> PULSE 2_8: b0 */ + 136, /* 248 -> PULSE 2_9: b0 */ + 139, /* 249 -> PULSE 2_10: b0 */ + 182, /* 250 -> PULSE 3_6: b0 */ + 185, /* 251 -> PULSE 3_7: b0 */ + 188, /* 252 -> PULSE 3_8: b0 */ + 191, /* 253 -> PULSE 3_9: b0 */ + 194, /* 254 -> PULSE 3_10: b0 */ + 234, /* 255 -> PULSE 4_6: b0 */ + 237, /* 256 -> PULSE 4_7: b0 */ + 240, /* 257 -> PULSE 4_8: b0 */ + 243, /* 258 -> PULSE 4_9: b0 */ + 246, /* 259 -> PULSE 4_10: b0 */ +}; diff --git a/lib/decoding/osmocom/codec/gsm690.c b/lib/decoding/osmocom/codec/gsm690.c new file mode 100644 index 0000000..1955716 --- /dev/null +++ b/lib/decoding/osmocom/codec/gsm690.c @@ -0,0 +1,318 @@ +/*! \file gsm690.c + * GSM 06.90 - GSM AMR Codec. */ +/* + * (C) 2010 Sylvain Munaut + * + * 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 +#include +#include +#include + +#include +#include +/* + * These table map between the raw encoder parameter output and + * the format used before channel coding. Both in GSM and in various + * file/network format (same tables used in several specs). + */ + +/* AMR 12.2 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 7 + * It's also TS 26.101 Table B.8 + */ +const uint16_t gsm690_12_2_bitorder[244] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 23, 15, 16, 17, 18, + 19, 20, 21, 22, 24, 25, 26, 27, 28, 38, + 141, 39, 142, 40, 143, 41, 144, 42, 145, 43, + 146, 44, 147, 45, 148, 46, 149, 47, 97, 150, + 200, 48, 98, 151, 201, 49, 99, 152, 202, 86, + 136, 189, 239, 87, 137, 190, 240, 88, 138, 191, + 241, 91, 194, 92, 195, 93, 196, 94, 197, 95, + 198, 29, 30, 31, 32, 33, 34, 35, 50, 100, + 153, 203, 89, 139, 192, 242, 51, 101, 154, 204, + 55, 105, 158, 208, 90, 140, 193, 243, 59, 109, + 162, 212, 63, 113, 166, 216, 67, 117, 170, 220, + 36, 37, 54, 53, 52, 58, 57, 56, 62, 61, + 60, 66, 65, 64, 70, 69, 68, 104, 103, 102, + 108, 107, 106, 112, 111, 110, 116, 115, 114, 120, + 119, 118, 157, 156, 155, 161, 160, 159, 165, 164, + 163, 169, 168, 167, 173, 172, 171, 207, 206, 205, + 211, 210, 209, 215, 214, 213, 219, 218, 217, 223, + 222, 221, 73, 72, 71, 76, 75, 74, 79, 78, + 77, 82, 81, 80, 85, 84, 83, 123, 122, 121, + 126, 125, 124, 129, 128, 127, 132, 131, 130, 135, + 134, 133, 176, 175, 174, 179, 178, 177, 182, 181, + 180, 185, 184, 183, 188, 187, 186, 226, 225, 224, + 229, 228, 227, 232, 231, 230, 235, 234, 233, 238, + 237, 236, 96, 199, +}; + +/* AMR 10.2 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 8 + * It's also TS 26.101 Table B.7 + */ +const uint16_t gsm690_10_2_bitorder[204] = { + 7, 6, 5, 4, 3, 2, 1, 0, 16, 15, + 14, 13, 12, 11, 10, 9, 8, 26, 27, 28, + 29, 30, 31, 115, 116, 117, 118, 119, 120, 72, + 73, 161, 162, 65, 68, 69, 108, 111, 112, 154, + 157, 158, 197, 200, 201, 32, 33, 121, 122, 74, + 75, 163, 164, 66, 109, 155, 198, 19, 23, 21, + 22, 18, 17, 20, 24, 25, 37, 36, 35, 34, + 80, 79, 78, 77, 126, 125, 124, 123, 169, 168, + 167, 166, 70, 67, 71, 113, 110, 114, 159, 156, + 160, 202, 199, 203, 76, 165, 81, 82, 92, 91, + 93, 83, 95, 85, 84, 94, 101, 102, 96, 104, + 86, 103, 87, 97, 127, 128, 138, 137, 139, 129, + 141, 131, 130, 140, 147, 148, 142, 150, 132, 149, + 133, 143, 170, 171, 181, 180, 182, 172, 184, 174, + 173, 183, 190, 191, 185, 193, 175, 192, 176, 186, + 38, 39, 49, 48, 50, 40, 52, 42, 41, 51, + 58, 59, 53, 61, 43, 60, 44, 54, 194, 179, + 189, 196, 177, 195, 178, 187, 188, 151, 136, 146, + 153, 134, 152, 135, 144, 145, 105, 90, 100, 107, + 88, 106, 89, 98, 99, 62, 47, 57, 64, 45, + 63, 46, 55, 56, +}; + +/* AMR 7.95 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 9 + * It's also TS 26.101 Table B.6 + */ +const uint16_t gsm690_7_95_bitorder[159] = { + 8, 7, 6, 5, 4, 3, 2, 14, 16, 9, + 10, 12, 13, 15, 11, 17, 20, 22, 24, 23, + 19, 18, 21, 56, 88, 122, 154, 57, 89, 123, + 155, 58, 90, 124, 156, 52, 84, 118, 150, 53, + 85, 119, 151, 27, 93, 28, 94, 29, 95, 30, + 96, 31, 97, 61, 127, 62, 128, 63, 129, 59, + 91, 125, 157, 32, 98, 64, 130, 1, 0, 25, + 26, 33, 99, 34, 100, 65, 131, 66, 132, 54, + 86, 120, 152, 60, 92, 126, 158, 55, 87, 121, + 153, 117, 116, 115, 46, 78, 112, 144, 43, 75, + 109, 141, 40, 72, 106, 138, 36, 68, 102, 134, + 114, 149, 148, 147, 146, 83, 82, 81, 80, 51, + 50, 49, 48, 47, 45, 44, 42, 39, 35, 79, + 77, 76, 74, 71, 67, 113, 111, 110, 108, 105, + 101, 145, 143, 142, 140, 137, 133, 41, 73, 107, + 139, 37, 69, 103, 135, 38, 70, 104, 136, +}; + +/* AMR 7.4 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 10 + * It's also TS 26.101 Table B.5 + */ +const uint16_t gsm690_7_4_bitorder[148] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 26, 87, 27, + 88, 28, 89, 29, 90, 30, 91, 51, 80, 112, + 141, 52, 81, 113, 142, 54, 83, 115, 144, 55, + 84, 116, 145, 58, 119, 59, 120, 21, 22, 23, + 17, 18, 19, 31, 60, 92, 121, 56, 85, 117, + 146, 20, 24, 25, 50, 79, 111, 140, 57, 86, + 118, 147, 49, 78, 110, 139, 48, 77, 53, 82, + 114, 143, 109, 138, 47, 76, 108, 137, 32, 33, + 61, 62, 93, 94, 122, 123, 41, 42, 43, 44, + 45, 46, 70, 71, 72, 73, 74, 75, 102, 103, + 104, 105, 106, 107, 131, 132, 133, 134, 135, 136, + 34, 63, 95, 124, 35, 64, 96, 125, 36, 65, + 97, 126, 37, 66, 98, 127, 38, 67, 99, 128, + 39, 68, 100, 129, 40, 69, 101, 130, +}; + +/* AMR 6.7 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 11 + * It's also TS 26.101 Table B.4 + */ +const uint16_t gsm690_6_7_bitorder[134] = { + 0, 1, 4, 3, 5, 6, 13, 7, 2, 8, + 9, 11, 15, 12, 14, 10, 28, 82, 29, 83, + 27, 81, 26, 80, 30, 84, 16, 55, 109, 56, + 110, 31, 85, 57, 111, 48, 73, 102, 127, 32, + 86, 51, 76, 105, 130, 52, 77, 106, 131, 58, + 112, 33, 87, 19, 23, 53, 78, 107, 132, 21, + 22, 18, 17, 20, 24, 25, 50, 75, 104, 129, + 47, 72, 101, 126, 54, 79, 108, 133, 46, 71, + 100, 125, 128, 103, 74, 49, 45, 70, 99, 124, + 42, 67, 96, 121, 39, 64, 93, 118, 38, 63, + 92, 117, 35, 60, 89, 114, 34, 59, 88, 113, + 44, 69, 98, 123, 43, 68, 97, 122, 41, 66, + 95, 120, 40, 65, 94, 119, 37, 62, 91, 116, + 36, 61, 90, 115, +}; + +/* AMR 5.9 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 12 + * It's also TS 26.101 Table B.3 + */ +const uint16_t gsm690_5_9_bitorder[118] = { + 0, 1, 4, 5, 3, 6, 7, 2, 13, 15, + 8, 9, 11, 12, 14, 10, 16, 28, 74, 29, + 75, 27, 73, 26, 72, 30, 76, 51, 97, 50, + 71, 96, 117, 31, 77, 52, 98, 49, 70, 95, + 116, 53, 99, 32, 78, 33, 79, 48, 69, 94, + 115, 47, 68, 93, 114, 46, 67, 92, 113, 19, + 21, 23, 22, 18, 17, 20, 24, 111, 43, 89, + 110, 64, 65, 44, 90, 25, 45, 66, 91, 112, + 54, 100, 40, 61, 86, 107, 39, 60, 85, 106, + 36, 57, 82, 103, 35, 56, 81, 102, 34, 55, + 80, 101, 42, 63, 88, 109, 41, 62, 87, 108, + 38, 59, 84, 105, 37, 58, 83, 104, +}; + +/* AMR 5.15 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 13 + * It's also TS 26.101 Table B.2 + */ +const uint16_t gsm690_5_15_bitorder[103] = { + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, + 13, 12, 11, 10, 9, 8, 23, 24, 25, 26, + 27, 46, 65, 84, 45, 44, 43, 64, 63, 62, + 83, 82, 81, 102, 101, 100, 42, 61, 80, 99, + 28, 47, 66, 85, 18, 41, 60, 79, 98, 29, + 48, 67, 17, 20, 22, 40, 59, 78, 97, 21, + 30, 49, 68, 86, 19, 16, 87, 39, 38, 58, + 57, 77, 35, 54, 73, 92, 76, 96, 95, 36, + 55, 74, 93, 32, 51, 33, 52, 70, 71, 89, + 90, 31, 50, 69, 88, 37, 56, 75, 94, 34, + 53, 72, 91, +}; + +/* AMR 4.75 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 14 + * It's also TS 26.101 Table B.1 + */ +const uint16_t gsm690_4_75_bitorder[95] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 23, 24, 25, 26, + 27, 28, 48, 49, 61, 62, 82, 83, 47, 46, + 45, 44, 81, 80, 79, 78, 17, 18, 20, 22, + 77, 76, 75, 74, 29, 30, 43, 42, 41, 40, + 38, 39, 16, 19, 21, 50, 51, 59, 60, 63, + 64, 72, 73, 84, 85, 93, 94, 32, 33, 35, + 36, 53, 54, 56, 57, 66, 67, 69, 70, 87, + 88, 90, 91, 34, 55, 68, 89, 37, 58, 71, + 92, 31, 52, 65, 86, +}; + +static const uint8_t amr_len_by_ft[16] = { + 12, 13, 15, 17, 19, 20, 26, 31, 7, 0, 0, 0, 0, 0, 0, 0 +}; + +const struct value_string osmo_amr_type_names[] = { + { AMR_4_75, "AMR 4,75 kbits/s" }, + { AMR_5_15, "AMR 5,15 kbit/s" }, + { AMR_5_90, "AMR 5,90 kbit/s" }, + { AMR_6_70, "AMR 6,70 kbit/s (PDC-EFR)" }, + { AMR_7_40, "AMR 7,40 kbit/s (TDMA-EFR)" }, + { AMR_7_95, "AMR 7,95 kbit/s" }, + { AMR_10_2, "AMR 10,2 kbit/s" }, + { AMR_12_2, "AMR 12,2 kbit/s (GSM-EFR)" }, + { AMR_SID, "AMR SID" }, + { AMR_GSM_EFR_SID, "GSM-EFR SID" }, + { AMR_TDMA_EFR_SID, "TDMA-EFR SID" }, + { AMR_PDC_EFR_SID, "PDC-EFR SID" }, + { AMR_NO_DATA, "No Data/NA" }, + { 0, NULL }, +}; + +/*! Decode various AMR parameters from RTP payload (RFC 4867) acording to + * 3GPP TS 26.101 + * \param[in] rtppayload Payload from RTP packet + * \param[in] payload_len length of rtppayload + * \param[out] cmr AMR Codec Mode Request, not filled if NULL + * \param[out] cmi AMR Codec Mode Indicator, -1 if not applicable for this type, + * not filled if NULL + * \param[out] ft AMR Frame Type, not filled if NULL + * \param[out] bfi AMR Bad Frame Indicator, not filled if NULL + * \param[out] sti AMR SID Type Indicator, -1 if not applicable for this type, + * not filled if NULL + * \returns length of AMR data or negative value on error + */ +int osmo_amr_rtp_dec(const uint8_t *rtppayload, int payload_len, uint8_t *cmr, + int8_t *cmi, enum osmo_amr_type *ft, + enum osmo_amr_quality *bfi, int8_t *sti) +{ + if (payload_len < 2 || !rtppayload) + return -EINVAL; + + /* RFC 4867 § 4.4.2 ToC - compound payloads are not supported: F = 0 */ + uint8_t type = (rtppayload[1] >> 3) & 0xf; + + /* compound payloads are not supported */ + if (rtppayload[1] >> 7) + return -ENOTSUP; + + if (payload_len < amr_len_by_ft[type]) + return -ENOTSUP; + + if (ft) + *ft = type; + + if (cmr) + *cmr = rtppayload[0] >> 4; + + if (bfi) + *bfi = (rtppayload[1] >> 2) & 1; + + /* Table 6 in 3GPP TS 26.101 */ + if (cmi) + *cmi = (type == AMR_SID) ? ((rtppayload[6] >> 1) & 7) : -1; + + if (sti) + *sti = (type == AMR_SID) ? (rtppayload[6] & 0x10) : -1; + + return 2 + amr_len_by_ft[type]; +} + +/*! Encode various AMR parameters from RTP payload (RFC 4867) + * \param[out] payload Payload for RTP packet, contains speech data (if any) + * except for have 2 first bytes where header will be built + * \param[in] cmr AMR codec Mode Request + * \param[in] ft AMR Frame Type + * \param[in] bfi AMR Bad Frame Indicator + * \returns length of AMR data (header + ToC + speech data) or negative value + * on error + * + * Note: only octet-aligned mode is supported so the header occupies 2 full + * bytes. Optional interleaving header is not supported. + */ +int osmo_amr_rtp_enc(uint8_t *payload, uint8_t cmr, enum osmo_amr_type ft, + enum osmo_amr_quality bfi) +{ + if (cmr > 15) + return -EINVAL; + + if (ft > 15) + return -ENOTSUP; + + /* RFC 4867 § 4.3.1 payload header */ + payload[0] = cmr << 4; + + /* RFC 4867 § 4.4.2 ToC - compound payloads are not supported: F = 0 */ + payload[1] = (((uint8_t)ft) << 3) | (((uint8_t)bfi) << 2); + + /* speech data */ + return 2 + amr_len_by_ft[ft]; +} diff --git a/lib/decoding/osmocom/coding/CMakeLists.txt b/lib/decoding/osmocom/coding/CMakeLists.txt index 8e68577..c0a4d92 100644 --- a/lib/decoding/osmocom/coding/CMakeLists.txt +++ b/lib/decoding/osmocom/coding/CMakeLists.txt @@ -25,4 +25,3 @@ add_sources( gsm0503_parity.c gsm0503_tables.c ) - diff --git a/lib/decoding/osmocom/coding/gsm0503.h b/lib/decoding/osmocom/coding/gsm0503.h index e1c62a6..ccd421a 100644 --- a/lib/decoding/osmocom/coding/gsm0503.h +++ b/lib/decoding/osmocom/coding/gsm0503.h @@ -1,13 +1,13 @@ + /* - * gsm0503.h - * + * Copyright (C) 2011-2016 Sylvain Munaut * Copyright (C) 2016 sysmocom s.f.m.c. GmbH * * 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 + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -22,162 +22,264 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - - #include - #include -/*! \file gsm0503.h - * Osmocom convolutional encoder/decoder for xCCH channels, see 3GPP TS 05.03 - */ - -/*! \brief structure describing convolutional code xCCH - * - * Non-recursive code, flushed, not punctured code. +/*! structure describing xCCH convolutional code:. + * 228 bits blocks, rate 1/2, k = 5 + * G0 = 1 + D3 + D4 + * G1 = 1 + D + D3 + D4 */ extern const struct osmo_conv_code gsm0503_xcch; -/*! \brief structure describing convolutional code RACH +/*! structure describing RACH convolutional code. */ extern const struct osmo_conv_code gsm0503_rach; -/*! \brief structure describing convolutional code SCH +/*! structure describing Extended RACH (11 bit) convolutional code. + */ +extern const struct osmo_conv_code gsm0503_rach_ext; + +/*! structure describing SCH convolutional code. */ extern const struct osmo_conv_code gsm0503_sch; -/*! \brief structures describing convolutional codes CS2/3 +/*! structure describing CS2 convolutional code:. + * G0 = 1 + D3 + D4 + * G1 = 1 + D + D3 + D4 */ extern const struct osmo_conv_code gsm0503_cs2; + +/*! structure describing CS3 convolutional code:. + * G0 = 1 + D3 + D4 + * G1 = 1 + D + D3 + D4 + */ extern const struct osmo_conv_code gsm0503_cs3; -/*! \brief structure describing convolutional code TCH/FR +/*! structure describing CS2 convolutional code (non-punctured):. + * G0 = 1 + D3 + D4 + * G1 = 1 + D + D3 + D4 */ -extern const struct osmo_conv_code gsm0503_tch_fr; +extern const struct osmo_conv_code gsm0503_cs2_np; -/*! \brief structure describing convolutional code TCH/HR +/*! structure describing CS3 convolutional code (non-punctured):. + * G0 = 1 + D3 + D4 + * G1 = 1 + D + D3 + D4 */ -extern const struct osmo_conv_code gsm0503_tch_hr; +extern const struct osmo_conv_code gsm0503_cs3_np; -/*! \brief structure describing convolutional code TCH/AFS 12.2 +/*! structure describing TCH/AFS 12.2 kbits convolutional code:. + * 250 bits block, rate 1/2, punctured + * G0/G0 = 1 + * G1/G0 = 1 + D + D3 + D4 / 1 + D3 + D4 */ extern const struct osmo_conv_code gsm0503_tch_afs_12_2; -/*! \brief structure describing convolutional code TCH/AFS 10.2 +/*! structure describing TCH/AFS 10.2 kbits convolutional code:. + * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4 + * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4 + * G3/G3 = 1 */ extern const struct osmo_conv_code gsm0503_tch_afs_10_2; -/*! \brief structure describing convolutional code TCH/AFS 7.95 +/*! structure describing TCH/AFS 7.95 kbits convolutional code:. + * G4/G4 = 1 + * G5/G4 = 1 + D + D4 + D6 / 1 + D2 + D3 + D5 + D6 + * G6/G4 = 1 + D + D2 + D3 + D4 + D6 / 1 + D2 + D3 + D5 + D6 */ extern const struct osmo_conv_code gsm0503_tch_afs_7_95; -/*! \brief structure describing convolutional code TCH/AFS 7.4 +/*! structure describing TCH/AFS 7.4 kbits convolutional code:. + * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4 + * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4 + * G3/G3 = 1 */ extern const struct osmo_conv_code gsm0503_tch_afs_7_4; -/*! \brief structure describing convolutional code TCH/AFS 6.7 +/*! structure describing TCH/AFS 6.7 kbits convolutional code:. + * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4 + * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4 + * G3/G3 = 1 + * G3/G3 = 1 */ extern const struct osmo_conv_code gsm0503_tch_afs_6_7; -/*! \brief structure describing convolutional code TCH/AFS 5.9 +/*! structure describing TCH/AFS 5.9 kbits convolutional code:. + * 124 bits + * G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6 + * G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6 + * G6/G6 = 1 + * G6/G6 = 1 */ extern const struct osmo_conv_code gsm0503_tch_afs_5_9; -/*! \brief structure describing convolutional code TCH/AFS 5.15 +/*! structure describing TCH/AFS 5.15 kbits convolutional code:. + * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4 + * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4 + * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4 + * G3/G3 = 1 + * G3/G3 = 1 */ extern const struct osmo_conv_code gsm0503_tch_afs_5_15; -/*! \brief structure describing convolutional code TCH/AFS 4.75 +/*! structure describing TCH/AFS 4.75 kbits convolutional code:. + * G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6 + * G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6 + * G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6 + * G6/G6 = 1 + * G6/G6 = 1 */ extern const struct osmo_conv_code gsm0503_tch_afs_4_75; -/*! \brief structure describing convolutional code TCH/AHS 7.95 +/*! structure describing TCH/F convolutional code. + */ +extern const struct osmo_conv_code gsm0503_tch_fr; + +/*! structure describing TCH/H convolutional code. + */ +extern const struct osmo_conv_code gsm0503_tch_hr; + +/*! structure describing TCH/AHS 7.95 kbits convolutional code. */ extern const struct osmo_conv_code gsm0503_tch_ahs_7_95; -/*! \brief structure describing convolutional code TCH/AHS 7.4 +/*! structure describing TCH/AHS 7.4 kbits convolutional code. */ extern const struct osmo_conv_code gsm0503_tch_ahs_7_4; -/*! \brief structure describing convolutional code TCH/AHS 6.7 +/*! structure describing TCH/AHS 6.7 kbits convolutional code. */ extern const struct osmo_conv_code gsm0503_tch_ahs_6_7; -/*! \brief structure describing convolutional code TCH/AHS 5.9 +/*! structure describing TCH/AHS 5.9 kbits convolutional code. */ extern const struct osmo_conv_code gsm0503_tch_ahs_5_9; -/*! \brief structure describing convolutional code TCH/AHS 5.15 +/*! structure describing TCH/AHS 5.15 kbits convolutional code. */ extern const struct osmo_conv_code gsm0503_tch_ahs_5_15; -/*! \brief structure describing convolutional code TCH/AHS 4.75 +/*! structure describing TCH/AHS 4.75 kbits convolutional code. */ extern const struct osmo_conv_code gsm0503_tch_ahs_4_75; -/*! \brief structure describing convolutional code EDGE MCS-1 DL HDR +/*! structure describing EDGE MCS-1 DL header convolutional code:. + * 42 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs1_dl_hdr; -/*! \brief structure describing convolutional code EDGE MCS-1 UL HDR +/*! structure describing EDGE MCS-1 UL header convolutional code:. + * 45 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs1_ul_hdr; -/*! \brief structure describing convolutional code EDGE MCS-1 +/*! structure describing EDGE MCS-1 data convolutional code:. + * 196 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs1; -/*! \brief structure describing convolutional code EDGE MCS-2 +/*! structure describing EDGE MCS-2 data convolutional code:. + * 244 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs2; -/*! \brief structure describing convolutional code EDGE MCS-3 +/*! structure describing EDGE MCS-3 data convolutional code:. + * 316 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs3; -/*! \brief structure describing convolutional code EDGE MCS-4 +/*! structure describing EDGE MCS-4 data convolutional code:. + * 372 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs4; -/*! \brief structure describing convolutional code EDGE MCS-5 DL HDR +/*! structure describing EDGE MCS-5 DL header convolutional code:. + * 39 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs5_dl_hdr; -/*! \brief structure describing convolutional code EDGE MCS-5 UL HDR +/*! structure describing EDGE MCS-5 UL header convolutional code:. + * 51 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs5_ul_hdr; -/*! \brief structure describing convolutional code EDGE MCS-5 +/*! structure describing EDGE MCS-5 data convolutional code:. + * 468 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs5; -/*! \brief structure describing convolutional code EDGE MCS-6 +/*! structure describing EDGE MCS-6 data convolutional code:. + * 612 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs6; -/*! \brief structure describing convolutional code EDGE MCS-7 DL HDR +/*! structure describing EDGE MCS-7 DL header convolutional code:. + * 51 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs7_dl_hdr; -/*! \brief structure describing convolutional code EDGE MCS-7 UL HDR +/*! structure describing EDGE MCS-7 UL header convolutional code:. + * 60 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs7_ul_hdr; -/*! \brief structure describing convolutional code EDGE MCS-7 +/*! structure describing EDGE MCS-7 data convolutional code:. + * 468 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs7; -/*! \brief structure describing convolutional code EDGE MCS-8 +/*! structure describing EDGE MCS-8 data convolutional code:. + * 564 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs8; -/*! \brief structure describing convolutional code EDGE MCS-9 +/*! structure describing EDGE MCS-9 data convolutional code:. + * 612 bits blocks, rate 1/3, k = 7 + * G4 = 1 + D2 + D3 + D5 + D6 + * G7 = 1 + D + D2 + D3 + D6 + * G5 = 1 + D + D4 + D6 */ extern const struct osmo_conv_code gsm0503_mcs9; -#ifdef __cplusplus -} -#endif diff --git a/lib/decoding/osmocom/coding/gsm0503_coding.c b/lib/decoding/osmocom/coding/gsm0503_coding.c index afbba88..3812c9f 100644 --- a/lib/decoding/osmocom/coding/gsm0503_coding.c +++ b/lib/decoding/osmocom/coding/gsm0503_coding.c @@ -2,9 +2,12 @@ * (C) 2013 by Andreas Eversberg * (C) 2015 by Alexander Chemeris * (C) 2016 by Tom Tsou + * (C) 2017 by Harald Welte * * 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 @@ -24,31 +27,83 @@ #include #include #include - -#ifdef __cplusplus -extern "C" { -#endif +#include #include #include -#include +//#include #include #include -#include -#include "gsm0503.h" +//#include +#include +#include + +#include #include -#include "gsm0503_interleaving.h" -#include "gsm0503_mapping.h" -#include "gsm0503_tables.h" -#include "gsm0503_coding.h" -#include "gsm0503_parity.h" +#include +#include +#include +#include +#include + +/*! \mainpage libosmocoding Documentation + * + * \section sec_intro Introduction + * This library is a collection of definitions, tables and functions + * implementing the GSM/GPRS/EGPRS channel coding (and decoding) as + * specified in 3GPP TS 05.03 / 45.003. + * + * libosmocoding is developed as part of the Osmocom (Open Source Mobile + * Communications) project, a community-based, collaborative development + * project to create Free and Open Source implementations of mobile + * communications systems. For more information about Osmocom, please + * see https://osmocom.org/ + * + * \section sec_copyright Copyright and License + * Copyright © 2013 by Andreas Eversberg\n + * Copyright © 2015 by Alexander Chemeris\n + * Copyright © 2016 by Tom Tsou\n + * Documentation Copyright © 2017 by Harald Welte\n + * All rights reserved. \n\n + * The source code of libosmocoding is licensed under the terms of the GNU + * General Public License as published by the Free Software Foundation; + * either version 2 of the License, or (at your option) any later + * version.\n + * See or COPYING included in the source + * code package istelf.\n + * The information detailed here is provided AS IS with NO WARRANTY OF + * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. + * \n\n + * + * \section sec_tracker Homepage + Issue Tracker + * libosmocoding is distributed as part of libosmocore and shares its + * project page at http://osmocom.org/projects/libosmocore + * + * An Issue Tracker can be found at + * https://osmocom.org/projects/libosmocore/issues + * + * \section sec_contact Contact and Support + * Community-based support is available at the OpenBSC mailing list + * \n + * Commercial support options available upon request from + * + */ -#ifdef __cplusplus -} -#endif +/*! \addtogroup coding + * @{ + * + * GSM TS 05.03 coding + * + * This module is the "master module" of libosmocoding. It uses the + * various other modules (mapping, parity, interleaving) in order to + * implement the complete channel coding (and decoding) chain for the + * various channel types as defined in TS 05.03 / 45.003. + * + * \file gsm0503_coding.c */ /* * EGPRS coding limits @@ -76,29 +131,49 @@ extern "C" { #define EGPRS_DATA_C1 612 #define EGPRS_DATA_C2 EGPRS_DATA_C1 -/* TS 101318 Chapter 5.1: 260 bits + 4bit sig */ -#define GSM_FR_BYTES 33 -/* TS 101318 Chapter 5.2: 112 bits, no sig */ -#define GSM_HR_BYTES 14 -/* TS 101318 Chapter 5.3: 244 bits + 4bit sig */ -#define GSM_EFR_BYTES 31 +/*! union across the three different EGPRS Uplink header types */ +union gprs_rlc_ul_hdr_egprs { + struct gprs_rlc_ul_header_egprs_1 type1; + struct gprs_rlc_ul_header_egprs_2 type2; + struct gprs_rlc_ul_header_egprs_3 type3; +}; + +/*! union across the three different EGPRS Downlink header types */ +union gprs_rlc_dl_hdr_egprs { + struct gprs_rlc_dl_header_egprs_1 type1; + struct gprs_rlc_dl_header_egprs_2 type2; + struct gprs_rlc_dl_header_egprs_3 type3; +}; +/*! Structure describing a Modulation and Coding Scheme */ struct gsm0503_mcs_code { + /*! Modulation and Coding Scheme (MSC) number */ uint8_t mcs; + /*! Length of Uplink Stealing Flag (USF) in bits */ uint8_t usf_len; /* Header coding */ + /*! Length of header (bits) */ uint8_t hdr_len; + /*! Length of header convolutional code */ uint8_t hdr_code_len; + /*! Length of header code puncturing sequence */ uint8_t hdr_punc_len; + /*! header convolutional code */ const struct osmo_conv_code *hdr_conv; + /*! header puncturing sequence */ const uint8_t *hdr_punc; /* Data coding */ + /*! length of data (bits) */ uint16_t data_len; + /*! length of data convolutional code */ uint16_t data_code_len; + /*! length of data code puncturing sequence */ uint16_t data_punc_len; + /*! data convolutional code */ const struct osmo_conv_code *data_conv; + /*! data puncturing sequences */ const uint8_t *data_punc[3]; }; @@ -113,7 +188,7 @@ static int osmo_conv_decode_ber(const struct osmo_conv_code *code, if (n_bits_total || n_errors) { coded_len = osmo_conv_encode(code, output, recoded); - OSMO_ASSERT(sizeof(recoded) / sizeof(recoded[0]) >= coded_len); + //OSMO_ASSERT(sizeof(recoded) / sizeof(recoded[0]) >= coded_len); } /* Count bit errors */ @@ -132,7 +207,13 @@ static int osmo_conv_decode_ber(const struct osmo_conv_code *code, return res; } -static int _xcch_decode_cB(uint8_t *l2_data, sbit_t *cB, +/*! convenience wrapper for decoding coded bits + * \param[out] l2_data caller-allocated buffer for L2 Frame + * \param[in] cB 456 coded (soft) bits as per TS 05.03 4.1.3 + * \param[out] n_errors Number of detected errors + * \param[out] n_bits_total Number of total coded bits + * \returns 0 on success; -1 on CRC error */ +static int _xcch_decode_cB(uint8_t *l2_data, const sbit_t *cB, int *n_errors, int *n_bits_total) { ubit_t conv[224]; @@ -151,7 +232,11 @@ static int _xcch_decode_cB(uint8_t *l2_data, sbit_t *cB, return 0; } -static int _xcch_encode_cB(ubit_t *cB, uint8_t *l2_data) +/*! convenience wrapper for encoding to coded bits + * \param[out] cB caller-allocated buffer for 456 coded bits as per TS 05.03 4.1.3 + * \param[out] l2_data to-be-encoded L2 Frame + * \returns 0 */ +static int _xcch_encode_cB(ubit_t *cB, const uint8_t *l2_data) { ubit_t conv[224]; @@ -167,7 +252,14 @@ static int _xcch_encode_cB(ubit_t *cB, uint8_t *l2_data) /* * GSM xCCH block transcoding */ -int gsm0503_xcch_decode(uint8_t *l2_data, sbit_t *bursts, + +/*! Decoding of xCCH data from bursts to L2 frame + * \param[out] l2_data caller-allocated output data buffer + * \param[in] bursts four GSM bursts in soft-bits + * \param[out] n_errors Number of detected errors + * \param[out] n_bits_total Number of total coded bits + */ +int gsm0503_xcch_decode(uint8_t *l2_data, const sbit_t *bursts, int *n_errors, int *n_bits_total) { sbit_t iB[456], cB[456]; @@ -181,7 +273,12 @@ int gsm0503_xcch_decode(uint8_t *l2_data, sbit_t *bursts, return _xcch_decode_cB(l2_data, cB, n_errors, n_bits_total); } -int gsm0503_xcch_encode(ubit_t *bursts, uint8_t *l2_data) +/*! Encoding of xCCH data from L2 frame to bursts + * \param[out] bursts caller-allocated burst data (unpacked bits) + * \param[in] l2_data L2 input data (MAC block) + * \returns 0 + */ +int gsm0503_xcch_encode(ubit_t *bursts, const uint8_t *l2_data) { ubit_t iB[456], cB[456], hl = 1, hn = 1; int i; @@ -202,7 +299,14 @@ int gsm0503_xcch_encode(ubit_t *bursts, uint8_t *l2_data) * GSM PDTCH block transcoding */ -int gsm0503_pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p, +/*! Decode GPRS PDTCH + * \param[out] l2_data caller-allocated buffer for L2 Frame + * \param[in] bursts burst input data as soft unpacked bits + * \param[out] usf_p uplink stealing flag + * \param[out] n_errors number of detected bit-errors + * \param[out] n_bits_total total number of dcoded bits + * \returns 0 on success; negative on error */ +int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p, int *n_errors, int *n_bits_total) { sbit_t iB[456], cB[676], hl_hn[8]; @@ -219,7 +323,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p, if (i == 0 || k < best) { best = k; - cs = i+1; + cs = i + 1; } } @@ -246,7 +350,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p, cB[i] = 0; } - osmo_conv_decode_ber(&gsm0503_cs2, cB, + osmo_conv_decode_ber(&gsm0503_cs2_np, cB, conv, n_errors, n_bits_total); for (i = 0; i < 8; i++) { @@ -281,7 +385,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p, cB[i] = 0; } - osmo_conv_decode_ber(&gsm0503_cs3, cB, + osmo_conv_decode_ber(&gsm0503_cs3_np, cB, conv, n_errors, n_bits_total); for (i = 0; i < 8; i++) { @@ -351,7 +455,13 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p, return -1; } -int gsm0503_pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) + +/*! GPRS DL message encoding + * \param[out] bursts caller-allocated buffer for unpacked burst bits + * \param[in] l2_data L2 (MAC) block to be encoded + * \param[in] l2_len length of l2_data in bytes, used to determine CS + * \returns 0 on success; negative on error */ +int gsm0503_pdtch_encode(ubit_t *bursts, const uint8_t *l2_data, uint8_t l2_len) { ubit_t iB[456], cB[676]; const ubit_t *hl_hn; @@ -378,7 +488,7 @@ int gsm0503_pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) memcpy(conv, gsm0503_usf2six[usf], 6); - osmo_conv_encode(&gsm0503_cs2, conv, cB); + osmo_conv_encode(&gsm0503_cs2_np, conv, cB); for (i = 0, j = 0; i < 588; i++) if (!gsm0503_puncture_cs2[i]) @@ -396,7 +506,7 @@ int gsm0503_pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) memcpy(conv, gsm0503_usf2six[usf], 6); - osmo_conv_encode(&gsm0503_cs3, conv, cB); + osmo_conv_encode(&gsm0503_cs3_np, conv, cB); for (i = 0, j = 0; i < 676; i++) if (!gsm0503_puncture_cs3[i]) @@ -435,8 +545,12 @@ int gsm0503_pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) * GSM TCH/F FR/EFR transcoding */ +/*! assemble a FR codec frame in format as used inside RTP + * \param[out] tch_data Codec frame in RTP format + * \param[in] b_bits Codec frame in 'native' format + * \param[in] net_order FIXME */ static void tch_fr_reassemble(uint8_t *tch_data, - ubit_t *b_bits, int net_order) + const ubit_t *b_bits, int net_order) { int i, j, k, l, o; @@ -468,7 +582,7 @@ static void tch_fr_reassemble(uint8_t *tch_data, } static void tch_fr_disassemble(ubit_t *b_bits, - uint8_t *tch_data, int net_order) + const uint8_t *tch_data, int net_order) { int i, j, k, l, o; @@ -495,7 +609,8 @@ static void tch_fr_disassemble(ubit_t *b_bits, } } -static void tch_hr_reassemble(uint8_t *tch_data, ubit_t *b_bits) +/* assemble a HR codec frame in format as used inside RTP */ +static void tch_hr_reassemble(uint8_t *tch_data, const ubit_t *b_bits) { int i, j; @@ -506,7 +621,7 @@ static void tch_hr_reassemble(uint8_t *tch_data, ubit_t *b_bits) tch_data[j >> 3] |= (b_bits[i] << (7 - (j & 7))); } -static void tch_hr_disassemble(ubit_t *b_bits, uint8_t *tch_data) +static void tch_hr_disassemble(ubit_t *b_bits, const uint8_t *tch_data) { int i, j; @@ -514,7 +629,8 @@ static void tch_hr_disassemble(ubit_t *b_bits, uint8_t *tch_data) b_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1; } -static void tch_efr_reassemble(uint8_t *tch_data, ubit_t *b_bits) +/* assemble a EFR codec frame in format as used inside RTP */ +static void tch_efr_reassemble(uint8_t *tch_data, const ubit_t *b_bits) { int i, j; @@ -525,7 +641,7 @@ static void tch_efr_reassemble(uint8_t *tch_data, ubit_t *b_bits) tch_data[j >> 3] |= (b_bits[i] << (7 - (j & 7))); } -static void tch_efr_disassemble(ubit_t *b_bits, uint8_t *tch_data) +static void tch_efr_disassemble(ubit_t *b_bits, const uint8_t *tch_data) { int i, j; @@ -533,7 +649,8 @@ static void tch_efr_disassemble(ubit_t *b_bits, uint8_t *tch_data) b_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1; } -static void tch_amr_reassemble(uint8_t *tch_data, ubit_t *d_bits, int len) +/* assemble a AMR codec frame in format as used inside RTP */ +static void tch_amr_reassemble(uint8_t *tch_data, const ubit_t *d_bits, int len) { int i, j; @@ -543,7 +660,7 @@ static void tch_amr_reassemble(uint8_t *tch_data, ubit_t *d_bits, int len) tch_data[j >> 3] |= (d_bits[i] << (7 - (j & 7))); } -static void tch_amr_disassemble(ubit_t *d_bits, uint8_t *tch_data, int len) +static void tch_amr_disassemble(ubit_t *d_bits, const uint8_t *tch_data, int len) { int i, j; @@ -551,7 +668,8 @@ static void tch_amr_disassemble(ubit_t *d_bits, uint8_t *tch_data, int len) d_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1; } -static void tch_fr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) +/* re-arrange according to TS 05.03 Table 2 (receiver) */ +static void tch_fr_d_to_b(ubit_t *b_bits, const ubit_t *d_bits) { int i; @@ -559,7 +677,8 @@ static void tch_fr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) b_bits[gsm610_bitorder[i]] = d_bits[i]; } -static void tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) +/* re-arrange according to TS 05.03 Table 2 (transmitter) */ +static void tch_fr_b_to_d(ubit_t *d_bits, const ubit_t *b_bits) { int i; @@ -567,7 +686,8 @@ static void tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) d_bits[i] = b_bits[gsm610_bitorder[i]]; } -static void tch_hr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) +/* re-arrange according to TS 05.03 Table 3a (receiver) */ +static void tch_hr_d_to_b(ubit_t *b_bits, const ubit_t *d_bits) { int i; @@ -582,7 +702,8 @@ static void tch_hr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) b_bits[map[i]] = d_bits[i]; } -static void tch_hr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) +/* re-arrange according to TS 05.03 Table 3a (transmitter) */ +static void tch_hr_b_to_d(ubit_t *d_bits, const ubit_t *b_bits) { int i; const uint16_t *map; @@ -596,7 +717,8 @@ static void tch_hr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) d_bits[i] = b_bits[map[i]]; } -static void tch_efr_d_to_w(ubit_t *b_bits, ubit_t *d_bits) +/* re-arrange according to TS 05.03 Table 6 (receiver) */ +static void tch_efr_d_to_w(ubit_t *b_bits, const ubit_t *d_bits) { int i; @@ -604,7 +726,8 @@ static void tch_efr_d_to_w(ubit_t *b_bits, ubit_t *d_bits) b_bits[gsm660_bitorder[i]] = d_bits[i]; } -static void tch_efr_w_to_d(ubit_t *d_bits, ubit_t *b_bits) +/* re-arrange according to TS 05.03 Table 6 (transmitter) */ +static void tch_efr_w_to_d(ubit_t *d_bits, const ubit_t *b_bits) { int i; @@ -612,7 +735,8 @@ static void tch_efr_w_to_d(ubit_t *d_bits, ubit_t *b_bits) d_bits[i] = b_bits[gsm660_bitorder[i]]; } -static void tch_efr_protected(ubit_t *s_bits, ubit_t *b_bits) +/* extract the 65 protected class1a+1b bits */ +static void tch_efr_protected(const ubit_t *s_bits, ubit_t *b_bits) { int i; @@ -620,7 +744,7 @@ static void tch_efr_protected(ubit_t *s_bits, ubit_t *b_bits) b_bits[i] = s_bits[gsm0503_gsm_efr_protected_bits[i] - 1]; } -static void tch_fr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) +static void tch_fr_unreorder(ubit_t *d, ubit_t *p, const ubit_t *u) { int i; @@ -633,7 +757,7 @@ static void tch_fr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) p[i] = u[91 + i]; } -static void tch_fr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) +static void tch_fr_reorder(ubit_t *u, const ubit_t *d, const ubit_t *p) { int i; @@ -646,19 +770,19 @@ static void tch_fr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) u[91 + i] = p[i]; } -static void tch_hr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) +static void tch_hr_unreorder(ubit_t *d, ubit_t *p, const ubit_t *u) { memcpy(d, u, 95); memcpy(p, u + 95, 3); } -static void tch_hr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) +static void tch_hr_reorder(ubit_t *u, const ubit_t *d, const ubit_t *p) { memcpy(u, d, 95); memcpy(u + 95, p, 3); } -static void tch_efr_reorder(ubit_t *w, ubit_t *s, ubit_t *p) +static void tch_efr_reorder(ubit_t *w, const ubit_t *s, const ubit_t *p) { memcpy(w, s, 71); w[71] = w[72] = s[69]; @@ -672,55 +796,66 @@ static void tch_efr_reorder(ubit_t *w, ubit_t *s, ubit_t *p) memcpy(w + 252, p, 8); } -static void tch_efr_unreorder(ubit_t *s, ubit_t *p, ubit_t *w) +static void tch_efr_unreorder(ubit_t *s, ubit_t *p, const ubit_t *w) { int sum; memcpy(s, w, 71); sum = s[69] + w[71] + w[72]; - s[69] = (sum > 2); + s[69] = (sum >= 2); memcpy(s + 71, w + 73, 50); sum = s[119] + w[123] + w[124]; - s[119] = (sum > 2); + s[119] = (sum >= 2); memcpy(s + 121, w + 125, 53); sum = s[172] + w[178] + w[179]; s[172] = (sum > 2); memcpy(s + 174, w + 180, 50); - sum = s[220] + w[230] + w[231]; - s[222] = (sum > 2); + sum = s[222] + w[230] + w[231]; + s[222] = (sum >= 2); memcpy(s + 224, w + 232, 20); memcpy(p, w + 252, 8); } -static void tch_amr_merge(ubit_t *u, ubit_t *d, ubit_t *p, int len, int prot) +static void tch_amr_merge(ubit_t *u, const ubit_t *d, const ubit_t *p, int len, int prot) { memcpy(u, d, prot); memcpy(u + prot, p, 6); memcpy(u + prot + 6, d + prot, len - prot); } -static void tch_amr_unmerge(ubit_t *d, ubit_t *p, - ubit_t *u, int len, int prot) +static void tch_amr_unmerge(ubit_t *d, ubit_t *p, const ubit_t *u, int len, int prot) { memcpy(d, u, prot); - memcpy(p, u+prot, 6); + memcpy(p, u + prot, 6); memcpy(d + prot, u + prot + 6, len - prot); } -int gsm0503_tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, +/*! Perform channel decoding of a FR/EFR channel according TS 05.03 + * \param[out] tch_data Codec frame in RTP payload format + * \param[in] bursts buffer containing the symbols of 8 bursts + * \param[in] net_order FIXME + * \param[in] efr Is this channel using EFR (1) or FR (0) + * \param[out] n_errors Number of detected bit errors + * \param[out] n_bits_total Total number of bits + * \returns length of bytes used in \a tch_data output buffer */ +int gsm0503_tch_fr_decode(uint8_t *tch_data, const sbit_t *bursts, int net_order, int efr, int *n_errors, int *n_bits_total) { sbit_t iB[912], cB[456], h; ubit_t conv[185], s[244], w[260], b[65], d[260], p[8]; int i, rv, len, steal = 0; - for (i=0; i<8; i++) { + /* map from 8 bursts to interleaved data bits (iB) */ + for (i = 0; i < 8; i++) { gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, i >> 2); steal -= h; } + /* we now have the bits of the four bursts (interface 4 in + * Figure 1a of TS 05.03 */ gsm0503_tch_fr_deinterleave(cB, iB); + /* we now have the coded bits c(B): interface 3 in Fig. 1a */ if (steal > 0) { rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total); @@ -733,12 +868,15 @@ int gsm0503_tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, } osmo_conv_decode_ber(&gsm0503_tch_fr, cB, conv, n_errors, n_bits_total); + /* we now have the data bits 'u': interface 2 in Fig. 1a */ + /* input: 'conv', output: d[ata] + p[arity] */ tch_fr_unreorder(d, p, conv); for (i = 0; i < 78; i++) d[i + 182] = (cB[i + 378] < 0) ? 1 : 0; + /* check if parity of first 50 (class 1) 'd'-bits match 'p' */ rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d, 50, p); if (rv) { /* Error checking CRC8 for the FR part of an EFR/FR frame */ @@ -747,11 +885,17 @@ int gsm0503_tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, if (efr) { tch_efr_d_to_w(w, d); + /* we now have the preliminary-coded bits w(k) */ tch_efr_unreorder(s, p, w); + /* we now have the data delivered to the preliminary + * channel encoding unit s(k) */ + /* extract the 65 most important bits according TS 05.03 3.1.1.1 */ tch_efr_protected(s, b); + /* perform CRC-8 on 65 most important bits (50 bits of + * class 1a + 15 bits of class 1b) */ rv = osmo_crc8gen_check_bits(&gsm0503_tch_efr_crc8, b, 65, p); if (rv) { /* Error checking CRC8 for the EFR part of an EFR frame */ @@ -772,7 +916,13 @@ int gsm0503_tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, return len; } -int gsm0503_tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, +/*! Perform channel encoding on a TCH/FS channel according to TS 05.03 + * \param[out] bursts caller-allocated output buffer for bursts bits + * \param[in] tch_data Codec input data in RTP payload format + * \param[in] len Length of \a tch_data in bytes + * \param[in] net_order FIXME + * \returns 0 in case of success; negative on error */ +int gsm0503_tch_fr_encode(ubit_t *bursts, const uint8_t *tch_data, int len, int net_order) { ubit_t iB[912], cB[456], h; @@ -830,7 +980,14 @@ coding_efr_fr: return 0; } -int gsm0503_tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd, +/*! Perform channel decoding of a HR(v1) channel according TS 05.03 + * \param[out] tch_data Codec frame in RTP payload format + * \param[in] bursts buffer containing the symbols of 8 bursts + * \param[in] odd Odd (1) or even (0) frame number + * \param[out] n_errors Number of detected bit errors + * \param[out] n_bits_total Total number of bits + * \returns length of bytes used in \a tch_data output buffer */ +int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd, int *n_errors, int *n_bits_total) { sbit_t iB[912], cB[456], h; @@ -900,7 +1057,12 @@ int gsm0503_tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd, return 15; } -int gsm0503_tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len) +/*! Perform channel encoding on a TCH/HS channel according to TS 05.03 + * \param[out] bursts caller-allocated output buffer for bursts bits + * \param[in] tch_data Codec input data in RTP payload format + * \param[in] len Length of \a tch_data in bytes + * \returns 0 in case of success; negative on error */ +int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len) { ubit_t iB[912], cB[456], h; ubit_t conv[98], b[112], d[112], p[3]; @@ -937,12 +1099,12 @@ int gsm0503_tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len) gsm0503_tch_fr_interleave(cB, iB); - for (i=0; i<6; i++) { + for (i = 0; i < 6; i++) { gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i >> 2); } - for (i=2; i<4; i++) { + for (i = 2; i < 4; i++) { gsm0503_tch_burst_map(&iB[i * 114 + 456], &bursts[i * 116], &h, 1); } @@ -955,7 +1117,18 @@ int gsm0503_tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len) return 0; } -int gsm0503_tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, +/*! Perform channel decoding of a TCH/AFS channel according TS 05.03 + * \param[out] tch_data Codec frame in RTP payload format + * \param[in] bursts buffer containing the symbols of 8 bursts + * \param[in] codec_mode_req is this CMR (1) or CMC (0) + * \param[in] codec array of active codecs (active codec set) + * \param[in] codecs number of codecs in \a codec + * \param ft Frame Type; Input if \a codec_mode_req = 1, Output * otherwise + * \param[out] cmr Output in \a codec_mode_req = 1 + * \param[out] n_errors Number of detected bit errors + * \param[out] n_bits_total Total number of bits + * \returns length of bytes used in \a tch_data output buffer */ +int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts, int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, int *n_errors, int *n_bits_total) { @@ -1150,7 +1323,17 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, return len; } -int gsm0503_tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len, +/*! Perform channel encoding on a TCH/AFS channel according to TS 05.03 + * \param[out] bursts caller-allocated output buffer for bursts bits + * \param[in] tch_data Codec input data in RTP payload format + * \param[in] len Length of \a tch_data in bytes + * \param[in] codec_mode_req Use CMR (1) or FT (0) + * \param[in] codec Array of codecs (active codec set) + * \param[in] codecs Number of entries in \a codec + * \param[in] ft Frame Type to be used for encoding (index to \a codec) + * \param[in] cmr Codec Mode Request (used in codec_mode_req = 1 only) + * \returns 0 in case of success; negative on error */ +int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len, int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr) { @@ -1310,7 +1493,19 @@ invalid_length: return -1; } -int gsm0503_tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, +/*! Perform channel decoding of a TCH/AFS channel according TS 05.03 + * \param[out] tch_data Codec frame in RTP payload format + * \param[in] bursts buffer containing the symbols of 8 bursts + * \param[in] odd Is this an odd (1) or even (0) frame number? + * \param[in] codec_mode_req is this CMR (1) or CMC (0) + * \param[in] codec array of active codecs (active codec set) + * \param[in] codecs number of codecs in \a codec + * \param ft Frame Type; Input if \a codec_mode_req = 1, Output * otherwise + * \param[out] cmr Output in \a codec_mode_req = 1 + * \param[out] n_errors Number of detected bit errors + * \param[out] n_bits_total Total number of bits + * \returns length of bytes used in \a tch_data output buffer */ +int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd, int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, int *n_errors, int *n_bits_total) { @@ -1513,7 +1708,17 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, return len; } -int gsm0503_tch_ahs_encode(ubit_t *bursts, uint8_t *tch_data, int len, +/*! Perform channel encoding on a TCH/AHS channel according to TS 05.03 + * \param[out] bursts caller-allocated output buffer for bursts bits + * \param[in] tch_data Codec input data in RTP payload format + * \param[in] len Length of \a tch_data in bytes + * \param[in] codec_mode_req Use CMR (1) or FT (0) + * \param[in] codec Array of codecs (active codec set) + * \param[in] codecs Number of entries in \a codec + * \param[in] ft Frame Type to be used for encoding (index to \a codec) + * \param[in] cmr Codec Mode Request (used in codec_mode_req = 1 only) + * \returns 0 in case of success; negative on error */ +int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len, int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr) { @@ -1676,46 +1881,140 @@ invalid_length: * b(0) = MSB of PLMN colour code * b(5) = LSB of BS colour code */ -static int rach_apply_bsic(ubit_t *d, uint8_t bsic) +static inline void rach_apply_bsic(ubit_t *d, uint8_t bsic, uint8_t start) { int i; /* Apply it */ for (i = 0; i < 6; i++) - d[8 + i] ^= ((bsic >> (5 - i)) & 1); - - return 0; + d[start + i] ^= ((bsic >> (5 - i)) & 1); } -int gsm0503_rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic) +static inline int16_t rach_decode_ber(const sbit_t *burst, uint8_t bsic, bool is_11bit, + int *n_errors, int *n_bits_total) { - ubit_t conv[14]; + ubit_t conv[17]; + uint8_t ra[2] = { 0 }, nbits = is_11bit ? 11 : 8; int rv; - osmo_conv_decode(&gsm0503_rach, burst, conv); + osmo_conv_decode_ber(is_11bit ? &gsm0503_rach_ext : &gsm0503_rach, burst, conv, + n_errors, n_bits_total); - rach_apply_bsic(conv, bsic); + rach_apply_bsic(conv, bsic, nbits); - rv = osmo_crc8gen_check_bits(&gsm0503_rach_crc6, conv, 8, conv + 8); + rv = osmo_crc8gen_check_bits(&gsm0503_rach_crc6, conv, nbits, conv + nbits); if (rv) return -1; - osmo_ubit2pbit_ext(ra, 0, conv, 0, 8, 1); + osmo_ubit2pbit_ext(ra, 0, conv, 0, nbits, 1); + + return is_11bit ? osmo_load16le(ra) : ra[0]; +} + +/*! Decode the Extended (11-bit) RACH according to 3GPP TS 45.003 + * \param[out] ra output buffer for RACH data + * \param[in] burst Input burst data + * \param[in] bsic BSIC used in this cell + * \returns 0 on success; negative on error (e.g. CRC error) */ +int gsm0503_rach_ext_decode(uint16_t *ra, const sbit_t *burst, uint8_t bsic) +{ + int16_t r = rach_decode_ber(burst, bsic, true, NULL, NULL); + + if (r < 0) + return r; + + *ra = r; + + return 0; +} + +/*! Decode the (8-bit) RACH according to TS 05.03 + * \param[out] ra output buffer for RACH data + * \param[in] burst Input burst data + * \param[in] bsic BSIC used in this cell + * \returns 0 on success; negative on error (e.g. CRC error) */ +int gsm0503_rach_decode(uint8_t *ra, const sbit_t *burst, uint8_t bsic) +{ + int16_t r = rach_decode_ber(burst, bsic, false, NULL, NULL); + if (r < 0) + return r; + + *ra = r; + return 0; +} + +/*! Decode the Extended (11-bit) RACH according to 3GPP TS 45.003 + * \param[out] ra output buffer for RACH data + * \param[in] burst Input burst data + * \param[in] bsic BSIC used in this cell + * \param[out] n_errors Number of detected bit errors + * \param[out] n_bits_total Total number of bits + * \returns 0 on success; negative on error (e.g. CRC error) */ +int gsm0503_rach_ext_decode_ber(uint16_t *ra, const sbit_t *burst, uint8_t bsic, + int *n_errors, int *n_bits_total) +{ + int16_t r = rach_decode_ber(burst, bsic, true, n_errors, n_bits_total); + if (r < 0) + return r; + + *ra = r; + return 0; +} + +/*! Decode the (8-bit) RACH according to TS 05.03 + * \param[out] ra output buffer for RACH data + * \param[in] burst Input burst data + * \param[in] bsic BSIC used in this cell + * \param[out] n_errors Number of detected bit errors + * \param[out] n_bits_total Total number of bits + * \returns 0 on success; negative on error (e.g. CRC error) */ +int gsm0503_rach_decode_ber(uint8_t *ra, const sbit_t *burst, uint8_t bsic, + int *n_errors, int *n_bits_total) +{ + int16_t r = rach_decode_ber(burst, bsic, false, n_errors, n_bits_total); + + if (r < 0) + return r; + + *ra = r; return 0; } -int gsm0503_rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic) +/*! Encode the (8-bit) RACH according to TS 05.03 + * \param[out] burst Caller-allocated output burst buffer + * \param[in] ra Input RACH data + * \param[in] bsic BSIC used in this cell + * \returns 0 on success; negative on error */ +int gsm0503_rach_encode(ubit_t *burst, const uint8_t *ra, uint8_t bsic) +{ + return gsm0503_rach_ext_encode(burst, *ra, bsic, false); +} + +/*! Encode the Extended (11-bit) or regular (8-bit) RACH according to 3GPP TS 45.003 + * \param[out] burst Caller-allocated output burst buffer + * \param[in] ra11 Input RACH data + * \param[in] bsic BSIC used in this cell + * \param[in] is_11bit whether given RA is 11 bit or not + * \returns 0 on success; negative on error */ +int gsm0503_rach_ext_encode(ubit_t *burst, uint16_t ra11, uint8_t bsic, bool is_11bit) { - ubit_t conv[14]; + ubit_t conv[17]; + uint8_t ra[2] = { 0 }, nbits = 8; - osmo_pbit2ubit_ext(conv, 0, ra, 0, 8, 1); + if (is_11bit) { + osmo_store16le(ra11, ra); + nbits = 11; + } else + ra[0] = (uint8_t)ra11; - osmo_crc8gen_set_bits(&gsm0503_rach_crc6, conv, 8, conv + 8); + osmo_pbit2ubit_ext(conv, 0, ra, 0, nbits, 1); - rach_apply_bsic(conv, bsic); + osmo_crc8gen_set_bits(&gsm0503_rach_crc6, conv, nbits, conv + nbits); - osmo_conv_encode(&gsm0503_rach, conv, burst); + rach_apply_bsic(conv, bsic, nbits); + + osmo_conv_encode(is_11bit ? &gsm0503_rach_ext : &gsm0503_rach, conv, burst); return 0; } @@ -1723,7 +2022,12 @@ int gsm0503_rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic) /* * GSM SCH transcoding */ -int gsm0503_sch_decode(uint8_t *sb_info, sbit_t *burst) + +/*! Decode the SCH according to TS 05.03 + * \param[out] sb_info output buffer for SCH data + * \param[in] burst Input burst data + * \returns 0 on success; negative on error (e.g. CRC error) */ +int gsm0503_sch_decode(uint8_t *sb_info, const sbit_t *burst) { ubit_t conv[35]; int rv; @@ -1739,7 +2043,11 @@ int gsm0503_sch_decode(uint8_t *sb_info, sbit_t *burst) return 0; } -int gsm0503_sch_encode(ubit_t *burst, uint8_t *sb_info) +/*! Encode the SCH according to TS 05.03 + * \param[out] burst Caller-allocated output burst buffer + * \param[in] sb_info Input SCH data + * \returns 0 on success; negative on error */ +int gsm0503_sch_encode(ubit_t *burst, const uint8_t *sb_info) { ubit_t conv[35]; @@ -1751,3 +2059,5 @@ int gsm0503_sch_encode(ubit_t *burst, uint8_t *sb_info) return 0; } + +/*! @} */ diff --git a/lib/decoding/osmocom/coding/gsm0503_coding.h b/lib/decoding/osmocom/coding/gsm0503_coding.h index 5e3e9db..98038f8 100644 --- a/lib/decoding/osmocom/coding/gsm0503_coding.h +++ b/lib/decoding/osmocom/coding/gsm0503_coding.h @@ -1,68 +1,83 @@ -/* - * (C) 2013 by Andreas Eversberg - * (C) 2015 by Alexander Chemeris - * (C) 2016 by Tom Tsou - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +/*! \file gsm0503_coding.h + * GSM TS 05.03 coding */ #pragma once #include + +#include #include +/*! \addtogroup coding + * @{ + * \file gsm0503_coding.h */ + #define GSM0503_GPRS_BURSTS_NBITS (116 * 4) #define GSM0503_EGPRS_BURSTS_NBITS (348 * 4) -#define NUM_BYTES(N) ((N + 8 - 1) / 8) - +enum gsm0503_egprs_mcs { + EGPRS_MCS0, + EGPRS_MCS1, + EGPRS_MCS2, + EGPRS_MCS3, + EGPRS_MCS4, + EGPRS_MCS5, + EGPRS_MCS6, + EGPRS_MCS7, + EGPRS_MCS8, + EGPRS_MCS9, + EGPRS_NUM_MCS, +}; -int gsm0503_xcch_encode(ubit_t *bursts, uint8_t *l2_data); -int gsm0503_xcch_decode(uint8_t *l2_data, sbit_t *bursts, +int gsm0503_xcch_encode(ubit_t *bursts, const uint8_t *l2_data); +int gsm0503_xcch_decode(uint8_t *l2_data, const sbit_t *bursts, int *n_errors, int *n_bits_total); -int gsm0503_pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); -int gsm0503_pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p, +int gsm0503_pdtch_encode(ubit_t *bursts, const uint8_t *l2_data, uint8_t l2_len); +int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p, int *n_errors, int *n_bits_total); -int gsm0503_tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, +int gsm0503_pdtch_egprs_encode(ubit_t *bursts, const uint8_t *l2_data, + uint8_t l2_len); +int gsm0503_pdtch_egprs_decode(uint8_t *l2_data, const sbit_t *bursts, + uint16_t nbits, uint8_t *usf_p, int *n_errors, int *n_bits_total); + +int gsm0503_tch_fr_encode(ubit_t *bursts, const uint8_t *tch_data, int len, int net_order); -int gsm0503_tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, +int gsm0503_tch_fr_decode(uint8_t *tch_data, const sbit_t *bursts, int net_order, int efr, int *n_errors, int *n_bits_total); -int gsm0503_tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len); -int gsm0503_tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd, +int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len); +int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd, int *n_errors, int *n_bits_total); -int gsm0503_tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len, +int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len, int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr); -int gsm0503_tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, +int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts, int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, int *n_errors, int *n_bits_total); -int gsm0503_tch_ahs_encode(ubit_t *bursts, uint8_t *tch_data, int len, +int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len, int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr); -int gsm0503_tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, +int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd, int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, int *n_errors, int *n_bits_total); -int gsm0503_rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic); -int gsm0503_rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic); +int gsm0503_rach_ext_encode(ubit_t *burst, uint16_t ra, uint8_t bsic, bool is_11bit); +int gsm0503_rach_encode(ubit_t *burst, const uint8_t *ra, uint8_t bsic) OSMO_DEPRECATED("Use gsm0503_rach_ext_encode() instead"); + +int gsm0503_rach_decode(uint8_t *ra, const sbit_t *burst, uint8_t bsic) + OSMO_DEPRECATED("Use gsm0503_rach_decode_ber() instead"); +int gsm0503_rach_decode_ber(uint8_t *ra, const sbit_t *burst, uint8_t bsic, + int *n_errors, int *n_bits_total); +int gsm0503_rach_ext_decode(uint16_t *ra, const sbit_t *burst, uint8_t bsic) + OSMO_DEPRECATED("Use gsm0503_rach_ext_decode_ber() instead"); +int gsm0503_rach_ext_decode_ber(uint16_t *ra, const sbit_t *burst, uint8_t bsic, + int *n_errors, int *n_bits_total); + +int gsm0503_sch_encode(ubit_t *burst, const uint8_t *sb_info); +int gsm0503_sch_decode(uint8_t *sb_info, const sbit_t *burst); -int gsm0503_sch_encode(ubit_t *burst, uint8_t *sb_info); -int gsm0503_sch_decode(uint8_t *sb_info, sbit_t *burst); +/*! @} */ diff --git a/lib/decoding/osmocom/coding/gsm0503_interleaving.c b/lib/decoding/osmocom/coding/gsm0503_interleaving.c index 333e105..d5008d0 100644 --- a/lib/decoding/osmocom/coding/gsm0503_interleaving.c +++ b/lib/decoding/osmocom/coding/gsm0503_interleaving.c @@ -1,9 +1,12 @@ /* * (C) 2013 by Andreas Eversberg * (C) 2016 by Tom Tsou + * (C) 2017 by Hrald Welte * * 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 @@ -23,11 +26,17 @@ #include #include -#include "gsm0503_tables.h" -#include "gsm0503_interleaving.h" +#include +#include -/* - * GSM xCCH interleaving and burst mapping +/*! \addtogroup interleaving + * @{ + * GSM TS 05.03 interleaving + * + * This module contains interleaving / de-interleaving routines for + * various channel types, as defined in 3GPP TS 05.03 / 45.003. + * + * GSM xCCH interleaving and burst mapping: * * Interleaving: * @@ -46,8 +55,34 @@ * e(B, 58) = h_n(B) * * Where hl(B) and hn(B) are bits in burst B indicating flags. - */ + * + * GSM TCH HR/AHS interleaving and burst mapping: + * + * Interleaving: + * + * Given 288 coded input bits, form 4 blocks of 114 bits, + * where even bits of the first 2 blocks and odd bits of the last 2 blocks + * are used: + * + * i(B, j) = c(n, k) k = 0, ..., 227 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 2n + b + * j, b = table[k]; + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where hl(B) and hn(B) are bits in burst B indicating flags. + * + * \file gsm0503_interleaving.c */ +/*! De-Interleave burst bits according to TS 05.03 4.1.4 + * \param[out] cB caller-allocated output buffer for 456 soft coded bits + * \param[in] iB 456 soft input bits */ void gsm0503_xcch_deinterleave(sbit_t *cB, const sbit_t *iB) { int j, k, B; @@ -59,7 +94,10 @@ void gsm0503_xcch_deinterleave(sbit_t *cB, const sbit_t *iB) } } -void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB) +/*! Interleave burst bits according to TS 05.03 4.1.4 + * \param[out] iB caller-allocated output buffer for 456 soft interleaved bits + * \param[in] cB 456 soft input coded bits */ +void gsm0503_xcch_interleave(const ubit_t *cB, ubit_t *iB) { int j, k, B; @@ -70,6 +108,11 @@ void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB) } } +/*! De-Interleave MCS1 DL burst bits according to TS 05.03 5.1.5.1.5 + * \param[out] u caller-allocated output buffer for 12 soft coded bits + * \param[out] hc caller-allocated output buffer for 68 soft coded bits + * \param[out] dc caller-allocated output buffer for 372 soft coded bits + * \param[in] iB 452 interleaved soft input bits */ void gsm0503_mcs1_dl_deinterleave(sbit_t *u, sbit_t *hc, sbit_t *dc, const sbit_t *iB) { @@ -106,6 +149,11 @@ void gsm0503_mcs1_dl_deinterleave(sbit_t *u, sbit_t *hc, } } +/*! Interleave MCS1 DL burst bits according to TS 05.03 5.1.5.1.5 + * \param[in] up 12 input soft coded bits (usf) + * \param[in] hc 68 input soft coded bits (header) + * \param[in] dc 372 input soft bits (data) + * \param[out] iB 456 interleaved soft output bits */ void gsm0503_mcs1_dl_interleave(const ubit_t *up, const ubit_t *hc, const ubit_t *dc, ubit_t *iB) { @@ -139,6 +187,10 @@ void gsm0503_mcs1_dl_interleave(const ubit_t *up, const ubit_t *hc, gsm0503_xcch_interleave(cp, iB); } +/*! Interleave MCS1 UL burst bits according to TS 05.03 5.1.5.2.4 + * \param[out] hc caller-allocated output buffer for 80 soft coded header bits + * \param[out] dc caller-allocated output buffer for 372 soft coded data bits + * \param[in] iB 456 interleaved soft input bits */ void gsm0503_mcs1_ul_deinterleave(sbit_t *hc, sbit_t *dc, const sbit_t *iB) { int k; @@ -169,6 +221,10 @@ void gsm0503_mcs1_ul_deinterleave(sbit_t *hc, sbit_t *dc, const sbit_t *iB) } } +/*! Interleave MCS1 DL burst bits according to TS 05.03 5.1.5.2.4 + * \param[in] hc 80 input coded bits (header) + * \param[in] dc 372 input bits (data) + * \param[out] iB 456 interleaved output bits */ void gsm0503_mcs1_ul_interleave(const ubit_t *hc, const ubit_t *dc, ubit_t *iB) { int k; @@ -199,6 +255,11 @@ void gsm0503_mcs1_ul_interleave(const ubit_t *hc, const ubit_t *dc, ubit_t *iB) gsm0503_xcch_interleave(cp, iB); } +/*! Interleave MCS5 UL burst bits according to TS 05.03 5.1.9.2.4 + * \param[in] hc 136 soft coded header input bits + * \param[in] dc 1248 soft coded data input bits + * \param[out] hi 136 interleaved header output bits + * \param[out] di 1248 interleaved data output bits */ void gsm0503_mcs5_ul_interleave(const ubit_t *hc, const ubit_t *dc, ubit_t *hi, ubit_t *di) { @@ -217,6 +278,10 @@ void gsm0503_mcs5_ul_interleave(const ubit_t *hc, const ubit_t *dc, } } +/*! De-Interleave MCS5 UL burst bits according to TS 05.03 5.1.9.2.4 + * \param[out] hc caller-allocated output buffer for 136 soft coded header bits + * \param[out] dc caller-allocated output buffer for 1248 soft coded data bits + * \param[in] iB interleaved soft input bits */ void gsm0503_mcs5_ul_deinterleave(sbit_t *hc, sbit_t *dc, const sbit_t *hi, const sbit_t *di) { @@ -239,6 +304,11 @@ void gsm0503_mcs5_ul_deinterleave(sbit_t *hc, sbit_t *dc, } } +/*! Interleave MCS5 DL burst bits according to TS 05.03 5.1.9.1.5 + * \param[in] hc 100 soft coded header input bits + * \param[in] dc 1248 soft coded data input bits + * \param[out] hi 100 interleaved header output bits + * \param[out] di 1248 interleaved data output bits */ void gsm0503_mcs5_dl_interleave(const ubit_t *hc, const ubit_t *dc, ubit_t *hi, ubit_t *di) { @@ -257,6 +327,10 @@ void gsm0503_mcs5_dl_interleave(const ubit_t *hc, const ubit_t *dc, } } +/*! De-Interleave MCS5 UL burst bits according to TS 05.03 5.1.9.1.5 + * \param[out] hc caller-allocated output buffer for 100 soft coded header bits + * \param[out] dc caller-allocated output buffer for 1248 soft coded data bits + * \param[in] iB interleaved soft input bits */ void gsm0503_mcs5_dl_deinterleave(sbit_t *hc, sbit_t *dc, const sbit_t *hi, const sbit_t *di) { @@ -279,6 +353,12 @@ void gsm0503_mcs5_dl_deinterleave(sbit_t *hc, sbit_t *dc, } } +/*! Interleave MCS7 DL burst bits according to TS 05.03 5.1.11.1.5 + * \param[in] hc 124 soft coded header input bits + * \param[in] c1 612 soft coded data input bits + * \param[in] c2 612 soft coded data input bits + * \param[out] hi 124 interleaved header output bits + * \param[out] di 1224 interleaved data output bits */ void gsm0503_mcs7_dl_interleave(const ubit_t *hc, const ubit_t *c1, const ubit_t *c2, ubit_t *hi, ubit_t *di) { @@ -302,7 +382,12 @@ void gsm0503_mcs7_dl_interleave(const ubit_t *hc, const ubit_t *c1, } } - +/*! De-Interleave MCS7 DL burst bits according to TS 05.03 5.1.11.1.5 + * \param[out] hc caller-allocated output buffer for 124 soft coded header bits + * \param[out] c1 caller-allocated output buffer for 612 soft coded data bits + * \param[out] c2 caller-allocated output buffer for 612 soft coded data bits + * \param[in] hi interleaved soft input header bits + * \param[in] di interleaved soft input data bits */ void gsm0503_mcs7_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2, const sbit_t *hi, const sbit_t *di) { @@ -330,6 +415,12 @@ void gsm0503_mcs7_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2, } } +/*! Interleave MCS7 UL burst bits according to TS 05.03 5.1.11.2.4 + * \param[in] hc 124 soft coded header input bits + * \param[in] c1 612 soft coded data input bits + * \param[in] c2 612 soft coded data input bits + * \param[out] hi 124 interleaved header output bits + * \param[out] di 1224 interleaved data output bits */ void gsm0503_mcs7_ul_interleave(const ubit_t *hc, const ubit_t *c1, const ubit_t *c2, ubit_t *hi, ubit_t *di) { @@ -353,6 +444,12 @@ void gsm0503_mcs7_ul_interleave(const ubit_t *hc, const ubit_t *c1, } } +/*! De-Interleave MCS7 UL burst bits according to TS 05.03 5.1.11.2.4 + * \param[out] hc caller-allocated output buffer for 160 soft coded header bits + * \param[out] c1 caller-allocated output buffer for 612 soft coded data bits + * \param[out] c2 caller-allocated output buffer for 612 soft coded data bits + * \param[in] hi interleaved soft input header bits + * \param[in] di interleaved soft input data bits */ void gsm0503_mcs7_ul_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2, const sbit_t *hi, const sbit_t *di) { @@ -380,6 +477,12 @@ void gsm0503_mcs7_ul_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2, } } +/*! Interleave MCS8 UL burst bits according to TS 05.03 5.1.12.2.4 + * \param[in] hc 160 soft coded header input bits + * \param[in] c1 612 soft coded data input bits + * \param[in] c2 612 soft coded data input bits + * \param[out] hi 160 interleaved header output bits + * \param[out] di 1224 interleaved data output bits */ void gsm0503_mcs8_ul_interleave(const ubit_t *hc, const ubit_t *c1, const ubit_t *c2, ubit_t *hi, ubit_t *di) { @@ -403,6 +506,13 @@ void gsm0503_mcs8_ul_interleave(const ubit_t *hc, const ubit_t *c1, } } + +/*! De-Interleave MCS8 UL burst bits according to TS 05.03 5.1.12.2.4 + * \param[out] hc caller-allocated output buffer for 160 soft coded header bits + * \param[out] c1 caller-allocated output buffer for 612 soft coded data bits + * \param[out] c2 caller-allocated output buffer for 612 soft coded data bits + * \param[in] hi interleaved soft input header bits + * \param[in] di interleaved soft input data bits */ void gsm0503_mcs8_ul_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2, const sbit_t *hi, const sbit_t *di) { @@ -430,6 +540,12 @@ void gsm0503_mcs8_ul_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2, } } +/*! Interleave MCS8 DL burst bits according to TS 05.03 5.1.12.1.5 + * \param[in] hc 124 soft coded header input bits + * \param[in] c1 612 soft coded data input bits + * \param[in] c2 612 soft coded data input bits + * \param[out] hi 124 interleaved header output bits + * \param[out] di 1224 interleaved data output bits */ void gsm0503_mcs8_dl_interleave(const ubit_t *hc, const ubit_t *c1, const ubit_t *c2, ubit_t *hi, ubit_t *di) { @@ -453,6 +569,12 @@ void gsm0503_mcs8_dl_interleave(const ubit_t *hc, const ubit_t *c1, } } +/*! De-Interleave MCS8 DL burst bits according to TS 05.03 5.1.12.1.5 + * \param[out] hc caller-allocated output buffer for 124 soft coded header bits + * \param[out] c1 caller-allocated output buffer for 612 soft coded data bits + * \param[out] c2 caller-allocated output buffer for 612 soft coded data bits + * \param[in] hi interleaved soft input header bits + * \param[in] di interleaved soft input data bits */ void gsm0503_mcs8_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2, const sbit_t *hi, const sbit_t *di) { @@ -504,7 +626,10 @@ void gsm0503_mcs8_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2, * Where hl(B) and hn(B) are bits in burst B indicating flags. */ -void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB) +/*! GSM TCH FR/EFR/AFS De-Interleaving and burst mapping + * \param[out] cB caller-allocated buffer for 456 unpacked output bits + * \param[in] iB 456 unpacked interleaved input bits */ +void gsm0503_tch_fr_deinterleave(sbit_t *cB, const sbit_t *iB) { int j, k, B; @@ -515,7 +640,10 @@ void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB) } } -void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB) +/*! GSM TCH FR/EFR/AFS Interleaving and burst mapping + * \param[in] cB caller-allocated buffer for 456 unpacked input bits + * \param[out] iB 456 unpacked interleaved output bits */ +void gsm0503_tch_fr_interleave(const ubit_t *cB, ubit_t *iB) { int j, k, B; @@ -526,31 +654,10 @@ void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB) } } -/* - * GSM TCH HR/AHS interleaving and burst mapping - * - * Interleaving: - * - * Given 288 coded input bits, form 4 blocks of 114 bits, - * where even bits of the first 2 blocks and odd bits of the last 2 blocks - * are used: - * - * i(B, j) = c(n, k) k = 0, ..., 227 - * n = 0, ..., N, N + 1, ... - * B = B_0 + 2n + b - * j, b = table[k]; - * - * Mapping on Burst: - * - * e(B, j) = i(B, j) - * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 - * e(B, 57) = h_l(B) - * e(B, 58) = h_n(B) - * - * Where hl(B) and hn(B) are bits in burst B indicating flags. - */ - -void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB) +/*! GSM TCH HR/AHS De-Interleaving and burst mapping + * \param[out] cB caller-allocated buffer for 228 unpacked output bits + * \param[in] iB 228 unpacked interleaved input bits */ +void gsm0503_tch_hr_deinterleave(sbit_t *cB, const sbit_t *iB) { int j, k, B; @@ -561,7 +668,10 @@ void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB) } } -void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB) +/*! GSM TCH HR/AHS Interleaving and burst mapping + * \param[in] cB caller-allocated buffer for 228 unpacked input bits + * \param[out] iB 228 unpacked interleaved output bits */ +void gsm0503_tch_hr_interleave(const ubit_t *cB, ubit_t *iB) { int j, k, B; @@ -571,3 +681,5 @@ void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB) iB[B * 114 + j] = cB[k]; } } + +/*! @} */ diff --git a/lib/decoding/osmocom/coding/gsm0503_interleaving.h b/lib/decoding/osmocom/coding/gsm0503_interleaving.h index 05c0365..05b5e27 100644 --- a/lib/decoding/osmocom/coding/gsm0503_interleaving.h +++ b/lib/decoding/osmocom/coding/gsm0503_interleaving.h @@ -1,37 +1,23 @@ -/* - * (C) 2013 by Andreas Eversberg - * (C) 2016 by Tom Tsou - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +/*! \file gsm0503_interleaving.h + * GSM TS 05.03 interleaving. */ - #pragma once #include +/*! \addtogroup interleaving + * @{ + * \file gsm0503_interleaving.h */ + void gsm0503_xcch_deinterleave(sbit_t *cB, const sbit_t *iB); -void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB); +void gsm0503_xcch_interleave(const ubit_t *cB, ubit_t *iB); -void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB); -void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB); +void gsm0503_tch_fr_deinterleave(sbit_t *cB, const sbit_t *iB); +void gsm0503_tch_fr_interleave(const ubit_t *cB, ubit_t *iB); -void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB); -void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB); +void gsm0503_tch_hr_deinterleave(sbit_t *cB, const sbit_t *iB); +void gsm0503_tch_hr_interleave(const ubit_t *cB, ubit_t *iB); void gsm0503_mcs1_ul_deinterleave(sbit_t *hc, sbit_t *dc, const sbit_t *iB); void gsm0503_mcs1_ul_interleave(const ubit_t *hc, @@ -71,3 +57,5 @@ void gsm0503_mcs8_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2, const sbit_t *hi, const sbit_t *di); void gsm0503_mcs8_dl_interleave(const ubit_t *hc, const ubit_t *c1, const ubit_t *c2, ubit_t *hi, ubit_t *di); + +/*! @} */ diff --git a/lib/decoding/osmocom/coding/gsm0503_mapping.c b/lib/decoding/osmocom/coding/gsm0503_mapping.c index 45275bf..f7532eb 100644 --- a/lib/decoding/osmocom/coding/gsm0503_mapping.c +++ b/lib/decoding/osmocom/coding/gsm0503_mapping.c @@ -4,6 +4,8 @@ * * 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 @@ -23,7 +25,17 @@ #include #include -#include "gsm0503_mapping.h" +#include + +/*! \addtogroup mapping + * @{ + * + * GSM TS 05.03 burst mapping + * + * This module contains burst mapping routines as specified in 3GPP TS + * 05.03 / 45.003. + * + * \file gsm0503_mapping.c */ void gsm0503_xcch_burst_unmap(sbit_t *iB, const sbit_t *eB, sbit_t *hl, sbit_t *hn) @@ -38,7 +50,7 @@ void gsm0503_xcch_burst_unmap(sbit_t *iB, const sbit_t *eB, *hn = eB[58]; } -void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl, +void gsm0503_xcch_burst_map(const ubit_t *iB, ubit_t *eB, const ubit_t *hl, const ubit_t *hn) { memcpy(eB, iB, 57); @@ -50,7 +62,7 @@ void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl, eB[58] = *hn; } -void gsm0503_tch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd) +void gsm0503_tch_burst_unmap(sbit_t *iB, const sbit_t *eB, sbit_t *h, int odd) { int i; @@ -70,7 +82,7 @@ void gsm0503_tch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd) } } -void gsm0503_tch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd) +void gsm0503_tch_burst_map(const ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd) { int i; @@ -80,13 +92,8 @@ void gsm0503_tch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd) eB[i] = iB[i]; for (i = 58 - odd; i < 114; i += 2) eB[i + 2] = iB[i]; - } - - if (h) { - if (!odd) - eB[58] = *h; - else - eB[57] = *h; + if (h) + eB[odd ? 57 : 58] = *h; } } @@ -289,3 +296,5 @@ void gsm0503_mcs5_burst_swap(sbit_t *eB) eB[191] = t[12]; eB[194] = t[13]; } + +/*! @} */ diff --git a/lib/decoding/osmocom/coding/gsm0503_mapping.h b/lib/decoding/osmocom/coding/gsm0503_mapping.h index 417a94f..f391e8d 100644 --- a/lib/decoding/osmocom/coding/gsm0503_mapping.h +++ b/lib/decoding/osmocom/coding/gsm0503_mapping.h @@ -1,27 +1,15 @@ -/* - * (C) 2013 by Andreas Eversberg - * (C) 2016 by Tom Tsou - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +/*! \file gsm0503_mapping.c + * GSM TS 05.03 burst mapping. */ - + #pragma once #include +//#include "bits.h" + +/*! \addtogroup mapping + * @{ + * \file gsm0503_mapping.h */ void gsm0503_xcch_burst_unmap(sbit_t *iB, const sbit_t *eB, sbit_t *hl, sbit_t *hn); @@ -52,3 +40,5 @@ void gsm0503_mcs7_dl_burst_unmap(sbit_t *di, const sbit_t *eB, sbit_t *hi, sbit_t *up, int B); void gsm0503_mcs5_burst_swap(sbit_t *eB); + +/*! @} */ diff --git a/lib/decoding/osmocom/coding/gsm0503_parity.c b/lib/decoding/osmocom/coding/gsm0503_parity.c index 50977f7..874114f 100644 --- a/lib/decoding/osmocom/coding/gsm0503_parity.c +++ b/lib/decoding/osmocom/coding/gsm0503_parity.c @@ -4,6 +4,8 @@ * * 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 @@ -22,10 +24,19 @@ #include #include -#include "gsm0503_parity.h" +#include -/* - * GSM (SACCH) parity (FIRE code) +/*! \addtogroup parity + * @{ + * + * GSM TS 05.03 parity + * + * This module contains parity/crc code definitions for the various + * parity/crc schemes as defined in 3GPP TS 05.03 / 45.003 + * + * \file gsm0503_parity.c */ + +/*! GSM (SACCH) parity (FIRE code) * * g(x) = (x^23 + 1)(x^17 + x^3 + 1) * = x^40 + x^26 + x^23 + x^17 + x^3 + a1 @@ -37,8 +48,7 @@ const struct osmo_crc64gen_code gsm0503_fire_crc40 = { .remainder = 0xffffffffffULL, }; -/* - * GSM PDTCH CS-2, CS-3, CS-4 parity +/*! GSM PDTCH CS-2, CS-3, CS-4 parity * * g(x) = x^16 + x^12 + x^5 + 1 */ @@ -49,8 +59,7 @@ const struct osmo_crc16gen_code gsm0503_cs234_crc16 = { .remainder = 0xffff, }; -/* - * EDGE MCS header parity +/*! EDGE MCS header parity * */ const struct osmo_crc8gen_code gsm0503_mcs_crc8_hdr = { @@ -60,8 +69,7 @@ const struct osmo_crc8gen_code gsm0503_mcs_crc8_hdr = { .remainder = 0xff, }; -/* - * EDGE MCS data parity +/*! EDGE MCS data parity * */ const struct osmo_crc16gen_code gsm0503_mcs_crc12 = { @@ -71,8 +79,7 @@ const struct osmo_crc16gen_code gsm0503_mcs_crc12 = { .remainder = 0x0fff, }; -/* - * GSM RACH parity +/*! GSM RACH parity * * g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1 */ @@ -83,8 +90,7 @@ const struct osmo_crc8gen_code gsm0503_rach_crc6 = { .remainder = 0x3f, }; -/* - * GSM SCH parity +/*! GSM SCH parity * * g(x) = x^10 + x^8 + x^6 + x^5 + x^4 + x^2 + 1 */ @@ -95,8 +101,7 @@ const struct osmo_crc16gen_code gsm0503_sch_crc10 = { .remainder = 0x3ff, }; -/* - * GSM TCH FR/HR/EFR parity +/*! GSM TCH FR/HR/EFR parity * * g(x) = x^3 + x + 1 */ @@ -107,8 +112,7 @@ const struct osmo_crc8gen_code gsm0503_tch_fr_crc3 = { .remainder = 0x7, }; -/* - * GSM TCH EFR parity +/*! GSM TCH EFR parity * * g(x) = x^8 + x^4 + x^3 + x^2 + 1 */ @@ -119,8 +123,7 @@ const struct osmo_crc8gen_code gsm0503_tch_efr_crc8 = { .remainder = 0x00, }; -/* - * GSM AMR parity +/*! GSM AMR parity * * g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1 */ @@ -130,3 +133,5 @@ const struct osmo_crc8gen_code gsm0503_amr_crc6 = { .init = 0x00, .remainder = 0x3f, }; + +/*! @} */ diff --git a/lib/decoding/osmocom/coding/gsm0503_parity.h b/lib/decoding/osmocom/coding/gsm0503_parity.h index 6d8a062..28a5444 100644 --- a/lib/decoding/osmocom/coding/gsm0503_parity.h +++ b/lib/decoding/osmocom/coding/gsm0503_parity.h @@ -1,28 +1,15 @@ -/* - * (C) 2013 by Andreas Eversberg - * (C) 2016 by Tom Tsou - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +/*! \file gsm0503_parity.h + * GSM TS 05.03 parity. */ - + #pragma once #include +/*! \addtogroup parity + * @{ + * \file gsm0503_parity.h */ + const struct osmo_crc64gen_code gsm0503_fire_crc40; const struct osmo_crc16gen_code gsm0503_cs234_crc16; const struct osmo_crc8gen_code gsm0503_mcs_crc8_hdr; @@ -32,3 +19,5 @@ const struct osmo_crc16gen_code gsm0503_sch_crc10; const struct osmo_crc8gen_code gsm0503_tch_fr_crc3; const struct osmo_crc8gen_code gsm0503_tch_efr_crc8; const struct osmo_crc8gen_code gsm0503_amr_crc6; + +/*! @} */ diff --git a/lib/decoding/osmocom/coding/gsm0503_tables.c b/lib/decoding/osmocom/coding/gsm0503_tables.c index d4cabcc..5fe634b 100644 --- a/lib/decoding/osmocom/coding/gsm0503_tables.c +++ b/lib/decoding/osmocom/coding/gsm0503_tables.c @@ -4,6 +4,8 @@ * * 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 @@ -22,7 +24,18 @@ #include #include -#include "gsm0503_tables.h" +#include + +/*! \addtogroup tables + * @{ + * + * GSM TS 05.03 tables. + * + * This module contains various tables defining parts of 3GPP TS 05.03 + * / 45.003, primarily for the purpose of (de)puncturing, interleaving, + * etc. + * + * \file gsm0503_tables.c */ const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8] = { { 1,1, 1,1, 1,1, 1,1 }, @@ -1692,7 +1705,7 @@ const uint8_t gsm0503_tch_hr_interleaving[228][2] = { { 86 ,0 }, { 87 ,2 }, { 110,0 }, { 111,2 }, { 4 ,0 }, { 5 ,2 }, { 82 ,1 }, { 83 ,3 }, { 52 ,0 }, { 53 ,2 }, { 58 ,1 }, { 59 ,3 }, { 28 ,0 }, { 29 ,2 }, { 34 ,1 }, { 35 ,3 }, { 76 ,0 }, { 77 ,2 }, - { 10 ,1 }, { 12 ,3 }, { 100,0 }, { 101,2 }, { 16 ,0 }, { 17 ,2 }, + { 10 ,1 }, { 11 ,3 }, { 100,0 }, { 101,2 }, { 16 ,0 }, { 17 ,2 }, { 106,1 }, { 107,3 }, { 64 ,0 }, { 65 ,2 }, { 70 ,1 }, { 71 ,3 }, { 94 ,1 }, { 95 ,3 }, { 40 ,0 }, { 41 ,2 }, { 46 ,1 }, { 47 ,3 }, { 22 ,1 }, { 23 ,3 }, { 88 ,0 }, { 89 ,2 }, { 112,0 }, { 113,2 }, @@ -1730,3 +1743,5 @@ const ubit_t gsm0503_mcs5_usf_precode_table[8][36] = { { 0,0,1,0,0,1,1,0,1, 1,0,1,1,1,1,1,1,1, 0,1,1,0,1,0,0,0,1, 0,0,1,1,1,0,1,0,0, }, { 0,1,1,0,1,0,1,1,1, 0,1,0,1,0,1,1,1,1, 0,0,0,1,1,1,1,1,0, 0,1,0,0,1,0,0,1,1, }, }; + +/*! @} */ diff --git a/lib/decoding/osmocom/coding/gsm0503_tables.h b/lib/decoding/osmocom/coding/gsm0503_tables.h index 4976c81..c442549 100644 --- a/lib/decoding/osmocom/coding/gsm0503_tables.h +++ b/lib/decoding/osmocom/coding/gsm0503_tables.h @@ -1,28 +1,16 @@ -/* - * (C) 2013 by Andreas Eversberg - * (C) 2016 by Tom Tsou - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +/*! \file gsm0503_tables.h + * GSM TS 05.03 tables. */ #pragma once #include #include +//#include "bits.h" + +/*! \addtogroup tables + * @{ + * \file gsm0503_tables.h */ extern const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8]; extern const ubit_t gsm0503_pdtch_edge_hl_hn_ubit[3][8]; @@ -69,3 +57,5 @@ extern const ubit_t gsm0503_ahs_ic_ubit[4][4]; extern const sbit_t gsm0503_ahs_ic_sbit[4][4]; extern const uint8_t gsm0503_tch_hr_interleaving[228][2]; extern const ubit_t gsm0503_mcs5_usf_precode_table[8][36]; + +/*! @} */ diff --git a/lib/decoding/osmocom/core/CMakeLists.txt b/lib/decoding/osmocom/core/CMakeLists.txt new file mode 100644 index 0000000..ce8e60f --- /dev/null +++ b/lib/decoding/osmocom/core/CMakeLists.txt @@ -0,0 +1,11 @@ +add_sources( +bits.c +bitvec.c +conv_acc.c +conv_acc_generic.c +conv.c +crc16gen.c +crc64gen.c +crc8gen.c +panic.c +) diff --git a/lib/decoding/osmocom/core/bit16gen.h b/lib/decoding/osmocom/core/bit16gen.h new file mode 100644 index 0000000..5c6162c --- /dev/null +++ b/lib/decoding/osmocom/core/bit16gen.h @@ -0,0 +1,105 @@ +/* + * bit16gen.h + * + * Copyright (C) 2014 Max + * + * 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 + +/*! \brief load unaligned n-byte integer (little-endian encoding) into uint16_t + * \param[in] p Buffer where integer is stored + * \param[in] n Number of bytes stored in p + * \returns 16 bit unsigned integer + */ +static inline uint16_t osmo_load16le_ext(const void *p, uint8_t n) +{ + uint8_t i; + uint16_t r = 0; + const uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; r |= ((uint16_t)q[i] << (8 * i)), i++); + return r; +} + +/*! \brief load unaligned n-byte integer (big-endian encoding) into uint16_t + * \param[in] p Buffer where integer is stored + * \param[in] n Number of bytes stored in p + * \returns 16 bit unsigned integer + */ +static inline uint16_t osmo_load16be_ext(const void *p, uint8_t n) +{ + uint8_t i; + uint16_t r = 0; + const uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; r |= ((uint16_t)q[i] << (16 - 8* (1 + i))), i++); + return r; +} + + +/*! \brief store unaligned n-byte integer (little-endian encoding) from uint16_t + * \param[in] x unsigned 16 bit integer + * \param[out] p Buffer to store integer + * \param[in] n Number of bytes to store + */ +static inline void osmo_store16le_ext(uint16_t x, void *p, uint8_t n) +{ + uint8_t i; + uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++); +} + +/*! \brief store unaligned n-byte integer (big-endian encoding) from uint16_t + * \param[in] x unsigned 16 bit integer + * \param[out] p Buffer to store integer + * \param[in] n Number of bytes to store + */ +static inline void osmo_store16be_ext(uint16_t x, void *p, uint8_t n) +{ + uint8_t i; + uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++); +} + + +/* Convenience function for most-used cases */ + + +/*! \brief load unaligned 16-bit integer (little-endian encoding) */ +static inline uint16_t osmo_load16le(const void *p) +{ + return osmo_load16le_ext(p, 16 / 8); +} + +/*! \brief load unaligned 16-bit integer (big-endian encoding) */ +static inline uint16_t osmo_load16be(const void *p) +{ + return osmo_load16be_ext(p, 16 / 8); +} + + +/*! \brief store unaligned 16-bit integer (little-endian encoding) */ +static inline void osmo_store16le(uint16_t x, void *p) +{ + osmo_store16le_ext(x, p, 16 / 8); +} + +/*! \brief store unaligned 16-bit integer (big-endian encoding) */ +static inline void osmo_store16be(uint16_t x, void *p) +{ + osmo_store16be_ext(x, p, 16 / 8); +} diff --git a/lib/decoding/osmocom/core/bit32gen.h b/lib/decoding/osmocom/core/bit32gen.h new file mode 100644 index 0000000..6640e76 --- /dev/null +++ b/lib/decoding/osmocom/core/bit32gen.h @@ -0,0 +1,105 @@ +/* + * bit32gen.h + * + * Copyright (C) 2014 Max + * + * 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 + +/*! \brief load unaligned n-byte integer (little-endian encoding) into uint32_t + * \param[in] p Buffer where integer is stored + * \param[in] n Number of bytes stored in p + * \returns 32 bit unsigned integer + */ +static inline uint32_t osmo_load32le_ext(const void *p, uint8_t n) +{ + uint8_t i; + uint32_t r = 0; + const uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; r |= ((uint32_t)q[i] << (8 * i)), i++); + return r; +} + +/*! \brief load unaligned n-byte integer (big-endian encoding) into uint32_t + * \param[in] p Buffer where integer is stored + * \param[in] n Number of bytes stored in p + * \returns 32 bit unsigned integer + */ +static inline uint32_t osmo_load32be_ext(const void *p, uint8_t n) +{ + uint8_t i; + uint32_t r = 0; + const uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; r |= ((uint32_t)q[i] << (32 - 8* (1 + i))), i++); + return r; +} + + +/*! \brief store unaligned n-byte integer (little-endian encoding) from uint32_t + * \param[in] x unsigned 32 bit integer + * \param[out] p Buffer to store integer + * \param[in] n Number of bytes to store + */ +static inline void osmo_store32le_ext(uint32_t x, void *p, uint8_t n) +{ + uint8_t i; + uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++); +} + +/*! \brief store unaligned n-byte integer (big-endian encoding) from uint32_t + * \param[in] x unsigned 32 bit integer + * \param[out] p Buffer to store integer + * \param[in] n Number of bytes to store + */ +static inline void osmo_store32be_ext(uint32_t x, void *p, uint8_t n) +{ + uint8_t i; + uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++); +} + + +/* Convenience function for most-used cases */ + + +/*! \brief load unaligned 32-bit integer (little-endian encoding) */ +static inline uint32_t osmo_load32le(const void *p) +{ + return osmo_load32le_ext(p, 32 / 8); +} + +/*! \brief load unaligned 32-bit integer (big-endian encoding) */ +static inline uint32_t osmo_load32be(const void *p) +{ + return osmo_load32be_ext(p, 32 / 8); +} + + +/*! \brief store unaligned 32-bit integer (little-endian encoding) */ +static inline void osmo_store32le(uint32_t x, void *p) +{ + osmo_store32le_ext(x, p, 32 / 8); +} + +/*! \brief store unaligned 32-bit integer (big-endian encoding) */ +static inline void osmo_store32be(uint32_t x, void *p) +{ + osmo_store32be_ext(x, p, 32 / 8); +} diff --git a/lib/decoding/osmocom/core/bit64gen.h b/lib/decoding/osmocom/core/bit64gen.h new file mode 100644 index 0000000..8c7b709 --- /dev/null +++ b/lib/decoding/osmocom/core/bit64gen.h @@ -0,0 +1,105 @@ +/* + * bit64gen.h + * + * Copyright (C) 2014 Max + * + * 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 + +/*! \brief load unaligned n-byte integer (little-endian encoding) into uint64_t + * \param[in] p Buffer where integer is stored + * \param[in] n Number of bytes stored in p + * \returns 64 bit unsigned integer + */ +static inline uint64_t osmo_load64le_ext(const void *p, uint8_t n) +{ + uint8_t i; + uint64_t r = 0; + const uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; r |= ((uint64_t)q[i] << (8 * i)), i++); + return r; +} + +/*! \brief load unaligned n-byte integer (big-endian encoding) into uint64_t + * \param[in] p Buffer where integer is stored + * \param[in] n Number of bytes stored in p + * \returns 64 bit unsigned integer + */ +static inline uint64_t osmo_load64be_ext(const void *p, uint8_t n) +{ + uint8_t i; + uint64_t r = 0; + const uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; r |= ((uint64_t)q[i] << (64 - 8* (1 + i))), i++); + return r; +} + + +/*! \brief store unaligned n-byte integer (little-endian encoding) from uint64_t + * \param[in] x unsigned 64 bit integer + * \param[out] p Buffer to store integer + * \param[in] n Number of bytes to store + */ +static inline void osmo_store64le_ext(uint64_t x, void *p, uint8_t n) +{ + uint8_t i; + uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++); +} + +/*! \brief store unaligned n-byte integer (big-endian encoding) from uint64_t + * \param[in] x unsigned 64 bit integer + * \param[out] p Buffer to store integer + * \param[in] n Number of bytes to store + */ +static inline void osmo_store64be_ext(uint64_t x, void *p, uint8_t n) +{ + uint8_t i; + uint8_t *q = (uint8_t *)p; + for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++); +} + + +/* Convenience function for most-used cases */ + + +/*! \brief load unaligned 64-bit integer (little-endian encoding) */ +static inline uint64_t osmo_load64le(const void *p) +{ + return osmo_load64le_ext(p, 64 / 8); +} + +/*! \brief load unaligned 64-bit integer (big-endian encoding) */ +static inline uint64_t osmo_load64be(const void *p) +{ + return osmo_load64be_ext(p, 64 / 8); +} + + +/*! \brief store unaligned 64-bit integer (little-endian encoding) */ +static inline void osmo_store64le(uint64_t x, void *p) +{ + osmo_store64le_ext(x, p, 64 / 8); +} + +/*! \brief store unaligned 64-bit integer (big-endian encoding) */ +static inline void osmo_store64be(uint64_t x, void *p) +{ + osmo_store64be_ext(x, p, 64 / 8); +} diff --git a/lib/decoding/osmocom/core/bits.c b/lib/decoding/osmocom/core/bits.c new file mode 100644 index 0000000..8837c1f --- /dev/null +++ b/lib/decoding/osmocom/core/bits.c @@ -0,0 +1,311 @@ +/* + * (C) 2011 by Harald Welte + * (C) 2011 by Sylvain Munaut + * + * 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 + +#include + +/*! \addtogroup bits + * @{ + * Osmocom bit level support code. + * + * This module implements the notion of different bit-fields, such as + * - unpacked bits (\ref ubit_t), i.e. 1 bit per byte + * - packed bits (\ref pbit_t), i.e. 8 bits per byte + * - soft bits (\ref sbit_t), 1 bit per byte from -127 to 127 + * + * \file bits.c */ + +/*! convert unpacked bits to packed bits, return length in bytes + * \param[out] out output buffer of packed bits + * \param[in] in input buffer of unpacked bits + * \param[in] num_bits number of bits + */ +int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits) +{ + unsigned int i; + uint8_t curbyte = 0; + pbit_t *outptr = out; + + for (i = 0; i < num_bits; i++) { + uint8_t bitnum = 7 - (i % 8); + + curbyte |= (in[i] << bitnum); + + if(i % 8 == 7){ + *outptr++ = curbyte; + curbyte = 0; + } + } + /* we have a non-modulo-8 bitcount */ + if (i % 8) + *outptr++ = curbyte; + + return outptr - out; +} + +/*! Shift unaligned input to octet-aligned output + * \param[out] out output buffer, unaligned + * \param[in] in input buffer, octet-aligned + * \param[in] num_nibbles number of nibbles + */ +void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in, + unsigned int num_nibbles) +{ + unsigned int i, num_whole_bytes = num_nibbles / 2; + if (!num_whole_bytes) + return; + + /* first byte: upper nibble empty, lower nibble from src */ + out[0] = (in[0] >> 4); + + /* bytes 1.. */ + for (i = 1; i < num_whole_bytes; i++) + out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4); + + /* shift the last nibble, in case there's an odd count */ + i = num_whole_bytes; + if (num_nibbles & 1) + out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4); + else + out[i] = (in[i - 1] & 0xF) << 4; +} + +/*! Shift unaligned input to octet-aligned output + * \param[out] out output buffer, octet-aligned + * \param[in] in input buffer, unaligned + * \param[in] num_nibbles number of nibbles + */ +void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in, + unsigned int num_nibbles) +{ + unsigned int i, num_whole_bytes = num_nibbles / 2; + if (!num_whole_bytes) + return; + + for (i = 0; i < num_whole_bytes; i++) + out[i] = ((in[i] & 0xF) << 4) | (in[i + 1] >> 4); + + /* shift the last nibble, in case there's an odd count */ + i = num_whole_bytes; + if (num_nibbles & 1) + out[i] = (in[i] & 0xF) << 4; +} + +/*! convert unpacked bits to soft bits + * \param[out] out output buffer of soft bits + * \param[in] in input buffer of unpacked bits + * \param[in] num_bits number of bits + */ +void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits) +{ + unsigned int i; + for (i = 0; i < num_bits; i++) + out[i] = in[i] ? -127 : 127; +} + +/*! convert soft bits to unpacked bits + * \param[out] out output buffer of unpacked bits + * \param[in] in input buffer of soft bits + * \param[in] num_bits number of bits + */ +void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits) +{ + unsigned int i; + for (i = 0; i < num_bits; i++) + out[i] = in[i] < 0; +} + +/*! convert packed bits to unpacked bits, return length in bytes + * \param[out] out output buffer of unpacked bits + * \param[in] in input buffer of packed bits + * \param[in] num_bits number of bits + * \return number of bytes used in \ref out + */ +int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits) +{ + unsigned int i; + ubit_t *cur = out; + ubit_t *limit = out + num_bits; + + for (i = 0; i < (num_bits/8)+1; i++) { + pbit_t byte = in[i]; + *cur++ = (byte >> 7) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 6) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 5) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 4) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 3) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 2) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 1) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 0) & 1; + if (cur >= limit) + break; + } + return cur - out; +} + +/*! convert unpacked bits to packed bits (extended options) + * \param[out] out output buffer of packed bits + * \param[in] out_ofs offset into output buffer + * \param[in] in input buffer of unpacked bits + * \param[in] in_ofs offset into input buffer + * \param[in] num_bits number of bits + * \param[in] lsb_mode Encode bits in LSB orde instead of MSB + * \returns length in bytes (max written offset of output buffer + 1) + */ +int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs, + const ubit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode) +{ + int i, op, bn; + for (i=0; i>3] |= 1 << bn; + else + out[op>>3] &= ~(1 << bn); + } + return ((out_ofs + num_bits - 1) >> 3) + 1; +} + +/*! convert packed bits to unpacked bits (extended options) + * \param[out] out output buffer of unpacked bits + * \param[in] out_ofs offset into output buffer + * \param[in] in input buffer of packed bits + * \param[in] in_ofs offset into input buffer + * \param[in] num_bits number of bits + * \param[in] lsb_mode Encode bits in LSB orde instead of MSB + * \returns length in bytes (max written offset of output buffer + 1) + */ +int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs, + const pbit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode) +{ + int i, ip, bn; + for (i=0; i>3] & (1<> 1; + if (k & 2) x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; + if (k & 4) x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; + if (k & 8) x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8; + if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16; + + return x; +} + +/*! reverse the bit-order in each byte of a dword + * \param[in] x 32bit input value + * \returns 32bit value where bits of each byte have been reversed + * + * See Chapter 7 "Hackers Delight" + */ +uint32_t osmo_revbytebits_32(uint32_t x) +{ + x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1; + x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; + x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; + + return x; +} + +/*! reverse the bit order in a byte + * \param[in] x 8bit input value + * \returns 8bit value where bits order has been reversed + * + * See Chapter 7 "Hackers Delight" + */ +uint32_t osmo_revbytebits_8(uint8_t x) +{ + x = (x & 0x55) << 1 | (x & 0xAA) >> 1; + x = (x & 0x33) << 2 | (x & 0xCC) >> 2; + x = (x & 0x0F) << 4 | (x & 0xF0) >> 4; + + return x; +} + +/*! reverse bit-order of each byte in a buffer + * \param[in] buf buffer containing bytes to be bit-reversed + * \param[in] len length of buffer in bytes + * + * This function reverses the bits in each byte of the buffer + */ +void osmo_revbytebits_buf(uint8_t *buf, int len) +{ + unsigned int i; + unsigned int unaligned_cnt; + int len_remain = len; + + unaligned_cnt = ((unsigned long)buf & 3); + for (i = 0; i < unaligned_cnt; i++) { + buf[i] = osmo_revbytebits_8(buf[i]); + len_remain--; + if (len_remain <= 0) + return; + } + + for (i = unaligned_cnt; i + 3 < len; i += 4) { + osmo_store32be(osmo_revbytebits_32(osmo_load32be(buf + i)), buf + i); + len_remain -= 4; + } + + for (i = len - len_remain; i < len; i++) { + buf[i] = osmo_revbytebits_8(buf[i]); + len_remain--; + } +} + +/*! @} */ diff --git a/lib/decoding/osmocom/core/bits.h b/lib/decoding/osmocom/core/bits.h new file mode 100644 index 0000000..b1b8040 --- /dev/null +++ b/lib/decoding/osmocom/core/bits.h @@ -0,0 +1,122 @@ +/*! \file bits.h + * Osmocom bit level support code. + * + */ + +#pragma once + +#include +#include + +#include +#include +#include + +/*! \defgroup bits soft, unpacked and packed bits + * @{ + * \file bits.h */ + +/*! soft bit with value (-127...127), as commonly used in + * communications receivers such as [viterbi] decoders */ +typedef int8_t sbit_t; + +/*! unpacked bit (0 or 1): 1 bit per byte */ +typedef uint8_t ubit_t; + +/*! packed bits (8 bits in a byte). + * NOTE on the endian-ness of \ref pbit_t: + * - Bits in a \ref pbit_t are ordered MSB first, i.e. 0x80 is the first bit. + * - Bit i in a \ref pbit_t array is array[i/8] & (1<<(7-i%8)) */ +typedef uint8_t pbit_t; + +/*! determine how many bytes we would need for \a num_bits packed bits + * \param[in] num_bits Number of packed bits + * \returns number of bytes needed for \a num_bits packed bits + */ +static inline unsigned int osmo_pbit_bytesize(unsigned int num_bits) +{ + unsigned int pbit_bytesize = num_bits / 8; + + if (num_bits % 8) + pbit_bytesize++; + + return pbit_bytesize; +} + +int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits); + +int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits); + +void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in, + unsigned int num_nibbles); +void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in, + unsigned int num_nibbles); + +void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits); +void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits); + +int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs, + const ubit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode); + +int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs, + const pbit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode); + +#define OSMO_BIN_SPEC "%d%d%d%d%d%d%d%d" +#define OSMO_BIN_PRINT(byte) \ + (byte & 0x80 ? 1 : 0), \ + (byte & 0x40 ? 1 : 0), \ + (byte & 0x20 ? 1 : 0), \ + (byte & 0x10 ? 1 : 0), \ + (byte & 0x08 ? 1 : 0), \ + (byte & 0x04 ? 1 : 0), \ + (byte & 0x02 ? 1 : 0), \ + (byte & 0x01 ? 1 : 0) + +#define OSMO_BIT_SPEC "%c%c%c%c%c%c%c%c" +#define OSMO_BIT_PRINT_EX(byte, ch) \ + (byte & 0x80 ? ch : '.'), \ + (byte & 0x40 ? ch : '.'), \ + (byte & 0x20 ? ch : '.'), \ + (byte & 0x10 ? ch : '.'), \ + (byte & 0x08 ? ch : '.'), \ + (byte & 0x04 ? ch : '.'), \ + (byte & 0x02 ? ch : '.'), \ + (byte & 0x01 ? ch : '.') + +#define OSMO_BIT_PRINT(byte) OSMO_BIT_PRINT_EX(byte, '1') + +/* BIT REVERSAL */ + +/*! bit-reversal mode for osmo_bit_reversal() */ +enum osmo_br_mode { + /*! reverse all bits in a 32bit dword */ + OSMO_BR_BITS_IN_DWORD = 31, + /*! reverse byte order in a 32bit dword */ + OSMO_BR_BYTES_IN_DWORD = 24, + /*! reverse bits of each byte in a 32bit dword */ + OSMO_BR_BITS_IN_BYTE = 7, + /*! swap the two 16bit words in a 32bit dword */ + OSMO_BR_WORD_SWAP = 16, +}; + +uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k); + +uint32_t osmo_revbytebits_32(uint32_t x); + +uint32_t osmo_revbytebits_8(uint8_t x); + +void osmo_revbytebits_buf(uint8_t *buf, int len); + +/*! left circular shift + * \param[in] in The 16 bit unsigned integer to be rotated + * \param[in] shift Number of bits to shift \a in to, [0;16] bits + * \returns shifted value + */ +static inline uint16_t osmo_rol16(uint16_t in, unsigned shift) +{ + return (in << shift) | (in >> (16 - shift)); +} + +/*! @} */ diff --git a/lib/decoding/osmocom/core/bitvec.c b/lib/decoding/osmocom/core/bitvec.c new file mode 100644 index 0000000..414c719 --- /dev/null +++ b/lib/decoding/osmocom/core/bitvec.c @@ -0,0 +1,708 @@ +/* (C) 2009 by Harald Welte + * (C) 2012 Ivan Klyuchnikov + * (C) 2015 by sysmocom - s.f.m.c. GmbH + * + * 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 bitvec + * @{ + * Osmocom bit vector abstraction utility routines. + * + * These functions assume a MSB (most significant bit) first layout of the + * bits, so that for instance the 5 bit number abcde (a is MSB) can be + * embedded into a byte sequence like in xxxxxxab cdexxxxx. The bit count + * starts with the MSB, so the bits in a byte are numbered (MSB) 01234567 (LSB). + * Note that there are other incompatible encodings, like it is used + * for the EGPRS RLC data block headers (there the bits are numbered from LSB + * to MSB). + * + * \file bitvec.c */ + +#include +#include +#include +#include +#include + +#include +#include + +#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit) + +static inline unsigned int bytenum_from_bitnum(unsigned int bitnum) +{ + unsigned int bytenum = bitnum / 8; + + return bytenum; +} + +/* convert ZERO/ONE/L/H to a bitmask at given pos in a byte */ +static uint8_t bitval2mask(enum bit_value bit, uint8_t bitnum) +{ + int bitval; + + switch (bit) { + case ZERO: + bitval = (0 << bitnum); + break; + case ONE: + bitval = (1 << bitnum); + break; + case L: + bitval = ((0x2b ^ (0 << bitnum)) & (1 << bitnum)); + break; + case H: + bitval = ((0x2b ^ (1 << bitnum)) & (1 << bitnum)); + break; + default: + return 0; + } + return bitval; +} + +/*! check if the bit is 0 or 1 for a given position inside a bitvec + * \param[in] bv the bit vector on which to check + * \param[in] bitnr the bit number inside the bit vector to check + * \return value of the requested bit + */ +enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr) +{ + unsigned int bytenum = bytenum_from_bitnum(bitnr); + unsigned int bitnum = 7 - (bitnr % 8); + uint8_t bitval; + + if (bytenum >= bv->data_len) + return -EINVAL; + + bitval = bitval2mask(ONE, bitnum); + + if (bv->data[bytenum] & bitval) + return ONE; + + return ZERO; +} + +/*! check if the bit is L or H for a given position inside a bitvec + * \param[in] bv the bit vector on which to check + * \param[in] bitnr the bit number inside the bit vector to check + * \return value of the requested bit + */ +enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv, + unsigned int bitnr) +{ + unsigned int bytenum = bytenum_from_bitnum(bitnr); + unsigned int bitnum = 7 - (bitnr % 8); + uint8_t bitval; + + if (bytenum >= bv->data_len) + return -EINVAL; + + bitval = bitval2mask(H, bitnum); + + if ((bv->data[bytenum] & (1 << bitnum)) == bitval) + return H; + + return L; +} + +/*! get the Nth set bit inside the bit vector + * \param[in] bv the bit vector to use + * \param[in] n the bit number to get + * \returns the bit number (offset) of the Nth set bit in \a bv + */ +unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n) +{ + unsigned int i, k = 0; + + for (i = 0; i < bv->data_len*8; i++) { + if (bitvec_get_bit_pos(bv, i) == ONE) { + k++; + if (k == n) + return i; + } + } + + return 0; +} + +/*! set a bit at given position in a bit vector + * \param[in] bv bit vector on which to operate + * \param[in] bitnr number of bit to be set + * \param[in] bit value to which the bit is to be set + * \returns 0 on success, negative value on error + */ +inline int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr, + enum bit_value bit) +{ + unsigned int bytenum = bytenum_from_bitnum(bitnr); + unsigned int bitnum = 7 - (bitnr % 8); + uint8_t bitval; + + if (bytenum >= bv->data_len) + return -EINVAL; + + /* first clear the bit */ + bitval = bitval2mask(ONE, bitnum); + bv->data[bytenum] &= ~bitval; + + /* then set it to desired value */ + bitval = bitval2mask(bit, bitnum); + bv->data[bytenum] |= bitval; + + return 0; +} + +/*! set the next bit inside a bitvec + * \param[in] bv bit vector to be used + * \param[in] bit value of the bit to be set + * \returns 0 on success, negative value on error + */ +inline int bitvec_set_bit(struct bitvec *bv, enum bit_value bit) +{ + int rc; + + rc = bitvec_set_bit_pos(bv, bv->cur_bit, bit); + if (!rc) + bv->cur_bit++; + + return rc; +} + +/*! get the next bit (low/high) inside a bitvec + * \return value of th next bit in the vector */ +int bitvec_get_bit_high(struct bitvec *bv) +{ + int rc; + + rc = bitvec_get_bit_pos_high(bv, bv->cur_bit); + if (rc >= 0) + bv->cur_bit++; + + return rc; +} + +/*! set multiple bits (based on array of bitvals) at current pos + * \param[in] bv bit vector + * \param[in] bits array of \ref bit_value + * \param[in] count number of bits to set + * \return 0 on success; negative in case of error */ +int bitvec_set_bits(struct bitvec *bv, const enum bit_value *bits, unsigned int count) +{ + int i, rc; + + for (i = 0; i < count; i++) { + rc = bitvec_set_bit(bv, bits[i]); + if (rc) + return rc; + } + + return 0; +} + +/*! set multiple bits (based on numeric value) at current pos. + * \param[in] bv bit vector. + * \param[in] v mask representing which bits needs to be set. + * \param[in] num_bits number of meaningful bits in the mask. + * \param[in] use_lh whether to interpret the bits as L/H values or as 0/1. + * \return 0 on success; negative in case of error. */ +int bitvec_set_u64(struct bitvec *bv, uint64_t v, uint8_t num_bits, bool use_lh) +{ + uint8_t i; + + if (num_bits > 64) + return -E2BIG; + + for (i = 0; i < num_bits; i++) { + int rc; + enum bit_value bit = use_lh ? L : 0; + + if (v & ((uint64_t)1 << (num_bits - i - 1))) + bit = use_lh ? H : 1; + + rc = bitvec_set_bit(bv, bit); + if (rc != 0) + return rc; + } + + return 0; +} + +/*! set multiple bits (based on numeric value) at current pos. + * \return 0 in case of success; negative in case of error. */ +int bitvec_set_uint(struct bitvec *bv, unsigned int ui, unsigned int num_bits) +{ + return bitvec_set_u64(bv, ui, num_bits, false); +} + +/*! get multiple bits (num_bits) from beginning of vector (MSB side) + * \return 16bit signed integer retrieved from bit vector */ +int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits) +{ + if (num_bits > 15 || bv->cur_bit < num_bits) + return -EINVAL; + + if (num_bits < 9) + return bv->data[0] >> (8 - num_bits); + + return osmo_load16be(bv->data) >> (16 - num_bits); +} + +/*! get multiple bits (based on numeric value) from current pos + * \return integer value retrieved from bit vector */ +int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits) +{ + int i; + unsigned int ui = 0; + + for (i = 0; i < num_bits; i++) { + int bit = bitvec_get_bit_pos(bv, bv->cur_bit); + if (bit < 0) + return bit; + if (bit) + ui |= (1 << (num_bits - i - 1)); + bv->cur_bit++; + } + + return ui; +} + +/*! fill num_bits with \fill starting from the current position + * \return 0 on success; negative otherwise (out of vector boundary) + */ +int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill) +{ + unsigned i, stop = bv->cur_bit + num_bits; + for (i = bv->cur_bit; i < stop; i++) + if (bitvec_set_bit(bv, fill) < 0) + return -EINVAL; + + return 0; +} + +/*! pad all remaining bits up to num_bits + * \return 0 on success; negative otherwise */ +int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit) +{ + int n = up_to_bit - bv->cur_bit + 1; + if (n < 1) + return 0; + + return bitvec_fill(bv, n, L); +} + +/*! find first bit set in bit vector + * \return 0 on success; negative otherwise */ +int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, + enum bit_value val) +{ + unsigned int i; + + for (i = n; i < bv->data_len*8; i++) { + if (bitvec_get_bit_pos(bv, i) == val) + return i; + } + + return -1; +} + +/*! get multiple bytes from current pos + * Assumes MSB first encoding. + * \param[in] bv bit vector + * \param[in] bytes array + * \param[in] count number of bytes to copy + * \return 0 on success; negative otherwise + */ +int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, unsigned int count) +{ + int byte_offs = bytenum_from_bitnum(bv->cur_bit); + int bit_offs = bv->cur_bit % 8; + uint8_t c, last_c; + int i; + uint8_t *src; + + if (byte_offs + count + (bit_offs ? 1 : 0) > bv->data_len) + return -EINVAL; + + if (bit_offs == 0) { + memcpy(bytes, bv->data + byte_offs, count); + } else { + src = bv->data + byte_offs; + last_c = *(src++); + for (i = count; i > 0; i--) { + c = *(src++); + *(bytes++) = + (last_c << bit_offs) | + (c >> (8 - bit_offs)); + last_c = c; + } + } + + bv->cur_bit += count * 8; + return 0; +} + +/*! set multiple bytes at current pos + * Assumes MSB first encoding. + * \param[in] bv bit vector + * \param[in] bytes array + * \param[in] count number of bytes to copy + * \return 0 on success; negative otherwise + */ +int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count) +{ + int byte_offs = bytenum_from_bitnum(bv->cur_bit); + int bit_offs = bv->cur_bit % 8; + uint8_t c, last_c; + int i; + uint8_t *dst; + + if (byte_offs + count + (bit_offs ? 1 : 0) > bv->data_len) + return -EINVAL; + + if (bit_offs == 0) { + memcpy(bv->data + byte_offs, bytes, count); + } else if (count > 0) { + dst = bv->data + byte_offs; + /* Get lower bits of first dst byte */ + last_c = *dst >> (8 - bit_offs); + for (i = count; i > 0; i--) { + c = *(bytes++); + *(dst++) = + (last_c << (8 - bit_offs)) | + (c >> bit_offs); + last_c = c; + } + /* Overwrite lower bits of N+1 dst byte */ + *dst = (*dst & ((1 << (8 - bit_offs)) - 1)) | + (last_c << (8 - bit_offs)); + } + + bv->cur_bit += count * 8; + return 0; +} + +/*! Allocate a bit vector + * \param[in] size Number of bits in the vector + * \param[in] ctx Context from which to allocate + * \return pointer to allocated vector; NULL in case of error / +struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *ctx) +{ + struct bitvec *bv = talloc_zero(ctx, struct bitvec); + if (!bv) + return NULL; + + bv->data = talloc_zero_array(bv, uint8_t, size); + if (!(bv->data)) { + talloc_free(bv); + return NULL; + } + + bv->data_len = size; + bv->cur_bit = 0; + return bv; +} + +/*! Free a bit vector (release its memory) + * \param[in] bit vector to free * +void bitvec_free(struct bitvec *bv) +{ + talloc_free(bv->data); + talloc_free(bv); +} +*/ +/*! Export a bit vector to a buffer + * \param[in] bitvec (unpacked bits) + * \param[out] buffer for the unpacked bits + * \return number of bytes (= bits) copied */ +unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer) +{ + unsigned int i = 0; + for (i = 0; i < bv->data_len; i++) + buffer[i] = bv->data[i]; + + return i; +} + +/*! Copy buffer of unpacked bits into bit vector + * \param[in] buffer unpacked input bits + * \param[out] bv unpacked bit vector + * \return number of bytes (= bits) copied */ +unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer) +{ + unsigned int i = 0; + for (i = 0; i < bv->data_len; i++) + bv->data[i] = buffer[i]; + + return i; +} + +/*! read hexadecimap string into a bit vector + * \param[in] src string containing hex digits + * \param[out] bv unpacked bit vector + * \return 0 in case of success; 1 in case of error + */ +int bitvec_unhex(struct bitvec *bv, const char *src) +{ + unsigned i; + unsigned val; + unsigned write_index = 0; + unsigned digits = bv->data_len * 2; + + for (i = 0; i < digits; i++) { + if (sscanf(src + i, "%1x", &val) < 1) { + return 1; + } + bitvec_write_field(bv, &write_index, val, 4); + } + return 0; +} + +/*! read part of the vector + * \param[in] bv The boolean vector to work on + * \param[in,out] read_index Where reading supposed to start in the vector + * \param[in] len How many bits to read from vector + * \returns read bits or negative value on error + */ +uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned int len) +{ + unsigned int i; + uint64_t ui = 0; + bv->cur_bit = *read_index; + + for (i = 0; i < len; i++) { + int bit = bitvec_get_bit_pos((const struct bitvec *)bv, bv->cur_bit); + if (bit < 0) + return bit; + if (bit) + ui |= ((uint64_t)1 << (len - i - 1)); + bv->cur_bit++; + } + *read_index += len; + return ui; +} + +/*! write into the vector + * \param[in] bv The boolean vector to work on + * \param[in,out] write_index Where writing supposed to start in the vector + * \param[in] len How many bits to write + * \returns next write index or negative value on error + */ +int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t val, unsigned int len) +{ + int rc; + + bv->cur_bit = *write_index; + + rc = bitvec_set_u64(bv, val, len, false); + if (rc != 0) + return rc; + + *write_index += len; + + return 0; +} + +/*! convert enum to corresponding character + * \param v input value (bit) + * \return single character, either 0, 1, L or H */ +char bit_value_to_char(enum bit_value v) +{ + switch (v) { + case ZERO: return '0'; + case ONE: return '1'; + case L: return 'L'; + case H: return 'H'; + default: abort(); + } +} + +/*! prints bit vector to provided string + * It's caller's responsibility to ensure that we won't shoot him in the foot: + * the provided buffer should be at lest cur_bit + 1 bytes long + */ +void bitvec_to_string_r(const struct bitvec *bv, char *str) +{ + unsigned i, pos = 0; + char *cur = str; + for (i = 0; i < bv->cur_bit; i++) { + if (0 == i % 8) + *cur++ = ' '; + *cur++ = bit_value_to_char(bitvec_get_bit_pos(bv, i)); + pos++; + } + *cur = 0; +} + +/* we assume that x have at least 1 non-b bit */ +static inline unsigned leading_bits(uint8_t x, bool b) +{ + if (b) { + if (x < 0x80) return 0; + if (x < 0xC0) return 1; + if (x < 0xE0) return 2; + if (x < 0xF0) return 3; + if (x < 0xF8) return 4; + if (x < 0xFC) return 5; + if (x < 0xFE) return 6; + } else { + if (x > 0x7F) return 0; + if (x > 0x3F) return 1; + if (x > 0x1F) return 2; + if (x > 0xF) return 3; + if (x > 7) return 4; + if (x > 3) return 5; + if (x > 1) return 6; + } + return 7; +} +/*! force bit vector to all 0 and current bit to the beginnig of the vector */ +void bitvec_zero(struct bitvec *bv) +{ + bv->cur_bit = 0; + memset(bv->data, 0, bv->data_len); +} + +/*! Return number (bits) of uninterrupted bit run in vector starting from the MSB + * \param[in] bv The boolean vector to work on + * \param[in] b The boolean, sequence of which is looked at from the vector start + * \returns Number of consecutive bits of \p b in \p bv + */ +unsigned bitvec_rl(const struct bitvec *bv, bool b) +{ + unsigned i; + for (i = 0; i < (bv->cur_bit % 8 ? bv->cur_bit / 8 + 1 : bv->cur_bit / 8); i++) { + if ( (b ? 0xFF : 0) != bv->data[i]) + return i * 8 + leading_bits(bv->data[i], b); + } + + return bv->cur_bit; +} + +/*! Return number (bits) of uninterrupted bit run in vector + * starting from the current bit + * \param[in] bv The boolean vector to work on + * \param[in] b The boolean, sequence of 1's or 0's to be checked + * \param[in] max_bits Total Number of Uncmopresed bits + * \returns Number of consecutive bits of \p b in \p bv and cur_bit will + * \go to cur_bit + number of consecutive bit + */ +unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits) +{ + unsigned i = 0; + unsigned j = 8; + int temp_res = 0; + int count = 0; + unsigned readIndex = bv->cur_bit; + unsigned remaining_bits = max_bits % 8; + unsigned remaining_bytes = max_bits / 8; + unsigned byte_mask = 0xFF; + + if (readIndex % 8) { + for (j -= (readIndex % 8) ; j > 0 ; j--) { + if (readIndex < max_bits && bitvec_read_field(bv, &readIndex, 1) == b) + temp_res++; + else { + bv->cur_bit--; + return temp_res; + } + } + } + for (i = (readIndex / 8); + i < (remaining_bits ? remaining_bytes + 1 : remaining_bytes); + i++, count++) { + if ((b ? byte_mask : 0) != bv->data[i]) { + bv->cur_bit = (count * 8 + + leading_bits(bv->data[i], b) + readIndex); + return count * 8 + + leading_bits(bv->data[i], b) + temp_res; + } + } + bv->cur_bit = (temp_res + (count * 8)) + readIndex; + if (bv->cur_bit > max_bits) + bv->cur_bit = max_bits; + return (bv->cur_bit - readIndex + temp_res); +} + +/*! Shifts bitvec to the left, n MSB bits lost */ +void bitvec_shiftl(struct bitvec *bv, unsigned n) +{ + if (0 == n) + return; + if (n >= bv->cur_bit) { + bitvec_zero(bv); + return; + } + + memmove(bv->data, bv->data + n / 8, bv->data_len - n / 8); + + uint8_t tmp[2]; + unsigned i; + for (i = 0; i < bv->data_len - 2; i++) { + uint16_t t = osmo_load16be(bv->data + i); + osmo_store16be(t << (n % 8), &tmp); + bv->data[i] = tmp[0]; + } + + bv->data[bv->data_len - 1] <<= (n % 8); + bv->cur_bit -= n; +} + +/*! Add given array to bitvec + * \param[in,out] bv bit vector to work with + * \param[in] array elements to be added + * \param[in] array_len length of array + * \param[in] dry_run indicates whether to return number of bits required + * instead of adding anything to bv for real + * \param[in] num_bits number of bits to consider in each element of array + * \returns number of bits necessary to add array elements if dry_run is true, + * 0 otherwise (only in this case bv is actually changed) + * + * N. B: no length checks are performed on bv - it's caller's job to ensure + * enough space is available - for example by calling with dry_run = true first. + * + * Useful for common pattern in CSN.1 spec which looks like: + * { 1 < XXX : bit (num_bits) > } ** 0 + * which means repeat any times (between 0 and infinity), + * start each repetition with 1, mark end of repetitions with 0 bit + * see app. note in 3GPP TS 24.007 § B.2.1 Rule A2 + */ +unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array, + unsigned int array_len, bool dry_run, + unsigned int num_bits) +{ + unsigned i, bits = 1; /* account for stop bit */ + for (i = 0; i < array_len; i++) { + if (dry_run) { + bits += (1 + num_bits); + } else { + bitvec_set_bit(bv, 1); + bitvec_set_uint(bv, array[i], num_bits); + } + } + + if (dry_run) + return bits; + + bitvec_set_bit(bv, 0); /* stop bit - end of the sequence */ + return 0; +} + +/*! @} */ diff --git a/lib/decoding/osmocom/core/bitvec.h b/lib/decoding/osmocom/core/bitvec.h new file mode 100644 index 0000000..84db9a5 --- /dev/null +++ b/lib/decoding/osmocom/core/bitvec.h @@ -0,0 +1,87 @@ +/* (C) 2009 by Harald Welte + * (C) 2012 Ivan Klyuchnikov + * (C) 2015 sysmocom - s.f.m.c. GmbH + * + * 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 + +/*! \defgroup bitvec Bit vectors + * @{ + * \file bitvec.h */ + +#include +//#include +#include +#include + +/*! A single GSM bit + * + * In GSM mac blocks, every bit can be 0 or 1, or L or H. L/H are + * defined relative to the 0x2b padding pattern */ +enum bit_value { + ZERO = 0, /*!< A zero (0) bit */ + ONE = 1, /*!< A one (1) bit */ + L = 2, /*!< A CSN.1 "L" bit */ + H = 3, /*!< A CSN.1 "H" bit */ +}; + +/*! structure describing a bit vector */ +struct bitvec { + unsigned int cur_bit; /*!< cursor to the next unused bit */ + unsigned int data_len; /*!< length of data array in bytes */ + uint8_t *data; /*!< pointer to data array */ +}; + +enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr); +enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv, + unsigned int bitnr); +unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n); +int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum, + enum bit_value bit); +int bitvec_set_bit(struct bitvec *bv, enum bit_value bit); +int bitvec_get_bit_high(struct bitvec *bv); +int bitvec_set_bits(struct bitvec *bv, const enum bit_value *bits, unsigned int count); +int bitvec_set_u64(struct bitvec *bv, uint64_t v, uint8_t num_bits, bool use_lh); +int bitvec_set_uint(struct bitvec *bv, unsigned int in, unsigned int count); +int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits); +int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value val); +int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit); +int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, unsigned int count); +int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count); +/*struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *bvctx);*/ +/*void bitvec_free(struct bitvec *bv);*/ +int bitvec_unhex(struct bitvec *bv, const char *src); +unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer); +unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer); +uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned int len); +int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t val, unsigned int len); +int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill); +char bit_value_to_char(enum bit_value v); +void bitvec_to_string_r(const struct bitvec *bv, char *str); +void bitvec_zero(struct bitvec *bv); +unsigned bitvec_rl(const struct bitvec *bv, bool b); +unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits); +void bitvec_shiftl(struct bitvec *bv, unsigned int n); +int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits); +unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array, + unsigned int array_len, bool dry_run, + unsigned int num_bits); + +/*! @} */ diff --git a/lib/decoding/osmocom/core/conv.c b/lib/decoding/osmocom/core/conv.c new file mode 100644 index 0000000..e60ce35 --- /dev/null +++ b/lib/decoding/osmocom/core/conv.c @@ -0,0 +1,644 @@ +/*! \file conv.c + * Generic convolutional encoding / decoding. */ +/* + * Copyright (C) 2011 Sylvain Munaut + * + * 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 conv + * @{ + * Osmocom convolutional encoder and decoder. + * + * \file conv.c */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ALLOCA_H +#include +#endif +#include +#include +#include + +#include +#include + + +/* ------------------------------------------------------------------------ */ +/* Common */ +/* ------------------------------------------------------------------------ */ + +int +osmo_conv_get_input_length(const struct osmo_conv_code *code, int len) +{ + return len <= 0 ? code->len : len; +} + +int +osmo_conv_get_output_length(const struct osmo_conv_code *code, int len) +{ + int pbits, in_len, out_len; + + /* Input length */ + in_len = osmo_conv_get_input_length(code, len); + + /* Output length */ + out_len = in_len * code->N; + + if (code->term == CONV_TERM_FLUSH) + out_len += code->N * (code->K - 1); + + /* Count punctured bits */ + if (code->puncture) { + for (pbits=0; code->puncture[pbits] >= 0; pbits++); + out_len -= pbits; + } + + return out_len; +} + + +/* ------------------------------------------------------------------------ */ +/* Encoding */ +/* ------------------------------------------------------------------------ */ + +/*! Initialize a convolutional encoder + * \param[in,out] encoder Encoder state to initialize + * \param[in] code Description of convolutional code + */ +void +osmo_conv_encode_init(struct osmo_conv_encoder *encoder, + const struct osmo_conv_code *code) +{ + memset(encoder, 0x00, sizeof(struct osmo_conv_encoder)); + encoder->code = code; +} + +void +osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder, + const ubit_t *input) +{ + int i; + uint8_t state = 0; + + for (i=0; i<(encoder->code->K-1); i++) + state = (state << 1) | input[i]; + + encoder->state = state; +} + +static inline int +_conv_encode_do_output(struct osmo_conv_encoder *encoder, + uint8_t out, ubit_t *output) +{ + const struct osmo_conv_code *code = encoder->code; + int o_idx = 0; + int j; + + if (code->puncture) { + for (j=0; jN; j++) + { + int bit_no = code->N - j - 1; + int r_idx = encoder->i_idx * code->N + j; + + if (code->puncture[encoder->p_idx] == r_idx) + encoder->p_idx++; + else + output[o_idx++] = (out >> bit_no) & 1; + } + } else { + for (j=0; jN; j++) + { + int bit_no = code->N - j - 1; + output[o_idx++] = (out >> bit_no) & 1; + } + } + + return o_idx; +} + +int +osmo_conv_encode_raw(struct osmo_conv_encoder *encoder, + const ubit_t *input, ubit_t *output, int n) +{ + const struct osmo_conv_code *code = encoder->code; + uint8_t state; + int i; + int o_idx; + + o_idx = 0; + state = encoder->state; + + for (i=0; inext_output[state][bit]; + state = code->next_state[state][bit]; + + o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]); + + encoder->i_idx++; + } + + encoder->state = state; + + return o_idx; +} + +int +osmo_conv_encode_flush(struct osmo_conv_encoder *encoder, + ubit_t *output) +{ + const struct osmo_conv_code *code = encoder->code; + uint8_t state; + int n; + int i; + int o_idx; + + n = code->K - 1; + + o_idx = 0; + state = encoder->state; + + for (i=0; inext_term_output) { + out = code->next_term_output[state]; + state = code->next_term_state[state]; + } else { + out = code->next_output[state][0]; + state = code->next_state[state][0]; + } + + o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]); + + encoder->i_idx++; + } + + encoder->state = state; + + return o_idx; +} + +/*! All-in-one convolutional encoding function + * \param[in] code description of convolutional code to be used + * \param[in] input array of unpacked bits (uncoded) + * \param[out] output array of unpacked bits (encoded) + * \return Number of produced output bits + * + * This is an all-in-one function, taking care of + * \ref osmo_conv_init, \ref osmo_conv_encode_load_state, + * \ref osmo_conv_encode_raw and \ref osmo_conv_encode_flush as needed. + */ +int +osmo_conv_encode(const struct osmo_conv_code *code, + const ubit_t *input, ubit_t *output) +{ + struct osmo_conv_encoder encoder; + int l; + + osmo_conv_encode_init(&encoder, code); + + if (code->term == CONV_TERM_TAIL_BITING) { + int eidx = code->len - code->K + 1; + osmo_conv_encode_load_state(&encoder, &input[eidx]); + } + + l = osmo_conv_encode_raw(&encoder, input, output, code->len); + + if (code->term == CONV_TERM_FLUSH) + l += osmo_conv_encode_flush(&encoder, &output[l]); + + return l; +} + + +/* ------------------------------------------------------------------------ */ +/* Decoding (viterbi) */ +/* ------------------------------------------------------------------------ */ + +#define MAX_AE 0x00ffffff + +/* Forward declaration for accerlated decoding with certain codes */ +int +osmo_conv_decode_acc(const struct osmo_conv_code *code, + const sbit_t *input, ubit_t *output); + +void +osmo_conv_decode_init(struct osmo_conv_decoder *decoder, + const struct osmo_conv_code *code, int len, int start_state) +{ + int n_states; + + /* Init */ + if (len <= 0) + len = code->len; + + n_states = 1 << (code->K - 1); + + memset(decoder, 0x00, sizeof(struct osmo_conv_decoder)); + + decoder->code = code; + decoder->n_states = n_states; + decoder->len = len; + + /* Allocate arrays */ + decoder->ae = malloc(sizeof(unsigned int) * n_states); + decoder->ae_next = malloc(sizeof(unsigned int) * n_states); + + decoder->state_history = malloc(sizeof(uint8_t) * n_states * (len + decoder->code->K - 1)); + + /* Classic reset */ + osmo_conv_decode_reset(decoder, start_state); +} + +void +osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state) +{ + int i; + + /* Reset indexes */ + decoder->o_idx = 0; + decoder->p_idx = 0; + + /* Initial error */ + if (start_state < 0) { + /* All states possible */ + memset(decoder->ae, 0x00, sizeof(unsigned int) * decoder->n_states); + } else { + /* Fixed start state */ + for (i=0; in_states; i++) { + decoder->ae[i] = (i == start_state) ? 0 : MAX_AE; + } + } +} + +void +osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder) +{ + int i; + unsigned int min_ae = MAX_AE; + + /* Reset indexes */ + decoder->o_idx = 0; + decoder->p_idx = 0; + + /* Initial error normalize (remove constant) */ + for (i=0; in_states; i++) { + if (decoder->ae[i] < min_ae) + min_ae = decoder->ae[i]; + } + + for (i=0; in_states; i++) + decoder->ae[i] -= min_ae; +} + +void +osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder) +{ + free(decoder->ae); + free(decoder->ae_next); + free(decoder->state_history); + + memset(decoder, 0x00, sizeof(struct osmo_conv_decoder)); +} + +int +osmo_conv_decode_scan(struct osmo_conv_decoder *decoder, + const sbit_t *input, int n) +{ + const struct osmo_conv_code *code = decoder->code; + + int i, s, b, j; + + int n_states; + unsigned int *ae; + unsigned int *ae_next; + uint8_t *state_history; + sbit_t *in_sym; + + int i_idx, p_idx; + + /* Prepare */ + n_states = decoder->n_states; + + ae = decoder->ae; + ae_next = decoder->ae_next; + state_history = &decoder->state_history[n_states * decoder->o_idx]; + + in_sym = malloc(sizeof(sbit_t) * code->N); + + i_idx = 0; + p_idx = decoder->p_idx; + + /* Scan the treillis */ + for (i=0; ipuncture) { + /* Hard way ... */ + for (j=0; jN; j++) { + int idx = ((decoder->o_idx + i) * code->N) + j; + if (idx == code->puncture[p_idx]) { + in_sym[j] = 0; /* Undefined */ + p_idx++; + } else { + in_sym[j] = input[i_idx]; + i_idx++; + } + } + } else { + /* Easy, just copy N bits */ + memcpy(in_sym, &input[i_idx], code->N); + i_idx += code->N; + } + + /* Scan all state */ + for (s=0; snext_output[s][b]; + uint8_t state = code->next_state[s][b]; + + /* New error for this path */ + nae = ae[s]; /* start from last error */ + m = 1 << (code->N - 1); /* mask for 'out' bit selection */ + + for (j=0; jN; j++) { + int is = (int)in_sym[j]; + if (is) { + ov = (out & m) ? -127 : 127; /* sbit_t value for it */ + e = is - ov; /* raw error for this bit */ + nae += (e * e) >> 9; /* acc the squared/scaled value */ + } + m >>= 1; /* next mask bit */ + } + + /* Is it survivor ? */ + if (ae_next[state] > nae) { + ae_next[state] = nae; + state_history[(n_states * i) + state] = s; + } + } + } + + /* Copy accumulated error */ + memcpy(ae, ae_next, sizeof(unsigned int) * n_states); + } + + /* Update decoder state */ + decoder->p_idx = p_idx; + decoder->o_idx += n; + + free(in_sym); + return i_idx; +} + +int +osmo_conv_decode_flush(struct osmo_conv_decoder *decoder, + const sbit_t *input) +{ + const struct osmo_conv_code *code = decoder->code; + + int i, s, j; + + int n_states; + unsigned int *ae; + unsigned int *ae_next; + uint8_t *state_history; + sbit_t *in_sym; + + int i_idx, p_idx; + + /* Prepare */ + n_states = decoder->n_states; + + ae = decoder->ae; + ae_next = decoder->ae_next; + state_history = &decoder->state_history[n_states * decoder->o_idx]; + + in_sym = malloc(sizeof(sbit_t) * code->N); + + i_idx = 0; + p_idx = decoder->p_idx; + + /* Scan the treillis */ + for (i=0; iK-1; i++) + { + /* Reset next accumulated error */ + for (s=0; spuncture) { + /* Hard way ... */ + for (j=0; jN; j++) { + int idx = ((decoder->o_idx + i) * code->N) + j; + if (idx == code->puncture[p_idx]) { + in_sym[j] = 0; /* Undefined */ + p_idx++; + } else { + in_sym[j] = input[i_idx]; + i_idx++; + } + } + } else { + /* Easy, just copy N bits */ + memcpy(in_sym, &input[i_idx], code->N); + i_idx += code->N; + } + + /* Scan all state */ + for (s=0; snext_term_output) { + out = code->next_term_output[s]; + state = code->next_term_state[s]; + } else { + out = code->next_output[s][0]; + state = code->next_state[s][0]; + } + + /* New error for this path */ + nae = ae[s]; /* start from last error */ + m = 1 << (code->N - 1); /* mask for 'out' bit selection */ + + for (j=0; jN; j++) { + int is = (int)in_sym[j]; + if (is) { + ov = (out & m) ? -127 : 127; /* sbit_t value for it */ + e = is - ov; /* raw error for this bit */ + nae += (e * e) >> 9; /* acc the squared/scaled value */ + } + m >>= 1; /* next mask bit */ + } + + /* Is it survivor ? */ + if (ae_next[state] > nae) { + ae_next[state] = nae; + state_history[(n_states * i) + state] = s; + } + } + + /* Copy accumulated error */ + memcpy(ae, ae_next, sizeof(unsigned int) * n_states); + } + + /* Update decoder state */ + decoder->p_idx = p_idx; + decoder->o_idx += code->K - 1; + + free(in_sym); + return i_idx; +} + +int +osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder, + ubit_t *output, int has_flush, int end_state) +{ + const struct osmo_conv_code *code = decoder->code; + + int min_ae; + uint8_t min_state, cur_state; + int i, s, n; + + uint8_t *sh_ptr; + + /* End state ? */ + if (end_state < 0) { + /* Find state with least error */ + min_ae = MAX_AE; + min_state = 0xff; + + for (s=0; sn_states; s++) + { + if (decoder->ae[s] < min_ae) { + min_ae = decoder->ae[s]; + min_state = s; + } + } + + if (min_state == 0xff) + return -1; + } else { + min_state = (uint8_t) end_state; + min_ae = decoder->ae[end_state]; + } + + /* Traceback */ + cur_state = min_state; + + n = decoder->o_idx; + + sh_ptr = &decoder->state_history[decoder->n_states * (n-1)]; + + /* No output for the K-1 termination input bits */ + if (has_flush) { + for (i=0; iK-1; i++) { + cur_state = sh_ptr[cur_state]; + sh_ptr -= decoder->n_states; + } + n -= code->K - 1; + } + + /* Generate output backward */ + for (i=n-1; i>=0; i--) + { + min_state = cur_state; + cur_state = sh_ptr[cur_state]; + + sh_ptr -= decoder->n_states; + + if (code->next_state[cur_state][0] == min_state) + output[i] = 0; + else + output[i] = 1; + } + + return min_ae; +} + +/*! All-in-one convolutional decoding function + * \param[in] code description of convolutional code to be used + * \param[in] input array of soft bits (coded) + * \param[out] output array of unpacked bits (decoded) + * + * This is an all-in-one function, taking care of + * \ref osmo_conv_decode_init, \ref osmo_conv_decode_scan, + * \ref osmo_conv_decode_flush, \ref osmo_conv_decode_get_output and + * \ref osmo_conv_decode_deinit. + */ +int +osmo_conv_decode(const struct osmo_conv_code *code, + const sbit_t *input, ubit_t *output) +{ + struct osmo_conv_decoder decoder; + int rv, l; + + /* Use accelerated implementation for supported codes */ + if ((code->N <= 4) && ((code->K == 5) || (code->K == 7))) + return osmo_conv_decode_acc(code, input, output); + + osmo_conv_decode_init(&decoder, code, 0, 0); + + if (code->term == CONV_TERM_TAIL_BITING) { + osmo_conv_decode_scan(&decoder, input, code->len); + osmo_conv_decode_rewind(&decoder); + } + + l = osmo_conv_decode_scan(&decoder, input, code->len); + + if (code->term == CONV_TERM_FLUSH) + osmo_conv_decode_flush(&decoder, &input[l]); + + rv = osmo_conv_decode_get_output(&decoder, output, + code->term == CONV_TERM_FLUSH, /* has_flush */ + code->term == CONV_TERM_FLUSH ? 0 : -1 /* end_state */ + ); + + osmo_conv_decode_deinit(&decoder); + + return rv; +} + +/*! @} */ diff --git a/lib/decoding/osmocom/core/conv.h b/lib/decoding/osmocom/core/conv.h new file mode 100644 index 0000000..8b344f4 --- /dev/null +++ b/lib/decoding/osmocom/core/conv.h @@ -0,0 +1,139 @@ +/*! \file conv.h + * Osmocom convolutional encoder and decoder. */ +/* + * Copyright (C) 2011 Sylvain Munaut + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! \defgroup conv Convolutional encoding and decoding routines + * @{ + * \file conv.h */ + +#pragma once + +#include + +#include + +/*! possibe termination types + * + * The termination type will determine which state the encoder/decoder + * can start/end with. This is mostly taken care of in the high level API + * call. So if you use the low level API, you must take care of making the + * proper calls yourself. + */ +enum osmo_conv_term { + CONV_TERM_FLUSH = 0, /*!< Flush encoder state */ + CONV_TERM_TRUNCATION, /*!< Direct truncation */ + CONV_TERM_TAIL_BITING, /*!< Tail biting */ +}; + +/*! structure describing a given convolutional code + * + * The only required fields are N,K and the next_output/next_state arrays. The + * other can be left to default value of zero depending on what the code does. + * If 'len' is left at 0 then only the low level API can be used. + */ +struct osmo_conv_code { + int N; /*!< Inverse of code rate */ + int K; /*!< Constraint length */ + int len; /*!< # of data bits */ + + enum osmo_conv_term term; /*!< Termination type */ + + const uint8_t (*next_output)[2];/*!< Next output array */ + const uint8_t (*next_state)[2]; /*!< Next state array */ + + const uint8_t *next_term_output;/*!< Flush termination output */ + const uint8_t *next_term_state; /*!< Flush termination state */ + + const int *puncture; /*!< Punctured bits indexes */ +}; + + +/* Common */ + +int osmo_conv_get_input_length(const struct osmo_conv_code *code, int len); +int osmo_conv_get_output_length(const struct osmo_conv_code *code, int len); + + +/* Encoding */ + + /* Low level API */ + +/*! convolutional encoder state */ +struct osmo_conv_encoder { + const struct osmo_conv_code *code; /*!< for which code? */ + int i_idx; /*!< Next input bit index */ + int p_idx; /*!< Current puncture index */ + uint8_t state; /*!< Current state */ +}; + +void osmo_conv_encode_init(struct osmo_conv_encoder *encoder, + const struct osmo_conv_code *code); +void osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder, + const ubit_t *input); +int osmo_conv_encode_raw(struct osmo_conv_encoder *encoder, + const ubit_t *input, ubit_t *output, int n); +int osmo_conv_encode_flush(struct osmo_conv_encoder *encoder, ubit_t *output); + + /* All-in-one */ +int osmo_conv_encode(const struct osmo_conv_code *code, + const ubit_t *input, ubit_t *output); + + +/* Decoding */ + + /* Low level API */ + +/*! convolutional decoder state */ +struct osmo_conv_decoder { + const struct osmo_conv_code *code; /*!< for which code? */ + + int n_states; /*!< number of states */ + + int len; /*!< Max o_idx (excl. termination) */ + + int o_idx; /*!< output index */ + int p_idx; /*!< puncture index */ + + unsigned int *ae; /*!< accumulated error */ + unsigned int *ae_next; /*!< next accumulated error (tmp in scan) */ + uint8_t *state_history; /*!< state history [len][n_states] */ +}; + +void osmo_conv_decode_init(struct osmo_conv_decoder *decoder, + const struct osmo_conv_code *code, + int len, int start_state); +void osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state); +void osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder); +void osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder); + +int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder, + const sbit_t *input, int n); +int osmo_conv_decode_flush(struct osmo_conv_decoder *decoder, + const sbit_t *input); +int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder, + ubit_t *output, int has_flush, int end_state); + + /* All-in-one */ +int osmo_conv_decode(const struct osmo_conv_code *code, + const sbit_t *input, ubit_t *output); + + +/*! @} */ diff --git a/lib/decoding/osmocom/core/conv_acc.c b/lib/decoding/osmocom/core/conv_acc.c new file mode 100644 index 0000000..dce2682 --- /dev/null +++ b/lib/decoding/osmocom/core/conv_acc.c @@ -0,0 +1,720 @@ +/*! \file conv_acc.c + * Accelerated Viterbi decoder implementation. */ +/* + * Copyright (C) 2013, 2014 Thomas Tsou + * + * 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 +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __attribute__(_arg_) + +#include + +#define BIT2NRZ(REG,N) (((REG >> N) & 0x01) * 2 - 1) * -1 +#define NUM_STATES(K) (K == 7 ? 64 : 16) + +#define INIT_POINTERS(simd) \ +{ \ + osmo_conv_metrics_k5_n2 = osmo_conv_##simd##_metrics_k5_n2; \ + osmo_conv_metrics_k5_n3 = osmo_conv_##simd##_metrics_k5_n3; \ + osmo_conv_metrics_k5_n4 = osmo_conv_##simd##_metrics_k5_n4; \ + osmo_conv_metrics_k7_n2 = osmo_conv_##simd##_metrics_k7_n2; \ + osmo_conv_metrics_k7_n3 = osmo_conv_##simd##_metrics_k7_n3; \ + osmo_conv_metrics_k7_n4 = osmo_conv_##simd##_metrics_k7_n4; \ + vdec_malloc = &osmo_conv_##simd##_vdec_malloc; \ + vdec_free = &osmo_conv_##simd##_vdec_free; \ +} + +static int init_complete = 0; + +__attribute__ ((visibility("hidden"))) int avx2_supported = 0; +__attribute__ ((visibility("hidden"))) int ssse3_supported = 0; +__attribute__ ((visibility("hidden"))) int sse41_supported = 0; + +/** + * These pointers are being initialized at runtime by the + * osmo_conv_init() depending on supported SIMD extensions. + */ +static int16_t *(*vdec_malloc)(size_t n); +static void (*vdec_free)(int16_t *ptr); + +void (*osmo_conv_metrics_k5_n2)(const int8_t *seq, + const int16_t *out, int16_t *sums, int16_t *paths, int norm); +void (*osmo_conv_metrics_k5_n3)(const int8_t *seq, + const int16_t *out, int16_t *sums, int16_t *paths, int norm); +void (*osmo_conv_metrics_k5_n4)(const int8_t *seq, + const int16_t *out, int16_t *sums, int16_t *paths, int norm); +void (*osmo_conv_metrics_k7_n2)(const int8_t *seq, + const int16_t *out, int16_t *sums, int16_t *paths, int norm); +void (*osmo_conv_metrics_k7_n3)(const int8_t *seq, + const int16_t *out, int16_t *sums, int16_t *paths, int norm); +void (*osmo_conv_metrics_k7_n4)(const int8_t *seq, + const int16_t *out, int16_t *sums, int16_t *paths, int norm); + +/* Forward malloc wrappers */ +int16_t *osmo_conv_gen_vdec_malloc(size_t n); +void osmo_conv_gen_vdec_free(int16_t *ptr); + +#if defined(HAVE_SSSE3) +int16_t *osmo_conv_sse_vdec_malloc(size_t n); +void osmo_conv_sse_vdec_free(int16_t *ptr); +#endif + +#if defined(HAVE_SSSE3) && defined(HAVE_AVX2) +int16_t *osmo_conv_sse_avx_vdec_malloc(size_t n); +void osmo_conv_sse_avx_vdec_free(int16_t *ptr); +#endif + +/* Forward Metric Units */ +void osmo_conv_gen_metrics_k5_n2(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_gen_metrics_k5_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_gen_metrics_k5_n4(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_gen_metrics_k7_n2(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_gen_metrics_k7_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_gen_metrics_k7_n4(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); + +#if defined(HAVE_SSSE3) +void osmo_conv_sse_metrics_k5_n2(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_metrics_k5_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_metrics_k5_n4(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_metrics_k7_n2(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_metrics_k7_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_metrics_k7_n4(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +#endif + +#if defined(HAVE_SSSE3) && defined(HAVE_AVX2) +void osmo_conv_sse_avx_metrics_k5_n2(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_avx_metrics_k5_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_avx_metrics_k5_n4(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_avx_metrics_k7_n2(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_avx_metrics_k7_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +void osmo_conv_sse_avx_metrics_k7_n4(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm); +#endif + +/* Trellis State + * state - Internal lshift register value + * prev - Register values of previous 0 and 1 states + */ +struct vstate { + unsigned state; + unsigned prev[2]; +}; + +/* Trellis Object + * num_states - Number of states in the trellis + * sums - Accumulated path metrics + * outputs - Trellis output values + * vals - Input value that led to each state + */ +struct vtrellis { + int num_states; + int16_t *sums; + int16_t *outputs; + uint8_t *vals; +}; + +/* Viterbi Decoder + * n - Code order + * k - Constraint length + * len - Horizontal length of trellis + * recursive - Set to '1' if the code is recursive + * intrvl - Normalization interval + * trellis - Trellis object + * paths - Trellis paths + */ +struct vdecoder { + int n; + int k; + int len; + int recursive; + int intrvl; + struct vtrellis trellis; + int16_t **paths; + + void (*metric_func)(const int8_t *, const int16_t *, + int16_t *, int16_t *, int); +}; + +/* Accessor calls */ +static inline int conv_code_recursive(const struct osmo_conv_code *code) +{ + return code->next_term_output ? 1 : 0; +} + +/* Left shift and mask for finding the previous state */ +static unsigned vstate_lshift(unsigned reg, int k, int val) +{ + unsigned mask; + + if (k == 5) + mask = 0x0e; + else if (k == 7) + mask = 0x3e; + else + mask = 0; + + return ((reg << 1) & mask) | val; +} + +/* Bit endian manipulators */ +static inline unsigned bitswap2(unsigned v) +{ + return ((v & 0x02) >> 1) | ((v & 0x01) << 1); +} + +static inline unsigned bitswap3(unsigned v) +{ + return ((v & 0x04) >> 2) | ((v & 0x02) >> 0) | + ((v & 0x01) << 2); +} + +static inline unsigned bitswap4(unsigned v) +{ + return ((v & 0x08) >> 3) | ((v & 0x04) >> 1) | + ((v & 0x02) << 1) | ((v & 0x01) << 3); +} + +static inline unsigned bitswap5(unsigned v) +{ + return ((v & 0x10) >> 4) | ((v & 0x08) >> 2) | ((v & 0x04) >> 0) | + ((v & 0x02) << 2) | ((v & 0x01) << 4); +} + +static inline unsigned bitswap6(unsigned v) +{ + return ((v & 0x20) >> 5) | ((v & 0x10) >> 3) | ((v & 0x08) >> 1) | + ((v & 0x04) << 1) | ((v & 0x02) << 3) | ((v & 0x01) << 5); +} + +static unsigned bitswap(unsigned v, unsigned n) +{ + switch (n) { + case 1: + return v; + case 2: + return bitswap2(v); + case 3: + return bitswap3(v); + case 4: + return bitswap4(v); + case 5: + return bitswap5(v); + case 6: + return bitswap6(v); + default: + return 0; + } +} + +/* Generate non-recursive state output from generator state table + * Note that the shift register moves right (i.e. the most recent bit is + * shifted into the register at k-1 bit of the register), which is typical + * textbook representation. The API transition table expects the most recent + * bit in the low order bit, or left shift. A bitswap operation is required + * to accommodate the difference. + */ +static unsigned gen_output(struct vstate *state, int val, + const struct osmo_conv_code *code) +{ + unsigned out, prev; + + prev = bitswap(state->prev[0], code->K - 1); + out = code->next_output[prev][val]; + out = bitswap(out, code->N); + + return out; +} + +/* Populate non-recursive trellis state + * For a given state defined by the k-1 length shift register, find the + * value of the input bit that drove the trellis to that state. Also + * generate the N outputs of the generator polynomial at that state. + */ +static int gen_state_info(uint8_t *val, unsigned reg, + int16_t *output, const struct osmo_conv_code *code) +{ + int i; + unsigned out; + struct vstate state; + + /* Previous '0' state */ + state.state = reg; + state.prev[0] = vstate_lshift(reg, code->K, 0); + state.prev[1] = vstate_lshift(reg, code->K, 1); + + *val = (reg >> (code->K - 2)) & 0x01; + + /* Transition output */ + out = gen_output(&state, *val, code); + + /* Unpack to NRZ */ + for (i = 0; i < code->N; i++) + output[i] = BIT2NRZ(out, i); + + return 0; +} + +/* Generate recursive state output from generator state table */ +static unsigned gen_recursive_output(struct vstate *state, + uint8_t *val, unsigned reg, + const struct osmo_conv_code *code, int pos) +{ + int val0, val1; + unsigned out, prev; + + /* Previous '0' state */ + prev = vstate_lshift(reg, code->K, 0); + prev = bitswap(prev, code->K - 1); + + /* Input value */ + val0 = (reg >> (code->K - 2)) & 0x01; + val1 = (code->next_term_output[prev] >> pos) & 0x01; + *val = val0 == val1 ? 0 : 1; + + /* Wrapper for osmocom state access */ + prev = bitswap(state->prev[0], code->K - 1); + + /* Compute the transition output */ + out = code->next_output[prev][*val]; + out = bitswap(out, code->N); + + return out; +} + +/* Populate recursive trellis state + * The bit position of the systematic bit is not explicitly marked by the + * API, so it must be extracted from the generator table. Otherwise, + * populate the trellis similar to the non-recursive version. + * Non-systematic recursive codes are not supported. + */ +static int gen_recursive_state_info(uint8_t *val, + unsigned reg, int16_t *output, const struct osmo_conv_code *code) +{ + int i, j, pos = -1; + int ns = NUM_STATES(code->K); + unsigned out; + struct vstate state; + + /* Previous '0' and '1' states */ + state.state = reg; + state.prev[0] = vstate_lshift(reg, code->K, 0); + state.prev[1] = vstate_lshift(reg, code->K, 1); + + /* Find recursive bit location */ + for (i = 0; i < code->N; i++) { + for (j = 0; j < ns; j++) { + if ((code->next_output[j][0] >> i) & 0x01) + break; + } + + if (j == ns) { + pos = i; + break; + } + } + + /* Non-systematic recursive code not supported */ + if (pos < 0) + return -EPROTO; + + /* Transition output */ + out = gen_recursive_output(&state, val, reg, code, pos); + + /* Unpack to NRZ */ + for (i = 0; i < code->N; i++) + output[i] = BIT2NRZ(out, i); + + return 0; +} + +/* Release the trellis */ +static void free_trellis(struct vtrellis *trellis) +{ + if (!trellis) + return; + + vdec_free(trellis->outputs); + vdec_free(trellis->sums); + free(trellis->vals); +} + +/* Initialize the trellis object + * Initialization consists of generating the outputs and output value of a + * given state. Due to trellis symmetry and anti-symmetry, only one of the + * transition paths is utilized by the butterfly operation in the forward + * recursion, so only one set of N outputs is required per state variable. + */ +static int generate_trellis(struct vdecoder *dec, + const struct osmo_conv_code *code) +{ + struct vtrellis *trellis = &dec->trellis; + int16_t *outputs; + int i, rc; + + int ns = NUM_STATES(code->K); + int olen = (code->N == 2) ? 2 : 4; + + trellis->num_states = ns; + trellis->sums = vdec_malloc(ns); + trellis->outputs = vdec_malloc(ns * olen); + trellis->vals = (uint8_t *) malloc(ns * sizeof(uint8_t)); + + if (!trellis->sums || !trellis->outputs || !trellis->vals) { + rc = -ENOMEM; + goto fail; + } + + /* Populate the trellis state objects */ + for (i = 0; i < ns; i++) { + outputs = &trellis->outputs[olen * i]; + if (dec->recursive) { + rc = gen_recursive_state_info(&trellis->vals[i], + i, outputs, code); + } else { + rc = gen_state_info(&trellis->vals[i], + i, outputs, code); + } + + if (rc < 0) + goto fail; + + /* Set accumulated path metrics to zero */ + trellis->sums[i] = 0; + } + + /** + * For termination other than tail-biting, initialize the zero state + * as the encoder starting state. Initialize with the maximum + * accumulated sum at length equal to the constraint length. + */ + if (code->term != CONV_TERM_TAIL_BITING) + trellis->sums[0] = INT8_MAX * code->N * code->K; + + return 0; + +fail: + free_trellis(trellis); + return rc; +} + +static void _traceback(struct vdecoder *dec, + unsigned state, uint8_t *out, int len) +{ + int i; + unsigned path; + + for (i = len - 1; i >= 0; i--) { + path = dec->paths[i][state] + 1; + out[i] = dec->trellis.vals[state]; + state = vstate_lshift(state, dec->k, path); + } +} + +static void _traceback_rec(struct vdecoder *dec, + unsigned state, uint8_t *out, int len) +{ + int i; + unsigned path; + + for (i = len - 1; i >= 0; i--) { + path = dec->paths[i][state] + 1; + out[i] = path ^ dec->trellis.vals[state]; + state = vstate_lshift(state, dec->k, path); + } +} + +/* Traceback and generate decoded output + * Find the largest accumulated path metric at the final state except for + * the zero terminated case, where we assume the final state is always zero. + */ +static int traceback(struct vdecoder *dec, uint8_t *out, int term, int len) +{ + int i, sum, max = -1; + unsigned path, state = 0; + + if (term != CONV_TERM_FLUSH) { + for (i = 0; i < dec->trellis.num_states; i++) { + sum = dec->trellis.sums[i]; + if (sum > max) { + max = sum; + state = i; + } + } + + if (max < 0) + return -EPROTO; + } + + for (i = dec->len - 1; i >= len; i--) { + path = dec->paths[i][state] + 1; + state = vstate_lshift(state, dec->k, path); + } + + if (dec->recursive) + _traceback_rec(dec, state, out, len); + else + _traceback(dec, state, out, len); + + return 0; +} + +/* Release decoder object */ +static void vdec_deinit(struct vdecoder *dec) +{ + if (!dec) + return; + + free_trellis(&dec->trellis); + + if (dec->paths != NULL) { + vdec_free(dec->paths[0]); + free(dec->paths); + } +} + +/* Initialize decoder object with code specific params + * Subtract the constraint length K on the normalization interval to + * accommodate the initialization path metric at state zero. + */ +static int vdec_init(struct vdecoder *dec, const struct osmo_conv_code *code) +{ + int i, ns, rc; + + ns = NUM_STATES(code->K); + + dec->n = code->N; + dec->k = code->K; + dec->recursive = conv_code_recursive(code); + dec->intrvl = INT16_MAX / (dec->n * INT8_MAX) - dec->k; + + if (dec->k == 5) { + switch (dec->n) { + case 2: + dec->metric_func = osmo_conv_metrics_k5_n2; + break; + case 3: + dec->metric_func = osmo_conv_metrics_k5_n3; + break; + case 4: + dec->metric_func = osmo_conv_metrics_k5_n4; + break; + default: + return -EINVAL; + } + } else if (dec->k == 7) { + switch (dec->n) { + case 2: + dec->metric_func = osmo_conv_metrics_k7_n2; + break; + case 3: + dec->metric_func = osmo_conv_metrics_k7_n3; + break; + case 4: + dec->metric_func = osmo_conv_metrics_k7_n4; + break; + default: + return -EINVAL; + } + } else { + return -EINVAL; + } + + if (code->term == CONV_TERM_FLUSH) + dec->len = code->len + code->K - 1; + else + dec->len = code->len; + + rc = generate_trellis(dec, code); + if (rc) + return rc; + + dec->paths = (int16_t **) malloc(sizeof(int16_t *) * dec->len); + if (!dec->paths) + goto enomem; + + dec->paths[0] = vdec_malloc(ns * dec->len); + if (!dec->paths[0]) + goto enomem; + + for (i = 1; i < dec->len; i++) + dec->paths[i] = &dec->paths[0][i * ns]; + + return 0; + +enomem: + vdec_deinit(dec); + return -ENOMEM; +} + +/* Depuncture sequence with nagative value terminated puncturing matrix */ +static int depuncture(const int8_t *in, const int *punc, int8_t *out, int len) +{ + int i, n = 0, m = 0; + + for (i = 0; i < len; i++) { + if (i == punc[n]) { + out[i] = 0; + n++; + continue; + } + + out[i] = in[m++]; + } + + return 0; +} + +/* Forward trellis recursion + * Generate branch metrics and path metrics with a combined function. Only + * accumulated path metric sums and path selections are stored. Normalize on + * the interval specified by the decoder. + */ +static void forward_traverse(struct vdecoder *dec, const int8_t *seq) +{ + int i; + + for (i = 0; i < dec->len; i++) { + dec->metric_func(&seq[dec->n * i], + dec->trellis.outputs, + dec->trellis.sums, + dec->paths[i], + !(i % dec->intrvl)); + } +} + +/* Convolutional decode with a decoder object + * Initial puncturing run if necessary followed by the forward recursion. + * For tail-biting perform a second pass before running the backward + * traceback operation. + */ +static int conv_decode(struct vdecoder *dec, const int8_t *seq, + const int *punc, uint8_t *out, int len, int term) +{ + //int8_t depunc[dec->len * dec->n]; //!! this isn't portable, in strict C you can't use size of an array that is not known at compile time + int8_t * depunc = malloc(sizeof(int8_t)*dec->len * dec->n); + + + if (punc) { + depuncture(seq, punc, depunc, dec->len * dec->n); + seq = depunc; + } + + /* Propagate through the trellis with interval normalization */ + forward_traverse(dec, seq); + + if (term == CONV_TERM_TAIL_BITING) + forward_traverse(dec, seq); + + free(depunc); + return traceback(dec, out, term, len); +} + +static void osmo_conv_init(void) +{ + init_complete = 1; + +#ifdef HAVE___BUILTIN_CPU_SUPPORTS + /* Detect CPU capabilities */ + #ifdef HAVE_AVX2 + avx2_supported = __builtin_cpu_supports("avx2"); + #endif + + #ifdef HAVE_SSSE3 + ssse3_supported = __builtin_cpu_supports("ssse3"); + #endif + + #ifdef HAVE_SSE4_1 + sse41_supported = __builtin_cpu_supports("sse4.1"); + #endif +#endif + +/** + * Usage of curly braces is mandatory, + * because we use multi-line define. + */ +#if defined(HAVE_SSSE3) && defined(HAVE_AVX2) + if (ssse3_supported && avx2_supported) { + INIT_POINTERS(sse_avx); + } else if (ssse3_supported) { + INIT_POINTERS(sse); + } else { + INIT_POINTERS(gen); + } +#elif defined(HAVE_SSSE3) + if (ssse3_supported) { + INIT_POINTERS(sse); + } else { + INIT_POINTERS(gen); + } +#else + INIT_POINTERS(gen); +#endif +} + +/* All-in-one Viterbi decoding */ +int osmo_conv_decode_acc(const struct osmo_conv_code *code, + const sbit_t *input, ubit_t *output) +{ + int rc; + struct vdecoder dec; + + if (!init_complete) + osmo_conv_init(); + + if ((code->N < 2) || (code->N > 4) || (code->len < 1) || + ((code->K != 5) && (code->K != 7))) + return -EINVAL; + + rc = vdec_init(&dec, code); + if (rc) + return rc; + + rc = conv_decode(&dec, input, code->puncture, + output, code->len, code->term); + + vdec_deinit(&dec); + + return rc; +} diff --git a/lib/decoding/osmocom/core/conv_acc_generic.c b/lib/decoding/osmocom/core/conv_acc_generic.c new file mode 100644 index 0000000..7da0213 --- /dev/null +++ b/lib/decoding/osmocom/core/conv_acc_generic.c @@ -0,0 +1,213 @@ +/*! \file conv_acc_generic.c + * Accelerated Viterbi decoder implementation + * for generic architectures without SSE support. */ +/* + * Copyright (C) 2013, 2014 Thomas Tsou + * + * 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 +#include +#include + +#define __attribute__(_arg_) + +/* Add-Compare-Select (ACS-Butterfly) + * Compute 4 accumulated path metrics and 4 path selections. Note that path + * selections are store as -1 and 0 rather than 0 and 1. This is to match + * the output format of the SSE packed compare instruction 'pmaxuw'. + */ + +static void acs_butterfly(int state, int num_states, + int16_t metric, int16_t *sum, + int16_t *new_sum, int16_t *path) +{ + int state0, state1; + int sum0, sum1, sum2, sum3; + + state0 = *(sum + (2 * state + 0)); + state1 = *(sum + (2 * state + 1)); + + sum0 = state0 + metric; + sum1 = state1 - metric; + sum2 = state0 - metric; + sum3 = state1 + metric; + + if (sum0 >= sum1) { + *new_sum = sum0; + *path = -1; + } else { + *new_sum = sum1; + *path = 0; + } + + if (sum2 >= sum3) { + *(new_sum + num_states / 2) = sum2; + *(path + num_states / 2) = -1; + } else { + *(new_sum + num_states / 2) = sum3; + *(path + num_states / 2) = 0; + } +} + +/* Branch metrics unit N=2 */ +static void gen_branch_metrics_n2(int num_states, const int8_t *seq, + const int16_t *out, int16_t *metrics) +{ + int i; + + for (i = 0; i < num_states / 2; i++) { + metrics[i] = seq[0] * out[2 * i + 0] + + seq[1] * out[2 * i + 1]; + } +} + +/* Branch metrics unit N=3 */ +static void gen_branch_metrics_n3(int num_states, const int8_t *seq, + const int16_t *out, int16_t *metrics) +{ + int i; + + for (i = 0; i < num_states / 2; i++) { + metrics[i] = seq[0] * out[4 * i + 0] + + seq[1] * out[4 * i + 1] + + seq[2] * out[4 * i + 2]; + } +} + +/* Branch metrics unit N=4 */ +static void gen_branch_metrics_n4(int num_states, const int8_t *seq, + const int16_t *out, int16_t *metrics) +{ + int i; + + for (i = 0; i < num_states / 2; i++) { + metrics[i] = seq[0] * out[4 * i + 0] + + seq[1] * out[4 * i + 1] + + seq[2] * out[4 * i + 2] + + seq[3] * out[4 * i + 3]; + } +} + +/* Path metric unit */ +static void gen_path_metrics(int num_states, int16_t *sums, + int16_t *metrics, int16_t *paths, int norm) +{ + int i; + int16_t min; + int16_t * new_sums = malloc(sizeof(int16_t)*num_states); + + for (i = 0; i < num_states / 2; i++) + acs_butterfly(i, num_states, metrics[i], + sums, &new_sums[i], &paths[i]); + + if (norm) { + min = new_sums[0]; + + for (i = 1; i < num_states; i++) + if (new_sums[i] < min) + min = new_sums[i]; + + for (i = 0; i < num_states; i++) + new_sums[i] -= min; + } + + free(new_sums); + memcpy(sums, new_sums, num_states * sizeof(int16_t)); +} + +/* Not-aligned Memory Allocator */ +__attribute__ ((visibility("hidden"))) +int16_t *osmo_conv_gen_vdec_malloc(size_t n) +{ + return (int16_t *) malloc(sizeof(int16_t) * n); +} + +__attribute__ ((visibility("hidden"))) +void osmo_conv_gen_vdec_free(int16_t *ptr) +{ + free(ptr); +} + +/* 16-state branch-path metrics units (K=5) */ +__attribute__ ((visibility("hidden"))) +void osmo_conv_gen_metrics_k5_n2(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + int16_t metrics[8]; + + gen_branch_metrics_n2(16, seq, out, metrics); + gen_path_metrics(16, sums, metrics, paths, norm); +} + +__attribute__ ((visibility("hidden"))) +void osmo_conv_gen_metrics_k5_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + int16_t metrics[8]; + + gen_branch_metrics_n3(16, seq, out, metrics); + gen_path_metrics(16, sums, metrics, paths, norm); + +} + +__attribute__ ((visibility("hidden"))) +void osmo_conv_gen_metrics_k5_n4(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + int16_t metrics[8]; + + gen_branch_metrics_n4(16, seq, out, metrics); + gen_path_metrics(16, sums, metrics, paths, norm); + +} + +/* 64-state branch-path metrics units (K=7) */ +__attribute__ ((visibility("hidden"))) +void osmo_conv_gen_metrics_k7_n2(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + int16_t metrics[32]; + + gen_branch_metrics_n2(64, seq, out, metrics); + gen_path_metrics(64, sums, metrics, paths, norm); + +} + +__attribute__ ((visibility("hidden"))) +void osmo_conv_gen_metrics_k7_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + int16_t metrics[32]; + + gen_branch_metrics_n3(64, seq, out, metrics); + gen_path_metrics(64, sums, metrics, paths, norm); + +} + +__attribute__ ((visibility("hidden"))) +void osmo_conv_gen_metrics_k7_n4(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + int16_t metrics[32]; + + gen_branch_metrics_n4(64, seq, out, metrics); + gen_path_metrics(64, sums, metrics, paths, norm); +} diff --git a/lib/decoding/osmocom/core/crc16gen.c b/lib/decoding/osmocom/core/crc16gen.c new file mode 100644 index 0000000..ea69d88 --- /dev/null +++ b/lib/decoding/osmocom/core/crc16gen.c @@ -0,0 +1,120 @@ +/* + * crc16gen.c + * + * Generic CRC routines (for max 16 bits poly) + * + * Copyright (C) 2011 Sylvain Munaut + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! \addtogroup crcgen + * @{ + */ + +/*! \file crc16gen.c + * Osmocom generic CRC routines (for max 16 bits poly) + */ + +#include + +#include +#include + + +/*! \brief Compute the CRC value of a given array of hard-bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \returns The CRC value + */ +uint16_t +osmo_crc16gen_compute_bits(const struct osmo_crc16gen_code *code, + const ubit_t *in, int len) +{ + const uint16_t poly = code->poly; + uint16_t crc = code->init; + int i, n = code->bits-1; + + for (i=0; ibits) - 1; + } + + crc ^= code->remainder; + + return crc; +} + + +/*! \brief Checks the CRC value of a given array of hard-bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \param[in] crc_bits Array of hard bits with the alleged CRC + * \returns 0 if CRC matches. 1 in case of error. + * + * The crc_bits array must have a length of code->len + */ +int +osmo_crc16gen_check_bits(const struct osmo_crc16gen_code *code, + const ubit_t *in, int len, const ubit_t *crc_bits) +{ + uint16_t crc; + int i; + + crc = osmo_crc16gen_compute_bits(code, in, len); + + for (i=0; ibits; i++) + if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1)) + return 1; + + return 0; +} + + +/*! \brief Computes and writes the CRC value of a given array of bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \param[in] crc_bits Array of hard bits to write the computed CRC to + * + * The crc_bits array must have a length of code->len + */ +void +osmo_crc16gen_set_bits(const struct osmo_crc16gen_code *code, + const ubit_t *in, int len, ubit_t *crc_bits) +{ + uint16_t crc; + int i; + + crc = osmo_crc16gen_compute_bits(code, in, len); + + for (i=0; ibits; i++) + crc_bits[i] = ((crc >> (code->bits-i-1)) & 1); +} + +/*! @} */ + +/* vim: set syntax=c: */ diff --git a/lib/decoding/osmocom/core/crc16gen.h b/lib/decoding/osmocom/core/crc16gen.h new file mode 100644 index 0000000..567b74e --- /dev/null +++ b/lib/decoding/osmocom/core/crc16gen.h @@ -0,0 +1,56 @@ +/* + * crc16gen.h + * + * Copyright (C) 2011 Sylvain Munaut + * + * 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 + +/*! \addtogroup crcgen + * @{ + */ + +/*! \file crc16gen.h + * Osmocom generic CRC routines (for max 16 bits poly) header + */ + + +#include +#include + + +/*! \brief structure describing a given CRC code of max 16 bits */ +struct osmo_crc16gen_code { + int bits; /*!< \brief Actual number of bits of the CRC */ + uint16_t poly; /*!< \brief Polynom (normal representation, MSB omitted */ + uint16_t init; /*!< \brief Initialization value of the CRC state */ + uint16_t remainder; /*!< \brief Remainder of the CRC (final XOR) */ +}; + +uint16_t osmo_crc16gen_compute_bits(const struct osmo_crc16gen_code *code, + const ubit_t *in, int len); +int osmo_crc16gen_check_bits(const struct osmo_crc16gen_code *code, + const ubit_t *in, int len, const ubit_t *crc_bits); +void osmo_crc16gen_set_bits(const struct osmo_crc16gen_code *code, + const ubit_t *in, int len, ubit_t *crc_bits); + + +/*! @} */ + +/* vim: set syntax=c: */ diff --git a/lib/decoding/osmocom/core/crc32gen.h b/lib/decoding/osmocom/core/crc32gen.h new file mode 100644 index 0000000..3b95338 --- /dev/null +++ b/lib/decoding/osmocom/core/crc32gen.h @@ -0,0 +1,56 @@ +/* + * crc32gen.h + * + * Copyright (C) 2011 Sylvain Munaut + * + * 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 + +/*! \addtogroup crcgen + * @{ + */ + +/*! \file crc32gen.h + * Osmocom generic CRC routines (for max 32 bits poly) header + */ + + +#include +#include + + +/*! \brief structure describing a given CRC code of max 32 bits */ +struct osmo_crc32gen_code { + int bits; /*!< \brief Actual number of bits of the CRC */ + uint32_t poly; /*!< \brief Polynom (normal representation, MSB omitted */ + uint32_t init; /*!< \brief Initialization value of the CRC state */ + uint32_t remainder; /*!< \brief Remainder of the CRC (final XOR) */ +}; + +uint32_t osmo_crc32gen_compute_bits(const struct osmo_crc32gen_code *code, + const ubit_t *in, int len); +int osmo_crc32gen_check_bits(const struct osmo_crc32gen_code *code, + const ubit_t *in, int len, const ubit_t *crc_bits); +void osmo_crc32gen_set_bits(const struct osmo_crc32gen_code *code, + const ubit_t *in, int len, ubit_t *crc_bits); + + +/*! @} */ + +/* vim: set syntax=c: */ diff --git a/lib/decoding/osmocom/core/crc64gen.c b/lib/decoding/osmocom/core/crc64gen.c new file mode 100644 index 0000000..3e700d4 --- /dev/null +++ b/lib/decoding/osmocom/core/crc64gen.c @@ -0,0 +1,120 @@ +/* + * crc64gen.c + * + * Generic CRC routines (for max 64 bits poly) + * + * Copyright (C) 2011 Sylvain Munaut + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! \addtogroup crcgen + * @{ + */ + +/*! \file crc64gen.c + * Osmocom generic CRC routines (for max 64 bits poly) + */ + +#include + +#include +#include + + +/*! \brief Compute the CRC value of a given array of hard-bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \returns The CRC value + */ +uint64_t +osmo_crc64gen_compute_bits(const struct osmo_crc64gen_code *code, + const ubit_t *in, int len) +{ + const uint64_t poly = code->poly; + uint64_t crc = code->init; + int i, n = code->bits-1; + + for (i=0; ibits) - 1; + } + + crc ^= code->remainder; + + return crc; +} + + +/*! \brief Checks the CRC value of a given array of hard-bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \param[in] crc_bits Array of hard bits with the alleged CRC + * \returns 0 if CRC matches. 1 in case of error. + * + * The crc_bits array must have a length of code->len + */ +int +osmo_crc64gen_check_bits(const struct osmo_crc64gen_code *code, + const ubit_t *in, int len, const ubit_t *crc_bits) +{ + uint64_t crc; + int i; + + crc = osmo_crc64gen_compute_bits(code, in, len); + + for (i=0; ibits; i++) + if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1)) + return 1; + + return 0; +} + + +/*! \brief Computes and writes the CRC value of a given array of bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \param[in] crc_bits Array of hard bits to write the computed CRC to + * + * The crc_bits array must have a length of code->len + */ +void +osmo_crc64gen_set_bits(const struct osmo_crc64gen_code *code, + const ubit_t *in, int len, ubit_t *crc_bits) +{ + uint64_t crc; + int i; + + crc = osmo_crc64gen_compute_bits(code, in, len); + + for (i=0; ibits; i++) + crc_bits[i] = ((crc >> (code->bits-i-1)) & 1); +} + +/*! @} */ + +/* vim: set syntax=c: */ diff --git a/lib/decoding/osmocom/core/crc64gen.h b/lib/decoding/osmocom/core/crc64gen.h new file mode 100644 index 0000000..aa02e04 --- /dev/null +++ b/lib/decoding/osmocom/core/crc64gen.h @@ -0,0 +1,56 @@ +/* + * crc64gen.h + * + * Copyright (C) 2011 Sylvain Munaut + * + * 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 + +/*! \addtogroup crcgen + * @{ + */ + +/*! \file crc64gen.h + * Osmocom generic CRC routines (for max 64 bits poly) header + */ + + +#include +#include + + +/*! \brief structure describing a given CRC code of max 64 bits */ +struct osmo_crc64gen_code { + int bits; /*!< \brief Actual number of bits of the CRC */ + uint64_t poly; /*!< \brief Polynom (normal representation, MSB omitted */ + uint64_t init; /*!< \brief Initialization value of the CRC state */ + uint64_t remainder; /*!< \brief Remainder of the CRC (final XOR) */ +}; + +uint64_t osmo_crc64gen_compute_bits(const struct osmo_crc64gen_code *code, + const ubit_t *in, int len); +int osmo_crc64gen_check_bits(const struct osmo_crc64gen_code *code, + const ubit_t *in, int len, const ubit_t *crc_bits); +void osmo_crc64gen_set_bits(const struct osmo_crc64gen_code *code, + const ubit_t *in, int len, ubit_t *crc_bits); + + +/*! @} */ + +/* vim: set syntax=c: */ diff --git a/lib/decoding/osmocom/core/crc8gen.c b/lib/decoding/osmocom/core/crc8gen.c new file mode 100644 index 0000000..0b85b0d --- /dev/null +++ b/lib/decoding/osmocom/core/crc8gen.c @@ -0,0 +1,120 @@ +/* + * crc8gen.c + * + * Generic CRC routines (for max 8 bits poly) + * + * Copyright (C) 2011 Sylvain Munaut + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! \addtogroup crcgen + * @{ + */ + +/*! \file crc8gen.c + * Osmocom generic CRC routines (for max 8 bits poly) + */ + +#include + +#include +#include + + +/*! \brief Compute the CRC value of a given array of hard-bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \returns The CRC value + */ +uint8_t +osmo_crc8gen_compute_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len) +{ + const uint8_t poly = code->poly; + uint8_t crc = code->init; + int i, n = code->bits-1; + + for (i=0; ibits) - 1; + } + + crc ^= code->remainder; + + return crc; +} + + +/*! \brief Checks the CRC value of a given array of hard-bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \param[in] crc_bits Array of hard bits with the alleged CRC + * \returns 0 if CRC matches. 1 in case of error. + * + * The crc_bits array must have a length of code->len + */ +int +osmo_crc8gen_check_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len, const ubit_t *crc_bits) +{ + uint8_t crc; + int i; + + crc = osmo_crc8gen_compute_bits(code, in, len); + + for (i=0; ibits; i++) + if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1)) + return 1; + + return 0; +} + + +/*! \brief Computes and writes the CRC value of a given array of bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \param[in] crc_bits Array of hard bits to write the computed CRC to + * + * The crc_bits array must have a length of code->len + */ +void +osmo_crc8gen_set_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len, ubit_t *crc_bits) +{ + uint8_t crc; + int i; + + crc = osmo_crc8gen_compute_bits(code, in, len); + + for (i=0; ibits; i++) + crc_bits[i] = ((crc >> (code->bits-i-1)) & 1); +} + +/*! @} */ + +/* vim: set syntax=c: */ diff --git a/lib/decoding/osmocom/core/crc8gen.h b/lib/decoding/osmocom/core/crc8gen.h new file mode 100644 index 0000000..9513276 --- /dev/null +++ b/lib/decoding/osmocom/core/crc8gen.h @@ -0,0 +1,56 @@ +/* + * crc8gen.h + * + * Copyright (C) 2011 Sylvain Munaut + * + * 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 + +/*! \addtogroup crcgen + * @{ + */ + +/*! \file crc8gen.h + * Osmocom generic CRC routines (for max 8 bits poly) header + */ + + +#include +#include + + +/*! \brief structure describing a given CRC code of max 8 bits */ +struct osmo_crc8gen_code { + int bits; /*!< \brief Actual number of bits of the CRC */ + uint8_t poly; /*!< \brief Polynom (normal representation, MSB omitted */ + uint8_t init; /*!< \brief Initialization value of the CRC state */ + uint8_t remainder; /*!< \brief Remainder of the CRC (final XOR) */ +}; + +uint8_t osmo_crc8gen_compute_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len); +int osmo_crc8gen_check_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len, const ubit_t *crc_bits); +void osmo_crc8gen_set_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len, ubit_t *crc_bits); + + +/*! @} */ + +/* vim: set syntax=c: */ diff --git a/lib/decoding/osmocom/core/crcgen.h b/lib/decoding/osmocom/core/crcgen.h new file mode 100644 index 0000000..7cfe869 --- /dev/null +++ b/lib/decoding/osmocom/core/crcgen.h @@ -0,0 +1,34 @@ +/*! \file crcgen.h + * Osmocom generic CRC routines global header. */ +/* + * Copyright (C) 2011 Sylvain Munaut + * + * 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 + +/*! \defgroup crc Osmocom CRC routines + * @{ + * \file crcgen.h */ + +#include +#include +#include +#include + +/*! @} */ diff --git a/lib/decoding/osmocom/core/defs.h b/lib/decoding/osmocom/core/defs.h new file mode 100644 index 0000000..5e5aa90 --- /dev/null +++ b/lib/decoding/osmocom/core/defs.h @@ -0,0 +1,53 @@ +/*! \file defs.h + * General definitions that are meant to be included from header files. + */ + +#pragma once + +/*! \defgroup utils General-purpose utility functions + * @{ + * \file defs.h */ + +/*! Check for gcc and version. + * + * \note Albeit glibc provides a features.h file that contains a similar + * definition (__GNUC_PREREQ), this definition has been copied from there + * to have it available with other libraries, too. + * + * \return != 0 iff gcc is used and it's version is at least maj.min. + */ +#if defined __GNUC__ && defined __GNUC_MINOR__ +# define OSMO_GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +# define OSMO_GNUC_PREREQ(maj, min) 0 +#endif + +/*! Set the deprecated attribute with a message. + */ +#if defined(__clang__) +# define _OSMO_HAS_ATTRIBUTE_DEPRECATED __has_attribute(deprecated) +# define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE __has_extension(attribute_deprecated_with_message) +#elif defined(__GNUC__) +# define _OSMO_HAS_ATTRIBUTE_DEPRECATED 1 +# define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE OSMO_GNUC_PREREQ(4,5) +#endif + +#if _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE +# define OSMO_DEPRECATED(text) __attribute__((__deprecated__(text))) +#elif _OSMO_HAS_ATTRIBUTE_DEPRECATED +# define OSMO_DEPRECATED(text) __attribute__((__deprecated__)) +#else +# define OSMO_DEPRECATED(text) +#endif + +#if BUILDING_LIBOSMOCORE +# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE +#else +# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE OSMO_DEPRECATED("For internal use inside libosmocore only.") +#endif + +#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE +#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED + +/*! @} */ diff --git a/lib/decoding/osmocom/core/endian.h b/lib/decoding/osmocom/core/endian.h new file mode 100644 index 0000000..6107b12 --- /dev/null +++ b/lib/decoding/osmocom/core/endian.h @@ -0,0 +1,62 @@ +/*! \file endian.h + * + * GNU and FreeBSD have various ways to express the + * endianess but none of them is similiar enough. This + * will create two defines that allows to decide on the + * endian. The following will be defined to either 0 or + * 1 at the end of the file. + * + * OSMO_IS_LITTLE_ENDIAN + * OSMO_IS_BIG_ENDIAN + * + */ + +#pragma once + +#if defined(__FreeBSD__) +#include + #if BYTE_ORDER == LITTLE_ENDIAN + #define OSMO_IS_LITTLE_ENDIAN 1 + #define OSMO_IS_BIG_ENDIAN 0 + #elif BYTE_ORDER == BIG_ENDIAN + #define OSMO_IS_LITTLE_ENDIAN 0 + #define OSMO_IS_BIG_ENDIAN 1 + #else + #error "Unknown endian" + #endif +#elif defined(__APPLE__) +#include + #if defined(__DARWIN_LITTLE_ENDIAN) + #define OSMO_IS_LITTLE_ENDIAN 1 + #define OSMO_IS_BIG_ENDIAN 0 + #elif defined(__DARWIN_BIG_ENDIAN) + #define OSMO_IS_LITTLE_ENDIAN 0 + #define OSMO_IS_BIG_ENDIAN 1 + #else + #error "Unknown endian" + #endif +#elif defined(__linux__) +#include + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define OSMO_IS_LITTLE_ENDIAN 1 + #define OSMO_IS_BIG_ENDIAN 0 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define OSMO_IS_LITTLE_ENDIAN 0 + #define OSMO_IS_BIG_ENDIAN 1 + #else + #error "Unknown endian" + #endif +#else + /* let's try to rely on the compiler. GCC and CLANG/LLVM seem + * to support this ... */ + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define OSMO_IS_LITTLE_ENDIAN 1 + #define OSMO_IS_BIG_ENDIAN 0 + #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define OSMO_IS_LITTLE_ENDIAN 0 + #define OSMO_IS_BIG_ENDIAN 1 + #else + #error "Unknown endian" + #endif +#endif + diff --git a/lib/decoding/osmocom/core/linuxlist.h b/lib/decoding/osmocom/core/linuxlist.h new file mode 100644 index 0000000..fee0cc0 --- /dev/null +++ b/lib/decoding/osmocom/core/linuxlist.h @@ -0,0 +1,409 @@ +/*! \file linuxlist.h + * + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole llists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +#pragma once + +/*! \defgroup linuxlist Simple doubly linked list implementation + * @{ + * \file linuxlist.h */ + +#include + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) +# define __WINDOWS__ +#endif + +#ifndef inline +# ifndef __WINDOWS__ +# define inline __inline__ +# else +# define inline __inline +# endif +#endif + +static inline void prefetch(const void *x) {;} + +/*! cast a member of a structure out to the containing structure + * + * \param[in] ptr the pointer to the member. + * \param[in] type the type of the container struct this is embedded in. + * \param[in] member the name of the member within the struct. + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type, member) );}) + + +/*! + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized llist entries. + */ +#define LLIST_POISON1 ((void *) 0x00100100) +#define LLIST_POISON2 ((void *) 0x00200200) + +/*! (double) linked list header structure */ +struct llist_head { + /*! Pointer to next and previous item */ + struct llist_head *next, *prev; +}; + +#define LLIST_HEAD_INIT(name) { &(name), &(name) } + +/*! define a statically-initialized \ref llist_head + * \param[in] name Variable name + * + * This is a helper macro that will define a named variable of type + * \ref llist_head and initialize it */ +#define LLIST_HEAD(name) \ + struct llist_head name = LLIST_HEAD_INIT(name) + +/*! initialize a \ref llist_head to point back to self */ +#define INIT_LLIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/*! Insert a new entry between two known consecutive entries. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_add(struct llist_head *_new, + struct llist_head *prev, + struct llist_head *next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/*! add a new entry into a linked list (at head) + * \param _new New entry to be added + * \param head \ref llist_head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void llist_add(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head, head->next); +} + +/*! add a new entry into a linked list (at tail) + * \param _new New entry to be added + * \param head Head of linked list to whose tail we shall add \a _new + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head->prev, head); +} + +/* + * Delete a llist entry by making the prev/next entries + * point to each other. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_del(struct llist_head * prev, struct llist_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/*! Delete entry from linked list + * \param entry The element to delete from the llist + * + * Note: llist_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void llist_del(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + entry->next = (struct llist_head *)LLIST_POISON1; + entry->prev = (struct llist_head *)LLIST_POISON2; +} + +/*! Delete entry from linked list and reinitialize it + * \param entry The element to delete from the list + */ +static inline void llist_del_init(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + INIT_LLIST_HEAD(entry); +} + +/*! Delete from one llist and add as another's head + * \param llist The entry to move + * \param head The head that will precede our entry + */ +static inline void llist_move(struct llist_head *llist, struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add(llist, head); +} + +/*! Delete from one llist and add as another's tail + * \param llist The entry to move + * \param head The head that will follow our entry + */ +static inline void llist_move_tail(struct llist_head *llist, + struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add_tail(llist, head); +} + +/*! Test whether a linked list is empty + * \param[in] head The llist to test. + * \returns 1 if the list is empty, 0 otherwise + */ +static inline int llist_empty(const struct llist_head *head) +{ + return head->next == head; +} + +static inline void __llist_splice(struct llist_head *llist, + struct llist_head *head) +{ + struct llist_head *first = llist->next; + struct llist_head *last = llist->prev; + struct llist_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/*! Join two llists + * \param llist The new linked list to add + * \param head The place to add \a llist in the other list + */ +static inline void llist_splice(struct llist_head *llist, struct llist_head *head) +{ + if (!llist_empty(llist)) + __llist_splice(llist, head); +} + +/*! join two llists and reinitialise the emptied llist. + * \param llist The new linked list to add. + * \param head The place to add it in the first llist. + * + * The llist at @llist is reinitialised + */ +static inline void llist_splice_init(struct llist_head *llist, + struct llist_head *head) +{ + if (!llist_empty(llist)) { + __llist_splice(llist, head); + INIT_LLIST_HEAD(llist); + } +} + +/*! Get the struct containing this list entry + * \param ptr The \ref llist_head pointer + * \param type The type of the struct this is embedded in + * \param @member The name of the \ref llist_head within the struct + */ +#define llist_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/*! Get the first element from a list + * \param ptr the list head to take the element from. + * \param type the type of the struct this is embedded in. + * \param member the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define llist_first_entry(ptr, type, member) \ + llist_entry((ptr)->next, type, member) + +/*! Get the last element from a list + * \param ptr the list head to take the element from. + * \param type the type of the struct this is embedded in. + * \param member the name of the llist_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define llist_last_entry(ptr, type, member) \ + llist_entry((ptr)->prev, type, member) + +/*! Get the first element from a list, or NULL + * \param ptr the list head to take the element from. + * \param type the type of the struct this is embedded in. + * \param member the name of the list_head within the struct. + * + * Note that if the list is empty, it returns NULL. + */ +#define llist_first_entry_or_null(ptr, type, member) \ + (!llist_empty(ptr) ? llist_first_entry(ptr, type, member) : NULL) + +/*! Iterate over a linked list + * \param pos The \ref llist_head to use as a loop counter + * \param head The head of the list over which to iterate + */ +#define llist_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/*! Iterate over a llist (no prefetch) + * \param pos The \ref llist_head to use as a loop counter + * \param head The head of the list over which to iterate + * + * This variant differs from llist_for_each() in that it's the + * simplest possible llist iteration code, no prefetching is done. + * Use this for code that knows the llist to be very short (empty + * or 1 entry) most of the time. + */ +#define __llist_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/*! Iterate over a llist backwards + * \param pos The \ref llist_head to use as a loop counter + * \param head The head of the list over which to iterate + */ +#define llist_for_each_prev(pos, head) \ + for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ + pos = pos->prev, prefetch(pos->prev)) + +/*! Iterate over a list; safe against removal of llist entry + * \param pos The \ref llist_head to use as a loop counter + * \param n Another \ref llist_head to use as temporary storage + * \param head The head of the list over which to iterate + */ +#define llist_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/*! Iterate over llist of given type + * \param pos The 'type *' to use as a loop counter + * \param head The head of the list over which to iterate + * \param member The name of the \ref llist_head within struct \a pos + */ +#define llist_for_each_entry(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/*! Iterate backwards over llist of given type. + * \param pos The 'type *' to use as a loop counter + * \param head The head of the list over which to iterate + * \param member The name of the \ref llist_head within struct \a pos + */ +#define llist_for_each_entry_reverse(pos, head, member) \ + for (pos = llist_entry((head)->prev, typeof(*pos), member), \ + prefetch(pos->member.prev); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.prev, typeof(*pos), member), \ + prefetch(pos->member.prev)) + +/*! iterate over llist of given type continuing after existing + * point + * \param pos The 'type *' to use as a loop counter + * \param head The head of the list over which to iterate + * \param member The name of the \ref llist_head within struct \a pos + */ +#define llist_for_each_entry_continue(pos, head, member) \ + for (pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/*! iterate over llist of given type, safe against removal of + * non-consecutive(!) llist entries + * \param pos The 'type *' to use as a loop counter + * \param n Another type * to use as temporary storage + * \param head The head of the list over which to iterate + * \param member The name of the \ref llist_head within struct \a pos + */ +#define llist_for_each_entry_safe(pos, n, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + n = llist_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = llist_entry(n->member.next, typeof(*n), member)) + +/** + * llist_for_each_rcu - iterate over an rcu-protected llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_rcu(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next)) + +#define __llist_for_each_rcu(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;})) + +/** + * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe + * against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe_rcu(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) + +/** + * llist_for_each_entry_rcu - iterate over rcu llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_rcu(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + ({ smp_read_barrier_depends(); 0;}), \ + prefetch(pos->member.next)) + + +/** + * llist_for_each_continue_rcu - iterate over an rcu-protected llist + * continuing after existing point. + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_continue_rcu(pos, head) \ + for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \ + (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next)) + +/*! count nr of llist items by iterating. + * \param head The llist head to count items of. + * \returns Number of items. + * + * This function is not efficient, mostly useful for small lists and non time + * critical cases like unit tests. + */ +static inline unsigned int llist_count(struct llist_head *head) +{ + struct llist_head *entry; + unsigned int i = 0; + llist_for_each(entry, head) + i++; + return i; +} + +/*! + * @} + */ diff --git a/lib/decoding/osmocom/core/panic.c b/lib/decoding/osmocom/core/panic.c new file mode 100644 index 0000000..d545a60 --- /dev/null +++ b/lib/decoding/osmocom/core/panic.c @@ -0,0 +1,100 @@ +/*! \file panic.c + * Routines for panic handling. */ +/* + * (C) 2010 by Sylvain Munaut + * + * 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 utils + * @{ + * \file panic.c */ + +#include +//#include + +//#include "../config.h" + + +static osmo_panic_handler_t osmo_panic_handler = (void*)0; + + +#ifndef PANIC_INFLOOP + +#include +#include + +static void osmo_panic_default(const char *fmt, va_list args) +{ + vfprintf(stderr, fmt, args); + //osmo_generate_backtrace(); + abort(); +} + +#else + +static void osmo_panic_default(const char *fmt, va_list args) +{ + while (1); +} + +#endif + + +/*! Terminate the current program with a panic + * + * You can call this function in case some severely unexpected situation + * is detected and the program is supposed to terminate in a way that + * reports the fact that it terminates. + * + * The application can register a panic handler function using \ref + * osmo_set_panic_handler. If it doesn't, a default panic handler + * function is called automatically. + * + * The default function on most systems will generate a backtrace and + * then abort() the process. + */ +void osmo_panic(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (osmo_panic_handler) + osmo_panic_handler(fmt, args); + else + osmo_panic_default(fmt, args); + + va_end(args); +} + + +/*! Set the panic handler + * \param[in] h New panic handler function + * + * This changes the panic handling function from the currently active + * function to a new call-back function supplied by the caller. + */ +void osmo_set_panic_handler(osmo_panic_handler_t h) +{ + osmo_panic_handler = h; +} + +/*! @} */ diff --git a/lib/decoding/osmocom/core/panic.h b/lib/decoding/osmocom/core/panic.h new file mode 100644 index 0000000..2bb4240 --- /dev/null +++ b/lib/decoding/osmocom/core/panic.h @@ -0,0 +1,15 @@ +#pragma once + +/*! \addtogroup utils + * @{ + * \file panic.h */ + +#include + +/*! panic handler callback function type */ +typedef void (*osmo_panic_handler_t)(const char *fmt, va_list args); + +extern void osmo_panic(const char *fmt, ...); +extern void osmo_set_panic_handler(osmo_panic_handler_t h); + +/*! @} */ diff --git a/lib/decoding/osmocom/core/utils.h b/lib/decoding/osmocom/core/utils.h new file mode 100644 index 0000000..54c8216 --- /dev/null +++ b/lib/decoding/osmocom/core/utils.h @@ -0,0 +1,129 @@ +#pragma once + +#include + +//#include +//#include + +/*! \defgroup utils General-purpose utility functions + * @{ + * \file utils.h */ + +/*! Determine number of elements in an array of static size */ +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +/*! Return the maximum of two specified values */ +#define OSMO_MAX(a, b) ((a) >= (b) ? (a) : (b)) +/*! Return the minimum of two specified values */ +#define OSMO_MIN(a, b) ((a) >= (b) ? (b) : (a)) +/*! Stringify the name of a macro x, e.g. an FSM event name. + * Note: if nested within another preprocessor macro, this will + * stringify the value of x instead of its name. */ +#define OSMO_STRINGIFY(x) #x +/*! Stringify the value of a macro x, e.g. a port number. */ +#define OSMO_STRINGIFY_VAL(x) OSMO_STRINGIFY(x) +/*! Make a value_string entry from an enum value name */ +#define OSMO_VALUE_STRING(x) { x, #x } +/*! Number of bytes necessary to store given BITS */ +#define OSMO_BYTES_FOR_BITS(BITS) ((BITS + 8 - 1) / 8) + +#include +#include +#include + +#define __attribute__(_arg_) +#define __deprecated__ + +/*! A mapping between human-readable string and numeric value */ +struct value_string { + unsigned int value; /*!< numeric value */ + const char *str; /*!< human-readable string */ +}; + +//const char *get_value_string(const struct value_string *vs, uint32_t val); +const char *get_value_string_or_null(const struct value_string *vs, + uint32_t val); + +int get_string_value(const struct value_string *vs, const char *str); + +char osmo_bcd2char(uint8_t bcd); +/* only works for numbers in ascci */ +uint8_t osmo_char2bcd(char c); + +int osmo_hexparse(const char *str, uint8_t *b, int max_len); + +char *osmo_ubit_dump(const uint8_t *bits, unsigned int len); +char *osmo_hexdump(const unsigned char *buf, int len); +char *osmo_hexdump_nospc(const unsigned char *buf, int len); +char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) __attribute__((__deprecated__)); + +#define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1] __attribute__((__unused__)); + +void osmo_str2lower(char *out, const char *in); +void osmo_str2upper(char *out, const char *in); + +#define OSMO_SNPRINTF_RET(ret, rem, offset, len) \ +do { \ + len += ret; \ + if (ret > rem) \ + ret = rem; \ + offset += ret; \ + rem -= ret; \ +} while (0) + +/*! Helper macro to terminate when an assertion failes + * \param[in] exp Predicate to verify + * This function will generate a backtrace and terminate the program if + * the predicate evaluates to false (0). + */ +#define OSMO_ASSERT(exp) \ + if (!(exp)) { \ + fprintf(stderr, "Assert failed %s %s:%d\n", #exp, __BASE_FILE__, __LINE__); \ + /*osmo_generate_backtrace(); \ */\ + abort(); \ + } + +/*! duplicate a string using talloc and release its prior content (if any) + * \param[in] ctx Talloc context to use for allocation + * \param[out] dst pointer to string, will be updated with ptr to new string + * \param[in] newstr String that will be copieed to newly allocated string */ +/*static inline void osmo_talloc_replace_string(void *ctx, char **dst, const char *newstr)*/ +/*{*/ +/* if (*dst)*/ +/* talloc_free(*dst);*/ +/* *dst = talloc_strdup(ctx, newstr);*/ +/*}*/ + +/*! Append to a string and re-/allocate if necessary. + * \param[in] ctx Talloc context to use for initial allocation. + * \param[in,out] dest char* to re-/allocate and append to. + * \param[in] fmt printf-like string format. + * \param[in] args Arguments for fmt. + * + * \a dest may be passed in NULL, or a string previously allocated by talloc. + * If an existing string is passed in, it will remain associated with whichever + * ctx it was allocated before, regardless whether it matches \a ctx or not. + */ +/*#define osmo_talloc_asprintf(ctx, dest, fmt, args ...) \*/ +/* do { \*/ +/* if (!dest) \*/ +/* dest = talloc_asprintf(ctx, fmt, ## args); \*/ +/* else \*/ +/* dest = talloc_asprintf_append((char*)dest, fmt, ## args); \*/ +/* } while (0)*/ + +int osmo_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count); +uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len); +uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len); + +size_t osmo_strlcpy(char *dst, const char *src, size_t siz); + +bool osmo_is_hexstr(const char *str, int min_digits, int max_digits, + bool require_even); + +bool osmo_identifier_valid(const char *str); +bool osmo_separated_identifiers_valid(const char *str, const char *sep_chars); + +const char *osmo_escape_str(const char *str, int len); +const char *osmo_escape_str_buf(const char *str, int in_len, char *buf, size_t bufsize); + +/*! @} */ diff --git a/lib/decoding/osmocom/crypt/auth.h b/lib/decoding/osmocom/crypt/auth.h new file mode 100644 index 0000000..5c45a5d --- /dev/null +++ b/lib/decoding/osmocom/crypt/auth.h @@ -0,0 +1,111 @@ +#pragma once + +/*! \defgroup auth GSM/GPRS/3G Authentication + * @{ + * \file auth.h */ + +#include + +#include +#include + +#define OSMO_A5_MAX_KEY_LEN_BYTES (128/8) +#define OSMO_MILENAGE_IND_BITLEN_MAX 28 + +/*! Authentication Type (GSM/UMTS) */ +enum osmo_sub_auth_type { + OSMO_AUTH_TYPE_NONE = 0x00, + OSMO_AUTH_TYPE_GSM = 0x01, + OSMO_AUTH_TYPE_UMTS = 0x02, +}; + +extern const struct value_string osmo_sub_auth_type_names[]; +/*static inline const char *osmo_sub_auth_type_name(enum osmo_sub_auth_type val) +{ return get_value_string(osmo_sub_auth_type_names, val); } +*/ + +/*! Authentication Algorithm. + * See also osmo_auth_alg_name() and osmo_auth_alg_parse(). */ +enum osmo_auth_algo { + OSMO_AUTH_ALG_NONE, + OSMO_AUTH_ALG_COMP128v1, + OSMO_AUTH_ALG_COMP128v2, + OSMO_AUTH_ALG_COMP128v3, + OSMO_AUTH_ALG_XOR, + OSMO_AUTH_ALG_MILENAGE, + _OSMO_AUTH_ALG_NUM, +}; + +/*! permanent (secret) subscriber auth data */ +struct osmo_sub_auth_data { + enum osmo_sub_auth_type type; + enum osmo_auth_algo algo; + union { + struct { + uint8_t opc[16]; /*!< operator invariant value */ + uint8_t k[16]; /*!< secret key of the subscriber */ + uint8_t amf[2]; + uint64_t sqn; /*!< sequence number (in: prev sqn; out: used sqn) */ + int opc_is_op; /*!< is the OPC field OPC (0) or OP (1) ? */ + unsigned int ind_bitlen; /*!< nr of bits not in SEQ, only SQN */ + unsigned int ind; /*!< which IND slot to use an SQN from */ + uint64_t sqn_ms; /*!< sqn from AUTS (output value only) */ + } umts; + struct { + uint8_t ki[OSMO_A5_MAX_KEY_LEN_BYTES]; /*!< secret key */ + } gsm; + } u; +}; + +/* data structure describing a computed auth vector, generated by AuC */ +struct osmo_auth_vector { + uint8_t rand[16]; /*!< random challenge */ + uint8_t autn[16]; /*!< authentication nonce */ + uint8_t ck[16]; /*!< ciphering key */ + uint8_t ik[16]; /*!< integrity key */ + uint8_t res[16]; /*!< authentication result */ + uint8_t res_len; /*!< length (in bytes) of res */ + uint8_t kc[8]; /*!< Kc for GSM encryption (A5) */ + uint8_t sres[4]; /*!< authentication result for GSM */ + uint32_t auth_types; /*!< bitmask of OSMO_AUTH_TYPE_* */ +}; + +/* An implementation of an authentication algorithm */ +struct osmo_auth_impl { + struct llist_head list; + enum osmo_auth_algo algo; /*!< algorithm we implement */ + const char *name; /*!< name of the implementation */ + unsigned int priority; /*!< priority value (resp. othe implementations */ + + /*! callback for generate authentication vectors */ + int (*gen_vec)(struct osmo_auth_vector *vec, + struct osmo_sub_auth_data *aud, + const uint8_t *_rand); + + /* callback for generationg auth vectors + re-sync */ + int (*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); +}; + +int osmo_auth_gen_vec(struct osmo_auth_vector *vec, + struct osmo_sub_auth_data *aud, const uint8_t *_rand); + +int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec, + struct osmo_sub_auth_data *aud, + const uint8_t *auts, const uint8_t *rand_auts, + const uint8_t *_rand); + +int osmo_auth_register(struct osmo_auth_impl *impl); + +int osmo_auth_load(const char *path); + +int osmo_auth_supported(enum osmo_auth_algo algo); +void osmo_c4(uint8_t *ck, const uint8_t *kc); +const char *osmo_auth_alg_name(enum osmo_auth_algo alg); +//enum osmo_auth_algo osmo_auth_alg_parse(const char *name); + +void osmo_auth_c3(uint8_t kc[], const uint8_t ck[], const uint8_t ik[]); + +/* @} */ 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 + * + * 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 +#include +#include + +#include +#include +//#include + +/* 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<> 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 + * + * 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 + +#include +#include + +/*! \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 + * + * 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 +#include +#include + +#include +#include +//#include + +#include + +/*! \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 + * (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 +#include +#include + +#include +/*#include */ +/* #include */ +/* #include */ +#include +#include + +/*! \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 +#include +#include + +/* #include */ +/* #include */ +/* #include */ +#include + +/* 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 + * 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 +#include +#include + +/* 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 + +/*! 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 +#include + +#include +#include + +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 */ +}; -- cgit v1.2.3