From 254b924536384d7ad2dc9ad4846aef3f5e45b71c Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 11 May 2020 08:38:51 +0200 Subject: trau_frame: New API The old TRAU frame code in src/trau_frame.c (which is for some strange reason part of libosmo-abis, and not libosmo-trau!) was introduced more than a decade ago for the needs of bs11_abis AKA OpenBSC aka OsmoNITB. It is too constrained to implement TRAU frame parsing in a generic way, including: * no way to add support for 8k sub-slots * no way to handle CRC bits or UFI (i.e. no AMR) * no real API design, it was just ripped out from OsmoNITB and moved into a library (even the wrong one) For those reasons, let's introduce a new API for TRAU frame encoding/decoding - one that supports all the relevant use cases. Change-Id: I5cf42e6c445d9224be18503cebc7584b3beba08c --- include/Makefile.am | 3 +- include/osmocom/trau/trau_frame.h | 146 ++++ src/Makefile.am | 3 +- src/trau/trau_frame.c | 1364 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1514 insertions(+), 2 deletions(-) create mode 100644 include/osmocom/trau/trau_frame.h create mode 100644 src/trau/trau_frame.c diff --git a/include/Makefile.am b/include/Makefile.am index 2048520..aa735c5 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -4,4 +4,5 @@ nobase_include_HEADERS = osmocom/abis/ipa.h osmocom/abis/trau_frame.h \ osmocom/abis/ipa_proxy.h osmocom/abis/ipaccess.h osmocom/abis/abis.h \ osmocom/abis/subchan_demux.h osmocom/abis/e1_input.h \ osmocom/abis/lapd.h osmocom/abis/lapd_pcap.h osmocom/trau/osmo_ortp.h \ - osmocom/abis/unixsocket_proto.h + osmocom/abis/unixsocket_proto.h \ + osmocom/trau/trau_frame.h diff --git a/include/osmocom/trau/trau_frame.h b/include/osmocom/trau/trau_frame.h new file mode 100644 index 0000000..6784d1f --- /dev/null +++ b/include/osmocom/trau/trau_frame.h @@ -0,0 +1,146 @@ +#pragma once +/* TRAU frame handling according to GSM TS 48.060 / 48.061 */ + +/* (C) 2020 by Harald Welte + * 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +/*! \defgroup trau_frame TRAU frame handling + * @{ + * + * \file trau_frame.h + */ + +/*! \brief Maximum number of C-bits in a TRAU frame: + * 21 for FR/EFR, 25 for AMR, 15 for OM, 15 for data, 13 for E-data, 21 idle */ +#define MAX_C_BITS 25 +/*! \brief Maximum number of D-bits in a TRAU frame: + * 260 for FR/EFR, 256 for AMR, 264 for OM, 288 for E-data */ +#define MAX_D_BITS 288 +/*! \brief Maximum number of T-bits in a TRAU frame for all speech frames */ +#define MAX_T_BITS 4 +/*! \brief Maximum number of S-bits in a TRAU frame for OM */ +#define MAX_S_BITS 6 +/*! \brief Maximum number of M-bits in a TRAU frame for E-data */ +#define MAX_M_BITS 2 +/*! \brief Maximum number of XC-bits in a TRAU frame for 8k */ +#define MAX_XC_BITS 6 + +/*! 16k sub-slot types: Bits C1..C5 */ +#define TRAU_FT_FR_UP 0x02 /* 0 0 0 1 0 - 3.5.1.1.1 */ +#define TRAU_FT_HR_UP 0x03 /* 0 0 0 1 1 - TS 08.61 5.1.4.1.1 */ +#define TRAU_FT_FR_DOWN 0x1c /* 1 1 1 0 0 - 3.5.1.1.1 */ +#define TRAU_FT_HR_DOWN 0x1d /* 1 1 1 0 1 - TS 08.61 5.1.4.1.1 */ +#define TRAU_FT_EFR 0x1a /* 1 1 0 1 0 - 3.5.1.1.1 */ +#define TRAU_FT_AMR 0x06 /* 0 0 1 1 0 - 3.5.1.2 */ +#define TRAU_FT_OM_UP 0x05 /* 0 0 1 0 1 - 3.5.2 */ +#define TRAU_FT_OM_DOWN 0x1b /* 1 1 0 1 1 - 3.5.2 */ +#define TRAU_FT_DATA_UP 0x08 /* 0 1 0 0 0 - 3.5.3 */ +#define TRAU_FT_DATA_UP_HR 0x09 /* 0 1 0 0 1 - TS 08.61 5.1.4.2 */ +#define TRAU_FT_DATA_DOWN 0x16 /* 1 0 1 1 0 - 3.5.3 */ +#define TRAU_FT_DATA_DOWN_HR 0x17 /* 1 0 1 1 1 - TS 08.61 5.1.4.2 */ +#define TRAU_FT_D145_SYNC 0x14 /* 1 0 1 0 0 - 3.5.3 */ +#define TRAU_FT_EDATA 0x1f /* 1 1 1 1 1 - 3.5.4 */ +#define TRAU_FT_IDLE_UP 0x10 /* 1 0 0 0 0 - 3.5.5 */ +#define TRAU_FT_IDLE_DOWN 0x0e /* 0 1 1 1 0 - 3.5.5 */ + +/* 8k sub-slot types: Bits C1..C5*/ +#define TRAU8_FT_SPEECH_UP 0x02 /* 0 0 0 1 P - TS 08.61 5.2.4.1.1 */ +#define TRAU8_FT_DATA_UP 0x06 /* 0 0 1 1 P - TS 08.61 5.2.4.1.1 */ +#define TRAU8_FT_OM_UP 0x0a /* 0 1 0 1 P - TS 08.61 5.2.4.1.1 */ +#define TRAU8_FT_SPEECH_DOWN 0x00 /* 0 0 0 U P - TS 08.61 5.2.4.1.2 */ +#define TRAU8_FT_DATA_DOWN 0x04 /* 0 0 1 U P - TS 08.61 5.2.4.1.2 */ +#define TRAU8_FT_OM_DOWN 0x08 /* 0 1 0 U P - TS 08.61 5.2.4.1.2 */ + +/********************************************************************************* + * New API + *********************************************************************************/ + +enum osmo_trau_frame_type { + OSMO_TRAU_FT_NONE, + OSMO_TRAU16_FT_FR = 1, + OSMO_TRAU16_FT_HR, + OSMO_TRAU16_FT_EFR, + OSMO_TRAU16_FT_AMR, + OSMO_TRAU16_FT_OAM, + OSMO_TRAU16_FT_DATA, + OSMO_TRAU16_FT_EDATA, + OSMO_TRAU16_FT_D145_SYNC, + OSMO_TRAU16_FT_DATA_HR, + OSMO_TRAU16_FT_IDLE, + + OSMO_TRAU8_SPEECH, + OSMO_TRAU8_DATA, + OSMO_TRAU8_OAM, + OSMO_TRAU8_AMR_LOW, + OSMO_TRAU8_AMR_6k7, + OSMO_TRAU8_AMR_7k4, +}; + +extern const struct value_string osmo_trau_frame_type_names[]; +static inline const char *osmo_trau_frame_type_name(enum osmo_trau_frame_type ft) { + return get_value_string(osmo_trau_frame_type_names, ft); +} + +enum osmo_trau_frame_direction { + OSMO_TRAU_DIR_UL = 0, + OSMO_TRAU_DIR_DL = 1, +}; + +struct osmo_trau_frame { + enum osmo_trau_frame_type type; + enum osmo_trau_frame_direction dir; + + ubit_t c_bits[MAX_C_BITS]; + ubit_t d_bits[MAX_D_BITS]; + ubit_t t_bits[MAX_T_BITS]; + ubit_t s_bits[MAX_S_BITS]; + ubit_t m_bits[MAX_M_BITS]; + ubit_t ufi; /*!< Unreliable Frame Indication (HR) */ + ubit_t crc_bits[3]; /*!< CRC0..CRC2 (HR) */ + ubit_t xc_bits[MAX_XC_BITS]; /*!< XC1..XC6 (8k) */ +}; + +/*! decode an 8k TRAU frame + * \param[out] fr caller-allocated output data structure + * \param[in] bits unpacked input bits + * \param[in] dir direction + * \return 0 on success; negative in case of error */ +int osmo_trau_frame_decode_8k(struct osmo_trau_frame *fr, const ubit_t *bits, + enum osmo_trau_frame_direction dir); + +/*! decode an 16k TRAU frame + * \param[out] fr caller-allocated output data structure + * \param[in] bits unpacked input bits + * \param[in] dir direction + * \return 0 on success; negative in case of error */ +int osmo_trau_frame_decode_16k(struct osmo_trau_frame *fr, const ubit_t *bits, + enum osmo_trau_frame_direction dir); + +/*! encode a TRAU frame + * \param[out] bits caller-allocated memory for unpacked output bits + * \param[out] fr input data structure describing TRAU frame + * \return number of bits encoded */ +int osmo_trau_frame_encode(ubit_t *bits, size_t n_bits, const struct osmo_trau_frame *fr); + + +/* }@ */ diff --git a/src/Makefile.am b/src/Makefile.am index 3838a5a..d1743af 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,4 +35,5 @@ endif libosmotrau_la_CFLAGS = $(AM_CFLAGS) $(ORTP_CFLAGS) libosmotrau_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(TRAU_LIBVERSION) libosmotrau_la_LIBADD = $(COMMONLIBS) $(ORTP_LIBS) -libosmotrau_la_SOURCES = trau/osmo_ortp.c +libosmotrau_la_SOURCES = trau/osmo_ortp.c \ + trau/trau_frame.c diff --git a/src/trau/trau_frame.c b/src/trau/trau_frame.c new file mode 100644 index 0000000..a57d642 --- /dev/null +++ b/src/trau/trau_frame.c @@ -0,0 +1,1364 @@ +/* New (2020) TRAU frame handling according to GSM TS 48.060 + 48.061 */ + +/* (C) 2020 by Harald Welte + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +#include "internal.h" + +#include +#include +#include +#include +#include + +#include +#include + +/*! \addtogroup trau_frame + * @{ + * + * \file trau_frame.c + */ + +static uint32_t get_bits(const ubit_t *bitbuf, int offset, int num) +{ + int i; + uint32_t ret = 0; + + for (i = offset; i < offset + num; i++) { + ret = ret << 1; + if (bitbuf[i]) + ret |= 1; + } + return ret; +} + +const struct value_string osmo_trau_frame_type_names[] = { + { OSMO_TRAU_FT_NONE, "NONE" }, + { OSMO_TRAU16_FT_FR, "FR" }, + { OSMO_TRAU16_FT_HR, "HR" }, + { OSMO_TRAU16_FT_EFR, "EFR" }, + { OSMO_TRAU16_FT_AMR, "AMR" }, + { OSMO_TRAU16_FT_OAM, "OAM" }, + { OSMO_TRAU16_FT_DATA, "DATA" }, + { OSMO_TRAU16_FT_EDATA, "EDATA" }, + { OSMO_TRAU16_FT_D145_SYNC, "D145_SYNC" }, + { OSMO_TRAU16_FT_DATA_HR, "DATA_HR" }, + { OSMO_TRAU16_FT_IDLE, "IDLE" }, + { OSMO_TRAU8_SPEECH, "8SPEECH" }, + { OSMO_TRAU8_DATA, "8DATA" }, + { OSMO_TRAU8_OAM, "8OAM" }, + { OSMO_TRAU8_AMR_LOW, "8AMR_LOW" }, + { OSMO_TRAU8_AMR_6k7, "8AMR_6k7" }, + { OSMO_TRAU8_AMR_7k4, "8AMR_7k4" }, + { 0, NULL } +}; + +/********************************************************************************* + * New API; introduced in 2020 for use by osmo-mgw + *********************************************************************************/ + +/* 16k sub-slots */ +static const ubit_t ft_fr_up_bits[5] = { 0, 0, 0, 1, 0 }; +static const ubit_t ft_fr_down_bits[5] = { 1, 1, 1, 0, 0 }; +static const ubit_t ft_data_up_bits[5] = { 0, 1, 0, 0, 0 }; +static const ubit_t ft_data_down_bits[5] = { 1, 0, 1, 1, 0 }; +static const ubit_t ft_idle_up_bits[5] = { 1, 0, 0, 0, 0 }; +static const ubit_t ft_idle_down_bits[5] = { 0, 1, 1, 1, 0 }; +static const ubit_t ft_efr_bits[5] = { 1, 1, 0, 1, 0 }; +static const ubit_t ft_amr_bits[5] = { 0, 0, 1, 1, 0 }; +static const ubit_t ft_oam_up_bits[5] = { 0, 0, 1, 0, 1 }; +static const ubit_t ft_oam_down_bits[5] = { 1, 1, 0, 1, 1 }; +static const ubit_t ft_d145s_bits[5] = { 1, 0, 1, 0, 0 }; +static const ubit_t ft_edata_bits[5] = { 1, 1, 1, 1, 1 }; + +/* 8k sub-slots */ +static const ubit_t ft_hr_up_bits[5] = { 0, 0, 0, 1, 1 }; +static const ubit_t ft_hr_down_bits[5] = { 1, 1, 1, 0, 1 }; +static const ubit_t ft_data_hr_up_bits[5] = { 0, 1, 0, 0, 1 }; +static const ubit_t ft_data_hr_down_bits[5] = { 1, 0, 1, 1, 1 }; + + +/* generate the sync pattern described in TS 08.60 4.8.1 */ +static void encode_sync16(ubit_t *trau_bits) +{ + int i; + + /* 16 '0' bits in header */ + memset(trau_bits, 0, 16); + /* '1'-bit in regular intervals */ + for (i = 16; i < 40*8; i += 16) + trau_bits[i] = 1; +} + +#define T16_500us 8 /* 500 us = 8 bits */ +#define T16_250us 4 /* 250 us = 4 bits */ + +/* How many 16k bits to delay / advance (TS 48.060 Section 5.5.1.1.1) */ +static int fr_efr_alignment_bitcount(uint8_t c6_11) +{ + if (c6_11 <= 0x27) + return c6_11 * T16_500us; /* delay frame N x 500us */ + else if (c6_11 == 0x3e) + return T16_250us; /* delay frame 250us */ + else if (c6_11 == 0x3f) + return -T16_250us; /* advance frame 250us */ + else + return 0; +} + +/* TS 08.60 Section 3.1.1 */ +static int encode16_fr(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + const ubit_t *cbits5; + int i; + int d_idx = 0; + + switch (fr->type) { + case OSMO_TRAU16_FT_IDLE: + if (fr->dir == OSMO_TRAU_DIR_UL) + cbits5 = ft_idle_up_bits; + else + cbits5 = ft_idle_down_bits; + break; + case OSMO_TRAU16_FT_FR: + if (fr->dir == OSMO_TRAU_DIR_UL) + cbits5 = ft_fr_up_bits; + else + cbits5 = ft_fr_down_bits; + break; + case OSMO_TRAU16_FT_EFR: + cbits5 = ft_efr_bits; + break; + default: + return -EINVAL; + } + + encode_sync16(trau_bits); + + /* C1 .. C5 */ + memcpy(trau_bits + 17, cbits5 + 0, 5); + /* C6 .. C15 */ + memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 15 - 5); + /* D1 .. D255 */ + for (i = 32; i < 304; i += 16) { + trau_bits[i] = 1; + memcpy(trau_bits + i + 1, fr->d_bits + d_idx, 15); + d_idx += 15; + } + /* D256 .. D260 */ + trau_bits[304] = 1; + memcpy(trau_bits + 305, fr->d_bits + d_idx, 5); + /* C16 .. C21 */ + memcpy(trau_bits + 310, fr->c_bits + 15, 6); + + /* T1 .. T4 */ + memcpy(trau_bits + 316, fr->t_bits+0, 4); + + /* handle timing adjustment */ + if (fr->dir == OSMO_TRAU_DIR_DL) { + uint8_t cbits6_11 = get_bits(fr->c_bits, 5, 6); + int ta_bits = fr_efr_alignment_bitcount(cbits6_11); + if (ta_bits > 0) { + memset(trau_bits+320, 1, ta_bits); + return 320 + ta_bits; + } else if (ta_bits < 0) + return 320 - ta_bits; + } + return 40 * 8; +} + +/* TS 08.60 Section 3.1.1 */ +static int decode16_fr(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i; + int d_idx = 0; + + /* C1 .. C15 */ + memcpy(fr->c_bits + 0, trau_bits + 17, 15); + /* C16 .. C21 */ + memcpy(fr->c_bits + 15, trau_bits + 310, 6); + /* T1 .. T4 */ + memcpy(fr->t_bits + 0, trau_bits + 316, 4); + /* D1 .. D255 */ + for (i = 32; i < 304; i+= 16) { + memcpy(fr->d_bits + d_idx, trau_bits + i + 1, 15); + d_idx += 15; + } + /* D256 .. D260 */ + memcpy(fr->d_bits + d_idx, trau_bits + 305, 5); + + return 0; +} + +/* TS 08.60 Section 3.1.2 */ +static int encode16_amr(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + const ubit_t *cbits5 = ft_amr_bits; + int i, d_idx; + + encode_sync16(trau_bits); + + /* C1 .. C5 */ + memcpy(trau_bits + 17, cbits5 + 0, 5); + /* C6 .. C15 */ + memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 15 - 5); + + trau_bits[32] = 1; + /* C16 .. C25 */ + memcpy(trau_bits + 33, fr->c_bits + 15, 10); + /* D1 .. D5 */ + memcpy(trau_bits + 43, fr->d_bits + 0, 5); + + /* D6 .. D256 */ + for (i = 48, d_idx = 5; i <= 315; i += 16, d_idx += 15) { + trau_bits[i] = 1; + memcpy(trau_bits + i + 1, fr->d_bits + d_idx, 15); + } + + /* T1 .. T4 */ + memcpy(trau_bits + 316, fr->t_bits + 0, 4); + + /* handle timing adjustment */ + if (fr->dir == OSMO_TRAU_DIR_DL) { + uint8_t cbits6_11 = get_bits(fr->c_bits, 5, 6); + int ta_bits = fr_efr_alignment_bitcount(cbits6_11); + if (ta_bits > 0) { + memset(trau_bits+320, 1, ta_bits); + return 320 + ta_bits; + } else if (ta_bits < 0) + return 320 - ta_bits; + } + /* FIXME: handle TAE (Timing Alignment Extension) */ + return 40 * 8; +} + +/* TS 08.60 Section 3.1.2 */ +static int decode16_amr(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i; + int d_idx = 0; + + /* C1 .. C15 */ + memcpy(fr->c_bits + 0, trau_bits + 17, 15); + /* C16 .. C25 */ + memcpy(fr->c_bits + 15, trau_bits + 33, 10); + /* T1 .. T4 */ + memcpy(fr->t_bits + 0, trau_bits + 316, 4); + /* D1 .. D5 */ + memcpy(fr->d_bits, trau_bits + 43, 5); + d_idx += 5; + /* D6 .. D245 */ + for (i = 48; i < 304; i += 16) { + memcpy(fr->d_bits + d_idx, trau_bits + i + 1, 15); + d_idx += 15; + } + /* D246 .. D256 */ + memcpy(fr->d_bits + d_idx, trau_bits + 305, 11); + + return 0; +} + +/* TS 08.60 Section 3.2 */ +static int encode16_oam(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + const ubit_t *cbits5; + int i, d_idx; + + if (fr->dir == OSMO_TRAU_DIR_UL) + cbits5 = ft_oam_up_bits; + else + cbits5 = ft_oam_down_bits; + + encode_sync16(trau_bits); + + /* C1 .. C5 */ + memcpy(trau_bits + 17, cbits5, 5); + /* C6 .. C15 */ + memcpy(trau_bits + 17 + 5, fr->c_bits, 15 - 5); + + /* D1 .. D255 */ + for (i = 32, d_idx = 0; i < 304; i += 16, d_idx += 15) { + trau_bits[i] = 1; + memcpy(trau_bits + i + 1, fr->d_bits + d_idx, 15); + } + /* D256 .. D264 */ + memcpy(trau_bits + 305, fr->d_bits + 256, 9); + + /* S1 .. S6 */ + memcpy(trau_bits + 314, fr->s_bits, 6); + + return 40 * 8; +} + +/* TS 08.60 Section 3.2 */ +static int decode16_oam(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i, d_idx; + + /* C1 .. C15 */ + memcpy(fr->c_bits + 0, trau_bits + 17, 15); + + /* D1 .. D255 */ + for (i = 33, d_idx = 0; i < 312; i+= 16, d_idx += 15) + memcpy(fr->d_bits + d_idx, trau_bits + i + 1, 15); + /* D256 .. D264 */ + memcpy(fr->d_bits+d_idx, trau_bits + 305, 9); + + /* S1 .. S6 */ + memcpy(fr->s_bits, trau_bits + 314, 6); + + return 0; +} + +/* TS 08.61 Section 5.1.1.1 */ +static int encode16_hr(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + int d_idx = 0; + const ubit_t *cbits5; + + if (fr->dir == OSMO_TRAU_DIR_UL) + cbits5 = ft_hr_up_bits; + else + cbits5 = ft_hr_down_bits; + + encode_sync16(trau_bits); + + /* C1 .. C5 */ + memcpy(trau_bits + 17, cbits5, 5); + /* C6 .. C15 */ + memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 10); + + /* UFI */ + trau_bits[33] = fr->ufi; + + /* D1 .. D14 */ + memcpy(trau_bits + 4 * 8 + 2, fr->d_bits+d_idx, 14); d_idx += 14; + /* D15 .. D29 */ + memcpy(trau_bits + 6 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15; + /* D30 .. D44 */ + memcpy(trau_bits + 8 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15; + /* CRC */ + memcpy(trau_bits + 10 * 8 + 1, fr->crc_bits, 3); + /* D45 .. D56 */ + memcpy(trau_bits + 10 * 8 + 4, fr->d_bits+d_idx, 12); d_idx += 12; + /* D57 .. D71 */ + memcpy(trau_bits + 12 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15; + /* D72 .. D86 */ + memcpy(trau_bits + 14 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15; + /* D87 .. D101 */ + memcpy(trau_bits + 16 * 8 + 1, fr->d_bits+d_idx, 15); d_idx += 15; + /* D102 .. D112 */ + memcpy(trau_bits + 18 * 8 + 1, fr->d_bits+d_idx, 11); d_idx += 11; + + memset(trau_bits + 19 * 8 + 4, 0x01, 4 + 18 * 8 + 6); + /* C16 .. C21 */ + memcpy(trau_bits + 38 * 8 + 6, fr->c_bits + 15, 6); + + /* T1 .. T4 */ + memcpy(trau_bits + 39 * 8 + 4, fr->t_bits, 4); + + /* handle timing adjustment */ + if (fr->dir == OSMO_TRAU_DIR_DL) { + uint8_t cbits6_11 = get_bits(fr->c_bits, 5, 6); + int ta_bits = fr_efr_alignment_bitcount(cbits6_11); + if (ta_bits > 0) { + memset(trau_bits+320, 1, ta_bits); + return 320 + ta_bits; + } else if (ta_bits < 0) + return 320 - ta_bits; + } + return 40 * 8; +} + +/* TS 08.61 Section 5.1.1.1 */ +static int decode16_hr(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int d_idx = 0; + + /* C1 .. C15 */ + memcpy(fr->c_bits + 0, trau_bits + 17, 15); + /* C16 .. C21 */ + memcpy(fr->c_bits + 15, trau_bits + 38 * 8 + 6, 6); + /* T1 .. T4 */ + memcpy(fr->t_bits + 0, trau_bits + 39 * 8 + 4, 4); + + /* UFI */ + fr->ufi = trau_bits[33]; + /* D1 .. D14 */ + memcpy(fr->d_bits + d_idx, trau_bits + 4 * 8 + 2, 14); d_idx += 14; + /* D15 .. D29 */ + memcpy(fr->d_bits + d_idx, trau_bits + 6 * 8 + 1, 15); d_idx += 15; + /* D30 .. D44 */ + memcpy(fr->d_bits + d_idx, trau_bits + 8 * 8 + 1, 15); d_idx += 15; + /* CRC0..2 */ + memcpy(fr->crc_bits, trau_bits + 10 * 8 + 1, 3); + /* D45 .. D56 */ + memcpy(fr->d_bits + d_idx, trau_bits + 10 * 8 + 4, 12); d_idx += 12; + /* D57 .. D71 */ + memcpy(fr->d_bits + d_idx, trau_bits + 12 * 8 + 1, 15); d_idx += 15; + /* D72 .. D86 */ + memcpy(fr->d_bits + d_idx, trau_bits + 14 * 8 + 1, 15); d_idx += 15; + /* D87 .. D101 */ + memcpy(fr->d_bits + d_idx, trau_bits + 16 * 8 + 1, 15); d_idx += 15; + /* D102 .. D112 */ + memcpy(fr->d_bits + d_idx, trau_bits + 18 * 8 + 1, 11); d_idx += 11; + + return 0; +} + +static struct osmo_trau_frame fr_idle_frame = { + .type = OSMO_TRAU16_FT_IDLE, + .dir = OSMO_TRAU_DIR_DL, + .t_bits = { 1, 1, 1, 1 }, +}; +#define TRAU_FRAME_BITS (40*8) +static ubit_t encoded_idle_frame[TRAU_FRAME_BITS]; +static int dbits_initted = 0; + +/*! \brief return pointer to global buffer containing a TRAU idle frame */ +static ubit_t *trau_idle_frame(void) +{ + /* only initialize during the first call */ + if (!dbits_initted) { + /* set all D-bits to 1 */ + memset(&fr_idle_frame.d_bits, 0x01, 260); + + memset(&fr_idle_frame.c_bits, 0x01, 25); /* spare are set to 1 */ + /* set Downlink Idle Speech Frame pattern */ + fr_idle_frame.c_bits[0] = 0; /* C1 */ + fr_idle_frame.c_bits[1] = 1; /* C2 */ + fr_idle_frame.c_bits[2] = 1; /* C3 */ + fr_idle_frame.c_bits[3] = 1; /* C4 */ + fr_idle_frame.c_bits[4] = 0; /* C5 */ + /* set no Time Alignment pattern */ + fr_idle_frame.c_bits[5] = 0; /* C6 */ + fr_idle_frame.c_bits[6] = 0; /* C7 */ + fr_idle_frame.c_bits[7] = 0; /* C8 */ + fr_idle_frame.c_bits[8] = 0; /* C9 */ + fr_idle_frame.c_bits[9] = 0; /* C10 */ + fr_idle_frame.c_bits[10] = 0; /* C11 */ + /* already set to 1, but maybe we need to modify it in the future */ + fr_idle_frame.c_bits[11] = 1; /* C12 (UFE), good frame */ + fr_idle_frame.c_bits[15] = 1; /* C16 (SP), no DTX */ + + encode16_fr(encoded_idle_frame, &fr_idle_frame); + dbits_initted = 1; /* set it to 1 to not call it again */ + } + return encoded_idle_frame; +} + +/* TS 08.60 Section 3.4 */ +static int encode16_idle(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + memcpy(trau_bits, trau_idle_frame(), 40*8); + return 40 * 8; +} + +/* TS 08.60 Section 3.3.1 */ +static int decode16_data(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i, d_idx = 0; + + /* C1 .. C15 */ + memcpy(fr->c_bits + 0, trau_bits + 17, 15); + + /* D1 .. D63 */ + for (i = 0; i < 9; i++) { + memcpy(fr->d_bits + d_idx, trau_bits + (4 + i) * 8 + 1, 7); + d_idx += 7; + } + /* D64 .. D126 */ + for (i = 0; i < 9; i++) { + memcpy(fr->d_bits + d_idx, trau_bits + (13 + i) * 8 + 1, 7); + d_idx += 7; + } + + /* D127 .. D189 */ + for (i = 0; i < 9; i++) { + memcpy(fr->d_bits + d_idx, trau_bits + (22 + i) * 8 + 1, 7); + d_idx += 7; + } + + /* D190 .. D252 */ + for (i = 0; i < 9; i++) { + memcpy(fr->d_bits + d_idx, trau_bits + (31 + i) * 8 + 1, 7); + d_idx += 7; + } + + return 0; +} + +/* TS 08.60 Section 3.3.1 */ +static int encode16_data(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + const ubit_t *cbits5; + int i, d_idx = 0; + + if (fr->dir == OSMO_TRAU_DIR_UL) + cbits5 = ft_data_up_bits; + else + cbits5 = ft_data_down_bits; + + encode_sync16(trau_bits); + + /* C1 .. C5 */ + memcpy(trau_bits + 17, cbits5, 5); + /* C6 .. C15 */ + memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 15 - 5); + + /* D1 .. D63 */ + for (i = 0; i < 9; i++) { + unsigned int offset = (4 + i) * 8; + trau_bits[offset] = 1; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + + /* D64 .. D126 */ + for (i = 0; i < 9; i++) { + unsigned int offset = (13 + i) * 8; + trau_bits[offset] = 1; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + + /* D127 .. D189 */ + for (i = 0; i < 9; i++) { + unsigned int offset = (22 + i) * 8; + trau_bits[offset] = 1; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + + /* D190 .. D252 */ + for (i = 0; i < 9; i++) { + unsigned int offset = (31 + i) * 8; + trau_bits[offset] = 1; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + + return 40 * 8; +} + +/* TS 08.60 3.3.2 */ +static int decode16_edata(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + /* C1 .. C13 */ + memcpy(fr->c_bits, trau_bits + 17, 13); + + /* M1 .. M2 */ + memcpy(fr->m_bits, trau_bits + 30, 2); + + /* D1 .. D288 */ + memcpy(fr->d_bits, trau_bits + 4 * 8, 288); + + return 0; +} + +/* TS 08.60 Section 3.3.2 */ +static int encode16_edata(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + const ubit_t *cbits5; + + if (fr->type == OSMO_TRAU16_FT_D145_SYNC) + cbits5 = ft_d145s_bits; + else + cbits5 = ft_edata_bits; + + /* C1 .. C5 */ + memcpy(trau_bits + 17, cbits5, 5); + + /* C6 .. C13 */ + memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 8); + + /* M1 .. M2 */ + memcpy(trau_bits + 30, fr->m_bits, 2); + + /* D1 .. D288 */ + memcpy(trau_bits + 4 * 8, fr->d_bits, 288); + + return 40 * 8; +} + +/* TS 08.61 Section 5.1.2 */ +static int decode16_data_hr(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i, d_idx = 0; + + /* C1 .. C15 */ + memcpy(fr->c_bits+0, trau_bits + 17, 15); + + /* D1 .. D63 */ + for (i = 0; i < 9; i++) { + memcpy(fr->d_bits + d_idx, trau_bits + (4 + i) * 8 + 1, 7); + d_idx += 7; + } + + /* D'1 .. D'63 (mapped to D64..D127) */ + for (i = 0; i < 9; i++) { + memcpy(fr->d_bits + d_idx, trau_bits + (22 + i) * 8 + 1, 7); + d_idx += 7; + } + + return 0; +} + +/* TS 08.61 Section 5.1.2 */ +static int encode16_data_hr(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + const ubit_t *cbits5; + int i, d_idx = 0; + + if (fr->dir == OSMO_TRAU_DIR_UL) + cbits5 = ft_data_hr_up_bits; + else + cbits5 = ft_data_hr_down_bits; + + encode_sync16(trau_bits); + + /* C1 .. C5 */ + memcpy(trau_bits + 17, cbits5, 5); + /* C6 .. C15 */ + memcpy(trau_bits + 17 + 5, fr->c_bits + 5, 15 - 5); + + /* D1 .. D63 */ + for (i = 4; i < 4 + 9; i++) { + unsigned int offset = i * 8; + trau_bits[offset] = 1; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + + memset(trau_bits + 13*8, 1, 9*8); + + /* D'1 .. D'63 (mapped to D64..D127) */ + for (i = 22; i < 22 + 9; i++) { + unsigned int offset = i * 8; + trau_bits[offset] = 1; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + + memset(trau_bits + 31*8, 1, 9*8); + + return 40 * 8; +} + +/* TS 08.61 Section 5.2.1.1 */ +static int decode8_hr(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i, d_idx = 0; + + /* C1 .. C5 */ + memcpy(fr->c_bits, trau_bits + 9, 5); + /* XC1 .. XC2 */ + memcpy(fr->xc_bits, trau_bits + 8 + 6, 2); + /* XC3 .. XC6 */ + memcpy(fr->xc_bits + 2, trau_bits + 2 * 8 + 2, 4); + /* D1 .. D2 */ + memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 6, 2); + d_idx += 2; + + /* D1 .. D44 */ + for (i = 3; i < 3 + 6; i++) { + int offset = i * 8; + memcpy(fr->d_bits + d_idx, trau_bits + offset + 1, 7); + d_idx += 7; + } + + /* CRC0 .. CRC2 */ + fr->crc_bits[2] = trau_bits[82]; + fr->crc_bits[1] = trau_bits[83]; + fr->crc_bits[0] = trau_bits[84]; + + /* D45 .. D48 */ + memcpy(fr->d_bits + d_idx, trau_bits + 85, 4); + + /* D49 .. D111 */ + for (i = 10; i < 10 + 10; i++) { + int offset = i * 8; + memcpy(fr->d_bits + d_idx, trau_bits + offset + 1, 7); + d_idx += 7; + } + /* D112 */ + fr->d_bits[d_idx++] = trau_bits[19 * 8 + 1]; + + /* C6 .. C9 */ + memcpy(fr->c_bits + 5, trau_bits + 19 * 8 + 2, 4); + + /* T1 .. T2 */ + fr->t_bits[0] = trau_bits[19 * 8 + 6]; + fr->t_bits[1] = trau_bits[19 * 8 + 7]; + + return 0; +} + +/* compute the odd parity bit of the given input bit sequence */ +static ubit_t compute_odd_parity(const ubit_t *in, unsigned int num_bits) +{ + int i; + unsigned int sum = 0; + + for (i = 0; i < num_bits; i++) { + if (in[i]) + sum++; + } + + if (sum % 1) + return 0; + else + return 1; +} + +/* TS 08.61 Section 5.2.1.1 */ +static int encode8_hr(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + int i, d_idx = 0; + + /* sync pattern */ + memset(trau_bits, 0, 8); + trau_bits[8] = 1; + trau_bits[16] = 0; + trau_bits[17] = 1; + for (i = 3; i < 20; i++) + trau_bits[i * 8] = 1; + + /* C1 .. C5 */ + ubit_t *cbits_out = trau_bits + 1 * 8 + 1; + if (fr->dir == OSMO_TRAU_DIR_UL) { + cbits_out[0] = 0; + cbits_out[1] = 0; + cbits_out[2] = 0; + cbits_out[3] = 1; + cbits_out[4] = 1; + } else { + cbits_out[0] = 0; + cbits_out[1] = 0; + cbits_out[2] = 0; + cbits_out[3] = fr->c_bits[3]; + cbits_out[4] = compute_odd_parity(cbits_out, 4); + } + + /* XC1 .. XC2 */ + memcpy(trau_bits + 1 * 8 + 6, fr->xc_bits, 2); + /* XC3 .. XC6 */ + memcpy(trau_bits + 2 * 8 + 2, fr->xc_bits, 4); + /* D1 .. D2 */ + memcpy(trau_bits + 2 * 8 + 6, fr->d_bits, 2); + d_idx += 2; + + /* D1 .. D44 */ + for (i = 3; i < 3 + 6; i++) { + int offset = i * 8; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + }; + + /* CRC0 .. CRC2 */ + trau_bits[82] = fr->crc_bits[2]; + trau_bits[83] = fr->crc_bits[1]; + trau_bits[84] = fr->crc_bits[0]; + + /* D49 .. D111 */ + for (i = 10; i < 10 + 10; i++) { + int offset = i * 8; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + /* D112 */ + trau_bits[19 * 8 + 1] = fr->d_bits[d_idx++]; + + /* C6 .. C9 */ + memcpy(trau_bits + 19 * 8 + 2, fr->c_bits + 5, 4); + + /* FIXME: handle timing adjustment */ + /* T1 .. T2 */ + trau_bits[19 * 8 + 6] = fr->t_bits[0]; + trau_bits[19 * 8 + 7] = fr->t_bits[1]; + + return 20 * 8; +} + +/* TS 08.61 Section 5.2.1.2.1 */ +static int decode8_amr_low(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i, d_idx = 0; + + /* D1 .. D7 */ + memcpy(fr->d_bits + d_idx, trau_bits + 1 * 8 + 1, 7); + d_idx += 7; + /* C1 .. C5 */ + memcpy(fr->c_bits, trau_bits + 2 * 8 + 1, 5); + /* D8 .. D9 */ + memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 6, 2); + d_idx += 2; + /* D10 .. D15 */ + memcpy(fr->d_bits + d_idx, trau_bits + 3 * 8 + 2, 6); + d_idx += 6; + /* D16 .. D120 */ + for (i = 4; i < 19; i++) { + int offset = i * 8; + memcpy(fr->d_bits + d_idx, trau_bits + offset + 1, 7); + d_idx += 7; + } + /* D121 .. D126 */ + memcpy(fr->d_bits + d_idx, trau_bits + 19 * 8 + 1, 6); + d_idx += 6; + + /* T1 */ + fr->t_bits[0] = trau_bits[19 * 8 + 7]; + + return 0; +} + +/* TS 08.61 Section 5.2.1.2.1 */ +static int encode8_amr_low(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + int i, d_idx = 0; + + /* sync pattern */ + memset(trau_bits, 0, 8); + trau_bits[8] = 1; + trau_bits[16] = 1; + trau_bits[24] = 0; + trau_bits[25] = 1; + for (i = 4; i < 20; i++) + trau_bits[i * 8] = 1; + + /* D1 .. D7 */ + memcpy(trau_bits + 1 * 8 + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + /* C1 .. C5 */ + memcpy(trau_bits + 2 * 8 + 1, fr->c_bits, 5); + /* D8 .. D9 */ + memcpy(trau_bits + 2 * 8 + 6, fr->d_bits + d_idx, 2); + d_idx += 2; + /* D10 .. D15 */ + memcpy(trau_bits + 3 * 8 + 2, fr->d_bits + d_idx, 6); + d_idx += 6; + /* D16 .. D120 */ + for (i = 4; i < 19; i++) { + int offset = i * 8; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + /* D121 .. D126 */ + memcpy(trau_bits + 19 * 8 + 1, fr->d_bits + d_idx, 6); + d_idx += 6; + + /* T1 */ + trau_bits[19 * 8 + 7] = fr->t_bits[0]; + + return 20 * 8; +} + +/* TS 08.61 Section 5.2.1.2.2 */ +static int decode8_amr_67(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i, d_idx = 0; + + /* D1 .. D7 */ + memcpy(fr->d_bits + d_idx, trau_bits + 1 * 8 + 1, 7); + d_idx += 7; + /* C1 .. C3 */ + memcpy(fr->c_bits, trau_bits + 2 * 8 + 1, 3); + /* D8 .. D11 */ + memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 4, 4); + d_idx += 4; + /* D12 .. D39 */ + for (i = 3; i < 7; i++) { + int offset = i * 8; + memcpy(fr->d_bits + d_idx, trau_bits + offset + 2, 7); + d_idx += 7; + } + + /* D40 .. D137 */ + for (i = 7; i < 20; i+= 2) { + int offset = i * 8; + memcpy(fr->d_bits + d_idx, trau_bits + offset + 1, 15); + d_idx += 15; + } + + return 0; +} + +/* TS 08.61 Section 5.1.2.2 */ +static int encode8_amr_67(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + int i, d_idx = 0; + + /* sync pattern */ + memset(trau_bits, 0, 8); + trau_bits[1 * 8] = 1; + trau_bits[2 * 8] = 1; + trau_bits[3 * 8] = 1; + trau_bits[4 * 8] = 1; + trau_bits[5 * 8] = 0; + for (i = 5; i < 20; i += 2) + trau_bits[i * 8] = 1; + + /* D1 .. D7 */ + memcpy(trau_bits + 1 * 8 + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + /* C1 .. C3 */ + memcpy(trau_bits + 2 * 8 + 1, fr->c_bits, 3); + /* D8 .. D11 */ + memcpy(trau_bits + 2 * 8 + 4, fr->d_bits + d_idx, 4); + d_idx += 4; + /* D12 .. D39 */ + for (i = 3; i < 7; i++) { + int offset = i * 8; + memcpy(trau_bits + offset + 2, fr->d_bits + d_idx, 7); + d_idx += 7; + } + + /* D40 .. D137 */ + for (i = 7; i < 20; i+= 2) { + int offset = i * 8; + memcpy(trau_bits + offset + 1, fr->d_bits + d_idx, 15); + d_idx += 15; + } + + return 20 * 8; +} + +/* TS 08.61 Section 5.1.2.3 */ +static int decode8_amr_74(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int d_idx = 0; + + /* D1 .. D5 */ + memcpy(fr->d_bits + d_idx, trau_bits + 3, 5); + d_idx += 5; + /* D6 .. D12 */ + memcpy(fr->d_bits + d_idx, trau_bits + 1 * 8 + 1, 7); + d_idx += 7; + /* C1.. C3 */ + memcpy(fr->c_bits, trau_bits + 2 * 8 + 1, 3); + /* D13 .. D16 */ + memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 4, 4); + d_idx += 4; + /* D17 .. D23 */ + memcpy(fr->d_bits + d_idx, trau_bits + 3 * 8 + 1, 7); + d_idx += 7; + /* D24 .. D151 */ + memcpy(fr->d_bits + d_idx, trau_bits + 4 * 8, 16 * 8); + + return 0; +} + +/* TS 08.61 Section 5.1.2.3 */ +static int encode8_amr_74(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + int d_idx = 0; + + /* sync pattern */ + trau_bits[0] = 0; + trau_bits[1] = 0; + trau_bits[2] = 1; + trau_bits[1*8] = 0; + trau_bits[2*8] = 1; + trau_bits[3*8] = 0; + + /* D1 .. D5 */ + memcpy(trau_bits + 3, fr->d_bits + d_idx, 5); + d_idx += 5; + /* D6 .. D12 */ + memcpy(trau_bits + 1 * 8 + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + /* C1.. C3 */ + memcpy(trau_bits + 2 * 8 + 1, fr->c_bits, 3); + /* D13 .. D16 */ + memcpy(trau_bits + 2 * 8 + 4, fr->d_bits + d_idx, 4); + d_idx += 4; + /* D17 .. D23 */ + memcpy(trau_bits + 3 * 8 + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + /* D24 .. D151 */ + memcpy(trau_bits + 4 * 8, fr->d_bits + d_idx, 16 * 8); + + return 20 * 8; +} + +/* TS 08.61 Section 5.2.2 */ +static int decode8_data(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i, d_idx = 0; + + /* C1 .. C5 */ + memcpy(fr->c_bits, trau_bits + 1 * 8 + 1, 5); + /* D1 .. D2 */ + memcpy(fr->d_bits + d_idx, trau_bits + 1 * 8 + 6, 2); + d_idx += 2; + /* D3 .. D8 */ + memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 2, 6); + /* D9 .. D57 + D'1 .. D'57 */ + for (i = 3; i < 20; i++) { + memcpy(fr->d_bits + d_idx, trau_bits + i * 8 + 1, 7); + d_idx += 7; + } + /* D'58 .. D'62 */ + memcpy(fr->d_bits + d_idx, trau_bits + 19 * 8 + 1, 6); + d_idx += 6; + + return 0; +} + +/* TS 08.61 Section 5.2.2 */ +static int encode8_data(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + int i, d_idx = 0; + + /* sync pattern */ + memset(trau_bits, 0, 8); + trau_bits[1 * 8] = 1; + trau_bits[2 * 8] = 0; + trau_bits[2 * 8 + 1] = 1; + for (i = 3; i < 19; i++) + trau_bits[i * 8] = 1; + trau_bits[19 * 8 + 7] = 1; + + /* C1 .. C5 */ + memcpy(trau_bits + 1 * 8 + 1, fr->c_bits, 5); + /* D1 .. D2 */ + memcpy(trau_bits + 1 * 8 + 6, fr->d_bits + d_idx, 2); + d_idx += 2; + /* D3 .. D8 */ + memcpy(trau_bits + 2 * 8 + 2, fr->d_bits + d_idx, 6); + /* D9 .. D57 + D'1 .. D'57 */ + for (i = 3; i < 20; i++) { + memcpy(trau_bits + i * 8 + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + /* D'58 .. D'62 */ + memcpy(trau_bits + 19 * 8 + 1, fr->d_bits + d_idx, 6); + d_idx += 6; + + return 20 * 8; +} + +/* TS 08.61 Section 5.2.3 */ +static int decode8_oam(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i, d_idx = 0; + + /* C1 .. C5 */ + memcpy(fr->c_bits, trau_bits + 1 * 8 + 1, 5); + /* XC1 .. XC2 */ + memcpy(fr->xc_bits, trau_bits + 1 * 8 + 6, 2); + /* XC3 .. XC6 */ + memcpy(fr->xc_bits + 2, trau_bits + 2 * 8 + 2, 4); + /* D1 .. D2 */ + memcpy(fr->d_bits + d_idx, trau_bits + 2 * 8 + 6, 2); + d_idx += 2; + /* D3 .. D114 */ + for (i = 3; i < 19; i++) { + memcpy(fr->d_bits + d_idx, trau_bits + i * 8 + 1, 7); + d_idx += 7; + } + /* D115 .. D120 */ + memcpy(fr->d_bits + d_idx, trau_bits + 19 * 8 + 1, 6); + d_idx += 7; + + return 0; +} + +/* TS 08.61 Section 5.2.3 */ +static int encode8_oam(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + int i, d_idx = 0; + + /* sync pattern */ + memset(trau_bits, 0, 8); + trau_bits[1 * 8 + 0] = 1; + trau_bits[2 * 8 + 0] = 0; + trau_bits[2 * 8 + 1] = 1; + for (i = 3; i < 20; i++) + trau_bits[i * 8] = 1; + + /* C1 .. C5 */ + memcpy(trau_bits + 1 * 8 + 1, fr->c_bits, 5); + /* XC1 .. XC2 */ + memcpy(trau_bits + 1 * 8 + 6, fr->xc_bits, 2); + /* XC3 .. XC6 */ + memcpy(trau_bits + 2 * 8 + 2, fr->xc_bits + 2, 4); + /* D1 .. D2 */ + memcpy(trau_bits + 2 * 8 + 6, fr->d_bits + d_idx, 2); + d_idx += 2; + /* D3 .. D114 */ + for (i = 3; i < 19; i++) { + memcpy(trau_bits + i * 8 + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + /* D115 .. D120 */ + memcpy(trau_bits + 19 * 8 + 1, fr->d_bits + d_idx, 6); + d_idx += 7; + + return 20 * 8; +} + + +/*! Encode a TRAU frame from its decoded representation to a sequence of unpacked bits. + * \param[out] bits caller-allocated buffer for unpacked outpud bits + * \param[in] n_bits size of 'bits' oputput buffer in number of unpacked bits + * \param[in] fr decoded representation of TRAU frame to be encoded + * \return 0 number of unpacked output bits generated; negative in case of error */ +int osmo_trau_frame_encode(ubit_t *bits, size_t n_bits, const struct osmo_trau_frame *fr) +{ + switch (fr->type) { + case OSMO_TRAU16_FT_FR: + case OSMO_TRAU16_FT_EFR: + return encode16_fr(bits, fr); + case OSMO_TRAU16_FT_HR: + return encode16_hr(bits, fr); + case OSMO_TRAU16_FT_AMR: + return encode16_amr(bits, fr); + case OSMO_TRAU16_FT_OAM: + return encode16_oam(bits, fr); + case OSMO_TRAU16_FT_IDLE: + return encode16_idle(bits, fr); + case OSMO_TRAU16_FT_DATA_HR: + return encode16_data_hr(bits, fr); + case OSMO_TRAU16_FT_DATA: + return encode16_data(bits, fr); + case OSMO_TRAU16_FT_D145_SYNC: + case OSMO_TRAU16_FT_EDATA: + return encode16_edata(bits, fr); + case OSMO_TRAU8_SPEECH: + return encode8_hr(bits, fr); + case OSMO_TRAU8_DATA: + return encode8_data(bits, fr); + case OSMO_TRAU8_OAM: + return encode8_oam(bits, fr); + case OSMO_TRAU8_AMR_LOW: + return encode8_amr_low(bits, fr); + case OSMO_TRAU8_AMR_6k7: + return encode8_amr_67(bits, fr); + case OSMO_TRAU8_AMR_7k4: + return encode8_amr_74(bits, fr); + case OSMO_TRAU_FT_NONE: + default: + return -EINVAL; + } +} + +/*! Decode/parse a 16k TRAU frame from unpacked bits to decoded format. + * \param[out] fr caller-allocated output data structure + * \param[in] bits unpacked bits containing raw TRAU frame; must be aligned + * \param[in] dir direction (uplink/downlink) + * \returns 0 in case of success; negative on error */ +int osmo_trau_frame_decode_16k(struct osmo_trau_frame *fr, const ubit_t *bits, + enum osmo_trau_frame_direction dir) +{ + uint8_t cbits5 = get_bits(bits, 17, 5); + + fr->type = OSMO_TRAU_FT_NONE; + fr->dir = dir; + + switch (cbits5) { + case TRAU_FT_FR_UP: + case TRAU_FT_FR_DOWN: + fr->type = OSMO_TRAU16_FT_FR; + return decode16_fr(fr, bits, dir); + case TRAU_FT_IDLE_UP: + case TRAU_FT_IDLE_DOWN: + fr->type = OSMO_TRAU16_FT_IDLE; + return decode16_fr(fr, bits, dir); + case TRAU_FT_EFR: + fr->type = OSMO_TRAU16_FT_EFR; + return decode16_fr(fr, bits, dir); + case TRAU_FT_AMR: + fr->type = OSMO_TRAU16_FT_AMR; + return decode16_amr(fr, bits, dir); + case TRAU_FT_OM_UP: + case TRAU_FT_OM_DOWN: + fr->type = OSMO_TRAU16_FT_OAM; + return decode16_oam(fr, bits, dir); + case TRAU_FT_DATA_UP: + case TRAU_FT_DATA_DOWN: + fr->type = OSMO_TRAU16_FT_DATA; + return decode16_data(fr, bits, dir); + case TRAU_FT_HR_UP: + case TRAU_FT_HR_DOWN: + fr->type = OSMO_TRAU16_FT_HR; + return decode16_hr(fr, bits, dir); + case TRAU_FT_DATA_UP_HR: + case TRAU_FT_DATA_DOWN_HR: + fr->type = OSMO_TRAU16_FT_DATA_HR; + return decode16_data_hr(fr, bits, dir); + case TRAU_FT_EDATA: + fr->type = OSMO_TRAU16_FT_EDATA; + return decode16_edata(fr, bits, dir); + case TRAU_FT_D145_SYNC: + fr->type = OSMO_TRAU16_FT_D145_SYNC; + return decode16_edata(fr, bits, dir); + + default: + return -EINVAL; + } +} + +#define TRAU8_FT_AMR_NO_SPEECH_CMI 0x10 /* 1, 0, 0, 0, 0 */ +#define TRAU8_FT_AMR_NO_SPEECH_CMR 0x14 /* 1, 0, 1, 0, 0 */ +#define TRAU8_FT_AMR_475_515_590 0..7 + +const uint8_t bit8_0[16] = { 0, }; + +/*!< check sync pattern for hr/data/oam */ +static bool is_hr(const ubit_t *bits) +{ + int i; + + /* TS 08.61 Section 6.8.2.1.1 */ + if (memcmp(bits, bit8_0, sizeof(bit8_0))) + return false; + if (bits[8] != 1) + return false; + if (bits[16] != 0 || bits[17] != 1) + return false; + for (i = 24; i < 20 * 8; i += 16) { + if (bits[i] != 1) + return false; + } + return true; +} + + +/*!< check sync pattern for AMR No_Speech + low bit rate */ +static bool is_amr_low(const ubit_t *bits) +{ + int i; + + /* TS 08.61 Section 6.8.2.1.2 */ + if (memcmp(bits, bit8_0, sizeof(bit8_0))) + return false; + if (bits[8] != 1) + return false; + if (bits[16] != 1) + return false; + if (bits[24] != 0 || bits[25] != 1) + return false; + for (i = 32; i < 20 * 8; i += 16) { + if (bits[i] != 1) + return false; + } + return true; +} + +/*!< check sync pattern for AMR 6.7kBit/s */ +static bool is_amr_67(const ubit_t *bits) +{ + int i; + + /* TS 08.61 Section 6.8.2.1.3 */ + if (memcmp(bits, bit8_0, sizeof(bit8_0))) + return false; + if (bits[8] != 1) + return false; + if (bits[16] != 1) + return false; + if (bits[24] != 1) + return false; + if (bits[32] != 1) + return false; + if (bits[40] != 0) + return false; + for (i = 48; i < 20 * 8; i+= 16) + if (bits[i] != 1) + return false; + + return true; +} + +/*!< check sync pattern for AMR 7.4kBit/s */ +static bool is_amr_74(const ubit_t *bits) +{ + if (bits[0] != 0 || bits[1] != 0 || bits[2] != 0) + return false; + if (bits[8] != 0) + return false; + if (bits[16] != 1) + return false; + if (bits[24] != 0) + return false; + + return true; +} + +/*! Decode/parse a 8k TRAU frame from unpacked bits to decoded format. + * \param[out] fr caller-allocated output data structure + * \param[in] bits unpacked bits containing raw TRAU frame; must be aligned + * \param[in] dir direction (uplink/downlink) + * \returns 0 in case of success; negative on error */ +int osmo_trau_frame_decode_8k(struct osmo_trau_frame *fr, const ubit_t *bits, + enum osmo_trau_frame_direction dir) +{ + fr->type = OSMO_TRAU_FT_NONE; + fr->dir = dir; + + if (is_hr(bits)) { + /* normal sync pattern */ + uint8_t cbits5 = get_bits(bits, 9, 5); + if (dir == OSMO_TRAU_DIR_UL) { + switch (cbits5) { /* Section 5.2.4.1.1 */ + case 0x02: + return decode8_hr(fr, bits, dir); + case 0x07: + return decode8_data(fr, bits, dir); + case 0x13: + return decode8_oam(fr, bits, dir); + } + } else { + /* Downlink */ + switch (cbits5 >> 2) { /* Section 5.2.4.1.2 */ + case 0: + return decode8_hr(fr, bits, dir); + case 1: + return decode8_data(fr, bits, dir); + case 2: + return decode8_oam(fr, bits, dir); + } + } + } else if (is_amr_low(bits)) { + return decode8_amr_low(fr, bits, dir); + } else if (is_amr_67(bits)) { + return decode8_amr_67(fr, bits, dir); + } else if (is_amr_74(bits)) { + return decode8_amr_74(fr, bits, dir); + } + + return -EINVAL; +} + +/* }@ */ -- cgit v1.2.3