mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-bts.git
synced 2025-11-06 15:13:26 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7db79cc39a | ||
|
|
8d11671539 | ||
|
|
cb5b10e7b7 | ||
|
|
d9b456ce6d | ||
|
|
a9f3c01ece | ||
|
|
799ad7240b | ||
|
|
d4bb8d61e7 | ||
|
|
aa743cd97e | ||
|
|
65c497a69a | ||
|
|
b032208a4a | ||
|
|
c963f0b6eb | ||
|
|
a7b3a780ae | ||
|
|
62b37b32f9 | ||
|
|
c211b34955 | ||
|
|
88f03f8d1d | ||
|
|
c8946a077e | ||
|
|
8797837deb | ||
|
|
5bd8543a5f | ||
|
|
7b712121b8 | ||
|
|
f33e3c8378 | ||
|
|
ff4b83dc23 | ||
|
|
64d9f142ab | ||
|
|
4af496d44c | ||
|
|
6f9ccafde4 | ||
|
|
a928fce659 | ||
|
|
0a34af1530 | ||
|
|
02951e4af3 | ||
|
|
7accf7579d | ||
|
|
5bed286c6c | ||
|
|
414e1ca8ed | ||
|
|
c40e75277f | ||
|
|
59686cd276 | ||
|
|
82d500ccac | ||
|
|
aeb78dcc77 | ||
|
|
a880011a5f | ||
|
|
89415e7433 | ||
|
|
332976672e | ||
|
|
9512355ebc |
18
configure.ac
18
configure.ac
@@ -69,15 +69,15 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
dnl checks for libraries
|
dnl checks for libraries
|
||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
|
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.11.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
|
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
|
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
|
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.10.0)
|
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.11.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.10.0)
|
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.11.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.6.0)
|
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 2.0.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.6.0)
|
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 2.0.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.5.0)
|
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.6.0)
|
||||||
|
|
||||||
AC_MSG_CHECKING([whether to enable support for sysmobts calibration tool])
|
AC_MSG_CHECKING([whether to enable support for sysmobts calibration tool])
|
||||||
AC_ARG_ENABLE(sysmobts-calib,
|
AC_ARG_ENABLE(sysmobts-calib,
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
|||||||
export LD_LIBRARY_PATH="$inst/lib"
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
|
|
||||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||||
|
|
||||||
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
|
||||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||||
|
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
||||||
|
|
||||||
cd "$deps"
|
cd "$deps"
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
# shellcheck source=contrib/jenkins_common.sh
|
# shellcheck source=contrib/jenkins_common.sh
|
||||||
. $(dirname "$0")/jenkins_common.sh
|
. $(dirname "$0")/jenkins_common.sh
|
||||||
|
|
||||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
|
||||||
|
|
||||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||||
export LD_LIBRARY_PATH="$inst/lib"
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
|
|
||||||
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||||
|
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
||||||
|
|
||||||
cd "$deps"
|
cd "$deps"
|
||||||
osmo-layer1-headers.sh lc15 "$FIRMWARE_VERSION"
|
osmo-layer1-headers.sh lc15 "$FIRMWARE_VERSION"
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
# shellcheck source=contrib/jenkins_common.sh
|
# shellcheck source=contrib/jenkins_common.sh
|
||||||
. $(dirname "$0")/jenkins_common.sh
|
. $(dirname "$0")/jenkins_common.sh
|
||||||
|
|
||||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
|
||||||
|
|
||||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||||
export LD_LIBRARY_PATH="$inst/lib"
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
|
|
||||||
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||||
|
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
||||||
|
|
||||||
cd "$deps"
|
cd "$deps"
|
||||||
osmo-layer1-headers.sh oc2g "$FIRMWARE_VERSION"
|
osmo-layer1-headers.sh oc2g "$FIRMWARE_VERSION"
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
# shellcheck source=contrib/jenkins_common.sh
|
# shellcheck source=contrib/jenkins_common.sh
|
||||||
. $(dirname "$0")/jenkins_common.sh
|
. $(dirname "$0")/jenkins_common.sh
|
||||||
|
|
||||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
|
||||||
|
|
||||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||||
export LD_LIBRARY_PATH="$inst/lib"
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
|
|
||||||
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||||
|
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
||||||
|
|
||||||
cd "$deps"
|
cd "$deps"
|
||||||
osmo-layer1-headers.sh oct "$FIRMWARE_VERSION"
|
osmo-layer1-headers.sh oct "$FIRMWARE_VERSION"
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
|||||||
export LD_LIBRARY_PATH="$inst/lib"
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
|
|
||||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||||
|
|
||||||
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
|
||||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||||
|
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
||||||
|
|
||||||
cd "$deps"
|
cd "$deps"
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
# shellcheck source=contrib/jenkins_common.sh
|
# shellcheck source=contrib/jenkins_common.sh
|
||||||
. $(dirname "$0")/jenkins_common.sh
|
. $(dirname "$0")/jenkins_common.sh
|
||||||
|
|
||||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
|
||||||
|
|
||||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||||
export LD_LIBRARY_PATH="$inst/lib"
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
|
|
||||||
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||||
|
osmo-build-dep.sh libosmo-abis "" --disable-dahdi
|
||||||
|
|
||||||
cd "$deps"
|
cd "$deps"
|
||||||
osmo-layer1-headers.sh sysmo "$FIRMWARE_VERSION"
|
osmo-layer1-headers.sh sysmo "$FIRMWARE_VERSION"
|
||||||
|
|||||||
41
debian/changelog
vendored
41
debian/changelog
vendored
@@ -1,3 +1,44 @@
|
|||||||
|
osmo-bts (1.9.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Mychaela N. Falconia ]
|
||||||
|
* sysmo: generate empty TCH/H payload on FACCH/H Rx
|
||||||
|
* CSD: implement half-rate modes correctly
|
||||||
|
* csd_v110: set E2 bit correctly for TCH/[FH]4.8 NT
|
||||||
|
* CSD RTP: verify alignment of V.110 frames
|
||||||
|
* csd_v110_rtp_decode: preserve E2 & E3 bits for RLP alignment
|
||||||
|
* cosmetic: eliminate else-after-return in gsmtap_csd_rlp_process()
|
||||||
|
* cosmetic: move gsmtap_csd_rlp_process() to csd_rlp.c
|
||||||
|
* CSD NT modes: transmit properly aligned RLP frames on DL
|
||||||
|
|
||||||
|
[ Vadim Yanitskiy ]
|
||||||
|
* tests/csd: add NT variants for TCH/F4.8 and TCH/F9.6
|
||||||
|
* l1sap: prevent buffer overflow in l1sap_rtp_rx_cb()
|
||||||
|
* l1sap: move struct osmo_rlp_frame_decoded to the if-scope
|
||||||
|
* rsl: rsl_handle_chan_mod_ie(): set lchan->csd_mode for NT CSD
|
||||||
|
* vty: lchan_dump_full_vty(): print CSD mode
|
||||||
|
* l1sap: make send_ul_rtp_packet_hrdata() NULL-safe
|
||||||
|
* osmo-bts-trx: fix scheduling of DL FACCH/H for TCH/H4.8 and TCH/H2.4
|
||||||
|
* l1sap: l1sap_tch_rts_ind(): fix NULL ptr dereference
|
||||||
|
* csd_v110: use osmo_csd_ra2_* API from libosmotrau
|
||||||
|
* csd_v110: add CSD_V110_NUM_BITS macro
|
||||||
|
* csd_v110: clarify field names in csd_v110_lchan_desc[]
|
||||||
|
* csd_v110: clarify lchan description for TCH/F14.4
|
||||||
|
* csd_v110: handle TCH/F14.4
|
||||||
|
|
||||||
|
[ Pau Espin Pedrol ]
|
||||||
|
* jenkins.sh: libosmo-netif no longer depends on libosmo-abis
|
||||||
|
* bts-omldummy: Support configuring logging through cmdline
|
||||||
|
* bts-omldummy: print category names instead of hex values
|
||||||
|
* Fix missing quote char in log line
|
||||||
|
* abis: Log line and ts nr of signal
|
||||||
|
* abis: Fix reusing bts->*_link while it is being destroyed
|
||||||
|
* Drop use of libosmo-abis osmocom/abis/ipaccess.h
|
||||||
|
|
||||||
|
[ Philipp Maier ]
|
||||||
|
* pcu_sock: do not receive a TXT ind. with PCU_VERSION for a specific BTS
|
||||||
|
|
||||||
|
-- Oliver Smith <osmith@sysmocom.de> Wed, 12 Feb 2025 12:56:09 +0100
|
||||||
|
|
||||||
osmo-bts (1.8.0) unstable; urgency=medium
|
osmo-bts (1.8.0) unstable; urgency=medium
|
||||||
|
|
||||||
[ Vadim Yanitskiy ]
|
[ Vadim Yanitskiy ]
|
||||||
|
|||||||
6
debian/control
vendored
6
debian/control
vendored
@@ -7,9 +7,9 @@ Build-Depends: debhelper (>= 10),
|
|||||||
dh-autoreconf,
|
dh-autoreconf,
|
||||||
autotools-dev,
|
autotools-dev,
|
||||||
pkg-config,
|
pkg-config,
|
||||||
libosmocore-dev (>= 1.10.0),
|
libosmocore-dev (>= 1.11.0),
|
||||||
libosmo-abis-dev (>= 1.6.0),
|
libosmo-abis-dev (>= 2.0.0),
|
||||||
libosmo-netif-dev (>= 1.5.0),
|
libosmo-netif-dev (>= 1.6.0),
|
||||||
libgps-dev,
|
libgps-dev,
|
||||||
txt2man,
|
txt2man,
|
||||||
osmo-gsm-manuals-dev (>= 1.6.0)
|
osmo-gsm-manuals-dev (>= 1.6.0)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ noinst_HEADERS = \
|
|||||||
tx_power.h \
|
tx_power.h \
|
||||||
control_if.h \
|
control_if.h \
|
||||||
cbch.h \
|
cbch.h \
|
||||||
|
csd_rlp.h \
|
||||||
csd_v110.h \
|
csd_v110.h \
|
||||||
l1sap.h \
|
l1sap.h \
|
||||||
lchan.h \
|
lchan.h \
|
||||||
|
|||||||
36
include/osmo-bts/csd_rlp.h
Normal file
36
include/osmo-bts/csd_rlp.h
Normal file
@@ -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);
|
||||||
@@ -5,19 +5,20 @@
|
|||||||
|
|
||||||
struct gsm_lchan;
|
struct gsm_lchan;
|
||||||
|
|
||||||
struct csd_v110_frame_desc {
|
|
||||||
uint16_t num_blocks;
|
|
||||||
uint16_t num_bits;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct csd_v110_lchan_desc {
|
struct csd_v110_lchan_desc {
|
||||||
struct csd_v110_frame_desc fr;
|
uint16_t num_frames; /* number of V.110 frames in a radio block */
|
||||||
struct csd_v110_frame_desc hr;
|
uint16_t num_frame_bits; /* number of bits in each V.110 frame */
|
||||||
|
uint16_t num_other_bits; /* number of other bits (e.g. M-bits for TCH/F14.4) */
|
||||||
|
uint8_t ra2_ir; /* intermediate rate (8 or 16 kbit/s) for RA2 step */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct csd_v110_lchan_desc csd_v110_lchan_desc[256];
|
extern const struct csd_v110_lchan_desc csd_v110_lchan_desc[256];
|
||||||
|
|
||||||
|
#define CSD_V110_NUM_BITS(desc) \
|
||||||
|
((desc)->num_frames * (desc)->num_frame_bits + (desc)->num_other_bits)
|
||||||
|
|
||||||
int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
|
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,
|
int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
|
||||||
const uint8_t *rtp, size_t rtp_len);
|
uint8_t *align_bits, const uint8_t *rtp, size_t rtp_len);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/bits.h>
|
||||||
#include <osmocom/core/timer.h>
|
#include <osmocom/core/timer.h>
|
||||||
#include <osmocom/core/linuxlist.h>
|
#include <osmocom/core/linuxlist.h>
|
||||||
#include <osmocom/core/logging.h>
|
#include <osmocom/core/logging.h>
|
||||||
@@ -88,6 +89,12 @@ enum lchan_csd_mode {
|
|||||||
_LCHAN_CSD_M_NUM,
|
_LCHAN_CSD_M_NUM,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const struct value_string lchan_csd_mode_descs[];
|
||||||
|
static inline const char *lchan_csd_mode_desc(enum lchan_csd_mode mode)
|
||||||
|
{
|
||||||
|
return get_value_string(lchan_csd_mode_descs, mode);
|
||||||
|
}
|
||||||
|
|
||||||
/* State of the SAPIs in the lchan */
|
/* State of the SAPIs in the lchan */
|
||||||
enum lchan_sapi_state {
|
enum lchan_sapi_state {
|
||||||
LCHAN_SAPI_S_NONE,
|
LCHAN_SAPI_S_NONE,
|
||||||
@@ -285,13 +292,21 @@ struct gsm_lchan {
|
|||||||
bool dl_sid_transmitted;
|
bool dl_sid_transmitted;
|
||||||
/* The current frame in the DL is taken up by FACCH */
|
/* The current frame in the DL is taken up by FACCH */
|
||||||
bool dl_facch_stealing;
|
bool dl_facch_stealing;
|
||||||
|
/* UL SID filter to catch DTXu half-blocks,
|
||||||
|
* see tch_ul_fr_hr_efr() function. */
|
||||||
|
bool ul_sid_filter;
|
||||||
} dtx_fr_hr_efr;
|
} dtx_fr_hr_efr;
|
||||||
uint8_t last_cmr;
|
uint8_t last_cmr;
|
||||||
uint32_t last_fn;
|
uint32_t last_fn;
|
||||||
struct {
|
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_ul[576/8]; /* maximum size of RLP frame */
|
||||||
uint8_t rlp_buf_dl[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;
|
} csd;
|
||||||
} tch;
|
} tch;
|
||||||
|
|
||||||
|
|||||||
@@ -13,17 +13,49 @@
|
|||||||
|
|
||||||
struct msgb;
|
struct msgb;
|
||||||
|
|
||||||
/* Access 1st part of msgb control buffer */
|
/****************************************************************
|
||||||
|
* Accessor macros for control buffer words in RTP input path (DL)
|
||||||
|
*****************************************************************/
|
||||||
|
|
||||||
|
/* Storing RTP header fields in the path from RTP and Osmux
|
||||||
|
* Rx callback functions to TCH-RTS.ind handling.
|
||||||
|
* FIXME: do we really need this RTP header info downstream
|
||||||
|
* of the jitter buffer mechanism in the RTP endpoint library?
|
||||||
|
*/
|
||||||
#define rtpmsg_marker_bit(x) ((x)->cb[0])
|
#define rtpmsg_marker_bit(x) ((x)->cb[0])
|
||||||
|
#define rtpmsg_seq(x) ((x)->cb[1])
|
||||||
|
#define rtpmsg_ts(x) ((x)->cb[2])
|
||||||
|
|
||||||
/* Access 2nd part of msgb control buffer */
|
/* l1sap_rtp_rx_cb() does some preening or preparsing on some
|
||||||
#define rtpmsg_seq(x) ((x)->cb[1])
|
* RTP payloads, and in two cases (HR with RFC 5993 input and
|
||||||
|
* CSD NT modes) this preparsing step produces some metadata
|
||||||
/* Access 3rd part of msgb control buffer */
|
* that need to be passed to TCH-RTS.ind handling.
|
||||||
#define rtpmsg_ts(x) ((x)->cb[2])
|
*/
|
||||||
|
|
||||||
/* Access 4th part of msgb control buffer */
|
|
||||||
#define rtpmsg_is_rfc5993_sid(x) ((x)->cb[3])
|
#define rtpmsg_is_rfc5993_sid(x) ((x)->cb[3])
|
||||||
|
#define rtpmsg_csd_align_bits(x) ((x)->cb[4])
|
||||||
|
|
||||||
|
/********************************************************
|
||||||
|
* Accessor macros for control buffer words in TCH UL path
|
||||||
|
*********************************************************/
|
||||||
|
|
||||||
|
/* We provide an ability for BTS models to indicate BFI along with payload
|
||||||
|
* bits just like in GSM 08.60 TRAU-UL frames, and the same BFI flag can
|
||||||
|
* then be set by model-independent functions for higher-level BFI
|
||||||
|
* conditions. This cb word shall act as a Boolean flag.
|
||||||
|
*/
|
||||||
|
#define tch_ul_msg_bfi(x) ((x)->cb[0])
|
||||||
|
|
||||||
|
/* For HRv1 codec, we have to pass SID classification from the function
|
||||||
|
* that makes the initial determination to TS 101 318, RFC 5993 and
|
||||||
|
* TW-TS-002 output functions. Per classic GSM specs, common across
|
||||||
|
* FR/HR/EFR, SID classification code is an integer equal to 0, 1 or 2;
|
||||||
|
* in Osmocom it is enum osmo_gsm631_sid_class.
|
||||||
|
*
|
||||||
|
* NOTE: while the actual SID ternary classification exists in exactly
|
||||||
|
* the same form across all 3 of FR/HR/EFR, we store it in a cb word
|
||||||
|
* only for HR codec where we need it for RTP output functions.
|
||||||
|
*/
|
||||||
|
#define tch_ul_msg_hr_sid(x) ((x)->cb[1])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classification of OML message. ETSI for plain GSM 12.21
|
* Classification of OML message. ETSI for plain GSM 12.21
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ libbts_a_SOURCES = \
|
|||||||
bts_ctrl_commands.c \
|
bts_ctrl_commands.c \
|
||||||
bts_ctrl_lookup.c \
|
bts_ctrl_lookup.c \
|
||||||
bts_shutdown_fsm.c \
|
bts_shutdown_fsm.c \
|
||||||
|
csd_rlp.c \
|
||||||
csd_v110.c \
|
csd_v110.c \
|
||||||
l1sap.c \
|
l1sap.c \
|
||||||
cbch.c \
|
cbch.c \
|
||||||
|
|||||||
@@ -39,10 +39,10 @@
|
|||||||
#include <osmocom/core/signal.h>
|
#include <osmocom/core/signal.h>
|
||||||
#include <osmocom/core/macaddr.h>
|
#include <osmocom/core/macaddr.h>
|
||||||
#include <osmocom/core/fsm.h>
|
#include <osmocom/core/fsm.h>
|
||||||
|
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||||
|
#include <osmocom/gsm/ipa.h>
|
||||||
#include <osmocom/abis/abis.h>
|
#include <osmocom/abis/abis.h>
|
||||||
#include <osmocom/abis/e1_input.h>
|
#include <osmocom/abis/e1_input.h>
|
||||||
#include <osmocom/abis/ipaccess.h>
|
|
||||||
#include <osmocom/gsm/ipa.h>
|
|
||||||
|
|
||||||
#include <osmo-bts/abis.h>
|
#include <osmo-bts/abis.h>
|
||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
@@ -87,10 +87,17 @@ struct abis_link_fsm_priv {
|
|||||||
|
|
||||||
static void reset_oml_link(struct gsm_bts *bts)
|
static void reset_oml_link(struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
|
struct e1inp_sign_link *link;
|
||||||
|
|
||||||
if (bts->oml_link) {
|
if (bts->oml_link) {
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
|
|
||||||
e1inp_sign_link_destroy(bts->oml_link);
|
/* Mark bts->oml_link ptr null before calling sign_link_destroy,
|
||||||
|
* to avoid a callback triggering this same code path. */
|
||||||
|
link = bts->oml_link;
|
||||||
|
bts->oml_link = NULL;
|
||||||
|
|
||||||
|
e1inp_sign_link_destroy(link);
|
||||||
|
|
||||||
/* Log a special notice if the OML connection was dropped relatively quickly. */
|
/* Log a special notice if the OML connection was dropped relatively quickly. */
|
||||||
if (bts->oml_conn_established_timestamp.tv_sec != 0 && clock_gettime(CLOCK_MONOTONIC, &now) == 0 &&
|
if (bts->oml_conn_established_timestamp.tv_sec != 0 && clock_gettime(CLOCK_MONOTONIC, &now) == 0 &&
|
||||||
@@ -100,14 +107,16 @@ static void reset_oml_link(struct gsm_bts *bts)
|
|||||||
"A common error is a mismatch between unit_id configuration parameters of BTS and BSC.\n",
|
"A common error is a mismatch between unit_id configuration parameters of BTS and BSC.\n",
|
||||||
(uint64_t) (now.tv_sec - bts->oml_conn_established_timestamp.tv_sec));
|
(uint64_t) (now.tv_sec - bts->oml_conn_established_timestamp.tv_sec));
|
||||||
}
|
}
|
||||||
bts->oml_link = NULL;
|
|
||||||
}
|
}
|
||||||
memset(&bts->oml_conn_established_timestamp, 0, sizeof(bts->oml_conn_established_timestamp));
|
memset(&bts->oml_conn_established_timestamp, 0, sizeof(bts->oml_conn_established_timestamp));
|
||||||
|
|
||||||
/* Same for IPAC_PROTO_OSMO on the same ipa connection: */
|
/* Same for IPAC_PROTO_OSMO on the same ipa connection: */
|
||||||
if (bts->osmo_link) {
|
if (bts->osmo_link) {
|
||||||
e1inp_sign_link_destroy(bts->osmo_link);
|
/* Mark bts->osmo_link ptr null before calling sign_link_destroy,
|
||||||
|
* to avoid a callback triggering this same code path. */
|
||||||
|
link = bts->osmo_link;
|
||||||
bts->osmo_link = NULL;
|
bts->osmo_link = NULL;
|
||||||
|
e1inp_sign_link_destroy(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -226,8 +235,11 @@ static void abis_link_connected(struct osmo_fsm_inst *fi, uint32_t event, void *
|
|||||||
/* Then iterate over the RSL signalling links */
|
/* Then iterate over the RSL signalling links */
|
||||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||||
if (trx->bb_transc.rsl.link) {
|
if (trx->bb_transc.rsl.link) {
|
||||||
e1inp_sign_link_destroy(trx->bb_transc.rsl.link);
|
/* Mark link ptr null before calling sign_link_destroy,
|
||||||
|
* to avoid a callback triggering this same code path. */
|
||||||
|
struct e1inp_sign_link *link = trx->bb_transc.rsl.link;
|
||||||
trx->bb_transc.rsl.link = NULL;
|
trx->bb_transc.rsl.link = NULL;
|
||||||
|
e1inp_sign_link_destroy(link);
|
||||||
if (trx == trx->bts->c0)
|
if (trx == trx->bts->c0)
|
||||||
load_timer_stop(trx->bts);
|
load_timer_stop(trx->bts);
|
||||||
} else {
|
} else {
|
||||||
@@ -476,8 +488,11 @@ static int inp_s_cbfn(unsigned int subsys, unsigned int signal,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
struct input_signal_data *isd = signal_data;
|
struct input_signal_data *isd = signal_data;
|
||||||
DEBUGP(DABIS, "Input Signal %s received for link_type=%s\n",
|
DEBUGP(DABIS, "Input Signal %s received for ts-%u-%u link_type=%s\n",
|
||||||
get_value_string(e1inp_signal_names, signal), e1inp_signtype_name(isd->link_type));
|
get_value_string(e1inp_signal_names, signal),
|
||||||
|
isd->line ? isd->line->num : -1,
|
||||||
|
isd->ts_nr,
|
||||||
|
e1inp_signtype_name(isd->link_type));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,6 +394,7 @@ int bts_init(struct gsm_bts *bts)
|
|||||||
osmo_bts_set_feature(bts->features, BTS_FEAT_IPV6_NSVC);
|
osmo_bts_set_feature(bts->features, BTS_FEAT_IPV6_NSVC);
|
||||||
osmo_bts_set_feature(bts->features, BTS_FEAT_PAGING_COORDINATION);
|
osmo_bts_set_feature(bts->features, BTS_FEAT_PAGING_COORDINATION);
|
||||||
osmo_bts_set_feature(bts->features, BTS_FEAT_TWTS001);
|
osmo_bts_set_feature(bts->features, BTS_FEAT_TWTS001);
|
||||||
|
osmo_bts_set_feature(bts->features, BTS_FEAT_TWTS002);
|
||||||
|
|
||||||
/* Maximum TA supported by the PHY (can be overridden by PHY specific code) */
|
/* Maximum TA supported by the PHY (can be overridden by PHY specific code) */
|
||||||
bts->support.max_ta = MAX_TA_DEF;
|
bts->support.max_ta = MAX_TA_DEF;
|
||||||
@@ -896,8 +897,8 @@ static bool bts_supports_cm_data(const struct gsm_bts *bts,
|
|||||||
switch (bts->variant) {
|
switch (bts->variant) {
|
||||||
case BTS_OSMO_TRX:
|
case BTS_OSMO_TRX:
|
||||||
switch (cm->chan_rate) {
|
switch (cm->chan_rate) {
|
||||||
/* TODO: RSL_CMOD_CSD_NT_14k5 */
|
case RSL_CMOD_CSD_NT_14k5:
|
||||||
/* TODO: RSL_CMOD_CSD_T_14k4 */
|
case RSL_CMOD_CSD_T_14k4:
|
||||||
case RSL_CMOD_CSD_NT_12k0:
|
case RSL_CMOD_CSD_NT_12k0:
|
||||||
case RSL_CMOD_CSD_T_9k6:
|
case RSL_CMOD_CSD_T_9k6:
|
||||||
if (cm->chan_rt != RSL_CMOD_CRT_TCH_Bm)
|
if (cm->chan_rt != RSL_CMOD_CRT_TCH_Bm)
|
||||||
|
|||||||
228
src/common/csd_rlp.c
Normal file
228
src/common/csd_rlp.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <osmocom/core/bits.h>
|
#include <osmocom/core/bits.h>
|
||||||
@@ -28,33 +29,38 @@
|
|||||||
#include <osmocom/gsm/gsm_utils.h>
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||||
#include <osmocom/isdn/v110.h>
|
#include <osmocom/isdn/v110.h>
|
||||||
|
#include <osmocom/trau/csd_ra2.h>
|
||||||
|
#include <osmocom/trau/csd_raa_prime.h>
|
||||||
|
|
||||||
#include <osmo-bts/csd_v110.h>
|
#include <osmo-bts/csd_v110.h>
|
||||||
#include <osmo-bts/lchan.h>
|
#include <osmo-bts/lchan.h>
|
||||||
|
|
||||||
/* key is enum gsm48_chan_mode, so assuming a value in range 0..255 */
|
/* key is enum gsm48_chan_mode, so assuming a value in range 0..255 */
|
||||||
const struct csd_v110_lchan_desc csd_v110_lchan_desc[256] = {
|
const struct csd_v110_lchan_desc csd_v110_lchan_desc[256] = {
|
||||||
#if 0
|
|
||||||
[GSM48_CMODE_DATA_14k5] = {
|
[GSM48_CMODE_DATA_14k5] = {
|
||||||
/* TCH/F14.4: 290 bits every 20 ms (14.5 kbit/s) */
|
/* TCH/F14.4: 8 * 36 + 2 bits every 20 ms (14.5 kbit/s) */
|
||||||
.fr = { .num_blocks = 1, .num_bits = 290 },
|
.num_frames = 8,
|
||||||
|
.num_frame_bits = 36, /* D-bits */
|
||||||
|
.num_other_bits = 2, /* M-bits */
|
||||||
|
.ra2_ir = 16,
|
||||||
},
|
},
|
||||||
#endif
|
|
||||||
[GSM48_CMODE_DATA_12k0] = {
|
[GSM48_CMODE_DATA_12k0] = {
|
||||||
/* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */
|
/* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */
|
||||||
.fr = { .num_blocks = 4, .num_bits = 60 },
|
.num_frames = 4,
|
||||||
|
.num_frame_bits = 60,
|
||||||
|
.ra2_ir = 16,
|
||||||
},
|
},
|
||||||
[GSM48_CMODE_DATA_6k0] = {
|
[GSM48_CMODE_DATA_6k0] = {
|
||||||
/* TCH/F4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */
|
/* TCH/[FH]4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */
|
||||||
.fr = { .num_blocks = 2, .num_bits = 60 },
|
.num_frames = 2,
|
||||||
/* TCH/H4.8: 4 * 60 bits every 40 ms (6.0 kbit/s) */
|
.num_frame_bits = 60,
|
||||||
.hr = { .num_blocks = 4, .num_bits = 60 },
|
.ra2_ir = 8,
|
||||||
},
|
},
|
||||||
[GSM48_CMODE_DATA_3k6] = {
|
[GSM48_CMODE_DATA_3k6] = {
|
||||||
/* TCH/F2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */
|
/* TCH/[FH]2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */
|
||||||
.fr = { .num_blocks = 2, .num_bits = 36 },
|
.num_frames = 2,
|
||||||
/* TCH/H2.4: 4 * 36 bits every 40 ms (3.6 kbit/s) */
|
.num_frame_bits = 36,
|
||||||
.hr = { .num_blocks = 4, .num_bits = 36 },
|
.ra2_ir = 8,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -74,44 +80,74 @@ static const uint8_t e1e2e3_map[_LCHAN_CSD_M_NUM][3] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
|
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_frame_desc *desc;
|
const struct csd_v110_lchan_desc *desc;
|
||||||
ubit_t ra_bits[80 * 4];
|
ubit_t ra_bits[80 * 4];
|
||||||
|
|
||||||
OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
|
OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
|
||||||
if (lchan->type == GSM_LCHAN_TCH_F)
|
desc = &csd_v110_lchan_desc[lchan->tch_mode];
|
||||||
desc = &csd_v110_lchan_desc[lchan->tch_mode].fr;
|
if (OSMO_UNLIKELY(desc->num_frames == 0))
|
||||||
else
|
|
||||||
desc = &csd_v110_lchan_desc[lchan->tch_mode].hr;
|
|
||||||
if (OSMO_UNLIKELY(desc->num_blocks == 0))
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
|
||||||
|
/* TCH/F14.4 is special: RAA' function is employed */
|
||||||
|
if (lchan->tch_mode == GSM48_CMODE_DATA_14k5) {
|
||||||
|
/* 3GPP TS 44.021, section 10.3 "TCH/F14.4 channel coding"
|
||||||
|
* 3GPP TS 48.020, chapter 11 "THE RAA' FUNCTION" */
|
||||||
|
const ubit_t *m_bits = &data[0]; /* M-bits */
|
||||||
|
const ubit_t *d_bits = &data[2]; /* D-bits */
|
||||||
|
ubit_t c4, c5;
|
||||||
|
|
||||||
|
/* 3GPP TS 48.020, Table 3
|
||||||
|
* | C4 | Date Rate |
|
||||||
|
* | =1 | 14,4 kbit/s |
|
||||||
|
* | =0 | 14.4 kbit/s idle (IWF to BSS only) | */
|
||||||
|
c4 = 1;
|
||||||
|
/* 3GPP TS 48.020, Table 4
|
||||||
|
* | C5 | BSS to IWF FT | IWF to BSS UFE |
|
||||||
|
* | =1 | idle | framing error |
|
||||||
|
* | =0 | data | no framing error | */
|
||||||
|
c5 = 0;
|
||||||
|
|
||||||
|
/* Unless there is a bug, it's highly unlikely */
|
||||||
|
OSMO_ASSERT(data_len == CSD_V110_NUM_BITS(desc));
|
||||||
|
|
||||||
|
osmo_csd144_to_atrau_bits(&ra_bits[0], m_bits, d_bits, c4, c5);
|
||||||
|
goto ra1_ra2;
|
||||||
|
}
|
||||||
|
|
||||||
/* handle empty/incomplete Uplink frames gracefully */
|
/* handle empty/incomplete Uplink frames gracefully */
|
||||||
if (OSMO_UNLIKELY(data_len < (desc->num_blocks * desc->num_bits))) {
|
if (OSMO_UNLIKELY(data_len < CSD_V110_NUM_BITS(desc))) {
|
||||||
/* encode N idle frames as per 3GPP TS 44.021, section 8.1.6 */
|
/* encode N idle frames as per 3GPP TS 44.021, section 8.1.6 */
|
||||||
memset(&ra_bits[0], 0x01, sizeof(ra_bits));
|
memset(&ra_bits[0], 0x01, sizeof(ra_bits));
|
||||||
for (unsigned int i = 0; i < desc->num_blocks; i++)
|
for (unsigned int i = 0; i < desc->num_frames; i++)
|
||||||
memset(&ra_bits[i * 80], 0x00, 8); /* alignment pattern */
|
memset(&ra_bits[i * 80], 0x00, 8); /* alignment pattern */
|
||||||
goto ra1_ra2;
|
goto ra1_ra2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RA1'/RA1: convert from radio rate to an intermediate data rate */
|
/* RA1'/RA1: convert from radio rate to an intermediate data rate */
|
||||||
for (unsigned int i = 0; i < desc->num_blocks; i++) {
|
for (unsigned int i = 0; i < desc->num_frames; i++) {
|
||||||
struct osmo_v110_decoded_frame df;
|
struct osmo_v110_decoded_frame df;
|
||||||
|
|
||||||
/* convert a V.110 36-/60-bit frame to a V.110 80-bit frame */
|
/* convert a V.110 36-/60-bit frame to a V.110 80-bit frame */
|
||||||
if (desc->num_bits == 60)
|
if (desc->num_frame_bits == 60)
|
||||||
osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60);
|
osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60);
|
||||||
else /* desc->num_bits == 36 */
|
else /* desc->num_frame_bits == 36 */
|
||||||
osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36);
|
osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36);
|
||||||
|
|
||||||
/* E1 .. E3 must set by out-of-band knowledge */
|
/* E1 .. E3 must set by out-of-band knowledge */
|
||||||
if (lchan->csd_mode == LCHAN_CSD_M_NT) {
|
if (lchan->csd_mode == LCHAN_CSD_M_NT) {
|
||||||
/* non-transparent: as per 3GPP TS 48.020, Table 7 */
|
/* 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) */
|
/* 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[0] = 0;
|
||||||
df.e_bits[2] = (i >> 0) & 0x01; /* E3: 0 for Q1/Q3, 1 for Q2/Q4 */
|
/* E2: 0 for Q1/Q2, 1 for Q3/Q4 */
|
||||||
|
if (desc->num_frames == 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 {
|
} else {
|
||||||
/* transparent: as per 3GPP TS 44.021, Figure 4 */
|
/* transparent: as per 3GPP TS 44.021, Figure 4 */
|
||||||
df.e_bits[0] = e1e2e3_map[lchan->csd_mode][0]; /* E1 */
|
df.e_bits[0] = e1e2e3_map[lchan->csd_mode][0]; /* E1 */
|
||||||
@@ -124,65 +160,83 @@ int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
|
|||||||
|
|
||||||
ra1_ra2:
|
ra1_ra2:
|
||||||
/* RA1/RA2: convert from an intermediate rate to 64 kbit/s */
|
/* RA1/RA2: convert from an intermediate rate to 64 kbit/s */
|
||||||
if (desc->num_blocks == 4) {
|
if (desc->ra2_ir == 16)
|
||||||
/* 4 * 80 bits (16 kbit/s) => 2 bits per octet */
|
osmo_csd_ra2_16k_pack(&rtp[0], &ra_bits[0], RFC4040_RTP_PLEN);
|
||||||
for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) {
|
else /* desc->ra2_ir == 8 */
|
||||||
rtp[i] = (0xff >> 2);
|
osmo_csd_ra2_8k_pack(&rtp[0], &ra_bits[0], RFC4040_RTP_PLEN);
|
||||||
rtp[i] |= (ra_bits[j++] << 7);
|
|
||||||
rtp[i] |= (ra_bits[j++] << 6);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* 2 * 80 bits (8 kbit/s) => 1 bit per octet */
|
|
||||||
for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++) {
|
|
||||||
rtp[i] = (0xff >> 1);
|
|
||||||
rtp[i] |= (ra_bits[i] << 7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return RFC4040_RTP_PLEN;
|
return RFC4040_RTP_PLEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
|
static bool check_v110_align(const ubit_t *ra_bits)
|
||||||
const uint8_t *rtp, size_t rtp_len)
|
|
||||||
{
|
{
|
||||||
const struct csd_v110_frame_desc *desc;
|
int i;
|
||||||
|
ubit_t bit0 = 0, bit1 = 1;
|
||||||
|
|
||||||
|
/* The weird code structure is for performance optimization,
|
||||||
|
* to avoid conditionals inside loops. */
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
bit0 |= ra_bits[i];
|
||||||
|
for (i = 1; i < 10; i++)
|
||||||
|
bit1 &= ra_bits[i * 8];
|
||||||
|
return (bit0 == 0) && (bit1 == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
|
||||||
|
uint8_t *align_bits, const uint8_t *rtp, size_t rtp_len)
|
||||||
|
{
|
||||||
|
const struct csd_v110_lchan_desc *desc;
|
||||||
ubit_t ra_bits[80 * 4];
|
ubit_t ra_bits[80 * 4];
|
||||||
|
uint8_t align_accum = 0;
|
||||||
|
|
||||||
OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
|
OSMO_ASSERT(lchan->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
|
||||||
if (lchan->type == GSM_LCHAN_TCH_F)
|
desc = &csd_v110_lchan_desc[lchan->tch_mode];
|
||||||
desc = &csd_v110_lchan_desc[lchan->tch_mode].fr;
|
if (OSMO_UNLIKELY(desc->num_frames == 0))
|
||||||
else
|
|
||||||
desc = &csd_v110_lchan_desc[lchan->tch_mode].hr;
|
|
||||||
if (OSMO_UNLIKELY(desc->num_blocks == 0))
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
|
||||||
if (OSMO_UNLIKELY(rtp_len != RFC4040_RTP_PLEN))
|
if (OSMO_UNLIKELY(rtp_len != RFC4040_RTP_PLEN))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* RA1/RA2: convert from 64 kbit/s to an intermediate rate */
|
/* RA1/RA2: convert from 64 kbit/s to an intermediate rate */
|
||||||
if (desc->num_blocks == 4) {
|
if (desc->ra2_ir == 16)
|
||||||
/* 4 * 80 bits (16 kbit/s) => 2 bits per octet */
|
osmo_csd_ra2_16k_unpack(&ra_bits[0], &rtp[0], RFC4040_RTP_PLEN);
|
||||||
for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) {
|
else /* desc->ra2_ir == 8 */
|
||||||
ra_bits[j++] = (rtp[i] >> 7);
|
osmo_csd_ra2_8k_unpack(&ra_bits[0], &rtp[0], RFC4040_RTP_PLEN);
|
||||||
ra_bits[j++] = (rtp[i] >> 6) & 0x01;
|
|
||||||
}
|
/* TCH/F14.4 is special: RAA' function is employed */
|
||||||
} else {
|
if (lchan->tch_mode == GSM48_CMODE_DATA_14k5) {
|
||||||
/* 2 * 80 bits (8 kbit/s) => 1 bit per octet */
|
/* 3GPP TS 44.021, section 10.3 "TCH/F14.4 channel coding"
|
||||||
for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++)
|
* 3GPP TS 48.020, chapter 11 "THE RAA' FUNCTION" */
|
||||||
ra_bits[i] = (rtp[i] >> 7);
|
ubit_t *m_bits = &data[0]; /* M-bits */
|
||||||
|
ubit_t *d_bits = &data[2]; /* D-bits */
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_csd144_from_atrau_bits(m_bits, d_bits, NULL, NULL, &ra_bits[0]);
|
||||||
|
return rc == 0 ? CSD_V110_NUM_BITS(desc) : rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RA1'/RA1: convert from an intermediate rate to radio rate */
|
/* RA1'/RA1: convert from an intermediate rate to radio rate */
|
||||||
for (unsigned int i = 0; i < desc->num_blocks; i++) {
|
for (unsigned int i = 0; i < desc->num_frames; i++) {
|
||||||
struct osmo_v110_decoded_frame df;
|
struct osmo_v110_decoded_frame df;
|
||||||
|
|
||||||
|
/* We require our RTP input to consist of aligned V.110
|
||||||
|
* frames. If we get misaligned input, let's catch it
|
||||||
|
* explicitly, rather than send garbage downstream. */
|
||||||
|
if (!check_v110_align(&ra_bits[i * 80]))
|
||||||
|
return -EINVAL;
|
||||||
/* convert a V.110 80-bit frame to a V.110 36-/60-bit frame */
|
/* convert a V.110 80-bit frame to a V.110 36-/60-bit frame */
|
||||||
osmo_v110_decode_frame(&df, &ra_bits[i * 80], 80);
|
osmo_v110_decode_frame(&df, &ra_bits[i * 80], 80);
|
||||||
if (desc->num_bits == 60)
|
if (desc->num_frame_bits == 60)
|
||||||
osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df);
|
osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df);
|
||||||
else /* desc->num_bits == 36 */
|
else /* desc->num_frame_bits == 36 */
|
||||||
osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df);
|
osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df);
|
||||||
|
/* save bits E2 & E3 that may be needed for RLP alignment */
|
||||||
|
align_accum <<= 2;
|
||||||
|
align_accum |= df.e_bits[1] << 1;
|
||||||
|
align_accum |= df.e_bits[2] << 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return desc->num_blocks * desc->num_bits;
|
if (align_bits)
|
||||||
|
*align_bits = align_accum;
|
||||||
|
return CSD_V110_NUM_BITS(desc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
#include <osmo-bts/pcuif_proto.h>
|
#include <osmo-bts/pcuif_proto.h>
|
||||||
#include <osmo-bts/cbch.h>
|
#include <osmo-bts/cbch.h>
|
||||||
#include <osmo-bts/asci.h>
|
#include <osmo-bts/asci.h>
|
||||||
|
#include <osmo-bts/csd_rlp.h>
|
||||||
#include <osmo-bts/csd_v110.h>
|
#include <osmo-bts/csd_v110.h>
|
||||||
|
|
||||||
/* determine the CCCH block number based on the frame number */
|
/* determine the CCCH block number based on the frame number */
|
||||||
@@ -1490,6 +1491,216 @@ static void fr_hr_efr_dtxd_output(struct gsm_lchan *lchan, uint32_t fn,
|
|||||||
lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = true;
|
lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TDMA frame number of burst 'a' % 26 is the table index.
|
||||||
|
* This mapping is valid for both TCH/H(0) and TCH/H(1). */
|
||||||
|
const uint8_t sched_tchh_dl_csd_map[26] = {
|
||||||
|
[0] = 1, /* TCH/H(0): B0(0 ... 19) */
|
||||||
|
[1] = 1, /* TCH/H(1): B0(1 ... 20) */
|
||||||
|
[8] = 1, /* TCH/H(0): B1(8 ... 2) */
|
||||||
|
[9] = 1, /* TCH/H(1): B1(9 ... 3) */
|
||||||
|
[17] = 1, /* TCH/H(0): B2(17 ... 10) */
|
||||||
|
[18] = 1, /* TCH/H(1): B2(18 ... 11) */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* In the case of half-rate CSD, we need to send TCH.req every 40 ms instead of
|
||||||
|
* every 20 ms. However, both TS 48.103 and common sense desire for
|
||||||
|
* interoperability (between FR and HR, between OsmoBTS and E1 BTS)
|
||||||
|
* require 20 ms RTP packetization interval. Hence a special adapter
|
||||||
|
* function is needed. */
|
||||||
|
static int tch_rts_ind_csd_hr(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;
|
||||||
|
const struct csd_v110_lchan_desc *desc;
|
||||||
|
unsigned bits_per_20ms;
|
||||||
|
struct msgb *input_msg[2], *phy_msg;
|
||||||
|
struct osmo_phsap_prim *resp_l1sap, empty_l1sap;
|
||||||
|
uint8_t *phy_data;
|
||||||
|
struct gsm_time g_time;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* The generic scheduler still sends us TCH-RTS.ind every 20 ms,
|
||||||
|
* hence we have to filter out half of them here. */
|
||||||
|
if (!sched_tchh_dl_csd_map[fn % 26])
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
gsm_fn2gsmtime(&g_time, fn);
|
||||||
|
|
||||||
|
desc = &csd_v110_lchan_desc[lchan->tch_mode];
|
||||||
|
bits_per_20ms = CSD_V110_NUM_BITS(desc);
|
||||||
|
OSMO_ASSERT(bits_per_20ms != 0);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(input_msg); 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[i] = msgb_dequeue_count(&lchan->dl_tch_queue,
|
||||||
|
&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);
|
||||||
|
} else {
|
||||||
|
/* IDLE frame, filled with 1 bits */
|
||||||
|
memset(phy_data, 0x01, bits_per_20ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp_l1sap = &empty_l1sap;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(input_msg); i++) {
|
||||||
|
if (input_msg[i])
|
||||||
|
msgb_free(input_msg[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 */
|
/* TCH-RTS-IND prim received from bts model */
|
||||||
static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
|
static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
|
||||||
struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind)
|
struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind)
|
||||||
@@ -1515,6 +1726,15 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
|
|||||||
LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Rx TCH-RTS.ind\n");
|
LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Rx TCH-RTS.ind\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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) {
|
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
|
||||||
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
|
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
|
||||||
/* FIXME: we _assume_ that we never miss TDMA
|
/* FIXME: we _assume_ that we never miss TDMA
|
||||||
@@ -1561,6 +1781,27 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
|
|||||||
&resp_l1sap, &empty_l1sap);
|
&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:
|
||||||
|
if (resp_msg != NULL) {
|
||||||
|
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));
|
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
|
||||||
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
|
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
|
||||||
resp_msg);
|
resp_msg);
|
||||||
@@ -1850,119 +2091,16 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
|
|||||||
return 1;
|
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;
|
|
||||||
struct osmo_rlp_frame_decoded rlpf;
|
|
||||||
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) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_ul_rtp_packet_data(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
|
|
||||||
const uint8_t *data, uint16_t data_len)
|
|
||||||
{
|
|
||||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
|
||||||
uint8_t rtp_pl[RFC4040_RTP_PLEN];
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
gsmtap_csd_rlp_process(lchan, true, tch_ind, data, data_len);
|
|
||||||
|
|
||||||
rc = csd_v110_rtp_encode(lchan, &rtp_pl[0], data, data_len);
|
|
||||||
if (rc < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL);
|
|
||||||
if (lchan->rtp_tx_marker)
|
|
||||||
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_MARKER);
|
|
||||||
|
|
||||||
osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
|
|
||||||
&rtp_pl[0], sizeof(rtp_pl),
|
|
||||||
fn_ms_adj(tch_ind->fn, lchan),
|
|
||||||
lchan->rtp_tx_marker);
|
|
||||||
/* Only clear the marker bit once we have sent a RTP packet with it */
|
|
||||||
lchan->rtp_tx_marker = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* a helper function for the logic in l1sap_tch_ind() */
|
/* a helper function for the logic in l1sap_tch_ind() */
|
||||||
static void send_ul_rtp_packet_speech(struct gsm_lchan *lchan, uint32_t fn,
|
static void send_ul_rtp_packet(struct gsm_lchan *lchan, uint32_t fn,
|
||||||
const uint8_t *rtp_pl, uint16_t rtp_pl_len)
|
const uint8_t *rtp_pl, uint16_t rtp_pl_len)
|
||||||
{
|
{
|
||||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||||
|
|
||||||
if (lchan->abis_ip.osmux.use) {
|
if (lchan->abis_ip.osmux.use) {
|
||||||
lchan_osmux_send_frame(lchan, rtp_pl, rtp_pl_len,
|
lchan_osmux_send_frame(lchan, rtp_pl, rtp_pl_len,
|
||||||
fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
|
fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
|
||||||
} else if (lchan->abis_ip.rtp_socket) {
|
} else if (lchan->abis_ip.rtp_socket != NULL) {
|
||||||
osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
|
osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
|
||||||
rtp_pl, rtp_pl_len, fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
|
rtp_pl, rtp_pl_len, fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
|
||||||
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL);
|
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL);
|
||||||
@@ -1973,36 +2111,106 @@ static void send_ul_rtp_packet_speech(struct gsm_lchan *lchan, uint32_t fn,
|
|||||||
lchan->rtp_tx_marker = false;
|
lchan->rtp_tx_marker = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a helper function for emitting HR1 UL in RFC 5993 format */
|
/* A modified version of send_ul_rtp_packet() that skips fn_ms_adj() check:
|
||||||
static void send_rtp_rfc5993(struct gsm_lchan *lchan, uint32_t fn,
|
* this check will produce a lot of noise when we send two RTP packets
|
||||||
struct msgb *msg)
|
* back-to-back every 40 ms on the same frame number, */
|
||||||
|
static void send_ul_rtp_packet_hrdata(struct gsm_lchan *lchan,
|
||||||
|
const uint8_t *rtp_pl, uint16_t rtp_pl_len)
|
||||||
{
|
{
|
||||||
uint8_t toc;
|
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||||
|
|
||||||
OSMO_ASSERT(msg->len == GSM_HR_BYTES);
|
if (lchan->abis_ip.rtp_socket != NULL) {
|
||||||
/* FIXME: implement proper SID classification per GSM 06.41 section
|
osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
|
||||||
* 6.1.1; see OS#6036. Until then, detect error-free SID frames
|
rtp_pl, rtp_pl_len,
|
||||||
* using our existing osmo_hr_check_sid() function. */
|
GSM_RTP_DURATION,
|
||||||
if (osmo_hr_check_sid(msg->data, msg->len))
|
lchan->rtp_tx_marker);
|
||||||
toc = 0x20;
|
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL);
|
||||||
|
if (lchan->rtp_tx_marker)
|
||||||
|
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_MARKER);
|
||||||
|
}
|
||||||
|
/* Only clear the marker bit once we have sent a RTP packet with it */
|
||||||
|
lchan->rtp_tx_marker = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = csd_tchf48_nt_e2_map[tch_ind->fn % 26];
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
gsmtap_csd_rlp_process(lchan, true, tch_ind, 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;
|
||||||
|
|
||||||
|
send_ul_rtp_packet(lchan, tch_ind->fn, rtp_pl, sizeof(rtp_pl));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_csd_hr_bfi(struct gsm_lchan *lchan)
|
||||||
|
{
|
||||||
|
uint8_t rtp_pl[RFC4040_RTP_PLEN];
|
||||||
|
int rc, i;
|
||||||
|
|
||||||
|
rc = csd_v110_rtp_encode(lchan, rtp_pl, NULL, 0, 0);
|
||||||
|
if (rc < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
send_ul_rtp_packet_hrdata(lchan, rtp_pl, sizeof(rtp_pl));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tch_ind_csd_hr(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
|
||||||
|
const uint8_t *data, uint16_t data_len)
|
||||||
|
{
|
||||||
|
const struct csd_v110_lchan_desc *desc;
|
||||||
|
unsigned bits_per_20ms;
|
||||||
|
uint8_t rtp_pl[RFC4040_RTP_PLEN];
|
||||||
|
int rc, i;
|
||||||
|
|
||||||
|
desc = &csd_v110_lchan_desc[lchan->tch_mode];
|
||||||
|
bits_per_20ms = CSD_V110_NUM_BITS(desc);
|
||||||
|
OSMO_ASSERT(bits_per_20ms != 0);
|
||||||
|
|
||||||
|
if (data_len != bits_per_20ms * 2) {
|
||||||
|
handle_csd_hr_bfi(lchan);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gsmtap_csd_rlp_process(lchan, true, tch_ind, data, data_len);
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
rc = csd_v110_rtp_encode(lchan, rtp_pl,
|
||||||
|
data + i * bits_per_20ms,
|
||||||
|
bits_per_20ms, i);
|
||||||
|
if (rc < 0)
|
||||||
|
return;
|
||||||
|
send_ul_rtp_packet_hrdata(lchan, rtp_pl, sizeof(rtp_pl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tch_ind_csd(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind,
|
||||||
|
const uint8_t *data, uint16_t data_len)
|
||||||
|
{
|
||||||
|
if (lchan->type == GSM_LCHAN_TCH_F)
|
||||||
|
handle_tch_ind_csd_fr(lchan, tch_ind, data, data_len);
|
||||||
else
|
else
|
||||||
toc = 0x00;
|
handle_tch_ind_csd_hr(lchan, tch_ind, data, data_len);
|
||||||
msgb_push_u8(msg, toc);
|
|
||||||
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a helper function for emitting FR/EFR UL in TW-TS-001 format */
|
/* a helper function for emitting FR/EFR UL in TW-TS-001 format */
|
||||||
static void send_rtp_twts001(struct gsm_lchan *lchan, uint32_t fn,
|
static void send_rtp_twts001(struct gsm_lchan *lchan, uint32_t fn,
|
||||||
struct msgb *msg, bool good_frame)
|
struct msgb *msg)
|
||||||
{
|
{
|
||||||
uint8_t teh;
|
uint8_t teh;
|
||||||
bool send_frame;
|
bool send_frame;
|
||||||
|
|
||||||
if (msg->len == GSM_FR_BYTES || msg->len == GSM_EFR_BYTES) {
|
if (msg->len == GSM_FR_BYTES || msg->len == GSM_EFR_BYTES) {
|
||||||
if (good_frame)
|
teh = 0xE0;
|
||||||
teh = 0xE0;
|
if (tch_ul_msg_bfi(msg))
|
||||||
else
|
teh |= 0x02;
|
||||||
teh = 0xE2;
|
|
||||||
send_frame = true;
|
send_frame = true;
|
||||||
} else {
|
} else {
|
||||||
teh = 0xE6;
|
teh = 0xE6;
|
||||||
@@ -2015,18 +2223,69 @@ static void send_rtp_twts001(struct gsm_lchan *lchan, uint32_t fn,
|
|||||||
teh |= 0x01;
|
teh |= 0x01;
|
||||||
if (send_frame) {
|
if (send_frame) {
|
||||||
msgb_push_u8(msg, teh);
|
msgb_push_u8(msg, teh);
|
||||||
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
|
send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
|
||||||
} else {
|
} else {
|
||||||
send_ul_rtp_packet_speech(lchan, fn, &teh, 1);
|
send_ul_rtp_packet(lchan, fn, &teh, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See Section 5.2 of RFC5993 and TW-TS-002 */
|
||||||
|
enum super5993_ft {
|
||||||
|
FT_GOOD_SPEECH = 0,
|
||||||
|
FT_INVALID_SID = 1,
|
||||||
|
FT_GOOD_SID = 2,
|
||||||
|
FT_BFI_WITH_DATA = 6,
|
||||||
|
FT_NO_DATA = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* a helper function for emitting GSM-HR UL in TW-TS-002 format */
|
||||||
|
static void send_rtp_twts002(struct gsm_lchan *lchan, uint32_t fn,
|
||||||
|
struct msgb *msg)
|
||||||
|
{
|
||||||
|
enum super5993_ft ft;
|
||||||
|
uint8_t toc;
|
||||||
|
|
||||||
|
if (msg->len == GSM_HR_BYTES) {
|
||||||
|
switch (tch_ul_msg_hr_sid(msg)) {
|
||||||
|
case OSMO_GSM631_SID_CLASS_SPEECH:
|
||||||
|
ft = tch_ul_msg_bfi(msg) ? FT_BFI_WITH_DATA
|
||||||
|
: FT_GOOD_SPEECH;
|
||||||
|
break;
|
||||||
|
case OSMO_GSM631_SID_CLASS_INVALID:
|
||||||
|
ft = FT_INVALID_SID;
|
||||||
|
break;
|
||||||
|
case OSMO_GSM631_SID_CLASS_VALID:
|
||||||
|
ft = tch_ul_msg_bfi(msg) ? FT_INVALID_SID : FT_GOOD_SID;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ft = FT_NO_DATA;
|
||||||
|
}
|
||||||
|
/* ToC octet of TW-TS-002 is an extension of RFC 5993 */
|
||||||
|
toc = ft << 4;
|
||||||
|
if (ft == FT_INVALID_SID)
|
||||||
|
toc |= 0x04; /* TW-TS-002 version 1.2.0 */
|
||||||
|
/* always set DTXd and TAF bits */
|
||||||
|
if (lchan->ts->trx->bts->dtxd)
|
||||||
|
toc |= 0x08;
|
||||||
|
if (fr_hr_efr_sid_position(lchan, fn))
|
||||||
|
toc |= 0x01;
|
||||||
|
if (ft != FT_NO_DATA) {
|
||||||
|
msgb_push_u8(msg, toc);
|
||||||
|
send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
|
||||||
|
} else {
|
||||||
|
send_ul_rtp_packet(lchan, fn, &toc, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A helper function for l1sap_tch_ind(): handling BFI
|
/* A helper function for l1sap_tch_ind(): handling BFI
|
||||||
*
|
*
|
||||||
* Please note that the msgb passed to this function is used only when
|
* Please note that the msgb passed to this function is used only when
|
||||||
* the CN asked the BSS to emit extended RTP formats (currently TW-TS-001,
|
* the CN asked the BSS to emit extended RTP formats of TW-TS-001 or
|
||||||
* later TW-TS-002 as well) that can indicate BFI along with deemed-bad
|
* TW-TS-002 that can indicate BFI along with deemed-bad frame data bits,
|
||||||
* frame data bits, just like GSM 08.60 and 08.61 TRAU-UL frames.
|
* just like GSM 08.60 and 08.61 TRAU-UL frames.
|
||||||
*/
|
*/
|
||||||
static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
|
static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
|
||||||
const struct gsm_time *g_time, struct msgb *msg)
|
const struct gsm_time *g_time, struct msgb *msg)
|
||||||
@@ -2045,7 +2304,15 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
|
|||||||
lchan->type == GSM_LCHAN_TCH_F &&
|
lchan->type == GSM_LCHAN_TCH_F &&
|
||||||
(lchan->tch_mode == GSM48_CMODE_SPEECH_V1 ||
|
(lchan->tch_mode == GSM48_CMODE_SPEECH_V1 ||
|
||||||
lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)) {
|
lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)) {
|
||||||
send_rtp_twts001(lchan, fn, msg, false);
|
send_rtp_twts001(lchan, fn, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ditto for TCH/HS and TW-TS-002. */
|
||||||
|
if ((lchan->abis_ip.rtp_extensions & OSMO_RTP_EXT_TWTS002) &&
|
||||||
|
lchan->type == GSM_LCHAN_TCH_H &&
|
||||||
|
lchan->tch_mode == GSM48_CMODE_SPEECH_V1) {
|
||||||
|
send_rtp_twts002(lchan, fn, msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2056,7 +2323,7 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
|
|||||||
/* did it actually give us some output? */
|
/* did it actually give us some output? */
|
||||||
if (rc > 0) {
|
if (rc > 0) {
|
||||||
/* yes, send it out in RTP */
|
/* yes, send it out in RTP */
|
||||||
send_ul_rtp_packet_speech(lchan, fn, ecu_out, rc);
|
send_ul_rtp_packet(lchan, fn, ecu_out, rc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2064,7 +2331,7 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
|
|||||||
/* Are we in rtp continuous-streaming special mode? If so, send out
|
/* Are we in rtp continuous-streaming special mode? If so, send out
|
||||||
* a BFI packet as zero-length RTP payload. */
|
* a BFI packet as zero-length RTP payload. */
|
||||||
if (lchan->ts->trx->bts->rtp_nogaps_mode) {
|
if (lchan->ts->trx->bts->rtp_nogaps_mode) {
|
||||||
send_ul_rtp_packet_speech(lchan, fn, NULL, 0);
|
send_ul_rtp_packet(lchan, fn, NULL, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2079,6 +2346,164 @@ static void tch_ul_bfi_handler(struct gsm_lchan *lchan,
|
|||||||
lchan->rtp_tx_marker = true;
|
lchan->rtp_tx_marker = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Helper function for l1sap_tch_ind(): RTP output for GSM-HR in either
|
||||||
|
* of the two standard, non-ThemWi-extended payload formats, with restrictions
|
||||||
|
* inherent to these non-TRAU-UL-like formats. */
|
||||||
|
static void send_gsmhr_std_rtp(struct gsm_lchan *lchan,
|
||||||
|
const struct gsm_time *g_time, struct msgb *msg,
|
||||||
|
bool emit_rfc5993)
|
||||||
|
{
|
||||||
|
uint32_t fn = g_time->fn;
|
||||||
|
|
||||||
|
OSMO_ASSERT(msg->len == GSM_HR_BYTES);
|
||||||
|
|
||||||
|
switch (tch_ul_msg_hr_sid(msg)) {
|
||||||
|
case OSMO_GSM631_SID_CLASS_SPEECH:
|
||||||
|
break;
|
||||||
|
case OSMO_GSM631_SID_CLASS_INVALID:
|
||||||
|
/* neither of these RTP formats allows invalid SID */
|
||||||
|
tch_ul_bfi_handler(lchan, g_time, msg);
|
||||||
|
return;
|
||||||
|
case OSMO_GSM631_SID_CLASS_VALID:
|
||||||
|
/* both formats require perfect, error-free SID output */
|
||||||
|
osmo_hr_sid_reset(msg->data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Are we emitting "bare" TS 101 318 or "decorated" RFC 5993? */
|
||||||
|
if (emit_rfc5993) {
|
||||||
|
uint8_t toc = tch_ul_msg_hr_sid(msg) << 4;
|
||||||
|
msgb_push_u8(msg, toc);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper function for l1sap_tch_ind(): SID classification and related logic
|
||||||
|
* for FR, HR and EFR speech codecs. */
|
||||||
|
static void tch_ul_fr_hr_efr(struct gsm_lchan *lchan, uint32_t fn, struct msgb *msg)
|
||||||
|
{
|
||||||
|
enum osmo_gsm631_sid_class sidc;
|
||||||
|
|
||||||
|
/* No matter what else is happening, even if we are about to bail out
|
||||||
|
* early because we received FACCH and thus BFI-no-data, if we are
|
||||||
|
* at a mandatory-Tx position for SID, we need to clear UL SID filter
|
||||||
|
* state so that the next valid SID frame will be allowed through,
|
||||||
|
* whether it occurs right now or in a later frame position because
|
||||||
|
* of FACCH. See the note in GSM 06.31 section 5.1.2: if the
|
||||||
|
* SACCH-aligned SID update position is stolen by FACCH, the next
|
||||||
|
* frame position shall carry the SID update as soon as FACCH stealing
|
||||||
|
* is over. */
|
||||||
|
if (fr_hr_efr_sid_position(lchan, fn))
|
||||||
|
lchan->tch.dtx_fr_hr_efr.ul_sid_filter = false;
|
||||||
|
|
||||||
|
/* If we got no payload (BFI without data), there is nothing more
|
||||||
|
* for us to do here. */
|
||||||
|
if (msg->len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* GSM 06.31, 06.41 and 06.81 are DTX specs for FR, HR and EFR,
|
||||||
|
* respectively. Section 6.1.1 in each of these specs defines a
|
||||||
|
* ternary SID classification whereby each channel-decoded traffic
|
||||||
|
* frame is valid SID, invalid SID or non-SID speech. Perform
|
||||||
|
* this classification. */
|
||||||
|
switch (lchan->tch_mode) {
|
||||||
|
case GSM48_CMODE_SPEECH_V1:
|
||||||
|
if (lchan->type == GSM_LCHAN_TCH_F) {
|
||||||
|
sidc = osmo_fr_sid_classify(msg->data);
|
||||||
|
} else {
|
||||||
|
/* None of our current BTS models has UFI or BCI
|
||||||
|
* error flags for TCH/HS UL Rx, hence we have to
|
||||||
|
* perform SID classification without BCI. */
|
||||||
|
sidc = osmo_hr_sid_classify(msg->data, false, NULL);
|
||||||
|
/* Pass it to RTP output functions */
|
||||||
|
tch_ul_msg_hr_sid(msg) = sidc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GSM48_CMODE_SPEECH_EFR:
|
||||||
|
sidc = osmo_efr_sid_classify(msg->data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* This static function should never be called except for
|
||||||
|
* V1 and EFR speech modes. */
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We use this SID classification for three purposes:
|
||||||
|
*
|
||||||
|
* 1) For those users who desire to have RTP marker bit set in the
|
||||||
|
* output packet corresponding to the first speech frame after
|
||||||
|
* a DTX pause, the logic of lchan_set_marker() is driven by
|
||||||
|
* the determination of whether or not each received traffic frame
|
||||||
|
* is an accepted SID frame in the definition of GSM 06.31
|
||||||
|
* and its HR & EFR counterparts.
|
||||||
|
*
|
||||||
|
* 2) As a result of how FR/HR/EFR DTX interacts with block diagonal
|
||||||
|
* interleaving, at the beginning and end of each DTX pause a
|
||||||
|
* correctly functioning TCH receiver will always pick up an
|
||||||
|
* artifact consisting of 4 received bursts (2 for TCH/HS)
|
||||||
|
* and same number of omitted bursts. Standard channel decoding
|
||||||
|
* of this Rx artifact will produce a "half-block" in which half
|
||||||
|
* of the bits prior to convolutional decoding will come from
|
||||||
|
* a SID repetition whose Tx was notionally suppressed, while
|
||||||
|
* the other half will be garbage. As a result of convolutional
|
||||||
|
* decoding, the result will often appear as valid SID per
|
||||||
|
* classification rules - but passing it as such to the Rx DTX
|
||||||
|
* handler is wrong. Classic E1 BTS and GSM MS implementations
|
||||||
|
* include a kind of SID filter at this point, setting BFI on
|
||||||
|
* these received half-blocks, so that the Rx DTX handler will
|
||||||
|
* see an invalid SID condition. Invalid SID means that comfort
|
||||||
|
* noise generation is to be continued, but no updated CN
|
||||||
|
* parameters are available - which is the truth in half-block
|
||||||
|
* Rx situations. We implement the same filter. More info here:
|
||||||
|
*
|
||||||
|
* https://osmocom.org/projects/retro-gsm/wiki/DTXu_half-blocks
|
||||||
|
*
|
||||||
|
* 3) For HR codec only, RTP output format functions need to know
|
||||||
|
* both BFI flag and SID classification.
|
||||||
|
*/
|
||||||
|
switch (sidc) {
|
||||||
|
case OSMO_GSM631_SID_CLASS_SPEECH:
|
||||||
|
/* Only a good speech frame, not an unusable frame,
|
||||||
|
* using GSM 06.31 definitions, marks exit from a DTX pause. */
|
||||||
|
if (!tch_ul_msg_bfi(msg)) {
|
||||||
|
lchan_set_marker(false, lchan);
|
||||||
|
lchan->tch.dtx_fr_hr_efr.ul_sid_filter = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OSMO_GSM631_SID_CLASS_VALID:
|
||||||
|
/* Here comes the just-described SID filter. The logic is
|
||||||
|
* thus: a valid SID is allowed through to the Rx DTX handler
|
||||||
|
* if it follows speech (end of talkspurt), if it appears
|
||||||
|
* in the expected mandatory-Tx position for SID updates,
|
||||||
|
* or if it happens after that mandatory-Tx position due to
|
||||||
|
* FACCH stealing. Otherwise, valid SID is converted to
|
||||||
|
* invalid by setting BFI.
|
||||||
|
*/
|
||||||
|
if (lchan->tch.dtx_fr_hr_efr.ul_sid_filter)
|
||||||
|
tch_ul_msg_bfi(msg) = true;
|
||||||
|
/* fall through */
|
||||||
|
case OSMO_GSM631_SID_CLASS_INVALID:
|
||||||
|
/* Whether we got valid or invalid SID, we know that the MS
|
||||||
|
* has entered or remains in a DTXu pause. (Invalid SID
|
||||||
|
* means that MS state is known to be DTXu, but no CN
|
||||||
|
* parameters are available.) Hence we indicate DTXu pause
|
||||||
|
* state for both RTP marker bit and SID filter mechanisms.
|
||||||
|
*/
|
||||||
|
lchan_set_marker(true, lchan);
|
||||||
|
lchan->tch.dtx_fr_hr_efr.ul_sid_filter = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* There are only 3 possible SID classifications per
|
||||||
|
* section 6.1.1 of each of the three DTX specs,
|
||||||
|
* and correspondingly only 3 possible output values
|
||||||
|
* from osmo_*_sid_classify() functions. */
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* TCH received from bts model */
|
/* TCH received from bts model */
|
||||||
static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
|
static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
|
||||||
struct ph_tch_param *tch_ind)
|
struct ph_tch_param *tch_ind)
|
||||||
@@ -2118,10 +2543,35 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
|
|||||||
msgb_pull_to_l2(msg);
|
msgb_pull_to_l2(msg);
|
||||||
|
|
||||||
/* Low level layers always call us when TCH content is expected, even if
|
/* Low level layers always call us when TCH content is expected, even if
|
||||||
* the content is not available due to decoding issues. Content not
|
* the content is not available due to decoding issues. Content not
|
||||||
* available is expected as empty payload. We also check if quality is
|
* available is indicated as empty payload, also termed BFI w/o data.
|
||||||
* good enough. */
|
* Alternatively, a BTS model can supply channel-decoded payload bits,
|
||||||
if (msg->len && tch_ind->lqual_cb >= bts->min_qual_norm) {
|
* but also set BFI flag, just like in TRAU-UL frames on E1 Abis.
|
||||||
|
*
|
||||||
|
* If the channel mode is speech (not CSD), we also check if quality is
|
||||||
|
* good enough - if it isn't, we set BFI. This quality check is
|
||||||
|
* essential with FRv1 codec and DTXu, otherwise DTXu pauses will be
|
||||||
|
* filled with very unpleasant sounds as channel-decoded radio noise
|
||||||
|
* gets declared as good traffic frames with 1/8 probability given
|
||||||
|
* only a 3-bit CRC. However, this check is restricted to speech
|
||||||
|
* because per the specs, BFI does not exist in CSD: every channel-
|
||||||
|
* decoded frame is passed along, error handling either falls on RLP
|
||||||
|
* or is the responsibility of user applications in Transparent mode.
|
||||||
|
*/
|
||||||
|
if ((lchan->rsl_cmode == RSL_CMOD_SPD_SPEECH) &&
|
||||||
|
(tch_ind->lqual_cb < bts->min_qual_norm))
|
||||||
|
tch_ul_msg_bfi(msg) = true;
|
||||||
|
|
||||||
|
/* For FR, HR or EFR speech we also have to perform SID classification
|
||||||
|
* and apply logic that results from such. This logic can also set
|
||||||
|
* BFI that wasn't set before! */
|
||||||
|
if (lchan->tch_mode == GSM48_CMODE_SPEECH_V1 ||
|
||||||
|
lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)
|
||||||
|
tch_ul_fr_hr_efr(lchan, fn, msg);
|
||||||
|
|
||||||
|
/* Good RTP output happens when we got some payload AND it is not
|
||||||
|
* marked as BFI. */
|
||||||
|
if (msg->len && !tch_ul_msg_bfi(msg)) {
|
||||||
/* feed the good frame to the ECU, if we are applying one */
|
/* feed the good frame to the ECU, if we are applying one */
|
||||||
if (lchan->ecu_state)
|
if (lchan->ecu_state)
|
||||||
osmo_ecu_frame_in(lchan->ecu_state, false, msg->data, msg->len);
|
osmo_ecu_frame_in(lchan->ecu_state, false, msg->data, msg->len);
|
||||||
@@ -2134,24 +2584,26 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
|
|||||||
lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)) {
|
lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)) {
|
||||||
/* FR and EFR codecs */
|
/* FR and EFR codecs */
|
||||||
if (lchan->abis_ip.rtp_extensions & OSMO_RTP_EXT_TWTS001)
|
if (lchan->abis_ip.rtp_extensions & OSMO_RTP_EXT_TWTS001)
|
||||||
send_rtp_twts001(lchan, fn, msg, true);
|
send_rtp_twts001(lchan, fn, msg);
|
||||||
else
|
else
|
||||||
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
|
send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
|
||||||
} else if (lchan->type == GSM_LCHAN_TCH_H &&
|
} else if (lchan->type == GSM_LCHAN_TCH_H &&
|
||||||
lchan->tch_mode == GSM48_CMODE_SPEECH_V1) {
|
lchan->tch_mode == GSM48_CMODE_SPEECH_V1) {
|
||||||
/* HR codec: TS 101 318 or RFC 5993,
|
/* HR codec: TW-TS-002 in ThemWi environment,
|
||||||
* will also support TW-TS-002 in the future. */
|
* or TS 101 318 or RFC 5993 in traditional
|
||||||
if (bts->emit_hr_rfc5993)
|
* 3GPP or Osmocom environments. */
|
||||||
send_rtp_rfc5993(lchan, fn, msg);
|
if (lchan->abis_ip.rtp_extensions & OSMO_RTP_EXT_TWTS002)
|
||||||
|
send_rtp_twts002(lchan, fn, msg);
|
||||||
else
|
else
|
||||||
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
|
send_gsmhr_std_rtp(lchan, &g_time, msg,
|
||||||
|
bts->emit_hr_rfc5993);
|
||||||
} else {
|
} else {
|
||||||
/* generic case, no RTP alterations */
|
/* generic case, no RTP alterations */
|
||||||
send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
|
send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RSL_CMOD_SPD_DATA:
|
case RSL_CMOD_SPD_DATA:
|
||||||
send_ul_rtp_packet_data(lchan, tch_ind, msg->data, msg->len);
|
handle_tch_ind_csd(lchan, tch_ind, msg->data, msg->len);
|
||||||
break;
|
break;
|
||||||
case RSL_CMOD_SPD_SIGN:
|
case RSL_CMOD_SPD_SIGN:
|
||||||
return 0; /* drop stale TCH.ind */
|
return 0; /* drop stale TCH.ind */
|
||||||
@@ -2166,7 +2618,11 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tch_ul_bfi_handler(lchan, &g_time, msg);
|
if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA &&
|
||||||
|
lchan->type == GSM_LCHAN_TCH_H)
|
||||||
|
handle_csd_hr_bfi(lchan);
|
||||||
|
else
|
||||||
|
tch_ul_bfi_handler(lchan, &g_time, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
lchan->tch.last_fn = fn;
|
lchan->tch.last_fn = fn;
|
||||||
@@ -2433,6 +2889,7 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
|||||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
bool rfc5993_sid = false;
|
bool rfc5993_sid = false;
|
||||||
|
uint8_t csd_align_bits = 0;
|
||||||
|
|
||||||
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_TOTAL);
|
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_TOTAL);
|
||||||
if (marker)
|
if (marker)
|
||||||
@@ -2460,18 +2917,16 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
|||||||
OSMO_ASSERT(0);
|
OSMO_ASSERT(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = l1sap_msgb_alloc(512);
|
#define L1SAP_MSGB_L2LEN_TCH 512
|
||||||
|
|
||||||
|
msg = l1sap_msgb_alloc(L1SAP_MSGB_L2LEN_TCH);
|
||||||
if (!msg)
|
if (!msg)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) {
|
if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) {
|
||||||
int rc = csd_v110_rtp_decode(lchan, msg->tail,
|
int rc = csd_v110_rtp_decode(lchan, msg->tail, &csd_align_bits,
|
||||||
rtp_pl, rtp_pl_len);
|
rtp_pl, rtp_pl_len);
|
||||||
if (rc > 0) {
|
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);
|
msgb_put(msg, rc);
|
||||||
} else {
|
} else {
|
||||||
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_V110_DEC);
|
rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_V110_DEC);
|
||||||
@@ -2479,6 +2934,12 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (OSMO_UNLIKELY(rtp_pl_len > L1SAP_MSGB_L2LEN_TCH)) {
|
||||||
|
LOGPLCHAN(lchan, DL1P, LOGL_ERROR,
|
||||||
|
"%s(): incoming RTP truncated: %u -> %u\n",
|
||||||
|
__func__, rtp_pl_len, L1SAP_MSGB_L2LEN_TCH);
|
||||||
|
rtp_pl_len = L1SAP_MSGB_L2LEN_TCH; /* truncate */
|
||||||
|
}
|
||||||
memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
|
memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2492,6 +2953,8 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
|||||||
rtpmsg_ts(msg) = timestamp;
|
rtpmsg_ts(msg) = timestamp;
|
||||||
/* Store RFC 5993 SID flag likewise */
|
/* Store RFC 5993 SID flag likewise */
|
||||||
rtpmsg_is_rfc5993_sid(msg) = rfc5993_sid;
|
rtpmsg_is_rfc5993_sid(msg) = rfc5993_sid;
|
||||||
|
/* ditto with CSD alignment bits */
|
||||||
|
rtpmsg_csd_align_bits(msg) = csd_align_bits;
|
||||||
|
|
||||||
/* make sure the queue doesn't get too long */
|
/* make sure the queue doesn't get too long */
|
||||||
lchan_dl_tch_queue_enqueue(lchan, msg, 1);
|
lchan_dl_tch_queue_enqueue(lchan, msg, 1);
|
||||||
|
|||||||
@@ -56,6 +56,20 @@ const struct value_string lchan_ciph_state_names[] = {
|
|||||||
{ 0, NULL }
|
{ 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const struct value_string lchan_csd_mode_descs[] = {
|
||||||
|
{ LCHAN_CSD_M_NT, "non-transparent" },
|
||||||
|
{ LCHAN_CSD_M_T_1200_75, "transparent @ 1200/75 bps" },
|
||||||
|
{ LCHAN_CSD_M_T_600, "transparent @ 600 bps" },
|
||||||
|
{ LCHAN_CSD_M_T_1200, "transparent @ 1200 bps" },
|
||||||
|
{ LCHAN_CSD_M_T_2400, "transparent @ 2400 bps" },
|
||||||
|
{ LCHAN_CSD_M_T_4800, "transparent @ 4800 bps" },
|
||||||
|
{ LCHAN_CSD_M_T_9600, "transparent @ 9600 bps" },
|
||||||
|
{ LCHAN_CSD_M_T_14400, "transparent @ 14400 bps" },
|
||||||
|
{ LCHAN_CSD_M_T_29000, "transparent @ 29000 bps" },
|
||||||
|
{ LCHAN_CSD_M_T_32000, "transparent @ 32000 bps" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
/* prepare the per-SAPI T200 arrays for a given lchan */
|
/* prepare the per-SAPI T200 arrays for a given lchan */
|
||||||
static int t200_by_lchan(uint32_t *t200_fn_dcch, uint32_t *t200_fn_acch, struct gsm_lchan *lchan)
|
static int t200_by_lchan(uint32_t *t200_fn_dcch, uint32_t *t200_fn_acch, struct gsm_lchan *lchan)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ int bts_main(int argc, char **argv)
|
|||||||
/* TODO: move this to gsm_bts_alloc() */
|
/* TODO: move this to gsm_bts_alloc() */
|
||||||
if (g_bts->gsmtap.remote_host != NULL) {
|
if (g_bts->gsmtap.remote_host != NULL) {
|
||||||
LOGP(DLGLOBAL, LOGL_NOTICE,
|
LOGP(DLGLOBAL, LOGL_NOTICE,
|
||||||
"Setting up GSMTAP Um forwarding '%s->'%s:%u'\n",
|
"Setting up GSMTAP Um forwarding '%s'->'%s:%u'\n",
|
||||||
g_bts->gsmtap.local_host, g_bts->gsmtap.remote_host, GSMTAP_UDP_PORT);
|
g_bts->gsmtap.local_host, g_bts->gsmtap.remote_host, GSMTAP_UDP_PORT);
|
||||||
g_bts->gsmtap.inst = gsmtap_source_init2(g_bts->gsmtap.local_host, 0,
|
g_bts->gsmtap.inst = gsmtap_source_init2(g_bts->gsmtap.local_host, 0,
|
||||||
g_bts->gsmtap.remote_host, GSMTAP_UDP_PORT, 1);
|
g_bts->gsmtap.remote_host, GSMTAP_UDP_PORT, 1);
|
||||||
|
|||||||
@@ -36,10 +36,10 @@
|
|||||||
#include <osmocom/core/msgb.h>
|
#include <osmocom/core/msgb.h>
|
||||||
#include <osmocom/core/utils.h>
|
#include <osmocom/core/utils.h>
|
||||||
#include <osmocom/gsm/protocol/gsm_12_21.h>
|
#include <osmocom/gsm/protocol/gsm_12_21.h>
|
||||||
|
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||||
#include <osmocom/gsm/abis_nm.h>
|
#include <osmocom/gsm/abis_nm.h>
|
||||||
#include <osmocom/gsm/tlv.h>
|
#include <osmocom/gsm/tlv.h>
|
||||||
#include <osmocom/abis/e1_input.h>
|
#include <osmocom/abis/e1_input.h>
|
||||||
#include <osmocom/abis/ipaccess.h>
|
|
||||||
|
|
||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
#include <osmo-bts/gsm_data.h>
|
#include <osmo-bts/gsm_data.h>
|
||||||
|
|||||||
@@ -858,25 +858,31 @@ static int pcu_tx_si_all(struct gsm_bts *bts)
|
|||||||
static int pcu_rx_txt_ind(struct gsm_bts *bts,
|
static int pcu_rx_txt_ind(struct gsm_bts *bts,
|
||||||
struct gsm_pcu_if_txt_ind *txt)
|
struct gsm_pcu_if_txt_ind *txt)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc = 0;
|
||||||
|
|
||||||
switch (txt->type) {
|
switch (txt->type) {
|
||||||
case PCU_VERSION:
|
case PCU_VERSION:
|
||||||
LOGP(DPCU, LOGL_INFO, "OsmoPCU version %s connected\n",
|
LOGP(DPCU, LOGL_INFO, "OsmoPCU version %s connected\n",
|
||||||
txt->text);
|
txt->text);
|
||||||
oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_CEASED, OSMO_EVT_PCU_VERS, txt->text);
|
|
||||||
osmo_strlcpy(bts->pcu_version, txt->text, MAX_VERSION_LENGTH);
|
|
||||||
|
|
||||||
/* patch SI to advertise GPRS, *if* the SI sent by BSC said so */
|
/* we use the reception of the PCU_VERSION as a trigger to make the PCU available for
|
||||||
regenerate_si3_restoctets(bts);
|
* all BTSs handled by this process (currently this is exactly one BTS, see FIXME notes) */
|
||||||
regenerate_si4_restoctets(bts);
|
llist_for_each_entry(bts, &g_bts_sm->bts_list, list) {
|
||||||
|
oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_CEASED, OSMO_EVT_PCU_VERS, txt->text);
|
||||||
|
osmo_strlcpy(bts->pcu_version, txt->text, MAX_VERSION_LENGTH);
|
||||||
|
|
||||||
rc = pcu_tx_si_all(bts);
|
/* patch SI to advertise GPRS, *if* the SI sent by BSC said so */
|
||||||
|
regenerate_si3_restoctets(bts);
|
||||||
|
regenerate_si4_restoctets(bts);
|
||||||
|
|
||||||
|
if (pcu_tx_si_all(bts) < 0)
|
||||||
|
rc = -EINVAL;
|
||||||
|
}
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return -EINVAL;
|
return rc;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PCU_OML_ALERT:
|
case PCU_OML_ALERT:
|
||||||
|
OSMO_ASSERT(bts);
|
||||||
oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_INDETERMINATE, OSMO_EVT_EXT_ALARM,
|
oml_tx_failure_event_rep(&bts->gprs.cell.mo, NM_SEVER_INDETERMINATE, OSMO_EVT_EXT_ALARM,
|
||||||
txt->text);
|
txt->text);
|
||||||
break;
|
break;
|
||||||
@@ -937,36 +943,52 @@ static int pcu_rx_act_req(struct gsm_bts *bts,
|
|||||||
return -EINVAL; \
|
return -EINVAL; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define ENSURE_BTS_OBJECT(bts) \
|
||||||
|
do { \
|
||||||
|
if ((bts = gsm_bts_num(g_bts_sm, pcu_prim->bts_nr)) == NULL) { \
|
||||||
|
LOGP(DPCU, LOGL_ERROR, "Received PCU Prim for non-existent BTS %u\n", pcu_prim->bts_nr); \
|
||||||
|
return -EINVAL; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
static int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim, size_t prim_len)
|
static int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim, size_t prim_len)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct gsm_bts *bts;
|
struct gsm_bts *bts;
|
||||||
size_t exp_len;
|
size_t exp_len;
|
||||||
|
|
||||||
if ((bts = gsm_bts_num(g_bts_sm, pcu_prim->bts_nr)) == NULL) {
|
|
||||||
LOGP(DPCU, LOGL_ERROR, "Received PCU Prim for non-existent BTS %u\n", pcu_prim->bts_nr);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (msg_type) {
|
switch (msg_type) {
|
||||||
case PCU_IF_MSG_DATA_REQ:
|
case PCU_IF_MSG_DATA_REQ:
|
||||||
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.data_req);
|
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.data_req);
|
||||||
|
ENSURE_BTS_OBJECT(bts);
|
||||||
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
|
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
|
||||||
break;
|
break;
|
||||||
case PCU_IF_MSG_PAG_REQ:
|
case PCU_IF_MSG_PAG_REQ:
|
||||||
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.pag_req);
|
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.pag_req);
|
||||||
|
ENSURE_BTS_OBJECT(bts);
|
||||||
rc = pcu_rx_pag_req(bts, msg_type, &pcu_prim->u.pag_req);
|
rc = pcu_rx_pag_req(bts, msg_type, &pcu_prim->u.pag_req);
|
||||||
break;
|
break;
|
||||||
case PCU_IF_MSG_ACT_REQ:
|
case PCU_IF_MSG_ACT_REQ:
|
||||||
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.act_req);
|
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.act_req);
|
||||||
|
ENSURE_BTS_OBJECT(bts);
|
||||||
rc = pcu_rx_act_req(bts, &pcu_prim->u.act_req);
|
rc = pcu_rx_act_req(bts, &pcu_prim->u.act_req);
|
||||||
break;
|
break;
|
||||||
case PCU_IF_MSG_TXT_IND:
|
case PCU_IF_MSG_TXT_IND:
|
||||||
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.txt_ind);
|
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.txt_ind);
|
||||||
rc = pcu_rx_txt_ind(bts, &pcu_prim->u.txt_ind);
|
if (pcu_prim->u.txt_ind.type == PCU_VERSION) {
|
||||||
|
/* A TXT indication that carries the PCU_VERSION is always addressed to the
|
||||||
|
* receiving process as a whole, which means we will not resolve a specific
|
||||||
|
* BTS object in this case. */
|
||||||
|
rc = pcu_rx_txt_ind(NULL, &pcu_prim->u.txt_ind);
|
||||||
|
} else {
|
||||||
|
ENSURE_BTS_OBJECT(bts);
|
||||||
|
rc = pcu_rx_txt_ind(bts, &pcu_prim->u.txt_ind);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PCU_IF_MSG_CONTAINER:
|
case PCU_IF_MSG_CONTAINER:
|
||||||
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.container);
|
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.container);
|
||||||
|
ENSURE_BTS_OBJECT(bts);
|
||||||
/* ^ check if we can access container fields, v check with container data length */
|
/* ^ check if we can access container fields, v check with container data length */
|
||||||
exp_len = PCUIF_HDR_SIZE + sizeof(pcu_prim->u.container) + osmo_load16be(&pcu_prim->u.container.length);
|
exp_len = PCUIF_HDR_SIZE + sizeof(pcu_prim->u.container) + osmo_load16be(&pcu_prim->u.container.length);
|
||||||
if (prim_len < exp_len) {
|
if (prim_len < exp_len) {
|
||||||
|
|||||||
@@ -206,6 +206,12 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
|
|||||||
if (params == NULL)
|
if (params == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* Average the input RxLev/RxQual samples (if needed). Do this before
|
||||||
|
* the loop suspension logic to keep the pre-processing state updated. */
|
||||||
|
ci_meas = lchan_get_ci_thresholds(lchan);
|
||||||
|
ul_lqual_cb_avg = do_avg_algo(ci_meas, &state->ci_meas_proc, ul_lqual_cb);
|
||||||
|
rxlev_avg = do_avg_algo(¶ms->rxlev_meas, &state->rxlev_meas_proc, dbm2rxlev(ul_rssi_dbm));
|
||||||
|
|
||||||
/* Shall we skip current block based on configured interval? */
|
/* Shall we skip current block based on configured interval? */
|
||||||
if (ctrl_interval_skip_block(params, state))
|
if (ctrl_interval_skip_block(params, state))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -225,15 +231,10 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ci_meas = lchan_get_ci_thresholds(lchan);
|
|
||||||
|
|
||||||
/* Is C/I based algo enabled by config?
|
/* Is C/I based algo enabled by config?
|
||||||
* FIXME: this can later be generalized when properly implementing P & N counting. */
|
* FIXME: this can later be generalized when properly implementing P & N counting. */
|
||||||
ci_on = ci_meas->lower_cmp_n && ci_meas->upper_cmp_n;
|
ci_on = ci_meas->lower_cmp_n && ci_meas->upper_cmp_n;
|
||||||
|
|
||||||
ul_lqual_cb_avg = do_avg_algo(ci_meas, &state->ci_meas_proc, ul_lqual_cb);
|
|
||||||
rxlev_avg = do_avg_algo(¶ms->rxlev_meas, &state->rxlev_meas_proc, dbm2rxlev(ul_rssi_dbm));
|
|
||||||
|
|
||||||
/* If computed C/I is enabled and out of acceptable thresholds: */
|
/* If computed C/I is enabled and out of acceptable thresholds: */
|
||||||
if (ci_on && ul_lqual_cb_avg < ci_meas->lower_thresh * 10) {
|
if (ci_on && ul_lqual_cb_avg < ci_meas->lower_thresh * 10) {
|
||||||
new_dbm = ms_dbm + params->inc_step_size_db;
|
new_dbm = ms_dbm + params->inc_step_size_db;
|
||||||
@@ -334,10 +335,6 @@ int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
|
|||||||
lchan->tch.dtx.dl_active ? "enabled" : "disabled",
|
lchan->tch.dtx.dl_active ? "enabled" : "disabled",
|
||||||
lchan->tch.dtx.dl_active ? "SUB" : "FULL");
|
lchan->tch.dtx.dl_active ? "SUB" : "FULL");
|
||||||
|
|
||||||
/* Shall we skip current block based on configured interval? */
|
|
||||||
if (ctrl_interval_skip_block(params, state))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* If DTx is active on Downlink, use the '-SUB' */
|
/* If DTx is active on Downlink, use the '-SUB' */
|
||||||
if (lchan->tch.dtx.dl_active) {
|
if (lchan->tch.dtx.dl_active) {
|
||||||
rxqual = mr->rxqual_sub;
|
rxqual = mr->rxqual_sub;
|
||||||
@@ -347,8 +344,15 @@ int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
|
|||||||
rxlev = mr->rxlev_full;
|
rxlev = mr->rxlev_full;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Average the input RxLev/RxQual samples (if needed). Do this before
|
||||||
|
* the loop suspension logic to keep the pre-processing state updated. */
|
||||||
rxlev_avg = do_avg_algo(¶ms->rxlev_meas, &state->rxlev_meas_proc, rxlev);
|
rxlev_avg = do_avg_algo(¶ms->rxlev_meas, &state->rxlev_meas_proc, rxlev);
|
||||||
rxqual_avg = do_avg_algo(¶ms->rxqual_meas, &state->rxqual_meas_proc, rxqual);
|
rxqual_avg = do_avg_algo(¶ms->rxqual_meas, &state->rxqual_meas_proc, rxqual);
|
||||||
|
|
||||||
|
/* Shall we skip current block based on configured interval? */
|
||||||
|
if (ctrl_interval_skip_block(params, state))
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* If RxQual > L_RXQUAL_XX_P, try to increase Tx power */
|
/* If RxQual > L_RXQUAL_XX_P, try to increase Tx power */
|
||||||
if (rxqual_avg > params->rxqual_meas.lower_thresh) {
|
if (rxqual_avg > params->rxqual_meas.lower_thresh) {
|
||||||
/* Increase Tx power by reducing Tx attenuation */
|
/* Increase Tx power by reducing Tx attenuation */
|
||||||
|
|||||||
@@ -205,37 +205,48 @@ static int rsl_handle_chan_mod_ie(struct gsm_lchan *lchan,
|
|||||||
/* If octet 4 indicates non-transparent data */
|
/* If octet 4 indicates non-transparent data */
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_14k5):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_14k5):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_14k5;
|
lchan->tch_mode = GSM48_CMODE_DATA_14k5;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_12k0):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_12k0):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_12k0;
|
lchan->tch_mode = GSM48_CMODE_DATA_12k0;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_6k0):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_6k0):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_6k0;
|
lchan->tch_mode = GSM48_CMODE_DATA_6k0;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_43k5):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_43k5):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_43k5;
|
lchan->tch_mode = GSM48_CMODE_DATA_43k5;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_28k8):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_28k8):
|
||||||
/* 28.8 kbit/s services, 29.0 kbit/s radio interface rate */
|
/* 28.8 kbit/s services, 29.0 kbit/s radio interface rate */
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_29k0;
|
lchan->tch_mode = GSM48_CMODE_DATA_29k0;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_14k5):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_14k5):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_43k5_14k5;
|
lchan->tch_mode = GSM48_CMODE_DATA_43k5_14k5;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_14k5):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_14k5):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_29k0_14k5;
|
lchan->tch_mode = GSM48_CMODE_DATA_29k0_14k5;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_29k0):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_29k0):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_43k5_29k0;
|
lchan->tch_mode = GSM48_CMODE_DATA_43k5_29k0;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_43k5):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_43k5):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_14k5_43k5;
|
lchan->tch_mode = GSM48_CMODE_DATA_14k5_43k5;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_29k0):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_29k0):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_14k5_29k0;
|
lchan->tch_mode = GSM48_CMODE_DATA_14k5_29k0;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_43k5):
|
case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_43k5):
|
||||||
lchan->tch_mode = GSM48_CMODE_DATA_29k0_43k5;
|
lchan->tch_mode = GSM48_CMODE_DATA_29k0_43k5;
|
||||||
|
lchan->csd_mode = LCHAN_CSD_M_NT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* If octet 4 indicates transparent data */
|
/* If octet 4 indicates transparent data */
|
||||||
@@ -2067,8 +2078,8 @@ static int rsl_rx_chan_activ(struct msgb *msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Spec explicitly states BTS should only perform
|
/* Spec explicitly states BTS should only perform
|
||||||
* autonomous MS power control loop in BTS if 'MS Power
|
* autonomous MS power control loop in BTS if 'MS Power
|
||||||
* Parameters' IE is present! */
|
* Parameters' IE is present! */
|
||||||
lchan->ms_power_ctrl.dpc_params = params;
|
lchan->ms_power_ctrl.dpc_params = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1997,6 +1997,11 @@ static void lchan_dump_full_vty(struct vty *vty, const struct gsm_lchan *lchan)
|
|||||||
vty_out(vty, " Channel Mode / Codec: %s%s",
|
vty_out(vty, " Channel Mode / Codec: %s%s",
|
||||||
gsm48_chan_mode_name(lchan->tch_mode),
|
gsm48_chan_mode_name(lchan->tch_mode),
|
||||||
VTY_NEWLINE);
|
VTY_NEWLINE);
|
||||||
|
if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) {
|
||||||
|
vty_out(vty, " CSD mode: %s%s",
|
||||||
|
lchan_csd_mode_desc(lchan->csd_mode),
|
||||||
|
VTY_NEWLINE);
|
||||||
|
}
|
||||||
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
|
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
|
||||||
const struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
|
const struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
|
||||||
const struct gsm48_multi_rate_conf *mr_conf =
|
const struct gsm48_multi_rate_conf *mr_conf =
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ uint32_t trx_get_hlayer1(const struct gsm_bts_trx *trx)
|
|||||||
{
|
{
|
||||||
const struct lc15l1_hdl *fl1h = trx_lc15l1_hdl(trx);
|
const struct lc15l1_hdl *fl1h = trx_lc15l1_hdl(trx);
|
||||||
|
|
||||||
return fl1h->hLayer1;
|
return (uint32_t)fl1h->hLayer1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int trx_close_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
|
static int trx_close_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
|
||||||
|
|||||||
@@ -68,8 +68,6 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
cur = msgb_put(msg, GSM_FR_BYTES);
|
cur = msgb_put(msg, GSM_FR_BYTES);
|
||||||
memcpy(cur, l1_payload, GSM_FR_BYTES);
|
memcpy(cur, l1_payload, GSM_FR_BYTES);
|
||||||
|
|
||||||
lchan_set_marker(osmo_fr_is_any_sid(l1_payload), lchan);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +100,6 @@ static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload,
|
|||||||
cur = msgb_put(msg, GSM_EFR_BYTES);
|
cur = msgb_put(msg, GSM_EFR_BYTES);
|
||||||
memcpy(cur, l1_payload, GSM_EFR_BYTES);
|
memcpy(cur, l1_payload, GSM_EFR_BYTES);
|
||||||
|
|
||||||
lchan_set_marker(osmo_efr_is_any_sid(l1_payload), lchan);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,8 +130,6 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
cur = msgb_put(msg, GSM_HR_BYTES);
|
cur = msgb_put(msg, GSM_HR_BYTES);
|
||||||
memcpy(cur, l1_payload, GSM_HR_BYTES);
|
memcpy(cur, l1_payload, GSM_HR_BYTES);
|
||||||
|
|
||||||
lchan_set_marker(osmo_hr_check_sid(l1_payload, payload_len), lchan);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -473,7 +473,7 @@ uint32_t trx_get_hlayer1(const struct gsm_bts_trx *trx)
|
|||||||
{
|
{
|
||||||
const struct oc2gl1_hdl *fl1h = trx_oc2gl1_hdl(trx);
|
const struct oc2gl1_hdl *fl1h = trx_oc2gl1_hdl(trx);
|
||||||
|
|
||||||
return fl1h->hLayer1;
|
return (uint32_t)fl1h->hLayer1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int trx_close_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
|
static int trx_close_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
|
||||||
|
|||||||
@@ -68,8 +68,6 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
cur = msgb_put(msg, GSM_FR_BYTES);
|
cur = msgb_put(msg, GSM_FR_BYTES);
|
||||||
memcpy(cur, l1_payload, GSM_FR_BYTES);
|
memcpy(cur, l1_payload, GSM_FR_BYTES);
|
||||||
|
|
||||||
lchan_set_marker(osmo_fr_is_any_sid(l1_payload), lchan);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +100,6 @@ static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload,
|
|||||||
cur = msgb_put(msg, GSM_EFR_BYTES);
|
cur = msgb_put(msg, GSM_EFR_BYTES);
|
||||||
memcpy(cur, l1_payload, GSM_EFR_BYTES);
|
memcpy(cur, l1_payload, GSM_EFR_BYTES);
|
||||||
|
|
||||||
lchan_set_marker(osmo_efr_is_any_sid(l1_payload), lchan);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,8 +130,6 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
cur = msgb_put(msg, GSM_HR_BYTES);
|
cur = msgb_put(msg, GSM_HR_BYTES);
|
||||||
memcpy(cur, l1_payload, GSM_HR_BYTES);
|
memcpy(cur, l1_payload, GSM_HR_BYTES);
|
||||||
|
|
||||||
lchan_set_marker(osmo_hr_check_sid(l1_payload, payload_len), lchan);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
#include <osmocom/core/application.h>
|
#include <osmocom/core/application.h>
|
||||||
|
#include <osmocom/core/logging.h>
|
||||||
#include <osmo-bts/logging.h>
|
#include <osmo-bts/logging.h>
|
||||||
#include <osmo-bts/abis.h>
|
#include <osmo-bts/abis.h>
|
||||||
#include <osmo-bts/bts.h>
|
#include <osmo-bts/bts.h>
|
||||||
@@ -44,10 +45,14 @@ void parse_cmdline(int argc, char **argv)
|
|||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
{"features", 1, 0, 'f'},
|
{"features", 1, 0, 'f'},
|
||||||
|
{ "debug", 1, 0, 'd' },
|
||||||
|
{ "disable-color", 0, 0, 's' },
|
||||||
|
{ "timestamp", 0, 0, 'T' },
|
||||||
|
{ "log-level", 1, 0, 'e' },
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "hf:", long_options, &option_index);
|
c = getopt_long(argc, argv, "hf:d:sTe:", long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -58,6 +63,18 @@ void parse_cmdline(int argc, char **argv)
|
|||||||
case 'f':
|
case 'f':
|
||||||
cmdline.features = optarg;
|
cmdline.features = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
log_set_use_color(osmo_stderr_target, 0);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
log_parse_category_mask(osmo_stderr_target, optarg);
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
log_set_print_timestamp(osmo_stderr_target, 1);
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
log_set_log_level(osmo_stderr_target, atoi(optarg));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* catch unknown options *as well as* missing arguments. */
|
/* catch unknown options *as well as* missing arguments. */
|
||||||
fprintf(stderr, "Error in command line options. Exiting.\n");
|
fprintf(stderr, "Error in command line options. Exiting.\n");
|
||||||
@@ -111,12 +128,14 @@ int main(int argc, char **argv)
|
|||||||
struct bsc_oml_host *bsc_oml_host;
|
struct bsc_oml_host *bsc_oml_host;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
parse_cmdline(argc, argv);
|
|
||||||
|
|
||||||
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
|
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
|
||||||
msgb_talloc_ctx_init(tall_bts_ctx, 10*1024);
|
msgb_talloc_ctx_init(tall_bts_ctx, 10*1024);
|
||||||
|
|
||||||
osmo_init_logging2(tall_bts_ctx, &bts_log_info);
|
osmo_init_logging2(tall_bts_ctx, &bts_log_info);
|
||||||
|
log_set_print_category(osmo_stderr_target, 1);
|
||||||
|
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||||
|
|
||||||
|
parse_cmdline(argc, argv);
|
||||||
|
|
||||||
g_bts_sm = gsm_bts_sm_alloc(tall_bts_ctx);
|
g_bts_sm = gsm_bts_sm_alloc(tall_bts_ctx);
|
||||||
if (!g_bts_sm)
|
if (!g_bts_sm)
|
||||||
|
|||||||
@@ -987,23 +987,28 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we got FACCH, the RTP clock needs to account for it,
|
/* When TCH UL receiver in sysmoBTS PHY detects FACCH,
|
||||||
* and if we have rtp continuous-streaming enabled,
|
* it behaves as follows (observed):
|
||||||
* an actual BFI packet will be emitted.
|
|
||||||
*
|
*
|
||||||
* Only the case of TCH/F is currently handled; the task of
|
* - In each 20 ms window (both TCH/F and TCH/H), a single
|
||||||
* supporting TCH/H is left as a FIXME for other/later
|
* PH-DATA.ind arrives.
|
||||||
* developers. The difficulty with supporting FACCH/H is that
|
* - In the case of TCH/F, this PH-DATA.ind carries GsmL1_Sapi_FacchF
|
||||||
* only one GsmL1_Sapi_FacchH message will be received,
|
* instead of GsmL1_Sapi_TchF.
|
||||||
* covering 40 ms of time, but two RTP "tick" output calls
|
* - In the case of TCH/H, the PH-DATA.ind for block 0 carries
|
||||||
* will need to be made, with appropriately adjusted frame
|
* GsmL1_Sapi_FacchH instead of GsmL1_Sapi_TchH. However,
|
||||||
* numbers. As a further consideration for rtp continuous-streaming
|
* in the following 20 ms window (block 1 of FACCH/H)
|
||||||
* mode, having the two RTP BFI packets sent directly back to back,
|
* a PH-DATA.ind with GsmL1_Sapi_TchH arrives as normal.
|
||||||
* as opposed to proper 20 ms timing, may be so undesirable
|
* Typically the latter PHY message carries a BFI (0 length payload),
|
||||||
* that some deployments may rather disable TCH/H (use only TCH/F)
|
* but nonzero payloads have also been observed under some conditions.
|
||||||
* than deal with the extra pain, or use TCH/H only on SDR-based
|
* (The latter case remains to be investigated further.)
|
||||||
* BTS with osmo-bts-trx where correct timing is easily achieved. */
|
*
|
||||||
if (data_ind->sapi == GsmL1_Sapi_FacchF)
|
* In those 20 ms windows where we receive PH-DATA.ind for FACCH
|
||||||
|
* instead of the usual TCH kind, we need to pass an empty payload
|
||||||
|
* to the upper layers so we can produce the correct effect in
|
||||||
|
* the outgoing RTP stream depending on configuration.
|
||||||
|
*/
|
||||||
|
if (data_ind->sapi == GsmL1_Sapi_FacchF ||
|
||||||
|
data_ind->sapi == GsmL1_Sapi_FacchH)
|
||||||
l1if_tch_rx_facch(trx, chan_nr, l1p_msg);
|
l1if_tch_rx_facch(trx, chan_nr, l1p_msg);
|
||||||
|
|
||||||
/* fill L1SAP header */
|
/* fill L1SAP header */
|
||||||
|
|||||||
@@ -77,8 +77,6 @@ static struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
cur[0] |= 0xD0;
|
cur[0] |= 0xD0;
|
||||||
#endif /* USE_L1_RTP_MODE */
|
#endif /* USE_L1_RTP_MODE */
|
||||||
|
|
||||||
lchan_set_marker(osmo_fr_is_any_sid(l1_payload), lchan);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,8 +130,6 @@ static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload,
|
|||||||
cur[0] |= 0xC0;
|
cur[0] |= 0xC0;
|
||||||
#endif /* USE_L1_RTP_MODE */
|
#endif /* USE_L1_RTP_MODE */
|
||||||
|
|
||||||
lchan_set_marker(osmo_efr_is_any_sid(l1_payload), lchan);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,8 +214,6 @@ static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len
|
|||||||
osmo_revbytebits_buf(cur, GSM_HR_BYTES);
|
osmo_revbytebits_buf(cur, GSM_HR_BYTES);
|
||||||
#endif /* USE_L1_RTP_MODE */
|
#endif /* USE_L1_RTP_MODE */
|
||||||
|
|
||||||
lchan_set_marker(osmo_hr_check_sid(l1_payload, payload_len), lchan);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -196,9 +196,6 @@ int bts_model_trx_init(struct gsm_bts_trx *trx)
|
|||||||
trx->support.chan_modes = NM_IPAC_MASK_CHANM_SPEECH
|
trx->support.chan_modes = NM_IPAC_MASK_CHANM_SPEECH
|
||||||
| NM_IPAC_MASK_CHANM_CSD_NT
|
| NM_IPAC_MASK_CHANM_CSD_NT
|
||||||
| NM_IPAC_MASK_CHANM_CSD_T;
|
| NM_IPAC_MASK_CHANM_CSD_T;
|
||||||
/* TODO: missing rate adaptation for TCH/F14.4 (see OS#6167) */
|
|
||||||
trx->support.chan_modes &= ~NM_IPAC_F_CHANM_CSD_T_14k4;
|
|
||||||
trx->support.chan_modes &= ~NM_IPAC_F_CHANM_CSD_NT_14k4;
|
|
||||||
|
|
||||||
/* The nominal value for each TRX is later overwritten through VTY cmd
|
/* The nominal value for each TRX is later overwritten through VTY cmd
|
||||||
* 'nominal-tx-power' if present, otherwise through TRXC cmd NOMTXPOWER.
|
* 'nominal-tx-power' if present, otherwise through TRXC cmd NOMTXPOWER.
|
||||||
|
|||||||
@@ -166,14 +166,10 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
|
|||||||
case GSM48_CMODE_SPEECH_V1: /* FR */
|
case GSM48_CMODE_SPEECH_V1: /* FR */
|
||||||
rc = gsm0503_tch_fr_decode(tch_data, BUFTAIL8(bursts_p),
|
rc = gsm0503_tch_fr_decode(tch_data, BUFTAIL8(bursts_p),
|
||||||
1, 0, &n_errors, &n_bits_total);
|
1, 0, &n_errors, &n_bits_total);
|
||||||
if (rc == GSM_FR_BYTES) /* only for valid *speech* frames */
|
|
||||||
lchan_set_marker(osmo_fr_is_any_sid(tch_data), lchan); /* DTXu */
|
|
||||||
break;
|
break;
|
||||||
case GSM48_CMODE_SPEECH_EFR: /* EFR */
|
case GSM48_CMODE_SPEECH_EFR: /* EFR */
|
||||||
rc = gsm0503_tch_fr_decode(tch_data, BUFTAIL8(bursts_p),
|
rc = gsm0503_tch_fr_decode(tch_data, BUFTAIL8(bursts_p),
|
||||||
1, 1, &n_errors, &n_bits_total);
|
1, 1, &n_errors, &n_bits_total);
|
||||||
if (rc == GSM_EFR_BYTES) /* only for valid *speech* frames */
|
|
||||||
lchan_set_marker(osmo_efr_is_any_sid(tch_data), lchan); /* DTXu */
|
|
||||||
break;
|
break;
|
||||||
case GSM48_CMODE_SPEECH_AMR: /* AMR */
|
case GSM48_CMODE_SPEECH_AMR: /* AMR */
|
||||||
/* the first FN 0,8,17 defines that CMI is included in frame,
|
/* the first FN 0,8,17 defines that CMI is included in frame,
|
||||||
|
|||||||
@@ -117,14 +117,7 @@ static const uint8_t sched_tchh_ul_csd_map[26] = {
|
|||||||
|
|
||||||
/* TDMA frame number of burst 'a' % 26 is the table index.
|
/* TDMA frame number of burst 'a' % 26 is the table index.
|
||||||
* This mapping is valid for both TCH/H(0) and TCH/H(1). */
|
* This mapping is valid for both TCH/H(0) and TCH/H(1). */
|
||||||
static const uint8_t sched_tchh_dl_csd_map[26] = {
|
extern const uint8_t sched_tchh_dl_csd_map[26];
|
||||||
[0] = 1, /* TCH/H(0): B0(0 ... 19) */
|
|
||||||
[1] = 1, /* TCH/H(1): B0(1 ... 20) */
|
|
||||||
[8] = 1, /* TCH/H(0): B1(8 ... 2) */
|
|
||||||
[9] = 1, /* TCH/H(1): B1(9 ... 3) */
|
|
||||||
[17] = 1, /* TCH/H(0): B2(17 ... 10) */
|
|
||||||
[18] = 1, /* TCH/H(1): B2(18 ... 11) */
|
|
||||||
};
|
|
||||||
|
|
||||||
static int decode_hr_facch(struct l1sched_ts *l1ts,
|
static int decode_hr_facch(struct l1sched_ts *l1ts,
|
||||||
const struct trx_ul_burst_ind *bi)
|
const struct trx_ul_burst_ind *bi)
|
||||||
@@ -239,10 +232,6 @@ int rx_tchh_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
|
|||||||
rc = gsm0503_tch_hr_decode2(tch_data, BUFTAIL8(bursts_p),
|
rc = gsm0503_tch_hr_decode2(tch_data, BUFTAIL8(bursts_p),
|
||||||
!sched_tchh_ul_facch_map[bi->fn % 26],
|
!sched_tchh_ul_facch_map[bi->fn % 26],
|
||||||
&n_errors, &n_bits_total);
|
&n_errors, &n_bits_total);
|
||||||
if (rc == GSM_HR_BYTES) { /* only for valid *speech* frames */
|
|
||||||
bool is_sid = osmo_hr_check_sid(tch_data, GSM_HR_BYTES);
|
|
||||||
lchan_set_marker(is_sid, lchan); /* DTXu */
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case GSM48_CMODE_SPEECH_AMR: /* AMR */
|
case GSM48_CMODE_SPEECH_AMR: /* AMR */
|
||||||
/* the first FN 0,8,17 or 1,9,18 defines that CMI is included
|
/* the first FN 0,8,17 or 1,9,18 defines that CMI is included
|
||||||
@@ -427,12 +416,6 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
|
|||||||
memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN);
|
memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN);
|
||||||
memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN);
|
memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN);
|
||||||
|
|
||||||
/* for half-rate CSD we dequeue every 4th burst */
|
|
||||||
if (chan_state->rsl_cmode == RSL_CMOD_SPD_DATA) {
|
|
||||||
if (!sched_tchh_dl_csd_map[br->fn % 26])
|
|
||||||
goto send_burst;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* dequeue a TCH and/or a FACCH message to be transmitted */
|
/* dequeue a TCH and/or a FACCH message to be transmitted */
|
||||||
tch_dl_dequeue(l1ts, br, &msg_tch, &msg_facch);
|
tch_dl_dequeue(l1ts, br, &msg_tch, &msg_facch);
|
||||||
|
|
||||||
@@ -536,17 +519,25 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
|
|||||||
break;
|
break;
|
||||||
/* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */
|
/* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */
|
||||||
case GSM48_CMODE_DATA_6k0:
|
case GSM48_CMODE_DATA_6k0:
|
||||||
if (msg_tch == NULL)
|
/* for half-rate CSD we run the encoder every 4th burst (like for TCH/F)
|
||||||
msg_tch = tch_dummy_msgb(4 * 60, 0x01);
|
* because the interleaving is done as specified for the TCH/F9.6 */
|
||||||
gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_tch));
|
if (sched_tchh_dl_csd_map[br->fn % 26]) {
|
||||||
|
if (msg_tch == NULL)
|
||||||
|
msg_tch = tch_dummy_msgb(4 * 60, 0x01);
|
||||||
|
gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_tch));
|
||||||
|
}
|
||||||
if (msg_facch != NULL)
|
if (msg_facch != NULL)
|
||||||
gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_facch));
|
gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_facch));
|
||||||
break;
|
break;
|
||||||
/* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */
|
/* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */
|
||||||
case GSM48_CMODE_DATA_3k6:
|
case GSM48_CMODE_DATA_3k6:
|
||||||
if (msg_tch == NULL)
|
/* for half-rate CSD we run the encoder every 4th burst (like for TCH/F)
|
||||||
msg_tch = tch_dummy_msgb(4 * 36, 0x01);
|
* because the interleaving is done as specified for the TCH/F9.6 */
|
||||||
gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_tch));
|
if (sched_tchh_dl_csd_map[br->fn % 26]) {
|
||||||
|
if (msg_tch == NULL)
|
||||||
|
msg_tch = tch_dummy_msgb(4 * 36, 0x01);
|
||||||
|
gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_tch));
|
||||||
|
}
|
||||||
if (msg_facch != NULL)
|
if (msg_facch != NULL)
|
||||||
gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_facch));
|
gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg_facch));
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -411,6 +411,7 @@ static int trx_fn_timer_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
struct timespec tv_now;
|
struct timespec tv_now;
|
||||||
uint64_t expire_count;
|
uint64_t expire_count;
|
||||||
int64_t elapsed_us, error_us;
|
int64_t elapsed_us, error_us;
|
||||||
|
const char *reason = NULL;
|
||||||
int rc, i;
|
int rc, i;
|
||||||
|
|
||||||
if (!(what & OSMO_FD_READ))
|
if (!(what & OSMO_FD_READ))
|
||||||
@@ -430,8 +431,9 @@ static int trx_fn_timer_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
|
|
||||||
/* check if transceiver is still alive */
|
/* check if transceiver is still alive */
|
||||||
if (tcs->fn_without_clock_ind++ == TRX_LOSS_FRAMES) {
|
if (tcs->fn_without_clock_ind++ == TRX_LOSS_FRAMES) {
|
||||||
LOGP(DL1C, LOGL_NOTICE, "No more clock from transceiver\n");
|
reason = "No more clock from transceiver";
|
||||||
goto no_clock;
|
LOGP(DL1C, LOGL_ERROR, "%s\n", reason);
|
||||||
|
goto shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* compute actual elapsed time and resulting OS scheduling error */
|
/* compute actual elapsed time and resulting OS scheduling error */
|
||||||
@@ -446,9 +448,11 @@ static int trx_fn_timer_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
|
|
||||||
/* if someone played with clock, or if the process stalled */
|
/* if someone played with clock, or if the process stalled */
|
||||||
if (elapsed_us > GSM_TDMA_FN_DURATION_uS * MAX_FN_SKEW || elapsed_us < 0) {
|
if (elapsed_us > GSM_TDMA_FN_DURATION_uS * MAX_FN_SKEW || elapsed_us < 0) {
|
||||||
LOGP(DL1C, LOGL_ERROR, "PC clock skew: elapsed_us=%" PRId64 ", error_us=%" PRId64 "\n",
|
LOGP(DL1C, LOGL_ERROR,
|
||||||
elapsed_us, error_us);
|
"PC clock skew: elapsed_us=%" PRId64 ", error_us=%" PRId64 "\n",
|
||||||
goto no_clock;
|
elapsed_us, error_us);
|
||||||
|
reason = "PC clock skew too high";
|
||||||
|
goto shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* call bts_sched_fn() for all expired FN */
|
/* call bts_sched_fn() for all expired FN */
|
||||||
@@ -457,9 +461,9 @@ static int trx_fn_timer_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
no_clock:
|
shutdown:
|
||||||
osmo_timerfd_disable(&tcs->fn_timer_ofd);
|
osmo_timerfd_disable(&tcs->fn_timer_ofd);
|
||||||
bts_shutdown(bts, "No clock from osmo-trx");
|
bts_shutdown(bts, reason);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,9 +99,6 @@ int bts_model_trx_init(struct gsm_bts_trx *trx)
|
|||||||
trx->support.chan_modes = NM_IPAC_MASK_CHANM_SPEECH
|
trx->support.chan_modes = NM_IPAC_MASK_CHANM_SPEECH
|
||||||
| NM_IPAC_MASK_CHANM_CSD_NT
|
| NM_IPAC_MASK_CHANM_CSD_NT
|
||||||
| NM_IPAC_MASK_CHANM_CSD_T;
|
| NM_IPAC_MASK_CHANM_CSD_T;
|
||||||
/* TODO: missing rate adaptation for TCH/F14.4 (see OS#6167) */
|
|
||||||
trx->support.chan_modes &= ~NM_IPAC_F_CHANM_CSD_T_14k4;
|
|
||||||
trx->support.chan_modes &= ~NM_IPAC_F_CHANM_CSD_NT_14k4;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,12 +53,24 @@ static const struct test_case tests[] = {
|
|||||||
.tch_mode = GSM48_CMODE_DATA_12k0,
|
.tch_mode = GSM48_CMODE_DATA_12k0,
|
||||||
.csd_mode = LCHAN_CSD_M_T_9600,
|
.csd_mode = LCHAN_CSD_M_T_9600,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "TCH/F9.6 NT",
|
||||||
|
.lchan_type = GSM_LCHAN_TCH_F,
|
||||||
|
.tch_mode = GSM48_CMODE_DATA_12k0,
|
||||||
|
.csd_mode = LCHAN_CSD_M_NT,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = "TCH/F4.8",
|
.name = "TCH/F4.8",
|
||||||
.lchan_type = GSM_LCHAN_TCH_F,
|
.lchan_type = GSM_LCHAN_TCH_F,
|
||||||
.tch_mode = GSM48_CMODE_DATA_6k0,
|
.tch_mode = GSM48_CMODE_DATA_6k0,
|
||||||
.csd_mode = LCHAN_CSD_M_T_4800,
|
.csd_mode = LCHAN_CSD_M_T_4800,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "TCH/F4.8 NT",
|
||||||
|
.lchan_type = GSM_LCHAN_TCH_F,
|
||||||
|
.tch_mode = GSM48_CMODE_DATA_6k0,
|
||||||
|
.csd_mode = LCHAN_CSD_M_NT,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = "TCH/H4.8",
|
.name = "TCH/H4.8",
|
||||||
.lchan_type = GSM_LCHAN_TCH_H,
|
.lchan_type = GSM_LCHAN_TCH_H,
|
||||||
@@ -81,7 +93,7 @@ static const struct test_case tests[] = {
|
|||||||
|
|
||||||
static void exec_test_case(const struct test_case *tc)
|
static void exec_test_case(const struct test_case *tc)
|
||||||
{
|
{
|
||||||
const struct csd_v110_frame_desc *desc;
|
const struct csd_v110_lchan_desc *desc;
|
||||||
uint8_t rtp[RFC4040_RTP_PLEN] = { 0 };
|
uint8_t rtp[RFC4040_RTP_PLEN] = { 0 };
|
||||||
ubit_t data_enc[BBUF_MAX];
|
ubit_t data_enc[BBUF_MAX];
|
||||||
ubit_t data_dec[BBUF_MAX];
|
ubit_t data_dec[BBUF_MAX];
|
||||||
@@ -89,13 +101,10 @@ static void exec_test_case(const struct test_case *tc)
|
|||||||
|
|
||||||
/* obtain a V.110 frame description for the given channel type/rate */
|
/* obtain a V.110 frame description for the given channel type/rate */
|
||||||
OSMO_ASSERT(tc->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
|
OSMO_ASSERT(tc->tch_mode < ARRAY_SIZE(csd_v110_lchan_desc));
|
||||||
if (tc->lchan_type == GSM_LCHAN_TCH_F)
|
desc = &csd_v110_lchan_desc[tc->tch_mode];
|
||||||
desc = &csd_v110_lchan_desc[tc->tch_mode].fr;
|
|
||||||
else
|
|
||||||
desc = &csd_v110_lchan_desc[tc->tch_mode].hr;
|
|
||||||
|
|
||||||
/* total number of bits carried by a radio interface block */
|
/* total number of bits carried by a radio interface block */
|
||||||
const unsigned int bit_num = desc->num_bits * desc->num_blocks;
|
const unsigned int bit_num = CSD_V110_NUM_BITS(desc);
|
||||||
if (bit_num == 0) {
|
if (bit_num == 0) {
|
||||||
fprintf(stderr, "[i] Skipping '%s' (not implemented)\n", tc->name);
|
fprintf(stderr, "[i] Skipping '%s' (not implemented)\n", tc->name);
|
||||||
return;
|
return;
|
||||||
@@ -115,7 +124,7 @@ static void exec_test_case(const struct test_case *tc)
|
|||||||
data_enc[i] = i & 0x01;
|
data_enc[i] = i & 0x01;
|
||||||
|
|
||||||
/* encode an RTP frame and print it */
|
/* 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);
|
fprintf(stderr, "[i] csd_v110_rtp_encode() returns %d\n", rc);
|
||||||
if (rc != RFC4040_RTP_PLEN)
|
if (rc != RFC4040_RTP_PLEN)
|
||||||
return;
|
return;
|
||||||
@@ -124,7 +133,7 @@ static void exec_test_case(const struct test_case *tc)
|
|||||||
fprintf(stderr, " %s\n", osmo_hexdump(&rtp[i * 16], 16));
|
fprintf(stderr, " %s\n", osmo_hexdump(&rtp[i * 16], 16));
|
||||||
|
|
||||||
/* decode the encoded RTP frame */
|
/* decode the encoded RTP frame */
|
||||||
rc = csd_v110_rtp_decode(&lchan, &data_dec[0], &rtp[0], sizeof(rtp));
|
rc = csd_v110_rtp_decode(&lchan, &data_dec[0], NULL, &rtp[0], sizeof(rtp));
|
||||||
fprintf(stderr, "[i] csd_v110_rtp_decode() returns %d\n", rc);
|
fprintf(stderr, "[i] csd_v110_rtp_decode() returns %d\n", rc);
|
||||||
if (rc != bit_num)
|
if (rc != bit_num)
|
||||||
return;
|
return;
|
||||||
@@ -136,10 +145,14 @@ static void exec_test_case(const struct test_case *tc)
|
|||||||
i, data_dec[i], data_enc[i]);
|
i, data_dec[i], data_enc[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* for TCH/F14.4, we always expect a valid block */
|
||||||
|
if (tc->tch_mode == GSM48_CMODE_DATA_14k5)
|
||||||
|
return;
|
||||||
|
|
||||||
fprintf(stderr, "[i] Testing '%s' (IDLE)\n", tc->name);
|
fprintf(stderr, "[i] Testing '%s' (IDLE)\n", tc->name);
|
||||||
|
|
||||||
/* encode an idle RTP frame and print it */
|
/* 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);
|
fprintf(stderr, "[i] csd_v110_rtp_encode() returns %d\n", rc);
|
||||||
if (rc != RFC4040_RTP_PLEN)
|
if (rc != RFC4040_RTP_PLEN)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
[i] Skipping 'TCH/F14.4' (not implemented)
|
[i] Testing 'TCH/F14.4' (bitnum=290)
|
||||||
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
|
3f 3f 3f 3f 3f 3f 3f 3f bf ff bf 7f bf bf bf bf
|
||||||
|
bf bf bf bf bf bf bf bf bf bf bf bf bf bf ff 7f
|
||||||
|
7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
|
||||||
|
7f bf bf bf bf bf bf bf bf bf bf bf bf bf bf bf
|
||||||
|
bf bf bf ff 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
|
||||||
|
7f 7f 7f 7f 7f 7f bf bf bf bf bf bf bf bf bf bf
|
||||||
|
bf bf bf bf bf bf bf bf ff 7f 7f 7f 7f 7f 7f 7f
|
||||||
|
7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f bf bf bf bf bf
|
||||||
|
bf bf bf bf bf bf bf bf bf bf bf bf bf ff 7f 7f
|
||||||
|
7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
|
||||||
|
[i] csd_v110_rtp_decode() returns 290
|
||||||
[i] Testing 'TCH/F9.6' (bitnum=240)
|
[i] Testing 'TCH/F9.6' (bitnum=240)
|
||||||
[i] csd_v110_rtp_encode() returns 160
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
||||||
@@ -13,6 +25,31 @@
|
|||||||
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||||
[i] csd_v110_rtp_decode() returns 240
|
[i] csd_v110_rtp_decode() returns 240
|
||||||
[i] Testing 'TCH/F9.6' (IDLE)
|
[i] Testing 'TCH/F9.6' (IDLE)
|
||||||
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
|
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
[i] Testing 'TCH/F9.6 NT' (bitnum=240)
|
||||||
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
|
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
||||||
|
ff 7f 7f 7f bf 3f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||||
|
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
|
||||||
|
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf 7f 7f 7f
|
||||||
|
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||||
|
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
||||||
|
ff 7f 7f 7f bf bf 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||||
|
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
|
||||||
|
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf ff 7f 7f
|
||||||
|
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
||||||
|
[i] csd_v110_rtp_decode() returns 240
|
||||||
|
[i] Testing 'TCH/F9.6 NT' (IDLE)
|
||||||
[i] csd_v110_rtp_encode() returns 160
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
|
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
@@ -49,29 +86,54 @@
|
|||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
[i] Testing 'TCH/H4.8' (bitnum=240)
|
[i] Testing 'TCH/F4.8 NT' (bitnum=120)
|
||||||
[i] csd_v110_rtp_encode() returns 160
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
|
||||||
ff 7f 7f 7f bf ff 7f 7f bf bf bf bf ff 7f 7f 7f
|
ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
|
||||||
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
|
ff ff 7f ff 7f ff 7f ff ff 7f 7f 7f 7f ff 7f ff
|
||||||
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf ff 7f 7f
|
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||||
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||||
3f 3f 3f 3f bf bf bf bf ff 7f 7f 7f bf bf bf bf
|
7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
|
||||||
ff 7f 7f 7f bf ff 7f 7f bf bf bf bf ff 7f 7f 7f
|
ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
|
||||||
bf bf bf bf ff 7f 7f 7f 3f 3f 3f 3f bf bf bf bf
|
ff ff 7f ff 7f ff 7f ff ff 7f 7f ff 7f ff 7f ff
|
||||||
ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f bf ff 7f 7f
|
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||||
bf bf bf bf ff 7f 7f 7f bf bf bf bf ff 7f 7f 7f
|
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||||
[i] csd_v110_rtp_decode() returns 240
|
[i] csd_v110_rtp_decode() returns 120
|
||||||
|
[i] Testing 'TCH/F4.8 NT' (IDLE)
|
||||||
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
|
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
[i] Testing 'TCH/H4.8' (bitnum=120)
|
||||||
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
|
7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
|
||||||
|
ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
|
||||||
|
ff ff 7f ff 7f ff 7f ff ff 7f ff ff 7f ff 7f ff
|
||||||
|
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||||
|
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||||
|
7f 7f 7f 7f 7f 7f 7f 7f ff 7f ff 7f ff 7f ff 7f
|
||||||
|
ff ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff 7f
|
||||||
|
ff ff 7f ff 7f ff 7f ff ff 7f ff ff 7f ff 7f ff
|
||||||
|
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||||
|
ff 7f ff 7f ff 7f ff 7f ff ff 7f ff 7f ff 7f ff
|
||||||
|
[i] csd_v110_rtp_decode() returns 120
|
||||||
[i] Testing 'TCH/H4.8' (IDLE)
|
[i] Testing 'TCH/H4.8' (IDLE)
|
||||||
[i] csd_v110_rtp_encode() returns 160
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
|
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
|
||||||
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
|
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
|
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
[i] Testing 'TCH/F2.4' (bitnum=72)
|
[i] Testing 'TCH/F2.4' (bitnum=72)
|
||||||
@@ -99,28 +161,28 @@
|
|||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
[i] Testing 'TCH/H2.4' (bitnum=144)
|
[i] Testing 'TCH/H2.4' (bitnum=72)
|
||||||
[i] csd_v110_rtp_encode() returns 160
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
3f 3f 3f 3f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f
|
7f 7f 7f 7f 7f 7f 7f 7f ff 7f 7f ff ff 7f 7f ff
|
||||||
bf 7f bf 7f ff 3f 7f 7f bf 7f bf 7f bf 7f bf 7f
|
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||||
bf 7f bf 7f bf 7f bf 7f 3f 3f 3f 3f bf 7f bf 7f
|
ff 7f 7f ff ff 7f 7f ff ff ff 7f 7f 7f ff 7f ff
|
||||||
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f ff 3f 7f 7f
|
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||||
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f
|
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||||
3f 3f 3f 3f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f
|
7f 7f 7f 7f 7f 7f 7f 7f ff 7f 7f ff ff 7f 7f ff
|
||||||
bf 7f bf 7f ff 3f 7f 7f bf 7f bf 7f bf 7f bf 7f
|
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||||
bf 7f bf 7f bf 7f bf 7f 3f 3f 3f 3f bf 7f bf 7f
|
ff 7f 7f ff ff 7f 7f ff ff ff 7f 7f 7f ff 7f ff
|
||||||
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f ff 3f 7f 7f
|
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||||
bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f bf 7f
|
ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff ff 7f 7f ff
|
||||||
[i] csd_v110_rtp_decode() returns 144
|
[i] csd_v110_rtp_decode() returns 72
|
||||||
[i] Testing 'TCH/H2.4' (IDLE)
|
[i] Testing 'TCH/H2.4' (IDLE)
|
||||||
[i] csd_v110_rtp_encode() returns 160
|
[i] csd_v110_rtp_encode() returns 160
|
||||||
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
|
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
7f 7f 7f 7f 7f 7f 7f 7f ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
3f 3f 3f 3f ff ff ff ff ff ff ff ff ff ff ff ff
|
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
|
||||||
ff ff ff ff ff ff ff ff 3f 3f 3f 3f ff ff ff ff
|
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|
||||||
|
|||||||
Reference in New Issue
Block a user