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@gnumonks.org + * (C) 2013 by Andreas Eversberg jolly@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);