falconia has uploaded this change for review.
trau: add AMR TRAU frame interworking facility (WIP)
The simplistic design of osmo_trau2rtp() and osmo_rtp2trau() APIs
is not sufficient for the greater complexity encountered in AMR.
Instead let us introduce an interworking facility for AMR frames
that centers around a common intermediate format, with encoding
and decoding functions that convert between this internal
intermediate and 3 types of external interfaces: TRAU/TFO frames,
RTP in 3 possible formats (RFC 4867 BWE, RFC 4867 OA and TRAU-like
TW-TS-006) and speech encoder/decoder engines.
This AMR TRAU frame interworking facility is needed in order to
add AMR support to ThemWi E1 Abis MGW (replacement for the limited
E1 Abis capability in OsmoMGW), and it will also be used in
tw-border-mgw (ThemWi CN sw component) to terminate TW-TS-006 RTP
legs, interfacing to libtwamr speech encoder & decoder and to
in-band TFO per TS 28.062.
The present state of this patch is WIP, not for merging:
implementation of desired functionality only begun, with only
TRAU-AMR-16k decoding implemented so far.
Change-Id: I42706c2f68ed29aff1bf451b37e783983f8de3b0
---
M include/Makefile.am
A include/osmocom/trau/amr_trau.h
M src/Makefile.am
A src/trau/amr_trau.c
4 files changed, 1,086 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-abis refs/changes/52/41252/1
diff --git a/include/Makefile.am b/include/Makefile.am
index 7c00ccc..36d3259 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -24,6 +24,7 @@
osmocom/abis/trau_frame.h \
osmocom/abis/unixsocket_proto.h \
osmocom/abis/version.h \
+ osmocom/trau/amr_trau.h \
osmocom/trau/clearmode.h \
osmocom/trau/csd_ra2.h \
osmocom/trau/csd_raa_prime.h \
diff --git a/include/osmocom/trau/amr_trau.h b/include/osmocom/trau/amr_trau.h
new file mode 100644
index 0000000..87be2dd
--- /dev/null
+++ b/include/osmocom/trau/amr_trau.h
@@ -0,0 +1,334 @@
+/*
+ * Interworking of AMR codec frames between TS 48.060 & 48.061 TRAU frame
+ * formats, RTP in RFC 4867 and TW-TS-006 formats, and speech encoder/decoder
+ * engines.
+ *
+ * This code was contributed to Osmocom Cellular Network Infrastructure
+ * project by Mother Mychaela N. Falconia of Themyscira Wireless.
+ * Mother Mychaela's contributions are NOT subject to copyright:
+ * no rights reserved, all rights relinquished.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/codec/codec.h>
+#include <osmocom/trau/trau_frame.h>
+
+/*! \defgroup amr_trau AMR TRAU frame interworking facility
+ * @{
+ *
+ * The present facility provides interworking of AMR codec frames between
+ * the following 3 possible external interfaces:
+ *
+ * - AMR TRAU frames as they appear on T1/E1 Abis per TS 48.060 & 48.061,
+ * or AMR TFO frames as they appear inside G.711 PCM sample stream
+ * per TS 28.062.
+ *
+ * - Representation of AMR codec in RTP, in either of the two formats
+ * (BWE or OA) defined in RFC 4867 or in the OAX (OA extended) format
+ * of TW-TS-006.
+ *
+ * - AMR speech encoder and decoder engines that operate on arrays of
+ * codec parameters in uint16_t or int16_t format, such as Themyscira
+ * libtwamr.
+ *
+ * All API functions, struct and enum definitions that are part of this
+ * facility are prefixed with osmo_amrt_ for namespace reasons.
+ */
+
+/*! Every AMR frame described in a struct osmo_amrt_if has a classification
+ * as in speech, SID etc. This classification is captured in the following
+ * enum, and is based on Frame_Classification and No_Speech_Classification
+ * definitions in TS 48.060. These TS 48.060 definitions have been chosen
+ * as the basis for the common intermediate representation, rather than
+ * the frame type system of RFC 4867, because the latter is crippled,
+ * removing Speech_Degraded and Onset classifications, and furthermore
+ * the distinctions between good and bad speech, good and bad SID and
+ * Sid_First vs Sid_Update are coded in a very indirect way in RTP.
+ * The present TRAU-based frame classification system maps directly to
+ * speech encoder and decoder frame types in libtwamr (based on 3GPP
+ * reference implementation), hence the entire burden of converting
+ * between the two classification systems is moved into RTP interface
+ * functions.
+ *
+ * For our enum osmo_amrt_fc we have chosen 5-bit code points that map
+ * directly to TS 48.060: bits [4:3] are Frame_Classification per section
+ * 5.5.1.2.1, bits [2:0] are No_Speech_Classification per section 5.5.1.2.2.
+ */
+enum osmo_amrt_fc {
+ OSMO_AMRT_FC_NO_DATA = 0x00,
+ OSMO_AMRT_FC_SID_BAD = 0x04,
+ OSMO_AMRT_FC_SID_UPDATE = 0x05,
+ OSMO_AMRT_FC_ONSET = 0x06,
+ OSMO_AMRT_FC_SID_FIRST = 0x07,
+ OSMO_AMRT_FC_SPEECH_BAD = 0x08,
+ OSMO_AMRT_FC_SPEECH_DEGRADED = 0x10,
+ OSMO_AMRT_FC_SPEECH_GOOD = 0x18,
+};
+
+/*! This enum captures the 3-bit Config_Prot field of TS 28.062 section
+ * C.6.1.2. This field is used to embed TFO configuration messages
+ * in AMR TRAU or TFO frames when 3GPP Rel4 method is in use, and it
+ * needs to be ferried between TRAU/TFO frames and TW-TS-006.
+ */
+enum osmo_amrt_tfo_config_prot {
+ OSMO_AMRT_CFG_PROT_NO_CON = 0x00,
+ OSMO_AMRT_CFG_PROT_CON_REQ = 0x01,
+ OSMO_AMRT_CFG_PROT_DIS_REQ = 0x02,
+ OSMO_AMRT_CFG_PROT_CON_ACK = 0x03,
+ OSMO_AMRT_CFG_PROT_UL_ACK = 0x05,
+ OSMO_AMRT_CFG_PROT_DL_ACK = 0x06,
+};
+
+/*! TW-TS-006 section 5.7 adds a new Param_Info field, used to describe
+ * presence or absence of TFO configuration parameters beyond Config_Prot
+ * and Message_No fields. This enum captures Param_Info definitions
+ * from TW-TS-006. Unshifted values are used, exactly as they appear
+ * in the Config_Prot extension octet.
+ */
+enum osmo_amrt_tfo_param_info {
+ OSMO_AMRT_TFOP_ABSENT_NATIVE = 0x00,
+ OSMO_AMRT_TFOP_ABSENT_NO_ROOM = 0x02,
+ OSMO_AMRT_TFOP_ABSENT_BAD_CRC = 0x04,
+ OSMO_AMRT_TFOP_PRESENT_VALID = 0x06,
+};
+
+/*! Every AMR frame that passes through the present interworking facility
+ * passes through a common intermediate format on its way between the
+ * externally received input format and the ultimate destination format.
+ * The following struct carries this intermediate format.
+ *
+ * Note regarding validity: because this intermediate structure is
+ * strictly internal to applications performing interworking conversions,
+ * library functions that take this struct as input are allowed to
+ * expect all stated validity rules to be observed, and are allowed to
+ * OSMO_ASSERT on invalid inputs.
+ */
+struct osmo_amrt_if {
+ /* Fields in this structure are grouped in logical order, but also
+ * with an eye toward structure packing optimization: in each logical
+ * section enum fields appear first, followed by fields of type
+ * uint8_t, ubit_t or bool.
+ */
+
+ /*! Fundamental class of this frame */
+ enum osmo_amrt_fc frame_class;
+ /*! Codec Mode Indication: if this field is valid as marked by
+ * cmi_valid being true, only modes 0 through 7 are valid here,
+ * _not_ AMR_SID or AMR_NO_DATA! */
+ enum osmo_amr_type cmi;
+ /*! Codec Mode Request: modes [0,7] and AMR_NO_DATA are potentially
+ * valid here; the latter can occur if the input was RTP. */
+ enum osmo_amr_type cmr;
+ /*! Is CMI field valid? */
+ bool cmi_valid;
+ /*! Is CMR field valid? */
+ bool cmr_valid;
+
+ /*! Frame payload bits in s-bit order as used in TRAU and TFO frames,
+ * also most convenient for conversion from/to speech codec parameter
+ * arrays, _not_ the d-bit order used on Um interface and in RTP.
+ * In the case of SID frames, only 35 bits are stored in this array
+ * as in TRAU frames, not 39 bits as in RTP.
+ */
+ ubit_t s_bits[244];
+
+ /*! Request or Indication Flag as defined in TS 48.060 section
+ * 5.5.1.2.1, present in TRAU frames and in TW-TS-006 RTP format. */
+ ubit_t rif;
+ /*! Is RIF field valid? */
+ bool rif_valid;
+
+ /*! The 3 fields that follow after this validity flag (TA, DTXd
+ * and TFOE) are valid only if this validity flag is true. */
+ bool ta_dtxd_tfoe_valid;
+ /*! Time Alignment field in TRAU frames, also used to convey TFO
+ * and handover notifications. */
+ uint8_t ta;
+ /*! DTXd: bit C19 in TRAU-AMR-16k or D63 in TRAU-AMR-8k-No_Speech */
+ ubit_t dtxd;
+ /*! TFOE: bit C20 in TRAU-AMR-16k or D64 in TRAU-AMR-8k-No_Speech */
+ ubit_t tfoe;
+
+ /*! All following TFO-related fields are valid only if this Config_Prot
+ * field is non-zero. */
+ enum osmo_amrt_tfo_config_prot config_prot;
+ /*! Param_Info field is defined in TW-TS-006 section 5.7,
+ * see enum definition above. */
+ enum osmo_amrt_tfo_param_info param_info;
+ /*! Message_No field is defined in TS 28.062 section C.6.1.3 */
+ uint8_t message_no;
+ /*! Payload bits of TFO parameters as specified in TW-TS-006
+ * section 5.8. 72 bits of space are provided, rather than 71,
+ * in order to allow more efficient packing and unpacking
+ * to and from bytes. */
+ ubit_t tfo_param_bits[72];
+};
+
+/*! Extraction of information content from a received stream of AMR TRAU
+ * frames is a stateful procedure, requiring state to be maintained
+ * from one decoded TRAU frame to the next. This structure captures
+ * the necessary state, as detailed in TW-TS-006 section 6.1.1.
+ *
+ * On initialization, set cmi_valid and cmr_valid to false, or zero out
+ * the whole struct.
+ */
+struct osmo_amrt_decode_state {
+ /*! Codec Mode Indication: if this field is valid as marked by
+ * cmi_valid being true, only modes 0 through 7 are valid here,
+ * _not_ AMR_SID or AMR_NO_DATA! */
+ enum osmo_amr_type cmi;
+ /*! Codec Mode Request: same range restriction as for CMI. */
+ enum osmo_amr_type cmr;
+ /*! Is CMI field valid? */
+ bool cmi_valid;
+ /*! Is CMR field valid? */
+ bool cmr_valid;
+};
+
+/*! The operation of decoding a received AMR TRAU frame can have three
+ * outcomes, in addition to filled struct osmo_amrt_if output.
+ * This enum defines the 3 possible result codes.
+ */
+enum osmo_amrt_decode_result {
+ /*! The frame that was received is found to be perfectly valid
+ * and properly formed - the normal case.
+ */
+ OSMO_AMRT_FRAME_DEC_GOOD,
+ /*! The frame that was received is bad, not in the sense of Rx radio
+ * errors or FACCH stealing etc, but in the sense of errors in
+ * frame coding or CRC etc. If this condition occurs when a TRAU
+ * or TRAU-like MGW decodes a TRAU-UL frame, UFE needs to be
+ * indicated back to the BTS/CCU. Note that the frame may have been
+ * decoded (struct osmo_amrt_if filled) if the bad CRC was CRC[2..4]
+ * for speech, rather than CRC1 that covers control bits.
+ */
+ OSMO_AMRT_FRAME_DEC_BAD,
+ /*! We decoded the frame just fine, but the far end indicated UFE/DFE
+ * via TRAU-AMR-16k bit C13 or TRAU-AMR-8k-No_Speech bit D7. DFE
+ * condition in TRAU-AMR-8k mode is important for TRAU or TRAU-like
+ * MGW implementations.
+ */
+ OSMO_AMRT_FRAME_DEC_REMOTE_BAD,
+};
+
+enum osmo_amrt_decode_result
+osmo_amrt_decode_trau_frame(struct osmo_amrt_decode_state *state,
+ struct osmo_amrt_if *fr,
+ const struct osmo_trau_frame *tf,
+ bool allow_config_prot);
+
+/*! Generation of AMR TRAU frames from a stream of struct osmo_amrt_if inputs
+ * (ultimately derived from RTP input in most cases) is likewise a stateful
+ * procedure, requiring state to be maintained from one encoded TRAU frame
+ * to the next. This structure captures the necessary state, as detailed in
+ * TW-TS-006 section 6.3.2.
+ *
+ * Initialization: there is no "one size fits all" answer, instead application
+ * implementors have to understand the problem and decide as appropriate for
+ * each use case. See TW-TS-006 sections 6.3.3.1 and 6.3.6.
+ */
+struct osmo_amrt_encode_state {
+ /*! Codec Mode Indication: valid range is [0,7] for TRAU-AMR-16k
+ * or [0,4] for TRAU-AMR-8k. In any case, no AMR_SID or AMR_NO_DATA
+ * can appear in this field. */
+ enum osmo_amr_type cmi;
+ /*! Codec Mode Request: same range restriction as for CMI. */
+ enum osmo_amr_type cmr;
+ /*! Request or Indication Flag. In between frames, this state
+ * variable remembers RIF of the last emitted frame. */
+ ubit_t rif;
+ /*! What should we emit in DTXd bit? */
+ ubit_t dtxd;
+ /*! What should we emit in TFOE bit? */
+ ubit_t tfoe;
+};
+
+/*! In TRAU frame encoding direction, we have split off CRC generation into
+ * a separate post-processing function, to be called after main AMR TRAU frame
+ * encoding functions. This split is needed so that applications can modify
+ * some control bits after encoding: setting C5 for TFO, setting C6..C11 for
+ * timing alignment, setting UFE etc. However, the exact manner of AMR CRC
+ * computation depends on speech or No_Speech mode, which cannot be derived
+ * from encoded struct osmo_trau_frame alone, especially when RIF=1.
+ * Therefore, we pass an additional CRC type code from our main AMR TRAU frame
+ * encoding functions to the CRC-setter post-processing function.
+ */
+enum osmo_amrt_crc_type {
+ OSMO_AMRT_CRC_16k_NO_SPEECH,
+ OSMO_AMRT_CRC_16k_SPEECH_MR475,
+ OSMO_AMRT_CRC_16k_SPEECH_MR515,
+ OSMO_AMRT_CRC_16k_SPEECH_MR59,
+ OSMO_AMRT_CRC_16k_SPEECH_MR67,
+ OSMO_AMRT_CRC_16k_SPEECH_MR74,
+ OSMO_AMRT_CRC_16k_SPEECH_MR795,
+ OSMO_AMRT_CRC_16k_SPEECH_MR102,
+ OSMO_AMRT_CRC_16k_SPEECH_MR122,
+ OSMO_AMRT_CRC_8k_NO_SPEECH,
+ OSMO_AMRT_CRC_8k_SPEECH_MR475,
+ OSMO_AMRT_CRC_8k_SPEECH_MR515,
+ OSMO_AMRT_CRC_8k_SPEECH_MR59,
+ OSMO_AMRT_CRC_8k_SPEECH_MR67,
+ OSMO_AMRT_CRC_8k_SPEECH_MR74,
+};
+
+enum osmo_amrt_crc_type
+osmo_amrt_encode_trau_frame_16k(struct osmo_amrt_encode_state *state,
+ struct osmo_trau_frame *tf,
+ const struct osmo_amrt_if *fr, bool is_tfo);
+
+/*! In TRAU-AMR-8k output direction, it may be necessary to restrict
+ * the type of frames that may be emitted: for example, a TRAU-like MGW
+ * may need to set UFE or obey DFE from the BTS/CCU. The following
+ * enum controls these restrictions.
+ */
+enum osmo_amrt_8k_restrict {
+ /*! No restriction, any TRAU-AMR-8k output is allowed */
+ OSMO_AMRT_OUT8k_NO_RESTRICT,
+ /*! Restrict to either No_Speech or lower modes */
+ OSMO_AMRT_OUT8k_REQUIRE_LOW,
+ /*! No_Speech output is required */
+ OSMO_AMRT_OUT8k_REQUIRE_NO_SPEECH,
+};
+
+enum osmo_amrt_crc_type
+osmo_amrt_encode_trau_frame_8k(struct osmo_amrt_encode_state *state,
+ struct osmo_trau_frame *tf,
+ const struct osmo_amrt_if *fr, bool is_tfo,
+ enum osmo_amrt_8k_restrict restr);
+
+void osmo_amrt_set_trau_crc(struct osmo_trau_frame *tf,
+ enum osmo_amrt_crc_type crc_type);
+
+/*! On RTP side we support 3 different formats: RFC 4867 BWE, RFC 4867 OA
+ * and TW-TS-006 OAX (OA extended). This enum captures the necessary
+ * format selector.
+ */
+enum osmo_amrt_rtp_format {
+ OSMO_AMRT_RTP_FMT_BWE,
+ OSMO_AMRT_RTP_FMT_OA,
+ OSMO_AMRT_RTP_FMT_OAX,
+};
+
+int osmo_amrt_decode_rtp(struct osmo_amrt_if *fr, const uint8_t *rtp_pl,
+ unsigned rtp_pl_len, enum osmo_amrt_rtp_format fmt);
+int osmo_amrt_encode_rtp(uint8_t *rtp_pl, unsigned pl_buf_size,
+ const struct osmo_amrt_if *fr,
+ enum osmo_amrt_rtp_format fmt);
+
+/* Interface to arrays of codec parameters, primarily for speech encoder and
+ * decoder engines, but also used by our DHF filler function.
+ */
+
+void osmo_amrt_param_to_sbits(ubit_t *s_bits, const uint16_t *param,
+ enum osmo_amr_type mode);
+void osmo_amrt_sbits_to_param(uint16_t *param, const ubit_t *s_bits,
+ enum osmo_amr_type mode);
+
+void osmo_amrt_fill_with_dhf(struct osmo_amrt_if *fr, enum osmo_amr_type mode);
+
+/*! @} */
diff --git a/src/Makefile.am b/src/Makefile.am
index b30ebfc..edc3668 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,7 +52,8 @@
-no-undefined \
$(NULL)
libosmotrau_la_LIBADD = $(COMMONLIBS) $(LIBOSMOCODEC_LIBS) $(ORTP_LIBS)
-libosmotrau_la_SOURCES = trau/csd_ra2.c \
+libosmotrau_la_SOURCES = trau/amr_trau.c \
+ trau/csd_ra2.c \
trau/csd_v110.c \
trau/raa_prime_decode.c \
trau/raa_prime_encode.c \
diff --git a/src/trau/amr_trau.c b/src/trau/amr_trau.c
new file mode 100644
index 0000000..f4ac9d3
--- /dev/null
+++ b/src/trau/amr_trau.c
@@ -0,0 +1,749 @@
+/*
+ * AMR TRAU interworking facility: encoding and decoding of TRAU frames.
+ *
+ * This code was contributed to Osmocom Cellular Network Infrastructure
+ * project by Mother Mychaela N. Falconia of Themyscira Wireless.
+ * Mother Mychaela's contributions are NOT subject to copyright:
+ * no rights reserved, all rights relinquished.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/crc8gen.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+#include <osmocom/trau/amr_trau.h>
+#include <osmocom/trau/trau_frame.h>
+
+/*! \addgroup amr_trau
+ * @{
+ */
+
+/*
+ * AMR TRAU parity (same as EFR and HR)
+ *
+ * g(x) = x^3 + x^1 + 1
+ */
+static const struct osmo_crc8gen_code gsm0860_amr_crc3 = {
+ .bits = 3,
+ .poly = 0x3,
+ .init = 0x0,
+ .remainder = 0x7,
+};
+
+/* Please refer to TS 28.062 Table C.6.1.5-1 for the details of how TFO
+ * config parameters are mapped to different TRAU frame types. The parameters
+ * are broken down into groups A, B and C, and that last block C is missing
+ * in 16k frames for MR102 and 8k frames for SID. When block C is missing
+ * from the transmitted frame, parameters in that block have to assume
+ * fixed values listed in that table. The following datum gives these fixed
+ * parameters, in the form in which we need them for TW-TS-006.
+ */
+static const ubit_t tfo_blockC_fixed_params[26] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, /* SCS_2 */
+ 0, /* OM_2 */
+ 1, 0, 0, /* MACS_2 */
+ 0, /* ATVN_2 */
+ 1, 1, 1, 1, 1, 1, 1, 1, /* SCS_3 */
+ 0, /* OM_3 */
+ 1, 0, 0, /* MACS_3 */
+ 0, /* ATVN_3 */
+};
+
+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;
+}
+
+/* This helper function is called when TRAU frame decoding ran into
+ * an unrecoverable error, such that the received frame has to be
+ * declared totally bad with no useful information content. In particular,
+ * if CRC1 covering control bits is bad, we cannot trust _any_ info
+ * from those control bits!
+ */
+static void decode_handle_error(struct osmo_amrt_decode_state *state,
+ struct osmo_amrt_if *fr)
+{
+ state->cmi_valid = false;
+ state->cmr_valid = false;
+
+ fr->frame_class = OSMO_AMRT_FC_NO_DATA;
+ fr->cmi_valid = false;
+ fr->cmr_valid = false;
+ fr->rif_valid = false;
+ fr->ta_dtxd_tfoe_valid = false;
+ fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON;
+}
+
+static enum osmo_amrt_decode_result
+decode_16k_no_speech(struct osmo_amrt_decode_state *state,
+ struct osmo_amrt_if *fr, const struct osmo_trau_frame *tf)
+{
+ ubit_t crc_collect[25 + 61];
+ int crc_stat;
+
+ /* Start with CRC verification: if CRC is bad, there is no point
+ * in decoding anything else. */
+ memcpy(crc_collect, tf->c_bits, 25);
+ memcpy(crc_collect + 25, tf->d_bits + 31, 61);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, crc_collect,
+ 25 + 61, tf->d_bits + 92);
+ if (crc_stat)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+
+ /* CRC is good - now decode the rest */
+ fr->frame_class = get_bits(tf->d_bits, 31, 3);
+ switch (fr->frame_class) {
+ case OSMO_AMRT_FC_NO_DATA:
+ case OSMO_AMRT_FC_ONSET:
+ /* no need to save s-bits */
+ break;
+ case OSMO_AMRT_FC_SID_FIRST:
+ case OSMO_AMRT_FC_SID_UPDATE:
+ case OSMO_AMRT_FC_SID_BAD:
+ /* For all SID variants, we need to extract s-bits,
+ * to be passed into RTP - even for Sid_First, where
+ * these bits are expected to be all ones. */
+ memcpy(fr->s_bits, tf->d_bits + 57, 35);
+ break;
+ default:
+ /* invalid No_Speech_Classification code */
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ }
+
+ /* We are past all potential error cases: finish the work. */
+ fr->cmi = get_bits(tf->d_bits, 34, 3);
+ fr->cmr = get_bits(tf->d_bits, 37, 3);
+ fr->cmi_valid = true;
+ fr->cmr_valid = true;
+
+ state->cmi = fr->cmi;
+ state->cmr = fr->cmr;
+ state->cmi_valid = true;
+ state->cmr_valid = true;
+
+ return OSMO_AMRT_FRAME_DEC_GOOD;
+}
+
+static enum osmo_amrt_decode_result
+decode_16k_speech_per_mode(struct osmo_amrt_if *fr,
+ const struct osmo_trau_frame *tf,
+ bool *bad_crc_234, bool *bad_mr102_crc4)
+{
+ ubit_t crc_collect[82]; /* largest in mode 6 */
+ int crc_stat;
+
+ /* For each mode we follow the same logic:
+ *
+ * - Check CRC1 first: if it is bad, we cannot trust control bits
+ * and the frame is totally bad.
+ *
+ * - Accept and extract s-bits from the frame.
+ *
+ * - Check the other 3 subframe CRCs. If any of them is bad,
+ * we change frame classification to Speech_Bad, but we don't
+ * have to toss the entire frame.
+ *
+ * - For MR102 only, if CRC4 is bad, we have to note this detail
+ * for potential impact on TFO config parameters.
+ */
+ switch (fr->cmi) {
+ case AMR_4_75:
+ /* check CRC1 */
+ memcpy(crc_collect, tf->c_bits, 25);
+ memcpy(crc_collect + 25, tf->d_bits + 44, 16);
+ memcpy(crc_collect + 41, tf->d_bits + 61, 2);
+ memcpy(crc_collect + 43, tf->d_bits + 64, 9);
+ memcpy(crc_collect + 52, tf->d_bits + 88, 4);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 56,
+ tf->d_bits + 92);
+ if (crc_stat)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ /* copy out s-bits */
+ memcpy(fr->s_bits, tf->d_bits + 44, 48);
+ memcpy(fr->s_bits + 48, tf->d_bits + 95, 13);
+ memcpy(fr->s_bits + 61, tf->d_bits + 111, 21);
+ memcpy(fr->s_bits + 82, tf->d_bits + 135, 13);
+ /* check CRC2 */
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ tf->d_bits + 95, 2,
+ tf->d_bits + 108);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC3 */
+ memcpy(crc_collect, tf->d_bits + 111, 2);
+ memcpy(crc_collect + 2, tf->d_bits + 128, 4);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 6,
+ tf->d_bits + 132);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC4 */
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ tf->d_bits + 135, 2,
+ tf->d_bits + 148);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ break;
+ case AMR_5_15:
+ /* check CRC1 */
+ memcpy(crc_collect, tf->c_bits, 25);
+ memcpy(crc_collect + 25, tf->d_bits + 46, 16);
+ memcpy(crc_collect + 41, tf->d_bits + 64, 11);
+ memcpy(crc_collect + 52, tf->d_bits + 87, 5);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 57,
+ tf->d_bits + 92);
+ if (crc_stat)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ /* copy out s-bits */
+ memcpy(fr->s_bits, tf->d_bits + 46, 46);
+ memcpy(fr->s_bits + 46, tf->d_bits + 95, 19);
+ memcpy(fr->s_bits + 65, tf->d_bits + 117, 19);
+ memcpy(fr->s_bits + 84, tf->d_bits + 139, 19);
+ /* check CRC2 */
+ memcpy(crc_collect, tf->d_bits + 95, 2);
+ memcpy(crc_collect + 2, tf->d_bits + 109, 5);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 7,
+ tf->d_bits + 114);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC3 */
+ memcpy(crc_collect, tf->d_bits + 117, 2);
+ memcpy(crc_collect + 2, tf->d_bits + 131, 5);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 7,
+ tf->d_bits + 136);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC4 */
+ memcpy(crc_collect, tf->d_bits + 139, 2);
+ memcpy(crc_collect + 2, tf->d_bits + 153, 5);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 7,
+ tf->d_bits + 158);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ break;
+ case AMR_5_90:
+ /* check CRC1 */
+ memcpy(crc_collect, tf->c_bits, 25);
+ memcpy(crc_collect + 25, tf->d_bits + 41, 17);
+ memcpy(crc_collect + 42, tf->d_bits + 67, 8);
+ memcpy(crc_collect + 50, tf->d_bits + 88, 4);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 54,
+ tf->d_bits + 92);
+ if (crc_stat)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ /* copy out s-bits */
+ memcpy(fr->s_bits, tf->d_bits + 41, 51);
+ memcpy(fr->s_bits + 51, tf->d_bits + 95, 21);
+ memcpy(fr->s_bits + 72, tf->d_bits + 119, 25);
+ memcpy(fr->s_bits + 97, tf->d_bits + 147, 21);
+ /* check CRC2 */
+ memcpy(crc_collect, tf->d_bits + 95, 3);
+ memcpy(crc_collect + 3, tf->d_bits + 112, 4);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 7,
+ tf->d_bits + 116);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC3 */
+ memcpy(crc_collect, tf->d_bits + 119, 8);
+ memcpy(crc_collect + 8, tf->d_bits + 140, 4);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 12,
+ tf->d_bits + 144);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC4 */
+ memcpy(crc_collect, tf->d_bits + 147, 3);
+ memcpy(crc_collect + 3, tf->d_bits + 164, 4);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 7,
+ tf->d_bits + 168);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ break;
+ case AMR_6_70:
+ /* check CRC1 */
+ memcpy(crc_collect, tf->c_bits, 25);
+ memcpy(crc_collect + 25, tf->d_bits + 37, 17);
+ crc_collect[42] = tf->d_bits[56];
+ crc_collect[43] = tf->d_bits[60];
+ memcpy(crc_collect + 44, tf->d_bits + 63, 8);
+ memcpy(crc_collect + 52, tf->d_bits + 85, 5);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 57,
+ tf->d_bits + 92);
+ if (crc_stat)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ /* copy out s-bits */
+ memcpy(fr->s_bits, tf->d_bits + 37, 55);
+ memcpy(fr->s_bits + 55, tf->d_bits + 95, 25);
+ memcpy(fr->s_bits + 80, tf->d_bits + 123, 29);
+ memcpy(fr->s_bits + 109, tf->d_bits + 155, 25);
+ /* check CRC2 */
+ memcpy(crc_collect, tf->d_bits + 95, 4);
+ memcpy(crc_collect + 4, tf->d_bits + 113, 5);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 9,
+ tf->d_bits + 120);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC3 */
+ memcpy(crc_collect, tf->d_bits + 123, 8);
+ memcpy(crc_collect + 8, tf->d_bits + 145, 5);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 13,
+ tf->d_bits + 152);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC4 */
+ memcpy(crc_collect, tf->d_bits + 155, 4);
+ memcpy(crc_collect + 4, tf->d_bits + 173, 5);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 9,
+ tf->d_bits + 180);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ break;
+ case AMR_7_40:
+ /* check CRC1 */
+ memcpy(crc_collect, tf->c_bits, 25);
+ memcpy(crc_collect + 25, tf->d_bits + 34, 20);
+ memcpy(crc_collect + 45, tf->d_bits + 55, 3);
+ memcpy(crc_collect + 48, tf->d_bits + 60, 6);
+ memcpy(crc_collect + 54, tf->d_bits + 85, 2);
+ memcpy(crc_collect + 56, tf->d_bits + 88, 3);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 59,
+ tf->d_bits + 92);
+ if (crc_stat)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ /* copy out s-bits */
+ memcpy(fr->s_bits, tf->d_bits + 34, 58);
+ memcpy(fr->s_bits + 58, tf->d_bits + 95, 29);
+ memcpy(fr->s_bits + 87, tf->d_bits + 127, 32);
+ memcpy(fr->s_bits + 119, tf->d_bits + 162, 29);
+ /* check CRC2 */
+ memcpy(crc_collect, tf->d_bits + 95, 3);
+ memcpy(crc_collect + 3, tf->d_bits + 117, 2);
+ memcpy(crc_collect + 5, tf->d_bits + 120, 3);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 8,
+ tf->d_bits + 124);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC3 */
+ memcpy(crc_collect, tf->d_bits + 127, 6);
+ memcpy(crc_collect + 6, tf->d_bits + 152, 2);
+ memcpy(crc_collect + 8, tf->d_bits + 155, 3);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 11,
+ tf->d_bits + 159);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC4 */
+ memcpy(crc_collect, tf->d_bits + 162, 3);
+ memcpy(crc_collect + 3, tf->d_bits + 184, 2);
+ memcpy(crc_collect + 5, tf->d_bits + 187, 2);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 7,
+ tf->d_bits + 191);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ break;
+ case AMR_7_95:
+ /* check CRC1 */
+ memcpy(crc_collect, tf->c_bits, 25);
+ memcpy(crc_collect + 25, tf->d_bits + 31, 35);
+ memcpy(crc_collect + 60, tf->d_bits + 83, 2);
+ crc_collect[62] = tf->d_bits[87];
+ crc_collect[63] = tf->d_bits[90];
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 64,
+ tf->d_bits + 92);
+ if (crc_stat)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ /* copy out s-bits */
+ memcpy(fr->s_bits, tf->d_bits + 31, 61);
+ memcpy(fr->s_bits + 61, tf->d_bits + 95, 32);
+ memcpy(fr->s_bits + 93, tf->d_bits + 130, 34);
+ memcpy(fr->s_bits + 127, tf->d_bits + 167, 32);
+ /* check CRC2 */
+ memcpy(crc_collect, tf->d_bits + 95, 4);
+ memcpy(crc_collect + 4, tf->d_bits + 118, 2);
+ memcpy(crc_collect + 6, tf->d_bits + 122, 4);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 10,
+ tf->d_bits + 127);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC3 */
+ memcpy(crc_collect, tf->d_bits + 130, 8);
+ memcpy(crc_collect + 8, tf->d_bits + 155, 2);
+ memcpy(crc_collect + 10, tf->d_bits + 159, 4);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 14,
+ tf->d_bits + 164);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC4 */
+ memcpy(crc_collect, tf->d_bits + 167, 4);
+ memcpy(crc_collect + 4, tf->d_bits + 190, 2);
+ memcpy(crc_collect + 6, tf->d_bits + 194, 4);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 10,
+ tf->d_bits + 199);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ break;
+ case AMR_10_2:
+ /* check CRC1 */
+ memcpy(crc_collect, tf->c_bits, 25);
+ memcpy(crc_collect + 25, tf->d_bits, 45);
+ memcpy(crc_collect + 70, tf->d_bits + 46, 8);
+ memcpy(crc_collect + 78, tf->d_bits + 85, 2);
+ memcpy(crc_collect + 80, tf->d_bits + 88, 2);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 82,
+ tf->d_bits + 92);
+ if (crc_stat)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ /* copy out s-bits */
+ memcpy(fr->s_bits, tf->d_bits + 20, 72);
+ memcpy(fr->s_bits + 72, tf->d_bits + 95, 43);
+ memcpy(fr->s_bits + 115, tf->d_bits + 141, 46);
+ memcpy(fr->s_bits + 161, tf->d_bits + 190, 43);
+ /* check CRC2 */
+ memcpy(crc_collect, tf->d_bits + 95, 4);
+ memcpy(crc_collect + 4, tf->d_bits + 131, 2);
+ memcpy(crc_collect + 6, tf->d_bits + 134, 2);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 8,
+ tf->d_bits + 138);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC3 */
+ memcpy(crc_collect, tf->d_bits + 141, 8);
+ memcpy(crc_collect + 8, tf->d_bits + 180, 2);
+ memcpy(crc_collect + 10, tf->d_bits + 183, 2);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 12,
+ tf->d_bits + 187);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC4 */
+ memcpy(crc_collect, tf->d_bits + 190, 4);
+ memcpy(crc_collect + 4, tf->d_bits + 226, 2);
+ memcpy(crc_collect + 6, tf->d_bits + 229, 2);
+ memcpy(crc_collect + 8, tf->d_bits + 233, 20);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 28,
+ tf->d_bits + 253);
+ if (crc_stat) {
+ *bad_crc_234 = true;
+ *bad_mr102_crc4 = true;
+ }
+ break;
+ case AMR_12_2:
+ /* check CRC1 */
+ memcpy(crc_collect, tf->c_bits, 25);
+ memcpy(crc_collect + 25, tf->d_bits, 29);
+ memcpy(crc_collect + 54, tf->d_bits + 38, 12);
+ memcpy(crc_collect + 66, tf->d_bits + 86, 3);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 69,
+ tf->d_bits + 91);
+ if (crc_stat)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ /* copy out s-bits */
+ memcpy(fr->s_bits, tf->d_bits, 91);
+ memcpy(fr->s_bits + 91, tf->d_bits + 94, 50);
+ memcpy(fr->s_bits + 141, tf->d_bits + 147, 53);
+ memcpy(fr->s_bits + 194, tf->d_bits + 203, 50);
+ /* check CRC2 */
+ memcpy(crc_collect, tf->d_bits + 94, 9);
+ memcpy(crc_collect + 9, tf->d_bits + 139, 3);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 12,
+ tf->d_bits + 144);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC3 */
+ memcpy(crc_collect, tf->d_bits + 147, 12);
+ memcpy(crc_collect + 12, tf->d_bits + 195, 3);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 15,
+ tf->d_bits + 200);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ /* check CRC4 */
+ memcpy(crc_collect, tf->d_bits + 203, 5);
+ memcpy(crc_collect + 5, tf->d_bits + 209, 3);
+ memcpy(crc_collect + 8, tf->d_bits + 248, 3);
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3,
+ crc_collect, 11,
+ tf->d_bits + 253);
+ if (crc_stat)
+ *bad_crc_234 = true;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ fr->cmi_valid = true;
+ return OSMO_AMRT_FRAME_DEC_GOOD;
+}
+
+static enum osmo_amrt_decode_result
+decode_16k_speech(struct osmo_amrt_decode_state *state, struct osmo_amrt_if *fr,
+ const struct osmo_trau_frame *tf, bool *bad_crc_234,
+ bool *bad_mr102_crc4)
+{
+ enum osmo_amrt_decode_result rc;
+
+ /* The bulk of decoding and CRC verification depends on RIF */
+ if (fr->rif) {
+ /* CMR frame - saved CMI is required */
+ if (!state->cmi_valid)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ fr->cmi = state->cmi;
+ rc = decode_16k_speech_per_mode(fr, tf, bad_crc_234,
+ bad_mr102_crc4);
+ if (rc == OSMO_AMRT_FRAME_DEC_BAD)
+ return rc;
+ /* output and state change specific to CMR frame */
+ fr->cmr = get_bits(tf->c_bits, 22, 3);
+ fr->cmr_valid = true;
+ state->cmr = fr->cmr;
+ state->cmr_valid = true;
+ state->cmi_valid = false;
+ } else {
+ /* CMI frame */
+ fr->cmi = get_bits(tf->c_bits, 22, 3);
+ rc = decode_16k_speech_per_mode(fr, tf, bad_crc_234,
+ bad_mr102_crc4);
+ if (rc == OSMO_AMRT_FRAME_DEC_BAD)
+ return rc;
+ /* output and state change specific to CMI frame */
+ if (state->cmr_valid) {
+ fr->cmr = state->cmr;
+ fr->cmr_valid = true;
+ } else {
+ fr->cmr_valid = false;
+ }
+ state->cmi = fr->cmi;
+ state->cmi_valid = true;
+ state->cmr_valid = false;
+ }
+
+ if (*bad_crc_234)
+ fr->frame_class = OSMO_AMRT_FC_SPEECH_BAD;
+ else
+ fr->frame_class = get_bits(tf->c_bits, 20, 2) << 3;
+ return OSMO_AMRT_FRAME_DEC_GOOD;
+}
+
+void decode_16k_tfo_mr102(struct osmo_amrt_if *fr,
+ const struct osmo_trau_frame *tf)
+{
+ /* CRC has already been checked as part of speech frame decoding;
+ * now we just need to extract the parameters. However, only a
+ * reduced set of params is transmitted in this mode; for the
+ * missing ones, we fill in fixed values as prescribed in TS 28.062
+ * Table C.6.1.5-1. */
+ memcpy(fr->tfo_param_bits, tf->d_bits, 20);
+ memset(fr->tfo_param_bits + 20, 0, 5);
+ memcpy(fr->tfo_param_bits + 25, tf->d_bits + 233, 20);
+ memcpy(fr->tfo_param_bits + 45, tfo_blockC_fixed_params, 26);
+ fr->tfo_param_bits[71] = 0;
+ fr->param_info = OSMO_AMRT_TFOP_PRESENT_VALID;
+}
+
+void decode_16k_tfo(struct osmo_amrt_if *fr, const struct osmo_trau_frame *tf,
+ bool bad_mr102_crc4)
+{
+ int crc_stat;
+
+ fr->config_prot = get_bits(tf->c_bits, 13, 3);
+ if (fr->config_prot == OSMO_AMRT_CFG_PROT_NO_CON)
+ return;
+ fr->message_no = get_bits(tf->c_bits, 16, 2);
+
+ /* Is it a speech frame? To understand the logic that follows,
+ * please refer to TS 28.062 Table C.6.1.5-1. */
+ if (fr->frame_class >= OSMO_AMRT_FC_SPEECH_BAD) {
+ switch (fr->cmi) {
+ case AMR_12_2:
+ fr->param_info = OSMO_AMRT_TFOP_ABSENT_NO_ROOM;
+ return;
+ case AMR_10_2:
+ if (bad_mr102_crc4) {
+ fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC;
+ return;
+ }
+ decode_16k_tfo_mr102(fr, tf);
+ return;
+ default:
+ break;
+ }
+ }
+
+ /* We have to check all 3 TFO-specific CRCs */
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, tf->d_bits, 28,
+ tf->d_bits + 28);
+ if (crc_stat) {
+ fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC;
+ return;
+ }
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, tf->d_bits + 233,
+ 20, tf->d_bits + 253);
+ if (crc_stat) {
+ fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC;
+ return;
+ }
+ crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, tf->d_bits + 202,
+ 28, tf->d_bits + 230);
+ if (crc_stat) {
+ fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC;
+ return;
+ }
+
+ /* CRCs are good - extract the parameters */
+ memcpy(fr->tfo_param_bits, tf->d_bits, 25);
+ memcpy(fr->tfo_param_bits + 25, tf->d_bits + 233, 20);
+ memcpy(fr->tfo_param_bits + 45, tf->d_bits + 202, 26);
+ fr->tfo_param_bits[71] = 0;
+ fr->param_info = OSMO_AMRT_TFOP_PRESENT_VALID;
+}
+
+static enum osmo_amrt_decode_result
+decode_16k(struct osmo_amrt_decode_state *state, struct osmo_amrt_if *fr,
+ const struct osmo_trau_frame *tf, bool allow_config_prot)
+{
+ enum osmo_amrt_decode_result rc;
+ bool bad_crc_234 = false, bad_mr102_crc4 = false;
+
+ /* Unless we have to kill it later because of CRC error, RIF is always
+ * valid and readily extracted. */
+ fr->rif = tf->c_bits[11];
+ fr->rif_valid = true;
+
+ /* speech vs No_Speech frames require different approach to decoding */
+ if (tf->c_bits[20] || tf->c_bits[21])
+ rc = decode_16k_speech(state, fr, tf, &bad_crc_234,
+ &bad_mr102_crc4);
+ else
+ rc = decode_16k_no_speech(state, fr, tf);
+ if (rc == OSMO_AMRT_FRAME_DEC_BAD) {
+ decode_handle_error(state, fr);
+ return rc;
+ }
+
+ /* CRC1 has been verified as part of decoding, and we know that all
+ * control bits are valid. */
+ fr->ta = get_bits(tf->c_bits, 5, 6);
+ fr->dtxd = tf->c_bits[18];
+ fr->tfoe = tf->c_bits[19];
+ fr->ta_dtxd_tfoe_valid = true;
+
+ if (allow_config_prot)
+ decode_16k_tfo(fr, tf, bad_mr102_crc4);
+ else
+ fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON;
+
+ if (bad_crc_234)
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ if (!tf->c_bits[12])
+ return OSMO_AMRT_FRAME_DEC_REMOTE_BAD;
+ return OSMO_AMRT_FRAME_DEC_GOOD;
+}
+
+static enum osmo_amrt_decode_result
+decode_8k_low(struct osmo_amrt_decode_state *state, struct osmo_amrt_if *fr,
+ const struct osmo_trau_frame *tf, bool allow_config_prot)
+{
+ /* to be implemented */
+ decode_handle_error(state, fr);
+ return OSMO_AMRT_FRAME_DEC_BAD;
+}
+
+static enum osmo_amrt_decode_result
+decode_8k_6k7(struct osmo_amrt_decode_state *state, struct osmo_amrt_if *fr,
+ const struct osmo_trau_frame *tf)
+{
+ /* to be implemented */
+ decode_handle_error(state, fr);
+ return OSMO_AMRT_FRAME_DEC_BAD;
+}
+
+static enum osmo_amrt_decode_result
+decode_8k_7k4(struct osmo_amrt_decode_state *state, struct osmo_amrt_if *fr,
+ const struct osmo_trau_frame *tf)
+{
+ /* to be implemented */
+ decode_handle_error(state, fr);
+ return OSMO_AMRT_FRAME_DEC_BAD;
+}
+
+/*! High-level decode a received AMR TRAU frame
+ *
+ * \param[inout] state Decoding state maintained from one frame to the next.
+ * \param[out] fr Intermediate representation of the decoded frame, primary
+ * output from this function.
+ * \param[in] tf Received TRAU frame after low-level decoding by
+ * osmo_trau_frame_decode_{8,16}k() functions.
+ * \param[in] allow_config_prot Should we accept Rel4 style of TFO config
+ * protocol as part of the decoded frame?
+ *
+ * Additional notes regarding \ref allow_config_prot flag: at first glance,
+ * it makes the most sense for this function to always decode whatever we
+ * received in TRAU frame C-bits and D-bits, put the distilled result into
+ * struct osmo_amrt_if, and let the application apply logic that suppresses
+ * Config_Prot if necessary. However, there exist some E1 BTS that implement
+ * only Rel5 version of TFO config protocol, not Rel4, and to make it worse
+ * (I am pointing at you, Nokia), they put total garbage into bit positions
+ * where Rel4 TFO config protocol would go. Therefore, if an E1 Abis MGW
+ * is configured to speak Rel5 version of TFO config protocol, or none at all,
+ * it makes sense to skip CPU cycles that would be spent decoding garbage
+ * in Rel4 TFO config bits.
+ */
+enum osmo_amrt_decode_result
+osmo_amrt_decode_trau_frame(struct osmo_amrt_decode_state *state,
+ struct osmo_amrt_if *fr,
+ const struct osmo_trau_frame *tf,
+ bool allow_config_prot)
+{
+ switch (tf->type) {
+ case OSMO_TRAU16_FT_AMR:
+ return decode_16k(state, fr, tf, allow_config_prot);
+ case OSMO_TRAU8_AMR_LOW:
+ return decode_8k_low(state, fr, tf, allow_config_prot);
+ case OSMO_TRAU8_AMR_6k7:
+ return decode_8k_6k7(state, fr, tf);
+ case OSMO_TRAU8_AMR_7k4:
+ return decode_8k_7k4(state, fr, tf);
+ default:
+ decode_handle_error(state, fr);
+ return OSMO_AMRT_FRAME_DEC_BAD;
+ }
+}
+
+/*! @} */
To view, visit change 41252. To unsubscribe, or for help writing mail filters, visit settings.