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 <[email protected]>
+ * (C) 2013 by Andreas Eversberg <[email protected]>
+ *
+ * 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 <[email protected]>

Reply via email to