mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-bts.git
synced 2025-11-03 21:53:29 +00:00
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
This commit is contained in:
@@ -11,6 +11,26 @@
|
||||
#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);
|
||||
|
||||
@@ -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 @@ struct gsm_lchan {
|
||||
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;
|
||||
|
||||
|
||||
@@ -41,6 +41,102 @@
|
||||
#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,
|
||||
@@ -71,13 +167,17 @@ void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink,
|
||||
* 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 */
|
||||
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);
|
||||
ubit_t e7 = data[4*7+3];
|
||||
if (e7 == 0) {
|
||||
if (csd_tchf48_nt_e2_map[tch_ind->fn % 26] == 0) {
|
||||
osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1);
|
||||
return;
|
||||
}
|
||||
@@ -116,3 +216,13 @@ void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink,
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1540,19 +1540,39 @@ static int tch_rts_ind_csd_hr(struct gsm_bts_trx *trx, struct gsm_lchan *lchan,
|
||||
&lchan->dl_tch_queue_len);
|
||||
}
|
||||
|
||||
if (lchan->csd_mode == LCHAN_CSD_M_NT) {
|
||||
for (i = 0; i < ARRAY_SIZE(input_msg); 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;
|
||||
if (lchan->csd_mode == LCHAN_CSD_M_NT) {
|
||||
bool good_rlp;
|
||||
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 < ARRAY_SIZE(input_msg); i++) {
|
||||
phy_data = msgb_put(phy_msg, bits_per_20ms);
|
||||
if (input_msg[i]) {
|
||||
memcpy(phy_data, input_msg[i]->data, bits_per_20ms);
|
||||
memcpy(phy_data, input_msg[i]->data,
|
||||
bits_per_20ms);
|
||||
} else {
|
||||
/* IDLE frame, filled with 1 bits */
|
||||
memset(phy_data, 0x01, bits_per_20ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resp_l1sap = &empty_l1sap;
|
||||
}
|
||||
@@ -1576,6 +1596,111 @@ static int tch_rts_ind_csd_hr(struct gsm_bts_trx *trx, struct gsm_lchan *lchan,
|
||||
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)
|
||||
@@ -1601,8 +1726,14 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
|
||||
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);
|
||||
@@ -1650,6 +1781,24 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
|
||||
&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);
|
||||
@@ -1979,20 +2128,11 @@ static void send_ul_rtp_packet_hrdata(struct gsm_lchan *lchan,
|
||||
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);
|
||||
@@ -2558,10 +2698,6 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user