laforge has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-abis/+/41210?usp=email )
Change subject: trau_frame: add support for config frames of 3GPP Rel5+ ......................................................................
trau_frame: add support for config frames of 3GPP Rel5+
3GPP Rel5 introduced Generic Config Frame format for TFO, defined in TS 28.062 Annex H (globally interoperable interface), and also defined equivalent Speech Codec Configuration Exchange frames on Abis (treated mostly as vendor-private interface by this point), added to TS 48.060 and 48.061. These Generic Config Frames are mandatory for AMR-WB codec, which not only requires TFO in order to be useful (in the absence of all-IP TrFO), but also lacks the ability to stuff TFO config parameters into regular speech frames in the way that was specified in 3GPP Rel4 for AMR-NB.
Support for these GCFs on Abis is necessary in order to support TFO-AMR with some vendors' E1 BTS. Some vendors of TDM-based GSM BSS felt no interest in TFO-AMR for its own sake, and only implemented TFO support for AMR (both NB and WB) when forced to do so by AMR-WB market demand; these vendors (Nokia is one known case) skipped 3GPP Rel4 version of TFO-AMR and went directly for Rel5 version, using Generic Config Frames on Abis for both AMR-NB and AMR-WB.
Change-Id: Ifbde07e4a3fb80e4faa0b6a6b32938ed98371c5b --- M include/osmocom/trau/trau_frame.h M src/trau/trau_frame.c 2 files changed, 178 insertions(+), 14 deletions(-)
Approvals: keith: Looks good to me, but someone else must approve fixeria: Looks good to me, approved Jenkins Builder: Verified
diff --git a/include/osmocom/trau/trau_frame.h b/include/osmocom/trau/trau_frame.h index dd14f27..9048dca 100644 --- a/include/osmocom/trau/trau_frame.h +++ b/include/osmocom/trau/trau_frame.h @@ -62,6 +62,7 @@ #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 */ +#define TRAU_FT_CONFIG_EXCH 0x1e /* 1 1 1 1 0 - TS 48.060 5.5.1.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 */ @@ -94,6 +95,10 @@ OSMO_TRAU8_AMR_LOW, OSMO_TRAU8_AMR_6k7, OSMO_TRAU8_AMR_7k4, + + /* new types at the end, for ABI compatibility */ + OSMO_TRAU16_FT_CONFIG, + OSMO_TRAU8_CONFIG, };
extern const struct value_string osmo_trau_frame_type_names[]; diff --git a/src/trau/trau_frame.c b/src/trau/trau_frame.c index d0cf3d1..896bc20 100644 --- a/src/trau/trau_frame.c +++ b/src/trau/trau_frame.c @@ -61,12 +61,14 @@ { OSMO_TRAU16_FT_D145_SYNC, "D145_SYNC" }, { OSMO_TRAU16_FT_DATA_HR, "DATA_HR" }, { OSMO_TRAU16_FT_IDLE, "IDLE" }, + { OSMO_TRAU16_FT_CONFIG, "CONFIG" }, { 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" }, + { OSMO_TRAU8_CONFIG, "8CONFIG" }, { 0, NULL } };
@@ -763,6 +765,57 @@ return 40 * 8; }
+/* TS 48.060 sections 5.1.5 and 5.5.1.5: generic configuration frames */ + +static int decode16_gcf(struct osmo_trau_frame *fr, const ubit_t *trau_bits, + enum osmo_trau_frame_direction dir) +{ + int i, d_idx; + + /* C1 .. C5 */ + memcpy(fr->c_bits, trau_bits + 17, 5); + + /* D1 .. D10 */ + memcpy(fr->d_bits, trau_bits + 22, 10); + d_idx = 10; + /* D11 .. D265 */ + for (i = 32; i < 304; i += 16) { + memcpy(fr->d_bits + d_idx, trau_bits + i + 1, 15); + d_idx += 15; + } + /* D266 .. D276 */ + memcpy(fr->d_bits + d_idx, trau_bits + 305, 11); + + /* T1 .. T4 */ + memcpy(fr->t_bits, trau_bits + 316, 4); + + return 0; +} + +static int encode16_gcf(ubit_t *trau_bits, const struct osmo_trau_frame *fr) +{ + int i, d_idx; + + encode_sync16(trau_bits); + + /* C1 .. C5 */ + memcpy(trau_bits + 17, fr->c_bits, 5); + /* D1 .. D10 */ + memcpy(trau_bits + 22, fr->d_bits, 10); + + /* D11 .. D276 */ + for (i = 32, d_idx = 10; 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, 4); + + /* handle timing adjustment */ + return encode16_handle_ta(trau_bits, fr); +} + /* 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) @@ -1229,6 +1282,70 @@ return 20 * 8; }
+/* TS 48.061 section 5.2.1.3: generic configuration frames */ + +static int decode8_gcf(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); + d_idx += 6; + /* D9 .. D120 */ + for (i = 3; i < 19; i++) { + memcpy(fr->d_bits + d_idx, trau_bits + i * 8 + 1, 7); + d_idx += 7; + } + /* D121 .. D125 */ + memcpy(fr->d_bits + d_idx, trau_bits + 19 * 8 + 1, 5); + + /* T1 .. T2 */ + memcpy(fr->t_bits, trau_bits + 19 * 8 + 6, 2); + + return 0; +} + +static int encode8_gcf(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); + /* 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); + d_idx += 6; + /* D9 .. D120 */ + for (i = 3; i < 19; i++) { + memcpy(trau_bits + i * 8 + 1, fr->d_bits + d_idx, 7); + d_idx += 7; + } + /* D121 .. D125 */ + memcpy(trau_bits + 19 * 8 + 1, fr->d_bits + d_idx, 5); + + /* T1 .. T2 */ + memcpy(trau_bits + 19 * 8 + 6, fr->t_bits, 2); + + /* handle timing adjustment */ + return encode8_handle_ta(trau_bits, fr); +} +
/*! Encode a TRAU frame from its decoded representation to a sequence of unpacked bits. * \param[out] bits caller-allocated buffer for unpacked output bits @@ -1250,17 +1367,18 @@ * The following list summarizes the behavior of the present function with * regard to C1..C5 bits for different frame types: * - * - For all TRAU-16k frame types in both UL and DL directions, and for - * OSMO_TRAU8_SPEECH (TRAU-8k for HRv1 speech) in UL direction, the first 5 - * bits of fr->c_bits[] are ignored and replaced with internally supplied - * constant values. + * - For all TRAU-16k frame types in both UL and DL directions, with the + * exception of newly added OSMO_TRAU16_FT_CONFIG, and for OSMO_TRAU8_SPEECH + * (TRAU-8k for HRv1 speech) in UL direction, the first 5 bits of fr->c_bits[] + * are ignored and replaced with internally supplied constant values. * * - For OSMO_TRAU8_SPEECH in DL direction, only fr->c_bits[3] is used to set * C4; constant values for C1..C3 and odd parity value for C5 are fixed * by the function. * - * - For OSMO_TRAU8_DATA, OSMO_TRAU8_OAM and all AMR-8k frame types, - * user-supplied fr->c_bits[] are always used. + * - For OSMO_TRAU8_DATA, OSMO_TRAU8_OAM and all AMR-8k frame types, as well as + * newly added OSMO_TRAU16_FT_CONFIG and OSMO_TRAU8_CONFIG, user-supplied + * fr->c_bits[] are always used. * * This unfortunate manipulation applies only to C1..C5 as listed above; * for control bits C6 and higher (for frame types that have them), @@ -1277,6 +1395,7 @@ case OSMO_TRAU16_FT_HR: case OSMO_TRAU16_FT_AMR: case OSMO_TRAU16_FT_IDLE: + case OSMO_TRAU16_FT_CONFIG: /* timing alignment may happen: increased space requirement */ if (fr->dir == OSMO_TRAU_DIR_DL && fr->dl_ta_usec > 0) space_req = 2 * 40 * 8 - 1; @@ -1294,6 +1413,7 @@ case OSMO_TRAU8_AMR_LOW: case OSMO_TRAU8_AMR_6k7: case OSMO_TRAU8_AMR_7k4: + case OSMO_TRAU8_CONFIG: /* timing alignment may happen: increased space requirement */ if (fr->dir == OSMO_TRAU_DIR_DL && fr->dl_ta_usec > 0) space_req = 2 * 20 * 8 - 1; @@ -1330,6 +1450,8 @@ return encode16_d145_sync(bits, fr); case OSMO_TRAU16_FT_EDATA: return encode16_edata(bits, fr); + case OSMO_TRAU16_FT_CONFIG: + return encode16_gcf(bits, fr); case OSMO_TRAU8_SPEECH: return encode8_hr(bits, fr, false); case OSMO_TRAU8_DATA: @@ -1342,6 +1464,8 @@ return encode8_amr_67(bits, fr); case OSMO_TRAU8_AMR_7k4: return encode8_amr_74(bits, fr); + case OSMO_TRAU8_CONFIG: + return encode8_gcf(bits, fr); default: return -EINVAL; } @@ -1371,6 +1495,7 @@ case OSMO_TRAU16_FT_FR: case OSMO_TRAU16_FT_EFR: case OSMO_TRAU16_FT_AMR: + case OSMO_TRAU16_FT_CONFIG: if (n_bits < 1 * 40 * 8) return -ENOSPC; break; @@ -1378,6 +1503,7 @@ case OSMO_TRAU8_AMR_LOW: case OSMO_TRAU8_AMR_6k7: case OSMO_TRAU8_AMR_7k4: + case OSMO_TRAU8_CONFIG: if (n_bits < 1 * 20 * 8) return -ENOSPC; break; @@ -1391,6 +1517,8 @@ return encode16_fr(bits, fr, true); case OSMO_TRAU16_FT_AMR: return encode16_amr(bits, fr, true); + case OSMO_TRAU16_FT_CONFIG: + return encode16_gcf(bits, fr); case OSMO_TRAU8_SPEECH: return encode8_hr(bits, fr, true); case OSMO_TRAU8_AMR_LOW: @@ -1399,6 +1527,8 @@ return encode8_amr_67(bits, fr); case OSMO_TRAU8_AMR_7k4: return encode8_amr_74(bits, fr); + case OSMO_TRAU8_CONFIG: + return encode8_gcf(bits, fr); default: return -EINVAL; } @@ -1455,6 +1585,9 @@ case TRAU_FT_D145_SYNC: fr->type = OSMO_TRAU16_FT_D145_SYNC; return decode16_data(fr, bits, dir); + case TRAU_FT_CONFIG_EXCH: + fr->type = OSMO_TRAU16_FT_CONFIG; + return decode16_gcf(fr, bits, dir);
default: return -EINVAL; @@ -1499,6 +1632,9 @@ case 0xB: /* OHR_AMR */ fr->type = OSMO_TRAU16_FT_AMR; return decode16_amr(fr, bits, fr->dir); + case TRAU_FT_CONFIG_EXCH >> 1: + fr->type = OSMO_TRAU16_FT_CONFIG; + return decode16_gcf(fr, bits, fr->dir); default: return -EINVAL; } @@ -1617,6 +1753,9 @@ case 0x0B: fr->type = OSMO_TRAU8_OAM; return decode8_oam(fr, bits, dir); + case 0x1F: + fr->type = OSMO_TRAU8_CONFIG; + return decode8_gcf(fr, bits, dir); } } else { /* Downlink */ @@ -1630,6 +1769,9 @@ case 2: fr->type = OSMO_TRAU8_OAM; return decode8_oam(fr, bits, dir); + case 7: + fr->type = OSMO_TRAU8_CONFIG; + return decode8_gcf(fr, bits, dir); } } } else if (is_amr_low(bits)) { @@ -1665,18 +1807,31 @@ * no need to distinguish between HRv1 and AMR-8k frames by their respective * sync patterns: per TS 28.062, these two TFO frame types never occur * in the same bit position in TFO-superimposed PCM speech octets. - * Because HRv1 is the only TFO frame type that can occur in the configuration - * of TS 28.062 section 5.3 (only the one lsb in each PCM speech octet is - * overwritten), this function skips all attempts at frame type classification: - * the TFO application is responsible for detecting the presence of valid - * TFO frames for the negotiated codec type. + * + * Frame type bits C1..C4 are checked only to distinguish between regular + * HRv1 speech frames and Generic Configuration Frames of TS 28.062 Annex H: + * Rel5+ versions of TFO spec allow these GCFs to be used with all codecs + * if both ends of TFO connection implement Rel5+ version of TFO. */ int osmo_trau_frame_decode_tfo_hr1(struct osmo_trau_frame *fr, const ubit_t *bits) { - fr->type = OSMO_TRAU8_SPEECH; + uint8_t cbits4 = get_bits(bits, 9, 4); + /* We don't look at bit C5 because it is EMBED bit in TFO frames */ + + fr->type = OSMO_TRAU_FT_NONE; fr->dir = OSMO_TRAU_DIR_UL; /* TFO frames are modified TRAU-UL */ fr->dl_ta_usec = 0; - return decode8_hr(fr, bits, fr->dir); + + switch (cbits4) { + case 0x01: + fr->type = OSMO_TRAU8_SPEECH; + return decode8_hr(fr, bits, fr->dir); + case 0x0F: + fr->type = OSMO_TRAU8_CONFIG; + return decode8_gcf(fr, bits, fr->dir); + default: + return -EINVAL; + } }
/*! Decode/parse an AMR_TFO_8+8k frame from unpacked bits to decoded format. @@ -1690,7 +1845,8 @@ * The second lsb extracted from PCM samples is the entity being decoded here. * Compared to osmo_trau_frame_decode_8k(), the present function requires * the direction to be TRAU-UL (as always the case in TFO) and accepts - * only AMR-8k frame types. + * only AMR-8k frame types, or Generic Configuration Frames of TS 28.062 + * Annex H that are used in Rel5+ version of TFO. */ int osmo_trau_frame_decode_tfo_amr_8k(struct osmo_trau_frame *fr, const ubit_t *bits) { @@ -1707,6 +1863,9 @@ } else if (is_amr_74(bits)) { fr->type = OSMO_TRAU8_AMR_7k4; return decode8_amr_74(fr, bits, fr->dir); + } else if (is_hr(bits) && get_bits(bits, 9, 4) == 0x0F) { + fr->type = OSMO_TRAU8_CONFIG; + return decode8_gcf(fr, bits, fr->dir); }
return -EINVAL;