falconia has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-bts/+/38554?usp=email )
Change subject: csd_v110: set E2 bit correctly for TCH/[FH]4.8 NT
......................................................................
csd_v110: set E2 bit correctly for TCH/[FH]4.8 NT
In all classic NT modes below 14.5 kbit/s, V.110 frame bits E2 and E3
are repurposed to indicate the alignment whereby a single 240-bit RLP
frame is composed of 4 pseudo-V.110 frames in switching transport.
(TS 48.020 section 15.1.) In the case of TCH/F9.6, setting both E2
and E3 is easy because all 4 pseudo-V.110 frames go out in the same
clearmode RTP packet as a result of a single channel decoding
operation. However, in the case of TCH/F4.8 there are two separate
channel decoding operations producing two separate RTP packets
20 ms apart. Furthermore, GSM 05.03 section 3.4.1 specifies which
TDMA frame positions hold which half of the RLP frame - therefore,
the correct emission of bit E2 needs to be based on the TDMA frame
number at which the block was received.
A similar situation occurs with TCH/H4.8 NT: we receive a single
block from the 05.03 decoder every 40 ms, containing a full RLP frame,
but we emit it as two separate RTP packets, each carrying 20 ms worth
of bits. These RTP packets also require correct setting of E2 bit.
Related: OS#6579
Change-Id: I485af5e01ea87c1721a298a486cd344a17884200
---
M include/osmo-bts/csd_v110.h
M src/common/csd_v110.c
M src/common/l1sap.c
M tests/csd/csd_test.c
4 files changed, 30 insertions(+), 10 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/54/38554/1
diff --git a/include/osmo-bts/csd_v110.h b/include/osmo-bts/csd_v110.h
index d589551..0fcb852 100644
--- a/include/osmo-bts/csd_v110.h
+++ b/include/osmo-bts/csd_v110.h
@@ -14,6 +14,7 @@
extern const struct csd_v110_lchan_desc csd_v110_lchan_desc[256];
int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
- const uint8_t *data, size_t data_len);
+ const uint8_t *data, size_t data_len,
+ uint8_t nt48_half_num);
int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
const uint8_t *rtp, size_t rtp_len);
diff --git a/src/common/csd_v110.c b/src/common/csd_v110.c
index 7254d2d..cbbb83b 100644
--- a/src/common/csd_v110.c
+++ b/src/common/csd_v110.c
@@ -78,7 +78,8 @@
};
int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
- const uint8_t *data, size_t data_len)
+ const uint8_t *data, size_t data_len,
+ uint8_t nt48_half_num)
{
const struct csd_v110_lchan_desc *desc;
ubit_t ra_bits[80 * 4];
@@ -110,9 +111,15 @@
/* E1 .. E3 must set by out-of-band knowledge */
if (lchan->csd_mode == LCHAN_CSD_M_NT) {
/* non-transparent: as per 3GPP TS 48.020, Table 7 */
- df.e_bits[0] = 0; /* E1: as per 15.1.2, shall be set to 0 (for BSS-MSC) */
- df.e_bits[1] = (i >> 1) & 0x01; /* E2: 0 for Q1/Q2, 1 for Q3/Q4 */
- df.e_bits[2] = (i >> 0) & 0x01; /* E3: 0 for Q1/Q3, 1 for Q2/Q4 */
+ /* E1: as per 15.1.2, shall be set to 0 (for BSS-MSC) */
+ df.e_bits[0] = 0;
+ /* E2: 0 for Q1/Q2, 1 for Q3/Q4 */
+ if (desc->num_blocks == 4)
+ df.e_bits[1] = (i >> 1) & 0x01;
+ else
+ df.e_bits[1] = nt48_half_num;
+ /* E3: 0 for Q1/Q3, 1 for Q2/Q4 */
+ df.e_bits[2] = (i >> 0) & 0x01;
} else {
/* transparent: as per 3GPP TS 44.021, Figure 4 */
df.e_bits[0] = e1e2e3_map[lchan->csd_mode][0]; /* E1 */
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index e579356..8bcb241 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -2060,15 +2060,27 @@
lchan->rtp_tx_marker = false;
}
+/* In the case of TCH/F4.8 NT, we have to set bit E2 based on the TDMA
+ * frame number at which we received the block in question. See
+ * GSM 05.03 section 3.4.1 and the mapping tables of GSM 05.02. */
+static const uint8_t tchf48_nt_e2_map[26] = {
+ [4] = 1, /* B1 position */
+ [13] = 1, /* B3 position */
+ [21] = 1, /* B5 position */
+};
+
static void handle_tch_ind_csd_fr(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
const uint8_t *data, uint16_t data_len)
{
uint8_t rtp_pl[RFC4040_RTP_PLEN];
+ uint8_t tchf48_half = tchf48_nt_e2_map[tch_ind->fn % 26];
int rc;
gsmtap_csd_rlp_process(lchan, true, tch_ind, data, data_len);
- rc = csd_v110_rtp_encode(lchan, rtp_pl, data, data_len);
+ /* the last argument matters only for TCH/F4.8 NT mode,
+ * ignored in all other cases. */
+ rc = csd_v110_rtp_encode(lchan, rtp_pl, data, data_len, tchf48_half);
if (rc < 0)
return;
@@ -2080,7 +2092,7 @@
uint8_t rtp_pl[RFC4040_RTP_PLEN];
int rc, i;
- rc = csd_v110_rtp_encode(lchan, rtp_pl, NULL, 0);
+ rc = csd_v110_rtp_encode(lchan, rtp_pl, NULL, 0, 0);
if (rc < 0)
return;
@@ -2115,7 +2127,7 @@
for (i = 0; i < 2; i++) {
rc = csd_v110_rtp_encode(lchan, rtp_pl,
data + i * bits_per_20ms,
- bits_per_20ms);
+ bits_per_20ms, i);
if (rc < 0)
return;
send_ul_rtp_packet_hrdata(lchan, rtp_pl, sizeof(rtp_pl));
diff --git a/tests/csd/csd_test.c b/tests/csd/csd_test.c
index b6edec9..15ee84f 100644
--- a/tests/csd/csd_test.c
+++ b/tests/csd/csd_test.c
@@ -124,7 +124,7 @@
data_enc[i] = i & 0x01;
/* encode an RTP frame and print it */
- rc = csd_v110_rtp_encode(&lchan, &rtp[0], &data_enc[0], bit_num);
+ rc = csd_v110_rtp_encode(&lchan, &rtp[0], &data_enc[0], bit_num, 0);
fprintf(stderr, "[i] csd_v110_rtp_encode() returns %d\n", rc);
if (rc != RFC4040_RTP_PLEN)
return;
@@ -148,7 +148,7 @@
fprintf(stderr, "[i] Testing '%s' (IDLE)\n", tc->name);
/* encode an idle RTP frame and print it */
- rc = csd_v110_rtp_encode(&lchan, &rtp[0], &data_enc[0], 0);
+ rc = csd_v110_rtp_encode(&lchan, &rtp[0], &data_enc[0], 0, 0);
fprintf(stderr, "[i] csd_v110_rtp_encode() returns %d\n", rc);
if (rc != RFC4040_RTP_PLEN)
return;
--
To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/38554?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: I485af5e01ea87c1721a298a486cd344a17884200
Gerrit-Change-Number: 38554
Gerrit-PatchSet: 1
Gerrit-Owner: falconia <falcon(a)freecalypso.org>
falconia has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-bts/+/38557?usp=email )
Change subject: CSD NT modes: transmit properly aligned RLP frames on DL
......................................................................
CSD NT modes: transmit properly aligned RLP frames on DL
There are two levels of alignment inside clearmode RTP packets
carrying CSData per TS 48.103 section 5.6:
1) Alignment of 2 or 4 V.110 (T) or pseudo-V.110 (NT) frames within
one RTP packet of 160 octets of an imaginary ISDN B channel;
2) For NT modes only, alignment of 4 pseudo-V.110 frames to form
a single 240-bit RLP frame.
Per previous patch, alignment 1 is to be treated as mandatory for
RTP transport inside an Osmocom network. Alignment 2 _could_ be
made mandatory for TCH/F9.6 NT, but the same is not possible for
TCH/[FH]4.8 NT: in the best case of half-alignment, alternating
RTP packets will carry alternating halves of RLP frames.
Implemented solution: allow arbitrary state of alignment 2
(aligned or misaligned) in the incoming RTP stream for all CSD NT
modes, and perform the necessary alignment internally.
This approach is consistent with the world of E1 BTS: a TRAU in
data mode is responsible for alignment 1 (with 20 ms TRAU frames
taking the place of our clearmode RTP packets), but only the BTS can
perform alignment 2, as the TRAU is agnostic to T vs NT distinction.
Related: OS#6579
Change-Id: Idaebfce6da13b23ba265a197502712d83991873e
---
M include/osmo-bts/Makefile.am
A include/osmo-bts/csd_rlp.h
M include/osmo-bts/lchan.h
M src/common/Makefile.am
A src/common/csd_rlp.c
M src/common/l1sap.c
6 files changed, 432 insertions(+), 100 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/57/38557/1
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index cbd0fc3..1922f0d 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -24,6 +24,7 @@
tx_power.h \
control_if.h \
cbch.h \
+ csd_rlp.h \
csd_v110.h \
l1sap.h \
lchan.h \
diff --git a/include/osmo-bts/csd_rlp.h b/include/osmo-bts/csd_rlp.h
new file mode 100644
index 0000000..94fa3d2
--- /dev/null
+++ b/include/osmo-bts/csd_rlp.h
@@ -0,0 +1,36 @@
+/*
+ * Declarations for functions in csd_rlp.c: alignment of downlink RLP frames
+ * and RLP GSMTAP mechanism for CSD NT modes.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/gsm/l1sap.h>
+#include <osmo-bts/lchan.h>
+
+extern const uint8_t csd_tchf48_nt_e2_map[26];
+
+/* Per TS 48.020 section 15.1, the cadence of E2+E3 bits in a properly
+ * aligned sequence of pseudo-V.110 frames forming a single RLP frame
+ * is 00-01-10-11. The following constant captures this bit sequence
+ * in hex, for comparison against align_bits output from
+ * csd_v110_rtp_decode() or against rlpdl_align_bits accumulator
+ * in CSD NT lchan state.
+ */
+#define NTCSD_ALIGNED_EBITS 0x1B
+
+void ntcsd_dl_reset(struct gsm_lchan *lchan);
+void ntcsd_dl_input_48(struct gsm_lchan *lchan, const ubit_t *data_bits,
+ uint8_t align_bits);
+void ntcsd_dl_input_96(struct gsm_lchan *lchan, const ubit_t *data_bits,
+ uint8_t align_bits);
+bool ntcsd_dl_output(struct gsm_lchan *lchan, ubit_t *rlp_frame_out);
+
+void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink,
+ const struct ph_tch_param *tch_ind,
+ const ubit_t *data, unsigned int data_len);
+void gsmtap_csd_rlp_dl(struct gsm_lchan *lchan, uint32_t fn,
+ const ubit_t *data, unsigned int data_len);
diff --git a/include/osmo-bts/lchan.h b/include/osmo-bts/lchan.h
index df2f4fa..5c34015 100644
--- a/include/osmo-bts/lchan.h
+++ b/include/osmo-bts/lchan.h
@@ -4,6 +4,7 @@
#include <stdint.h>
#include <netinet/in.h>
+#include <osmocom/core/bits.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
@@ -295,9 +296,14 @@
uint8_t last_cmr;
uint32_t last_fn;
struct {
- /* buffers to re-combine RLP frame from multiple Um blocks */
+ /* RLP GSMTAP mechanism */
uint8_t rlp_buf_ul[576/8]; /* maximum size of RLP frame */
uint8_t rlp_buf_dl[576/8]; /* maximum size of RLP frame */
+ /* alignment of RLP frames in DL for NT modes */
+ ubit_t rlpdl_data_bits[60 * 7];
+ uint16_t rlpdl_align_bits;
+ uint8_t rlpdl_fill_level;
+ ubit_t tchf48_nt_2ndhalf[120];
} csd;
} tch;
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index d13415d..1a62e8e 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -51,6 +51,7 @@
bts_ctrl_commands.c \
bts_ctrl_lookup.c \
bts_shutdown_fsm.c \
+ csd_rlp.c \
csd_v110.c \
l1sap.c \
cbch.c \
diff --git a/src/common/csd_rlp.c b/src/common/csd_rlp.c
new file mode 100644
index 0000000..349094c
--- /dev/null
+++ b/src/common/csd_rlp.c
@@ -0,0 +1,228 @@
+/* This module has been split from l1sap.c; original header comments preserved:
+ *
+ * (C) 2011 by Harald Welte <laforge(a)gnumonks.org>
+ * (C) 2013 by Andreas Eversberg <jolly(a)eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/l1sap.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/rlp.h>
+#include <osmocom/gsm/rtp_extensions.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/utils.h>
+
+#include <osmo-bts/logging.h>
+#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/lchan.h>
+#include <osmo-bts/bts.h>
+#include <osmo-bts/csd_rlp.h>
+
+/* In the case of TCH/F4.8 NT, each 240-bit RLP frame is split between
+ * two channel-coding blocks of 120 bits each. We need to know which
+ * frame numbers correspond to which half: in the UL-to-RTP path we have
+ * to set bit E2 based on the TDMA frame number at which we received the
+ * block in question, and in the DL direction we have to transmit the
+ * right half at the right time.
+ *
+ * See GSM 05.03 section 3.4.1 and the mapping tables of GSM 05.02;
+ * having "e2_map" in the array name shall serve as a mnemonic as to
+ * the sense of this array: 0 means 1st half and 1 means 2nd half,
+ * exactly as the value of bit E2 per TS 48.020 section 15.1.
+ */
+const uint8_t csd_tchf48_nt_e2_map[26] = {
+ [4] = 1, /* B1 position */
+ [13] = 1, /* B3 position */
+ [21] = 1, /* B5 position */
+};
+
+/* This function resets (clears) the state of the DL alignment buffer.
+ * It needs to be called when we encounter a gap (packet loss, invalid
+ * packets, etc) in our RTP input stream. */
+void ntcsd_dl_reset(struct gsm_lchan *lchan)
+{
+ lchan->tch.csd.rlpdl_fill_level = 0;
+}
+
+/* This function is to be called with the decoded content of a single
+ * incoming RTP packet (data and alignment bits) for TCH/[FH]4.8 NT. */
+void ntcsd_dl_input_48(struct gsm_lchan *lchan, const ubit_t *data_bits,
+ uint8_t align_bits)
+{
+ memmove(lchan->tch.csd.rlpdl_data_bits,
+ lchan->tch.csd.rlpdl_data_bits + 60 * 2, 60 * 5);
+ memcpy(lchan->tch.csd.rlpdl_data_bits + 60 * 5, data_bits, 60 * 2);
+ lchan->tch.csd.rlpdl_align_bits <<= 4;
+ lchan->tch.csd.rlpdl_align_bits |= (align_bits & 0xF);
+ lchan->tch.csd.rlpdl_fill_level += 2;
+ if (lchan->tch.csd.rlpdl_fill_level > 7)
+ lchan->tch.csd.rlpdl_fill_level = 7;
+}
+
+/* This function is to be called with the decoded content of a single
+ * incoming RTP packet (data and alignment bits) for TCH/F9.6 NT. */
+void ntcsd_dl_input_96(struct gsm_lchan *lchan, const ubit_t *data_bits,
+ uint8_t align_bits)
+{
+ memmove(lchan->tch.csd.rlpdl_data_bits,
+ lchan->tch.csd.rlpdl_data_bits + 60 * 4, 60 * 3);
+ memcpy(lchan->tch.csd.rlpdl_data_bits + 60 * 3, data_bits, 60 * 4);
+ lchan->tch.csd.rlpdl_align_bits <<= 8;
+ lchan->tch.csd.rlpdl_align_bits |= (align_bits & 0xFF);
+ lchan->tch.csd.rlpdl_fill_level += 4;
+ if (lchan->tch.csd.rlpdl_fill_level > 7)
+ lchan->tch.csd.rlpdl_fill_level = 7;
+}
+
+/* This function is to be called to obtain a complete RLP frame for
+ * downlink transmission. It will provide either a properly aligned
+ * frame (return value true) or a filler (return value false). */
+bool ntcsd_dl_output(struct gsm_lchan *lchan, ubit_t *rlp_frame_out)
+{
+ if (lchan->tch.csd.rlpdl_fill_level < 4)
+ goto no_frame_out;
+ if (((lchan->tch.csd.rlpdl_align_bits >> 0) & 0xFF) == NTCSD_ALIGNED_EBITS) {
+ memcpy(rlp_frame_out, lchan->tch.csd.rlpdl_data_bits + 60 * 3,
+ 60 * 4);
+ return true;
+ }
+ if (lchan->tch.csd.rlpdl_fill_level < 5)
+ goto no_frame_out;
+ if (((lchan->tch.csd.rlpdl_align_bits >> 2) & 0xFF) == NTCSD_ALIGNED_EBITS) {
+ memcpy(rlp_frame_out, lchan->tch.csd.rlpdl_data_bits + 60 * 2,
+ 60 * 4);
+ return true;
+ }
+ if (lchan->tch.csd.rlpdl_fill_level < 6)
+ goto no_frame_out;
+ if (((lchan->tch.csd.rlpdl_align_bits >> 4) & 0xFF) == NTCSD_ALIGNED_EBITS) {
+ memcpy(rlp_frame_out, lchan->tch.csd.rlpdl_data_bits + 60 * 1,
+ 60 * 4);
+ return true;
+ }
+ if (lchan->tch.csd.rlpdl_fill_level < 7)
+ goto no_frame_out;
+ if (((lchan->tch.csd.rlpdl_align_bits >> 6) & 0xFF) == NTCSD_ALIGNED_EBITS) {
+ memcpy(rlp_frame_out, lchan->tch.csd.rlpdl_data_bits, 60 * 4);
+ return true;
+ }
+no_frame_out:
+ /* TS 44.021 section 12.1 says that a missing/unavailable 240-bit
+ * RLP frame is to be filled with 0 bits, unlike ones-fill
+ * used everywhere else in the world of V.110 and CSD. */
+ memset(rlp_frame_out, 0, 60 * 4);
+ return false;
+}
+
+/* process one MAC block of unpacked bits of a non-transparent CSD channel */
+void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink,
+ const struct ph_tch_param *tch_ind,
+ const ubit_t *data, unsigned int data_len)
+{
+ struct gsm_bts_trx *trx = lchan->ts->trx;
+ struct gsmtap_inst *inst = trx->bts->gsmtap.inst;
+ pbit_t *rlp_buf;
+ uint16_t arfcn;
+ int byte_len;
+
+ if (!inst || !trx->bts->gsmtap.rlp)
+ return;
+
+ if (lchan->csd_mode != LCHAN_CSD_M_NT)
+ return;
+
+ if (is_uplink)
+ rlp_buf = lchan->tch.csd.rlp_buf_ul;
+ else
+ rlp_buf = lchan->tch.csd.rlp_buf_dl;
+
+ /* TCH/F 9.6: 4x60bit block => 240bit RLP frame
+ * TCH/F 4.8: 2x 2x60bit blocks starting at B0/B2/B4 => 240bit RLP frame
+ * TCH/H 4.8: 4x60bit block => 240bit RLP frame
+ * TCH/F 2.4: 2x36bit blocks => transparent only
+ * TCH/H 2.4: 4x36bit blocks => transparent only
+ * TCH/F 14.4: 2x 290 bit block (starting with M1=0) => 576-bit RLP frame
+ */
+
+ if (lchan->type == GSM_LCHAN_TCH_F &&
+ lchan->tch_mode == GSM48_CMODE_DATA_6k0 && is_uplink) {
+ /* In this mode we have 120-bit MAC blocks; two of them need
+ * to be concatenated to render a 240-bit RLP frame. The first
+ * block is present in B0/B2/B4, and we have to use FN to
+ * detect this position.
+ * This code path is only for UL: in the case of DL,
+ * alignment logic elsewhere in the code will present us
+ * with a fully assembled RLP frame. */
+ OSMO_ASSERT(data_len == 120);
+ if (csd_tchf48_nt_e2_map[tch_ind->fn % 26] == 0) {
+ osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
+ return;
+ }
+ osmo_ubit2pbit_ext(rlp_buf, 120, data, 0, data_len, 1);
+ byte_len = 240/8;
+ } else if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_14k5) {
+ /* in this mode we have 290bit MAC blocks containing M1, M2 and 288 data bits;
+ * two of them need to be concatenated to render a
+ * 576-bit RLP frame. The start of a RLP frame is
+ * denoted by a block with M1-bit set to 0. */
+ OSMO_ASSERT(data_len == 290);
+ ubit_t m1 = data[0];
+ if (m1 == 0) {
+ osmo_ubit2pbit_ext(rlp_buf, 0, data, 2, data_len, 1);
+ return;
+ }
+ osmo_ubit2pbit_ext(rlp_buf, 288, data, 2, data_len, 1);
+ byte_len = 576/8;
+ } else {
+ byte_len = osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
+ }
+
+ if (trx->bts->gsmtap.rlp_skip_null) {
+ struct osmo_rlp_frame_decoded rlpf;
+ int rc = osmo_rlp_decode(&rlpf, 0, rlp_buf, byte_len);
+ if (rc == 0 && rlpf.ftype == OSMO_RLP_FT_U && rlpf.u_ftype == OSMO_RLP_U_FT_NULL)
+ return;
+ }
+
+ arfcn = trx->arfcn;
+ if (is_uplink)
+ arfcn |= GSMTAP_ARFCN_F_UPLINK;
+
+ gsmtap_send_ex(inst, GSMTAP_TYPE_GSM_RLP, arfcn, lchan->ts->nr,
+ lchan->type == GSM_LCHAN_TCH_H ? GSMTAP_CHANNEL_VOICE_H : GSMTAP_CHANNEL_VOICE_F,
+ lchan->nr, tch_ind->fn, tch_ind->rssi, 0, rlp_buf, byte_len);
+
+}
+
+/* wrapper for downlink path */
+void gsmtap_csd_rlp_dl(struct gsm_lchan *lchan, uint32_t fn,
+ const ubit_t *data, unsigned int data_len)
+{
+ /* 'fake' tch_ind containing all-zero so gsmtap code can be shared
+ * between UL and DL */
+ const struct ph_tch_param fake_tch_ind = { .fn = fn };
+ gsmtap_csd_rlp_process(lchan, false, &fake_tch_ind, data, data_len);
+}
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index 793b7a8..6c4eefe 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -60,6 +60,7 @@
#include <osmo-bts/pcuif_proto.h>
#include <osmo-bts/cbch.h>
#include <osmo-bts/asci.h>
+#include <osmo-bts/csd_rlp.h>
#include <osmo-bts/csd_v110.h>
/* determine the CCCH block number based on the frame number */
@@ -1516,6 +1517,7 @@
struct osmo_phsap_prim *resp_l1sap, empty_l1sap;
uint8_t *phy_data;
struct gsm_time g_time;
+ bool good_rlp;
int i;
/* The generic scheduler still sends us TCH-RTS.ind every 20 ms,
@@ -1545,16 +1547,36 @@
&lchan->dl_tch_queue_len);
}
+ if (lchan->csd_mode == LCHAN_CSD_M_NT) {
+ for (i = 0; i < 2; i++) {
+ if (input_msg[i]) {
+ ntcsd_dl_input_48(lchan, input_msg[i]->data,
+ rtpmsg_csd_align_bits(input_msg[i]));
+ } else {
+ ntcsd_dl_reset(lchan);
+ }
+ }
+ }
+
phy_msg = l1sap_msgb_alloc(bits_per_20ms * 2);
if (phy_msg) {
resp_l1sap = msgb_l1sap_prim(phy_msg);
phy_msg->l2h = phy_msg->tail;
- for (i = 0; i < 2; i++) {
- phy_data = msgb_put(phy_msg, bits_per_20ms);
- if (input_msg[i])
- memcpy(phy_data, input_msg[i]->data, bits_per_20ms);
- else
- memset(phy_data, 0x01, bits_per_20ms);
+ if (lchan->csd_mode == LCHAN_CSD_M_NT) {
+ phy_data = msgb_put(phy_msg, 240); /* RLP frame */
+ good_rlp = ntcsd_dl_output(lchan, phy_data);
+ if (good_rlp)
+ gsmtap_csd_rlp_dl(lchan, fn, phy_data, 240);
+ } else {
+ for (i = 0; i < 2; i++) {
+ phy_data = msgb_put(phy_msg, bits_per_20ms);
+ if (input_msg[i]) {
+ memcpy(phy_data, input_msg[i]->data,
+ bits_per_20ms);
+ } else {
+ memset(phy_data, 0x01, bits_per_20ms);
+ }
+ }
}
} else {
resp_l1sap = &empty_l1sap;
@@ -1579,6 +1601,111 @@
return 0;
}
+/* The case of TCH/F4.8 NT also requires special processing that is
+ * somewhat similar to half-rate CSD. We have to produce an RLP frame
+ * for DL every 40 ms, thus it makes the most sense for us to poll
+ * the Rx jitter buffer every 40 ms just like with CSD-HR. However,
+ * we need to send TCH.req to the PHY every 20 ms, sending either
+ * the first half or the second half of the RLP frame we put together
+ * every 40 ms. */
+static int tch_rts_ind_tchf48_nt(struct gsm_bts_trx *trx,
+ struct gsm_lchan *lchan,
+ struct ph_tch_param *rts_ind)
+{
+ uint8_t chan_nr = rts_ind->chan_nr;
+ uint32_t fn = rts_ind->fn;
+ struct msgb *input_msg, *phy_msg;
+ struct osmo_phsap_prim *resp_l1sap, empty_l1sap;
+ ubit_t rlp_frame[240];
+ bool good_rlp;
+ struct gsm_time g_time;
+ int i;
+
+ gsm_fn2gsmtime(&g_time, fn);
+
+ /* Input processing happens every 40 ms */
+ if (csd_tchf48_nt_e2_map[fn % 26] == 0) {
+ for (i = 0; i < 2; i++) {
+ if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
+ osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
+ lchan->abis_ip.rtp_socket->rx_user_ts += GSM_RTP_DURATION;
+ }
+ input_msg = msgb_dequeue_count(&lchan->dl_tch_queue,
+ &lchan->dl_tch_queue_len);
+ if (input_msg) {
+ ntcsd_dl_input_48(lchan, input_msg->data,
+ rtpmsg_csd_align_bits(input_msg));
+ msgb_free(input_msg);
+ } else {
+ ntcsd_dl_reset(lchan);
+ }
+ }
+ good_rlp = ntcsd_dl_output(lchan, rlp_frame);
+ if (good_rlp)
+ gsmtap_csd_rlp_dl(lchan, fn, rlp_frame, 240);
+ memcpy(lchan->tch.csd.tchf48_nt_2ndhalf, rlp_frame+120, 120);
+ }
+
+ /* back to every 20 ms code path */
+ phy_msg = l1sap_msgb_alloc(120); /* half of RLP frame */
+ if (phy_msg) {
+ resp_l1sap = msgb_l1sap_prim(phy_msg);
+ phy_msg->l2h = msgb_put(phy_msg, 120);
+ if (csd_tchf48_nt_e2_map[fn % 26] == 0)
+ memcpy(phy_msg->l2h, rlp_frame, 120);
+ else
+ memcpy(phy_msg->l2h, lchan->tch.csd.tchf48_nt_2ndhalf, 120);
+ } else {
+ resp_l1sap = &empty_l1sap;
+ }
+
+ memset(resp_l1sap, 0, sizeof(*resp_l1sap));
+ osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
+ phy_msg);
+ resp_l1sap->u.tch.chan_nr = chan_nr;
+ resp_l1sap->u.tch.fn = fn;
+ resp_l1sap->u.tch.marker = 0; /* M bit is undefined for clearmode */
+
+ LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Tx TCH.req\n");
+
+ l1sap_down(trx, resp_l1sap);
+
+ return 0;
+}
+
+/* For TCH/F9.6 NT we need much less special processing than for TCH/F4.8 NT
+ * or for CSD-HR, but we still need to handle the possibility of misaligned
+ * RTP input, i.e., pseudo-V.110 frames aligned in the packet, but not
+ * forming proper RLP frame alignment via E2 & E3 bits. */
+static void tchf96_nt_dl_alignment(struct gsm_lchan *lchan, struct msgb *msg,
+ uint32_t fn)
+{
+ bool good_rlp;
+
+ if (!msg) {
+ ntcsd_dl_reset(lchan);
+ /* FIXME: do we really need to generate a PHY packet filled
+ * with 0 bits to satisfy TS 44.021 section 12.1, or can we
+ * get by with letting the PHY fill in ones like it does
+ * for all other CSD modes? */
+ return;
+ }
+ /* Fast path: handle the good case of already proper alignment */
+ if ((rtpmsg_csd_align_bits(msg) & 0xFF) == NTCSD_ALIGNED_EBITS) {
+ /* clear the buffer in case we have to do misaligned packets
+ * later, but otherwise let it go! */
+ ntcsd_dl_reset(lchan);
+ gsmtap_csd_rlp_dl(lchan, fn, msgb_l2(msg), msgb_l2len(msg));
+ return;
+ }
+ /* Slow path: realign like in other NT modes */
+ OSMO_ASSERT(msgb_l2len(msg) == 240);
+ ntcsd_dl_input_96(lchan, msgb_l2(msg), rtpmsg_csd_align_bits(msg));
+ good_rlp = ntcsd_dl_output(lchan, msgb_l2(msg));
+ if (good_rlp)
+ gsmtap_csd_rlp_dl(lchan, fn, msgb_l2(msg), msgb_l2len(msg));
+}
+
/* TCH-RTS-IND prim received from bts model */
static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind)
@@ -1604,8 +1731,14 @@
LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Rx TCH-RTS.ind\n");
}
- if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA && lchan->type == GSM_LCHAN_TCH_H)
+ /* CSD-HR requires special processing */
+ if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA &&
+ lchan->type == GSM_LCHAN_TCH_H)
return tch_rts_ind_csd_hr(trx, lchan, rts_ind);
+ /* so does TCH/F4.8 NT mode */
+ if (lchan->tch_mode == GSM48_CMODE_DATA_6k0 &&
+ lchan->csd_mode == LCHAN_CSD_M_NT)
+ return tch_rts_ind_tchf48_nt(trx, lchan, rts_ind);
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
@@ -1653,6 +1786,24 @@
&resp_l1sap, &empty_l1sap);
}
+ /* minimal extra handling for the remaining CSD NT modes */
+ if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA &&
+ lchan->csd_mode == LCHAN_CSD_M_NT) {
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_DATA_12k0:
+ tchf96_nt_dl_alignment(lchan, resp_msg, fn);
+ break;
+ case GSM48_CMODE_DATA_14k5:
+ gsmtap_csd_rlp_dl(lchan, fn, msgb_l2(resp_msg),
+ msgb_l2len(resp_msg));
+ break;
+ default:
+ LOGPLCGT(lchan, &g_time, DL1P, LOGL_ERROR,
+ "Invalid TCH mode in TCH-RTS.ind under CSD NT\n");
+ break;
+ }
+ }
+
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
resp_msg);
@@ -1942,84 +2093,6 @@
return 1;
}
-/* process one MAC block of unpacked bits of a non-transparent CSD channel */
-static void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink,
- const struct ph_tch_param *tch_ind,
- const uint8_t *data, unsigned int data_len)
-{
- struct gsm_bts_trx *trx = lchan->ts->trx;
- struct gsmtap_inst *inst = trx->bts->gsmtap.inst;
- pbit_t *rlp_buf;
- uint16_t arfcn;
- int byte_len;
-
- if (!inst || !trx->bts->gsmtap.rlp)
- return;
-
- if (lchan->csd_mode != LCHAN_CSD_M_NT)
- return;
-
- if (is_uplink)
- rlp_buf = lchan->tch.csd.rlp_buf_ul;
- else
- rlp_buf = lchan->tch.csd.rlp_buf_dl;
-
- /* TCH/F 9.6: 4x60bit block => 240bit RLP frame
- * TCH/F 4.8: 2x 2x60bit blocks starting at B0/B2/B4 => 240bit RLP frame
- * TCH/H 4.8: 4x60bit block => 240bit RLP frame
- * TCH/F 2.4: 2x36bit blocks => transparent only
- * TCH/H 2.4: 4x36bit blocks => transparent only
- * TCH/F 14.4: 2x 290 bit block (starting with M1=0) => 576-bit RLP frame
- */
-
- if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_6k0) {
- /* in this mode we have 120bit MAC blocks; two of them need to be concatenated
- * to render a 240-bit RLP frame. The fist block is present in B0/B2/B4.
- * The E7 bit is used to indicate the Frame MF0a */
- OSMO_ASSERT(data_len == 120);
- ubit_t e7 = data[4*7+3];
- if (e7 == 0) {
- osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
- return;
- } else {
- osmo_ubit2pbit_ext(rlp_buf, 120, data, 0, data_len, 1);
- byte_len = 240/8;
- }
- } else if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_14k5) {
- /* in this mode we have 290bit MAC blocks containing M1, M2 and 288 data bits;
- * two of them need to be concatenated to render a
- * 576-bit RLP frame. The start of a RLP frame is
- * denoted by a block with M1-bit set to 0. */
- OSMO_ASSERT(data_len == 290);
- ubit_t m1 = data[0];
- if (m1 == 0) {
- osmo_ubit2pbit_ext(rlp_buf, 0, data, 2, data_len, 1);
- return;
- } else {
- osmo_ubit2pbit_ext(rlp_buf, 288, data, 2, data_len, 1);
- byte_len = 576/8;
- }
- } else {
- byte_len = osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
- }
-
- if (trx->bts->gsmtap.rlp_skip_null) {
- struct osmo_rlp_frame_decoded rlpf;
- int rc = osmo_rlp_decode(&rlpf, 0, rlp_buf, byte_len);
- if (rc == 0 && rlpf.ftype == OSMO_RLP_FT_U && rlpf.u_ftype == OSMO_RLP_U_FT_NULL)
- return;
- }
-
- arfcn = trx->arfcn;
- if (is_uplink)
- arfcn |= GSMTAP_ARFCN_F_UPLINK;
-
- gsmtap_send_ex(inst, GSMTAP_TYPE_GSM_RLP, arfcn, lchan->ts->nr,
- lchan->type == GSM_LCHAN_TCH_H ? GSMTAP_CHANNEL_VOICE_H : GSMTAP_CHANNEL_VOICE_F,
- lchan->nr, tch_ind->fn, tch_ind->rssi, 0, rlp_buf, byte_len);
-
-}
-
/* a helper function for the logic in l1sap_tch_ind() */
static void send_ul_rtp_packet(struct gsm_lchan *lchan, uint32_t fn,
const uint8_t *rtp_pl, uint16_t rtp_pl_len)
@@ -2060,20 +2133,11 @@
lchan->rtp_tx_marker = false;
}
-/* In the case of TCH/F4.8 NT, we have to set bit E2 based on the TDMA
- * frame number at which we received the block in question. See
- * GSM 05.03 section 3.4.1 and the mapping tables of GSM 05.02. */
-static const uint8_t tchf48_nt_e2_map[26] = {
- [4] = 1, /* B1 position */
- [13] = 1, /* B3 position */
- [21] = 1, /* B5 position */
-};
-
static void handle_tch_ind_csd_fr(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
const uint8_t *data, uint16_t data_len)
{
uint8_t rtp_pl[RFC4040_RTP_PLEN];
- uint8_t tchf48_half = tchf48_nt_e2_map[tch_ind->fn % 26];
+ uint8_t tchf48_half = csd_tchf48_nt_e2_map[tch_ind->fn % 26];
int rc;
gsmtap_csd_rlp_process(lchan, true, tch_ind, data, data_len);
@@ -2645,10 +2709,6 @@
int rc = csd_v110_rtp_decode(lchan, msg->tail, &csd_align_bits,
rtp_pl, rtp_pl_len);
if (rc > 0) {
- /* 'fake' tch_ind containing all-zero so gsmtap code can be shared
- * between UL and DL */
- static const struct ph_tch_param fake_tch_ind = {};
- gsmtap_csd_rlp_process(lchan, false, &fake_tch_ind, msg->tail, rc);
msgb_put(msg, rc);
} else {
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_V110_DEC);
--
To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/38557?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: Idaebfce6da13b23ba265a197502712d83991873e
Gerrit-Change-Number: 38557
Gerrit-PatchSet: 1
Gerrit-Owner: falconia <falcon(a)freecalypso.org>
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/38543?usp=email )
Change subject: pySim-shell_test/utils: delete log files in general
......................................................................
pySim-shell_test/utils: delete log files in general
When we get rid of temporary files, we delete those using a wildcard,
but for the logs from pySim-shell we explicitly memorize the name
of the pySim-shell logfile and delete it later by this explicit
name. This is not necessary, let's just delete all log files present
using a wildcard.
Related: OS#6601
Change-Id: I09dc7e59d1a3dcb68f54e3a8dccb86a1bc6c9ee6
---
M tests/pySim-shell_test/utils.py
1 file changed, 1 insertion(+), 2 deletions(-)
Approvals:
laforge: Looks good to me, approved
Jenkins Builder: Verified
osmith: Looks good to me, but someone else must approve
diff --git a/tests/pySim-shell_test/utils.py b/tests/pySim-shell_test/utils.py
index fe8386a..943eb48 100644
--- a/tests/pySim-shell_test/utils.py
+++ b/tests/pySim-shell_test/utils.py
@@ -220,7 +220,7 @@
print("testcase execution done -- cleaning up ...")
if not self.keepfiles:
os.system("rm -f ./*.tmp")
- os.system("rm -f ./" + self.__pySim_shell_logfile_name)
+ os.system("rm -f ./*.log")
for template in self.__templates_generated:
os.system("rm -f ./" + template)
@@ -241,7 +241,6 @@
"""
logfile_name = "pySim-shell_" + self._testMethodName + ".log"
- self.__pySim_shell_logfile_name = logfile_name
# Make sure the script file is available
if not os.access(script, os.R_OK):
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/38543?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I09dc7e59d1a3dcb68f54e3a8dccb86a1bc6c9ee6
Gerrit-Change-Number: 38543
Gerrit-PatchSet: 2
Gerrit-Owner: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/38545?usp=email )
Change subject: pySim-shell_test/utils: display pySim-shell logfile content
......................................................................
pySim-shell_test/utils: display pySim-shell logfile content
When we configure the tests to display file content, we only display files that
we compare, let's also display log file contents from pySim-shell. This will
be useful in situations where we only have log output from the tests, but no
access to the file system of the test host.
Related: OS#6601
Change-Id: Ibf6f78d7e71c213c7ca1caaf21c4c890e892261e
---
M tests/pySim-shell_test/utils.py
1 file changed, 5 insertions(+), 0 deletions(-)
Approvals:
laforge: Looks good to me, approved
osmith: Looks good to me, but someone else must approve
Jenkins Builder: Verified
diff --git a/tests/pySim-shell_test/utils.py b/tests/pySim-shell_test/utils.py
index 6acbd70..348c9b1 100644
--- a/tests/pySim-shell_test/utils.py
+++ b/tests/pySim-shell_test/utils.py
@@ -272,6 +272,11 @@
# Check for exceptions
logfile = open(logfile_name)
logfile_content = logfile.read()
+ if self.print_content:
+ print("pySim-shell logfile content: (%s)" % os.path.basename(logfile_name))
+ print("-----------------------8<-----------------------")
+ print(logfile_content)
+ print("-----------------------8<-----------------------")
logfile.close()
exception_regex_compiled = re.compile('.*EXCEPTION.*')
exceptions_strings = re.findall(exception_regex_compiled, logfile_content)
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/38545?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Ibf6f78d7e71c213c7ca1caaf21c4c890e892261e
Gerrit-Change-Number: 38545
Gerrit-PatchSet: 2
Gerrit-Owner: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/38544?usp=email )
Change subject: pySim-shell_test/utils: enumerate pySim-shell logs
......................................................................
pySim-shell_test/utils: enumerate pySim-shell logs
When pySim-shell is called by a testcase, a logfile is createted. The logfile
filename contains the testcase name. However, a testcase may run pySim-shell
multiple times. In this case we overwrite the log from previous run. Let's use a
counter to generate unique file names for each run, so that we won't lose logs
from previous runs.
Related: OS#6601
Change-Id: Ib2195d9b2231f74d0a6c4fb28f4889b6c45efb1e
---
M tests/pySim-shell_test/utils.py
1 file changed, 3 insertions(+), 1 deletion(-)
Approvals:
Jenkins Builder: Verified
osmith: Looks good to me, but someone else must approve
laforge: Looks good to me, approved
diff --git a/tests/pySim-shell_test/utils.py b/tests/pySim-shell_test/utils.py
index 943eb48..6acbd70 100644
--- a/tests/pySim-shell_test/utils.py
+++ b/tests/pySim-shell_test/utils.py
@@ -170,6 +170,7 @@
testcasepath = inspect.getfile(self.__class__)
testcasename = testcasepath.split("/")[-2] + "." + self._testMethodName
print(boxed_heading_str("testcase: " + testcasename))
+ self.pysim_shell_log_counter = 0
# Find directories
self.test_dir = os.path.dirname(testcasepath)
@@ -240,7 +241,8 @@
no_exceptions : fail the testcase in case any exceptions occurred while running pySim_shell
"""
- logfile_name = "pySim-shell_" + self._testMethodName + ".log"
+ logfile_name = "pySim-shell_" + self._testMethodName + "_" + str(self.pysim_shell_log_counter) + ".log"
+ self.pysim_shell_log_counter+=1
# Make sure the script file is available
if not os.access(script, os.R_OK):
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/38544?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Ib2195d9b2231f74d0a6c4fb28f4889b6c45efb1e
Gerrit-Change-Number: 38544
Gerrit-PatchSet: 2
Gerrit-Owner: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>