aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYves Godin <yves.godin@nutaq.com>2016-12-22 13:32:14 -0500
committerYves Godin <yves.godin@nutaq.com>2016-12-22 13:32:14 -0500
commit2adc18ff23aa00664e2d4f8607b12c695b34095d (patch)
tree4dd464c6ebac6bb6c39ecf57509f23a2627b8c59
parentac5c989e516630610cfc2db9610234eac491251a (diff)
parent847f4ae30a8e4e7f44eb750616bbfdd3cc8a388e (diff)
Merge branch 'nrw/litecell15-merge-master-22122016' into 'nrw/litecell15-next'
Nrw/litecell15 merge master 22122016 This MR should have: * See merge request !64
-rw-r--r--README41
-rw-r--r--configure.ac11
-rwxr-xr-xcontrib/dtx_check.gawk89
-rwxr-xr-xcontrib/jenkins_bts_model.sh33
-rwxr-xr-xcontrib/jenkins_bts_trx.sh49
-rwxr-xr-xcontrib/jenkins_oct.sh54
-rwxr-xr-xcontrib/jenkins_oct_and_bts_trx.sh65
-rwxr-xr-xcontrib/jenkins_sysmobts.sh61
-rwxr-xr-xcontrib/superfemto.sh110
-rw-r--r--debian/compat2
-rw-r--r--debian/control22
-rwxr-xr-xdebian/rules12
-rw-r--r--include/osmo-bts/Makefile.am3
-rw-r--r--include/osmo-bts/amr.h3
-rw-r--r--include/osmo-bts/bts.h2
-rw-r--r--include/osmo-bts/bts_model.h3
-rw-r--r--include/osmo-bts/dtx_dl_amr_fsm.h38
-rw-r--r--include/osmo-bts/gsm_data.h1
-rw-r--r--include/osmo-bts/l1sap.h17
-rw-r--r--include/osmo-bts/msg_utils.h24
-rw-r--r--include/osmo-bts/phy_link.h5
-rw-r--r--include/osmo-bts/rsl.h5
-rw-r--r--src/common/Makefile.am7
-rw-r--r--src/common/amr.c45
-rw-r--r--src/common/bts.c4
-rw-r--r--src/common/dtx_dl_amr_fsm.c365
-rw-r--r--src/common/l1sap.c106
-rw-r--r--src/common/main.c6
-rw-r--r--src/common/msg_utils.c339
-rw-r--r--src/common/oml.c2
-rw-r--r--src/common/paging.c6
-rw-r--r--src/common/rsl.c62
-rw-r--r--src/common/scheduler.c79
-rw-r--r--src/common/sysinfo.c14
-rw-r--r--src/common/vty.c95
-rw-r--r--src/osmo-bts-litecell15/l1_if.c170
-rw-r--r--src/osmo-bts-litecell15/l1_if.h6
-rw-r--r--src/osmo-bts-litecell15/lc15bts_vty.c3
-rw-r--r--src/osmo-bts-litecell15/misc/lc15bts_mgr.c4
-rw-r--r--src/osmo-bts-litecell15/oml.c21
-rw-r--r--src/osmo-bts-litecell15/tch.c235
-rw-r--r--src/osmo-bts-octphy/Makefile.am4
-rw-r--r--src/osmo-bts-octphy/l1_if.c8
-rw-r--r--src/osmo-bts-octphy/l1_oml.c58
-rw-r--r--src/osmo-bts-octphy/l1_utils.c8
-rw-r--r--src/osmo-bts-octphy/octphy_hw_api.c7
-rw-r--r--src/osmo-bts-sysmo/Makefile.am2
-rw-r--r--src/osmo-bts-sysmo/l1_if.c114
-rw-r--r--src/osmo-bts-sysmo/l1_if.h6
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts_mgr.c4
-rw-r--r--src/osmo-bts-sysmo/oml.c21
-rw-r--r--src/osmo-bts-sysmo/sysmobts_vty.c3
-rw-r--r--src/osmo-bts-sysmo/tch.c236
-rw-r--r--src/osmo-bts-trx/l1_if.c60
-rw-r--r--src/osmo-bts-trx/l1_if.h4
-rw-r--r--src/osmo-bts-trx/scheduler_trx.c10
-rw-r--r--src/osmo-bts-trx/trx_if.c5
-rw-r--r--src/osmo-bts-trx/trx_if.h1
-rw-r--r--src/osmo-bts-trx/trx_vty.c54
-rw-r--r--tests/agch/Makefile.am4
-rw-r--r--tests/agch/agch_test.c5
-rw-r--r--tests/bursts/bursts_test.c2
-rw-r--r--tests/cipher/Makefile.am4
-rw-r--r--tests/cipher/cipher_test.c5
-rw-r--r--tests/handover/handover_test.c6
-rw-r--r--tests/misc/Makefile.am4
-rw-r--r--tests/paging/Makefile.am4
-rw-r--r--tests/paging/paging_test.c5
-rw-r--r--tests/stubs.c2
69 files changed, 2161 insertions, 704 deletions
diff --git a/README b/README
index 967b0808..1fe6e8c8 100644
--- a/README
+++ b/README
@@ -1,26 +1,29 @@
-Repository forw the Osmocom BTS implementation.
+= Repository for the Osmocom BTS implementation. =
-This code implementes the Layer 2 and higher of a more or less
-conventional GSM BTS (Base Transceiver Station) - however, using an
-Abis/IP interface, rather than the old-fashioned E1/T1.
+For most complete and accurate information, please refer to
+https://osmocom.org/projects/osmobts/wiki
-Specifically, this includes
- * BTS-Side implementation of TS 08.58 (RSL) and TS 12.21 (OML)
- * BTS-Side implementation of LAPDm (using libosmocore/libosmogsm)
- * A somewhat separated interface between those higher layer parts
- and the Layer1 interface.
+To submit patches, please refer to
+https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit
+(Note: github pull requests are rejected by a bot)
+
+== Summary ==
-Right now, only one hardware and Layer1 are supported: The sysmocom
-sysmoBTS.
+This code implements Layer 2 and higher of a more or less conventional GSM BTS
+(Base Transceiver Station) - however, using an Abis/IP interface, rather than
+the old-fashioned E1/T1.
-There is some experimental and way incomplete code to use a couple of
-OsmocomBB phones and run them in the BTS. However, the required code
-for the Calypso DSP code have not been written yet. This would still
-require a lot of work.
+Specifically, this includes
+ * BTS-side implementation of TS 08.58 (RSL) and TS 12.21 (OML)
+ * BTS-side implementation of LAPDm (using libosmocore/libosmogsm)
+ * A somewhat separated interface between those higher layer parts and the
+ Layer1 interface.
-Some additional work is being done in using some parts of the OpenBTS
-L1FEC and glue it against omso-bts. This code is called osmo-trx and
-requires the jolly/trx branch of this repository.
+Several kinds of BTS hardware are supported:
+ * sysmocom sysmoBTS
+ * Octasic octphy
+ * Nutaq litecell 1.5
+ * software-defined radio based osmo-bts-trx (e.g. B210)
== Known Limitations ==
@@ -52,5 +55,3 @@ implementation:
* Doesn't yet include MAC address in Abis/IP Identity message
* MphConfig.CNF can be returned to the wrong callback. E.g. with Tx Power
and ciphering. The dispatch should take a look at the hLayer3.
-
-
diff --git a/configure.ac b/configure.ac
index 0951e187..3fd6b8a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,6 +3,9 @@ AC_INIT([osmo-bts],
m4_esyscmd([./git-version-gen .tarball-version]),
[openbsc-devel@lists.openbsc.org])
+dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
+AC_CONFIG_AUX_DIR([.])
+
AM_INIT_AUTOMAKE([dist-bzip2])
AC_CONFIG_TESTDIR(tests)
@@ -15,6 +18,13 @@ AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_RANLIB
+dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
+AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
+if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
+ AC_MSG_WARN([You need to install pkg-config])
+fi
+PKG_PROG_PKG_CONFIG([0.20])
+
dnl checks for header files
AC_HEADER_STDC
@@ -65,6 +75,7 @@ if test "$enable_octphy" = "yes" ; then
AC_CHECK_HEADER([octphy/octvc1/gsm/octvc1_gsm_default.h],[],
[AC_MSG_ERROR([octphy/octvc1/gsm/octvc1_gsm_default.h can not be found in $octsdr2g_incdir])],
[#include <octphy/octvc1/gsm/octvc1_gsm_default.h>])
+ AC_CHECK_MEMBER([tOCTVC1_GSM_TRX_CONFIG.usCentreArfcn], AC_DEFINE([OCTPHY_MULTI_TRX], [1], [Define to 1 if your octphy header files support multi-trx]), [], [#include <octphy/octvc1/gsm/octvc1_gsm_api.h>])
CPPFLAGS=$oldCPPFLAGS
fi
diff --git a/contrib/dtx_check.gawk b/contrib/dtx_check.gawk
new file mode 100755
index 00000000..9a3ddcf6
--- /dev/null
+++ b/contrib/dtx_check.gawk
@@ -0,0 +1,89 @@
+#!/usr/bin/gawk -f
+
+# Expected input format: FN TYPE
+
+BEGIN {
+ DELTA = 0
+ ERR = 0
+ FORCE = 0
+ FN = 0
+ SILENCE = 0
+ TYPE = ""
+ CHK = ""
+ U_MAX = 8 * 20 + 120 / 26
+ U_MIN = 8 * 20 - 120 / 26
+ F_MAX = 3 * 20 + 120 / 26
+ F_MIN = 3 * 20 - 120 / 26
+}
+
+{
+ if (NR > 2) { # we have data from previous record to compare to
+ DELTA = ($1 - FN) * 120 / 26
+ CHK = "OK"
+ if ("FACCH" == $2 && "ONSET" == TYPE) { # ONSET due to FACCH is NOT a talkspurt
+ SILENCE = 1
+ }
+ if (("UPDATE" == TYPE || "FIRST" == TYPE) && ("FACCH" == $2 || "SPEECH" == $2)) { # check for missing ONSET:
+ CHK = "FAIL: missing ONSET (" $2 ") after " TYPE "."
+ ERR++
+ }
+ if ("SID_P1" == $2) {
+ CHK = "FAIL: regular AMR payload with FT SID and STI=0 (should be either pyaload Update or STI=1)."
+ ERR++
+ }
+ if ("FORCED_FIRST" == $2 || "FORCED_NODATA" == $2 || "FORCED_F_P2" == $2 || "FORCED_F_INH" == $2 || "FORCED_U_INH" == $2) {
+ CHK = "FAIL: event " $2 " inserted by DSP."
+ FORCE++
+ ERR++
+ }
+ if ("FIRST_P2" != $2 && "FIRST_P1" == TYPE) {
+ CHK = "FAIL: " TYPE " followed by " $2 " instead of P2."
+ ERR++
+ }
+ if ("FIRST" == $2 && "FIRST" == TYPE) {
+ CHK = "FAIL: multiple SID FIRST in a row."
+ ERR++
+ }
+ if ("OK" == CHK && "ONSET" != $2) { # check inter-SID distances:
+ if ("UPDATE" == TYPE) {
+ if (DELTA > U_MAX) {
+ CHK = "FAIL: delta (" $1 - FN "fn) from previous SID UPDATE (@" FN ") too big " DELTA "ms > " U_MAX "ms."
+ ERR++
+ }
+ if ("UPDATE" == $2 && DELTA < U_MIN) {
+ CHK = "FAIL: delta (" $1 - FN "fn) from previous SID UPDATE (@" FN ") too small " DELTA "ms < " U_MIN "ms."
+ ERR++
+ }
+ }
+ if ("FIRST" == TYPE) {
+ if (DELTA > F_MAX) {
+ CHK = "FAIL: delta (" $1 - FN "fn) from previous SID FIRST (@" FN ") too big " DELTA "ms > " F_MAX "ms."
+ ERR++
+ }
+ if ("UPDATE" == $2 && DELTA < F_MIN) {
+ CHK = "FAIL: delta (" $1 - FN "fn) from previous SID UPDATE (@" FN ") too small " DELTA "ms < " F_MIN "ms."
+ ERR++
+ }
+ }
+ }
+ if ("FACCH" == TYPE && "FIRST" != $2 && "FACCH" != $2 && 1 == SILENCE) { # check FACCH handling
+ CHK = "FAIL: incorrect silence resume with " $2 " after FACCH."
+ ERR++
+ }
+ }
+ if ("SPEECH" == $2 || "ONSET" == $2) { # talkspurt
+ SILENCE = 0
+ }
+ if ("UPDATE" == $2 || "FIRST" == $2) { # silence
+ SILENCE = 1
+ }
+ print $1, $2, CHK
+ if ($2 != "EMPTY") { # skip over EMPTY records
+ TYPE = $2
+ FN = $1
+ }
+}
+
+END {
+ print "Check completed: found " ERR " errors (" FORCE " events inserted by DSP) in " NR " records."
+}
diff --git a/contrib/jenkins_bts_model.sh b/contrib/jenkins_bts_model.sh
new file mode 100755
index 00000000..58eac5b1
--- /dev/null
+++ b/contrib/jenkins_bts_model.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+bts_model="$1"
+
+if [ ! -d "./contrib" ]; then
+ echo "Run ./contrib/jenkins_bts_model.sh from the root of the osmo-bts tree"
+ exit 1
+fi
+
+set -x -e
+
+case "$bts_model" in
+
+ sysmo)
+ ./contrib/jenkins_sysmobts.sh
+ ;;
+
+ oct)
+ ./contrib/jenkins_oct.sh
+ ;;
+
+ trx)
+ ./contrib/jenkins_bts_trx.sh
+ ;;
+
+ oct+trx)
+ ./contrib/jenkins_oct_and_bts_trx.sh
+ ;;
+
+ *)
+ set +x
+ echo "Unknown BTS model '$bts_model'"
+ ;;
+esac
diff --git a/contrib/jenkins_bts_trx.sh b/contrib/jenkins_bts_trx.sh
new file mode 100755
index 00000000..c4f05a93
--- /dev/null
+++ b/contrib/jenkins_bts_trx.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+
+set -ex
+
+base="$PWD"
+deps="$base/deps"
+inst="$deps/install"
+export deps inst
+
+mkdir "$deps" || true
+rm -rf "$inst"
+
+export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
+export LD_LIBRARY_PATH="$inst/lib"
+
+osmo-build-dep.sh libosmocore
+osmo-build-dep.sh libosmo-abis
+
+cd "$deps"
+
+# Get osmo-pcu for pcuif_proto.h
+osmo-deps.sh osmo-pcu
+
+# Get openbsc for gsm_data_shared.*
+osmo-deps.sh openbsc
+
+cd "$base"
+
+set +x
+echo
+echo
+echo
+echo " =============================== osmo-bts-trx ==============================="
+echo
+set -x
+
+autoreconf --install --force
+configure_flags="\
+ --with-openbsc=$deps/openbsc/openbsc/include \
+ --with-osmo-pcu=$deps/osmo-pcu/include \
+ --enable-trx \
+ "
+./configure $configure_flags
+$MAKE $PARALLEL_MAKE
+$MAKE check \
+ || cat-testlogs.sh
+DISTCHECK_CONFIGURE_FLAGS="$configure_flags" \
+ $MAKE distcheck \
+ || cat-testlogs.sh
diff --git a/contrib/jenkins_oct.sh b/contrib/jenkins_oct.sh
index 32b14073..4fecd0a3 100755
--- a/contrib/jenkins_oct.sh
+++ b/contrib/jenkins_oct.sh
@@ -2,33 +2,29 @@
set -ex
-rm -rf deps/install
-mkdir deps || true
-cd deps
+base="$PWD"
+deps="$base/deps"
+inst="$deps/install"
+export deps inst
+
+mkdir "$deps" || true
+rm -rf "$inst"
# Get the headers..
+cd "$deps"
git clone git://git.osmocom.org/openbsc || true
cd openbsc
git pull --rebase
+cd "$base"
+osmo-build-dep.sh libosmocore
-# Build the dependency
-cd ../
+export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
+export LD_LIBRARY_PATH="$inst/lib"
-osmo-deps.sh libosmocore
-cd libosmocore
-autoreconf --install --force
-./configure --prefix=$PWD/../install
-$MAKE $PARALLEL_MAKE install
+osmo-build-dep.sh libosmo-abis
-cd ../
-osmo-deps.sh libosmo-abis
-cd libosmo-abis
-autoreconf --install --force
-PKG_CONFIG_PATH=$PWD/../install/lib/pkgconfig ./configure --prefix=$PWD/../install
-PKG_CONFIG_PATH=$PWD/../install/lib/pkgconfig $MAKE $PARALLEL_MAKE install
-
-cd ../
+cd "$deps"
if ! test -d layer1-api;
then
git clone git://git.osmocom.org/octphy-2g-headers layer1-api
@@ -43,11 +39,21 @@ else
git reset --hard $FIRMWARE_VERSION
fi
+cd "$base"
+
+set +x
+echo
+echo
+echo
+echo " =============================== osmo-bts-octphy ==============================="
+echo
+set -x
-# Build osmo-bts
-cd ../../
autoreconf --install --force
-PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig ./configure --with-openbsc=$PWD/deps/openbsc/openbsc/include --with-octsdr-2g=$PWD/deps/layer1-api/ --enable-octphy
-PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig $MAKE $PARALLEL_MAKE
-PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig LD_LIBRARY_PATH=$PWD/deps/install/lib $MAKE check
-DISTCHECK_CONFIGURE_FLAGS="--with-octsdr-2g=$PWD/deps/layer1-api/ --with-openbsc=$PWD/deps/openbsc/openbsc/include --enable-octphy" PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig LD_LIBRARY_PATH=$PWD/deps/install/lib $MAKE distcheck
+./configure --with-openbsc="$deps/openbsc/openbsc/include" --with-octsdr-2g="$deps/layer1-api/" --enable-octphy
+$MAKE $PARALLEL_MAKE
+$MAKE check \
+ || cat-testlogs.sh
+DISTCHECK_CONFIGURE_FLAGS="--with-octsdr-2g=$deps/layer1-api/ --with-openbsc=$deps/openbsc/openbsc/include --enable-octphy" \
+ $MAKE distcheck \
+ || cat-testlogs.sh
diff --git a/contrib/jenkins_oct_and_bts_trx.sh b/contrib/jenkins_oct_and_bts_trx.sh
new file mode 100755
index 00000000..0740bd03
--- /dev/null
+++ b/contrib/jenkins_oct_and_bts_trx.sh
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+
+set -ex
+
+base="$PWD"
+deps="$base/deps"
+inst="$deps/install"
+export deps inst
+
+mkdir "$deps" || true
+rm -rf "$inst"
+
+export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
+export LD_LIBRARY_PATH="$inst/lib"
+
+osmo-build-dep.sh libosmocore
+osmo-build-dep.sh libosmo-abis
+
+cd "$deps"
+
+# Get osmo-pcu for pcuif_proto.h
+osmo-deps.sh osmo-pcu
+
+# Get openbsc for gsm_data_shared.*
+osmo-deps.sh openbsc
+
+cd "$deps"
+if ! test -d layer1-api;
+then
+ git clone git://git.osmocom.org/octphy-2g-headers layer1-api
+fi
+cd layer1-api
+git fetch origin
+if [ $FIRMWARE_VERSION = "master" ];
+then
+git reset --hard origin/master
+else
+git reset --hard $FIRMWARE_VERSION
+fi
+
+cd "$base"
+
+set +x
+echo
+echo
+echo
+echo " =============================== osmo-bts-octphy+trx ==============================="
+echo
+set -x
+
+autoreconf --install --force
+configure_flags="\
+ --with-openbsc=$deps/openbsc/openbsc/include \
+ --with-osmo-pcu=$deps/osmo-pcu/include \
+ --with-octsdr-2g=$deps/layer1-api/ \
+ --enable-octphy \
+ --enable-trx \
+ "
+./configure $configure_flags
+$MAKE $PARALLEL_MAKE
+$MAKE check \
+ || cat-testlogs.sh
+DISTCHECK_CONFIGURE_FLAGS="$configure_flags" \
+ $MAKE distcheck \
+ || cat-testlogs.sh
diff --git a/contrib/jenkins_sysmobts.sh b/contrib/jenkins_sysmobts.sh
index 051c8e0c..be544a78 100755
--- a/contrib/jenkins_sysmobts.sh
+++ b/contrib/jenkins_sysmobts.sh
@@ -2,33 +2,29 @@
set -ex
-rm -rf deps/install
-mkdir deps || true
-cd deps
+base="$PWD"
+deps="$base/deps"
+inst="$deps/install"
+export deps inst
+
+mkdir "$deps" || true
+rm -rf "$inst"
# Get the headers..
+cd "$deps"
git clone git://git.osmocom.org/openbsc || true
cd openbsc
git pull --rebase
+cd "$base"
+osmo-build-dep.sh libosmocore
-# Build the dependency
-cd ../
-
-osmo-deps.sh libosmocore
-cd libosmocore
-autoreconf --install --force
-./configure --prefix=$PWD/../install
-$MAKE $PARALLEL_MAKE install
+export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
+export LD_LIBRARY_PATH="$inst/lib"
-cd ../
-osmo-deps.sh libosmo-abis
-cd libosmo-abis
-autoreconf --install --force
-PKG_CONFIG_PATH=$PWD/../install/lib/pkgconfig ./configure --prefix=$PWD/../install
-PKG_CONFIG_PATH=$PWD/../install/lib/pkgconfig $MAKE $PARALLEL_MAKE install
+osmo-build-dep.sh libosmo-abis
-cd ../
+cd "$deps"
if ! test -d layer1-api;
then
git clone git://git.sysmocom.de/sysmo-bts/layer1-api.git layer1-api
@@ -42,20 +38,29 @@ git reset --hard origin/master
else
git reset --hard $FIRMWARE_VERSION
fi
-mkdir -p $PWD/../install/include/sysmocom/femtobts/
-cp include/*.h ../install/include/sysmocom/femtobts/
+mkdir -p "$inst/include/sysmocom/femtobts"
+cp include/*.h "$inst/include/sysmocom/femtobts/"
+cd "$base"
-# Build osmo-bts
-cd ../../
-autoreconf --install --force
-PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig ./configure --enable-sysmocom-bts --with-openbsc=$PWD/deps/openbsc/openbsc/include
-PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig $MAKE $PARALLEL_MAKE
-PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig LD_LIBRARY_PATH=$PWD/deps/install/lib $MAKE check
-DISTCHECK_CONFIGURE_FLAGS="--enable-sysmocom-bts --with-openbsc=$PWD/deps/openbsc/openbsc/include" PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig LD_LIBRARY_PATH=$PWD/deps/install/lib $MAKE distcheck
+set +x
+echo
+echo
+echo
+echo " =============================== osmo-bts-sysmo ==============================="
+echo
+set -x
+autoreconf --install --force
+./configure --enable-sysmocom-bts --with-openbsc="$deps/openbsc/openbsc/include"
+$MAKE $PARALLEL_MAKE
+$MAKE check \
+ || cat-testlogs.sh
+DISTCHECK_CONFIGURE_FLAGS="--enable-sysmocom-bts --with-openbsc=$deps/openbsc/openbsc/include" \
+ $MAKE distcheck \
+ || cat-testlogs.sh
# This will not work for the femtobts
if [ $FIRMWARE_VERSION != "femtobts_v2.7" ]; then
- PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig $MAKE -C contrib/sysmobts-calib
+ $MAKE -C contrib/sysmobts-calib
fi
diff --git a/contrib/superfemto.sh b/contrib/superfemto.sh
new file mode 100755
index 00000000..19dc4107
--- /dev/null
+++ b/contrib/superfemto.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+# Split common DSP call log file (produced by superfemto-compatible firmware) into 4 separate call leg files (MO/MT & DL/UL) with events in format "FN EVENT_TYPE":
+# MO Mobile Originated
+# MT Mobile Terminated
+# DL DownLink (BTS -> L1)
+# UL UpLink (L1 -> BTS)
+
+if [ -z $1 ]; then
+ echo "expecting DSP log file name as parameter"
+ exit 1
+fi
+
+# MO handle appear 1st in the logs
+MO=$(grep 'h=' $1 | head -n 1 | cut -f2 -d',' | cut -f2 -d= | cut -f1 -d']')
+
+# direction markers:
+DLST="_CodeBurst"
+ULST="_DecodeAndIdentify"
+
+# DL sed filters:
+D_EMP='s/ Empty frame request!/EMPTY/i'
+D_FAC='s/ Coding a FACCH\/. frame !!/FACCH/i'
+D_FST='s/ Coding a RTP SID First frame !!/FIRST/i'
+D_FS1='s/ Coding a SID First P1 frame !!/FIRST_P1/i'
+D_FS2='s/ Coding a SID First P2 frame !!/FIRST_P2/i'
+D_RP1='s/ Coding a RTP SID P1 frame !!/SID_P1/i'
+D_UPD='s/ Coding a RTP SID Update frame !!/UPDATE/i'
+D_SPE='s/ Coding a RTP Speech frame !!/SPEECH/i'
+D_ONS='s/ Coding a Onset frame !!/ONSET/i'
+D_FO1='s/ A speech frame is following a NoData or SID First without an Onset./FORCED_FIRST/i'
+D_FO2='s/ A speech frame is following a NoData without an Onset./FORCED_NODATA/i'
+D_FP2='s/ A speech frame is following a NoData or SID_FIRST_P2 without an Onset./FORCED_F_P2/i'
+D_FIN='s/ A speech frame is following a SID_FIRST without inhibit. A SID_FIRST_INH will be inserted./FORCED_F_INH/i'
+D_UIN='s/ A speech frame is following a SID_UPDATE without inhibit. A SID_UPDATE_INH will be inserted./FORCED_U_INH/i'
+
+# UL sed filters:
+U_NOD='s/ It is a No Data frame !!/NODATA/i'
+U_ONS='s/ It is an ONSET frame !!/ONSET/i'
+U_UPD='s/ It is a SID UPDATE frame !!/UPDATE/i'
+U_FST='s/ It is a SID FIRST frame !!/FIRST/i'
+U_FP1='s/ It is a SID-First P1 frame !!/FIRST_P1/i'
+U_FP2='s/ It is a SID-First P2 frame !!/FIRST_P2/i'
+U_SPE='s/ It is a SPEECH frame *!!/SPEECH/i'
+U_UIN='s/ It is a SID update InH frame !!/UPD_INH/i'
+U_FIN='s/ It is a SID-First InH frame !!/FST_INH/i'
+U_RAT='s/ It is a RATSCCH data frame !!/RATSCCH/i'
+
+DL () { # filter downlink-related entries
+ grep $DLST $1 > $1.DL.tmp
+}
+
+UL () { # uplink does not require special fix
+ grep $ULST $1 > $1.UL.tmp.fix
+}
+
+DL $1
+UL $1
+
+FIX() { # add MO/MT marker from preceding line to inserted ONSETs so filtering works as expected
+ cat $1.DL.tmp | awk 'BEGIN{ FS=" h="; H="" } { if (NF > 1) { H = $2; print $1 "h=" $2 } else { print $1 ", h=" H } }' > $1.DL.tmp.fix
+}
+
+FIX $1
+
+MO() { # filter MO call DL or UL logs
+ grep "h=$MO" $1.tmp.fix > $1.MO.raw
+}
+
+MT() { # filter MT call DL or UL logs
+ grep -v "h=$MO" $1.tmp.fix > $1.MT.raw
+}
+
+MO $1.DL
+MT $1.DL
+MO $1.UL
+MT $1.UL
+
+PREP() { # prepare logs for reformatting
+ cat $1.raw | cut -f2 -d')' | cut -f1 -d',' | cut -f2 -d'>' | sed 's/\[u32Fn/fn/' | sed 's/\[ u32Fn/fn/' | sed 's/fn = /fn=/' | sed 's/fn=//' | sed 's/\[Fn=//' | sed 's/ An Onset will be inserted.//' > $1.tmp1
+}
+
+PREP "$1.DL.MT"
+PREP "$1.DL.MO"
+PREP "$1.UL.MT"
+PREP "$1.UL.MO"
+
+RD() { # reformat DL logs for consistency checks
+ cat $1.tmp1 | sed "$D_FST" | sed "$D_SPE" | sed "$D_FS1" | sed "$D_FS2" | sed "$D_UIN" | sed "$D_FIN" | sed "$D_UPD" | sed "$D_INH" | sed "$D_RP1" | sed "$D_ONS" | sed "$D_EMP" | sed "$D_FAC" | sed "$D_FO1" | sed "$D_FO2" | sed "$D_FP2" > $1.tmp2
+}
+
+RU() { # reformat UL logs for consistency checks
+ cat $1.tmp1 | sed "$U_FST" | sed "$U_SPE" | sed "$U_FP1" | sed "$U_FP2" | sed "$U_UPD" | sed "$U_ONS" | sed "$U_NOD" | sed "$U_UIN" | sed "$U_FIN" | sed "$U_RAT" > $1.tmp2
+}
+
+RD "$1.DL.MT"
+RD "$1.DL.MO"
+RU "$1.UL.MT"
+RU "$1.UL.MO"
+
+SW() { # swap fields
+ cat $1.tmp2 | awk '{ print $2, $1 }' > $1
+}
+
+SW "$1.DL.MT"
+SW "$1.DL.MO"
+SW "$1.UL.MT"
+SW "$1.UL.MO"
+
+rm $1.*.tmp*
diff --git a/debian/compat b/debian/compat
index 7f8f011e..ec635144 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-7
+9
diff --git a/debian/control b/debian/control
index d067e317..e1a58b28 100644
--- a/debian/control
+++ b/debian/control
@@ -1,12 +1,24 @@
Source: osmo-bts
+Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
Section: net
Priority: optional
-Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
-Build-Depends: debhelper (>= 7.0.0~), dh-autoreconf, dh-systemd (>= 1.5), autotools-dev, pkg-config, libosmocore-dev, libortp-dev, libosmo-abis-dev, libosmo-netif-dev
-Standards-Version: 3.8.4
-Homepage: http://osmocom.org/projects/osmobts
-Vcs-Git: git://git.osmocom.org/osmo-bts
+Build-Depends: debhelper (>= 9),
+ pkg-config,
+ dh-autoreconf,
+ dh-systemd (>= 1.5),
+ autotools-dev,
+ pkg-config,
+ libosmocore-dev,
+ openbsc-dev,
+ libosmo-abis-dev,
+ libosmo-netif-dev,
+ libgps-dev,
+ libortp-dev,
+ txt2man
+Standards-Version: 3.9.8
Vcs-Browser: http://git.osmocom.org/osmo-bts/
+Vcs-Git: git://git.osmocom.org/osmo-bts
+Homepage: https://projects.osmocom.org/projects/osmobts
Package: osmo-bts-trx
Architecture: any
diff --git a/debian/rules b/debian/rules
index f7f941b3..80ba0b2c 100755
--- a/debian/rules
+++ b/debian/rules
@@ -5,7 +5,7 @@ DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
#export DH_VERBOSE=1
-export DEB_BUILD_HARDENING=1
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
%:
@@ -20,3 +20,13 @@ override_dh_autoreconf:
override_dh_auto_configure:
dh_auto_configure -- --enable-trx
+
+override_dh_clean:
+ dh_clean
+ $(RM) tests/package.m4
+ $(RM) tests/testsuite
+
+# Print test results in case of a failure
+override_dh_auto_test:
+ dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
+
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index d0b55ff9..ef4165f3 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -1,4 +1,5 @@
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h \
- power_control.h scheduler.h scheduler_backend.h phy_link.h
+ power_control.h scheduler.h scheduler_backend.h phy_link.h \
+ dtx_dl_amr_fsm.h
diff --git a/include/osmo-bts/amr.h b/include/osmo-bts/amr.h
index 6bdc41fc..f3132874 100644
--- a/include/osmo-bts/amr.h
+++ b/include/osmo-bts/amr.h
@@ -11,7 +11,8 @@ void amr_log_mr_conf(int ss, int logl, const char *pfx,
int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
const uint8_t *mr_conf, unsigned int len);
-int get_amr_mode_idx(const struct amr_multirate_conf *amr_mrc, uint8_t cmi);
+void amr_set_mode_pref(uint8_t *data, const struct amr_multirate_conf *amr_mrc,
+ uint8_t cmi, uint8_t cmr);
unsigned int amr_get_initial_mode(struct gsm_lchan *lchan);
#endif /* _OSMO_BTS_AMR_H */
diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h
index ec58eddc..567772ec 100644
--- a/include/osmo-bts/bts.h
+++ b/include/osmo-bts/bts.h
@@ -36,7 +36,7 @@ uint8_t *lchan_sacch_get(struct gsm_lchan *lchan);
int lchan_init_lapdm(struct gsm_lchan *lchan);
void load_timer_start(struct gsm_bts *bts);
-
+uint8_t num_agch(struct gsm_bts_trx *trx, const char * arg);
void bts_update_status(enum bts_global_status which, int on);
int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx);
diff --git a/include/osmo-bts/bts_model.h b/include/osmo-bts/bts_model.h
index 7e2d0887..7a87d786 100644
--- a/include/osmo-bts/bts_model.h
+++ b/include/osmo-bts/bts_model.h
@@ -45,6 +45,9 @@ int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan);
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
+int bts_model_lchan_deactivate(struct gsm_lchan *lchan);
+int bts_model_lchan_deactivate_sacch(struct gsm_lchan *lchan);
+
void bts_model_abis_close(struct gsm_bts *bts);
int bts_model_ctrl_cmds_install(struct gsm_bts *bts);
diff --git a/include/osmo-bts/dtx_dl_amr_fsm.h b/include/osmo-bts/dtx_dl_amr_fsm.h
new file mode 100644
index 00000000..4fb2f251
--- /dev/null
+++ b/include/osmo-bts/dtx_dl_amr_fsm.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+/* DTX DL AMR FSM */
+
+#define X(s) (1 << (s))
+
+enum dtx_dl_amr_fsm_states {
+ ST_VOICE,
+ ST_SID_F1,
+ ST_SID_F2,
+ ST_F1_INH,
+ ST_U_INH,
+ ST_F1_INH_REC,
+ ST_U_INH_REC,
+ ST_SID_U,
+ ST_ONSET_V,
+ ST_ONSET_F,
+ ST_ONSET_V_REC,
+ ST_ONSET_F_REC,
+ ST_FACCH,
+};
+
+enum dtx_dl_amr_fsm_events {
+ E_VOICE,
+ E_ONSET,
+ E_FACCH,
+ E_COMPL,
+ E_INHIB,
+ E_SID_F,
+ E_SID_U,
+};
+
+extern const struct value_string dtx_dl_amr_fsm_event_names[];
+extern struct osmo_fsm dtx_dl_amr_fsm;
diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h
index ed864d61..ceae25f1 100644
--- a/include/osmo-bts/gsm_data.h
+++ b/include/osmo-bts/gsm_data.h
@@ -86,6 +86,7 @@ struct gsm_bts_role_bts {
char *bsc_oml_host;
struct llist_head oml_queue;
unsigned int rtp_jitter_buf_ms;
+ bool rtp_jitter_adaptive;
struct {
uint8_t ciphers; /* flags A5/1==0x1, A5/2==0x2, A5/3==0x4 */
} support;
diff --git a/include/osmo-bts/l1sap.h b/include/osmo-bts/l1sap.h
index 981cd756..dcebc1d8 100644
--- a/include/osmo-bts/l1sap.h
+++ b/include/osmo-bts/l1sap.h
@@ -1,6 +1,12 @@
#ifndef L1SAP_H
#define L1SAP_H
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+/* lchan link ID */
+#define LID_SACCH 0x40
+#define LID_DEDIC 0x00
+
/* timeslot and subslot from chan_nr */
#define L1SAP_CHAN2TS(chan_nr) (chan_nr & 7)
#define L1SAP_CHAN2SS_TCHH(chan_nr) ((chan_nr >> 3) & 1)
@@ -8,7 +14,7 @@
#define L1SAP_CHAN2SS_SDCCH8(chan_nr) ((chan_nr >> 3) & 7)
/* logical channel from chan_nr + link_id */
-#define L1SAP_IS_LINK_SACCH(link_id) ((link_id & 0xC0) == 0x40)
+#define L1SAP_IS_LINK_SACCH(link_id) ((link_id & 0xC0) == LID_SACCH)
#define L1SAP_IS_CHAN_TCHF(chan_nr) ((chan_nr & 0xf8) == 0x08)
#define L1SAP_IS_CHAN_TCHH(chan_nr) ((chan_nr & 0xf0) == 0x10)
#define L1SAP_IS_CHAN_SDCCH4(chan_nr) ((chan_nr & 0xe0) == 0x20)
@@ -28,6 +34,12 @@
#define L1SAP_FN2PTCCHBLOCK(fn) ((fn / 104) & 3)
#define L1SAP_IS_PTCCH(fn) ((fn % 52) == 12)
+static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
+ 0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
+ 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
+ 0x2B, 0x2B, 0x2B
+};
+
/* subslot from any chan_nr */
static inline uint8_t l1sap_chan2ss(uint8_t chan_nr)
{
@@ -55,7 +67,8 @@ int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
/* call-back function for incoming RTP */
void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
- unsigned int rtp_pl_len, bool marker);
+ unsigned int rtp_pl_len, uint16_t seq_number,
+ uint32_t timestamp, bool marker);
/* channel control */
int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *tp);
diff --git a/include/osmo-bts/msg_utils.h b/include/osmo-bts/msg_utils.h
index f03eb438..55e8475d 100644
--- a/include/osmo-bts/msg_utils.h
+++ b/include/osmo-bts/msg_utils.h
@@ -5,14 +5,23 @@
#pragma once
#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
+
+#include <osmocom/codec/codec.h>
#include <stdbool.h>
struct msgb;
-/* Access 1st byte of msgb control buffer */
+/* Access 1st part of msgb control buffer */
#define rtpmsg_marker_bit(x) ((x)->cb[0])
+/* Access 2nd part of msgb control buffer */
+#define rtpmsg_seq(x) ((x)->cb[1])
+
+/* Access 3rd part of msgb control buffer */
+#define rtpmsg_ts(x) ((x)->cb[2])
+
/**
* Classification of OML message. ETSI for plain GSM 12.21
* messages and IPA/Osmo for manufacturer messages.
@@ -24,10 +33,15 @@ enum {
};
void lchan_set_marker(bool t, struct gsm_lchan *lchan);
-void save_last_sid(struct gsm_lchan *lchan, uint8_t *l1_payload, size_t length,
- uint32_t fn, bool update);
+bool dtx_dl_amr_enabled(const struct gsm_lchan *lchan);
+void dtx_dispatch(struct gsm_lchan *lchan, enum dtx_dl_amr_fsm_events e);
+bool dtx_recursion(const struct gsm_lchan *lchan);
+void dtx_int_signal(struct gsm_lchan *lchan);
+void dtx_cache_payload(struct gsm_lchan *lchan, const uint8_t *l1_payload,
+ size_t length, uint32_t fn, int update);
+int dtx_dl_amr_fsm_step(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
+ size_t rtp_pl_len, uint32_t fn, uint8_t *l1_payload,
+ bool marker, uint8_t *len, uint8_t *ft_out);
uint8_t repeat_last_sid(struct gsm_lchan *lchan, uint8_t *dst, uint32_t fn);
-bool dtx_amr_sid_optional(const struct gsm_lchan *lchan, uint32_t fn);
-bool dtx_sched_optional(struct gsm_lchan *lchan, uint32_t fn);
int msg_verify_ipa_structure(struct msgb *msg);
int msg_verify_oml_structure(struct msgb *msg);
diff --git a/include/osmo-bts/phy_link.h b/include/osmo-bts/phy_link.h
index 753dfd4b..b12d78f7 100644
--- a/include/osmo-bts/phy_link.h
+++ b/include/osmo-bts/phy_link.h
@@ -7,6 +7,7 @@
#include <osmo-bts/scheduler.h>
#include <linux/if_packet.h>
+#include "btsconfig.h"
struct gsm_bts_trx;
@@ -63,6 +64,10 @@ struct phy_link {
uint32_t rf_port_index;
uint32_t rx_gain_db;
uint32_t tx_atten_db;
+#if OCTPHY_MULTI_TRX == 1
+ /* arfcn used by TRX with id 0 */
+ uint16_t center_arfcn;
+#endif
struct octphy_hdl *hdl;
} octphy;
diff --git a/include/osmo-bts/rsl.h b/include/osmo-bts/rsl.h
index 093e9cb7..a2a6e3d9 100644
--- a/include/osmo-bts/rsl.h
+++ b/include/osmo-bts/rsl.h
@@ -9,8 +9,11 @@ enum {
LCHAN_REL_ACT_RSL,
LCHAN_REL_ACT_PCU,
LCHAN_REL_ACT_OML,
+ LCHAN_REL_ACT_REACT,
};
+#define LCHAN_FN_DUMMY 0xFFFFFFFF
+
int msgb_queue_flush(struct llist_head *list);
int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg);
@@ -24,6 +27,8 @@ int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay);
+int lchan_deactivate(struct gsm_lchan *lchan);
+
/* call-back for LAPDm code, called when it wants to send msgs UP */
int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx);
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index e7ecebbd..d104c481 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -1,6 +1,6 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
-LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS)
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOCODEC_CFLAGS)
+LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOCODEC_LIBS)
if ENABLE_LC15BTS
AM_CFLAGS += -DENABLE_LC15BTS
@@ -11,6 +11,7 @@ libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
rsl.c vty.c paging.c measurement.c amr.c lchan.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
- l1sap.c cbch.c power_control.c main.c phy_link.c
+ l1sap.c cbch.c power_control.c main.c phy_link.c \
+ dtx_dl_amr_fsm.c
libl1sched_a_SOURCES = scheduler.c
diff --git a/src/common/amr.c b/src/common/amr.c
index 56ed4302..05d1aaac 100644
--- a/src/common/amr.c
+++ b/src/common/amr.c
@@ -22,7 +22,8 @@ void amr_log_mr_conf(int ss, int logl, const char *pfx,
LOGPC(ss, logl, "\n");
}
-int get_amr_mode_idx(const struct amr_multirate_conf *amr_mrc, uint8_t cmi)
+static inline int get_amr_mode_idx(const struct amr_multirate_conf *amr_mrc,
+ uint8_t cmi)
{
unsigned int i;
for (i = 0; i < amr_mrc->num_modes; i++) {
@@ -32,6 +33,46 @@ int get_amr_mode_idx(const struct amr_multirate_conf *amr_mrc, uint8_t cmi)
return -EINVAL;
}
+static inline uint8_t set_cmr_mode_idx(const struct amr_multirate_conf *amr_mrc,
+ uint8_t cmr)
+{
+ int rc;
+
+ /* Codec Mode Request is in upper 4 bits of RTP payload header,
+ * and we simply copy the CMR into the CMC */
+ if (cmr == 0xF) {
+ /* FIXME: we need some state about the last codec mode */
+ return 0;
+ }
+
+ rc = get_amr_mode_idx(amr_mrc, cmr);
+ if (rc < 0) {
+ /* FIXME: we need some state about the last codec mode */
+ LOGP(DRTP, LOGL_INFO, "RTP->L1: overriding CMR %u\n", cmr);
+ return 0;
+ }
+ return rc;
+}
+
+static inline uint8_t set_cmi_mode_idx(const struct amr_multirate_conf *amr_mrc,
+ uint8_t cmi)
+{
+ int rc = get_amr_mode_idx(amr_mrc, cmi);
+ if (rc < 0) {
+ LOGP(DRTP, LOGL_ERROR, "AMR CMI %u not part of AMR MR set\n",
+ cmi);
+ return 0;
+ }
+ return rc;
+}
+
+void amr_set_mode_pref(uint8_t *data, const struct amr_multirate_conf *amr_mrc,
+ uint8_t cmi, uint8_t cmr)
+{
+ data[0] = set_cmi_mode_idx(amr_mrc, cmi);
+ data[1] = set_cmr_mode_idx(amr_mrc, cmr);
+}
+
/* parse a GSM 04.08 MultiRate Config IE (10.5.2.21aa) in a more
* comfortable internal data structure */
int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
@@ -42,7 +83,7 @@ int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
int i, j = 0;
if (mr_version != 1) {
- LOGP(DRSL, LOGL_ERROR, "AMR Multirate Version %u unknonw\n",
+ LOGP(DRSL, LOGL_ERROR, "AMR Multirate Version %u unknown\n",
mr_version);
goto ret_einval;
}
diff --git a/src/common/bts.c b/src/common/bts.c
index 42ac0967..e701b57f 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -40,6 +40,7 @@
#include <osmo-bts/abis.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/bts_model.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/oml.h>
@@ -109,6 +110,7 @@ int bts_init(struct gsm_bts *bts)
/* configurable via VTY */
btsb->paging_state = paging_init(btsb, 200, 0);
btsb->ul_power_target = -75; /* dBm default */
+ btsb->rtp_jitter_adaptive = false;
/* configurable via OML */
btsb->load.ccch.load_ind_period = 112;
@@ -180,6 +182,8 @@ int bts_init(struct gsm_bts *bts)
INIT_LLIST_HEAD(&btsb->lc15.ceased_alarm_list);
#endif
+ /* register DTX DL FSM */
+ osmo_fsm_register(&dtx_dl_amr_fsm);
return rc;
}
diff --git a/src/common/dtx_dl_amr_fsm.c b/src/common/dtx_dl_amr_fsm.c
new file mode 100644
index 00000000..d903b0cf
--- /dev/null
+++ b/src/common/dtx_dl_amr_fsm.c
@@ -0,0 +1,365 @@
+/* DTX DL AMR FSM */
+
+/* (C) 2016 by sysmocom s.f.m.c. GmbH
+ *
+ * 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 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 <osmo-bts/dtx_dl_amr_fsm.h>
+#include <osmo-bts/logging.h>
+
+void dtx_fsm_voice(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_VOICE:
+ case E_FACCH:
+ break;
+ case E_SID_F:
+ case E_SID_U:
+ osmo_fsm_inst_state_chg(fi, ST_SID_F1, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Inexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_sid_f1(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_SID_F:
+/* FIXME: what shall we do if we get SID-FIRST _again_ (twice in a row)?
+ Was observed during testing, let's just ignore it for now */
+ break;
+ case E_SID_U:
+ osmo_fsm_inst_state_chg(fi, ST_SID_U, 0, 0);
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ break;
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_SID_F2, 0, 0);
+ break;
+ case E_INHIB:
+ osmo_fsm_inst_state_chg(fi, ST_F1_INH, 0, 0);
+ break;
+ case E_ONSET:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_V, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_sid_f2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_SID_U:
+ osmo_fsm_inst_state_chg(fi, ST_SID_U, 0, 0);
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ break;
+ case E_ONSET:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_V, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_f1_inh(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_F1_INH_REC, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_u_inh(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_U_INH_REC, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_f1_inh_rec(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_u_inh_rec(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_sid_upd(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_FACCH:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_F, 0, 0);
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_INHIB:
+ osmo_fsm_inst_state_chg(fi, ST_U_INH, 0, 0);
+ break;
+ case E_SID_U:
+ case E_SID_F:
+/* FIXME: what shall we do if we get SID-FIRST _after_ sending SID-UPDATE?
+ Was observed during testing, let's just ignore it for now */
+ break;
+ case E_ONSET:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_V, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_onset_v(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_V_REC, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_onset_f(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_ONSET_F_REC, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_onset_v_rec(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_onset_f_rec(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_FACCH, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+void dtx_fsm_facch(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case E_SID_U:
+ case E_SID_F:
+ case E_FACCH:
+ break;
+ case E_VOICE:
+ osmo_fsm_inst_state_chg(fi, ST_VOICE, 0, 0);
+ break;
+ case E_COMPL:
+ osmo_fsm_inst_state_chg(fi, ST_SID_F1, 0, 0);
+ break;
+ default:
+ LOGP(DL1P, LOGL_ERROR, "Unexpected event %d\n", event);
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+static struct osmo_fsm_state dtx_dl_amr_fsm_states[] = {
+ /* default state for non-DTX and DTX when SPEECH is in progress */
+ [ST_VOICE] = {
+ .in_event_mask = X(E_SID_F) | X(E_SID_U) | X(E_VOICE) | X(E_FACCH),
+ .out_state_mask = X(ST_SID_F1),
+ .name = "Voice",
+ .action = dtx_fsm_voice,
+ },
+ /* SID-FIRST or SID-FIRST-P1 in case of AMR HR:
+ start of silence period (might be interrupted in case of AMR HR) */
+ [ST_SID_F1]= {
+ .in_event_mask = X(E_SID_F) | X(E_SID_U) | X(E_VOICE) | X(E_FACCH) | X(E_COMPL) | X(E_INHIB) | X(E_ONSET),
+ .out_state_mask = X(ST_SID_U) | X(ST_VOICE) | X(ST_ONSET_F) | X(ST_SID_F2) | X(ST_F1_INH) | X(ST_ONSET_V),
+ .name = "SID-FIRST (P1)",
+ .action = dtx_fsm_sid_f1,
+ },
+ /* SID-FIRST P2 (only for AMR HR):
+ actual start of silence period in case of AMR HR */
+ [ST_SID_F2]= {
+ .in_event_mask = X(E_SID_U) | X(E_VOICE) | X(E_FACCH) | X(E_ONSET),
+ .out_state_mask = X(ST_SID_U) | X(ST_VOICE) | X(ST_ONSET_F) | X(ST_ONSET_V),
+ .name = "SID-FIRST (P2)",
+ .action = dtx_fsm_sid_f2,
+ },
+ /* SID-FIRST Inhibited: incoming SPEECH (only for AMR HR) */
+ [ST_F1_INH]= {
+ .in_event_mask = X(E_COMPL),
+ .out_state_mask = X(ST_F1_INH_REC),
+ .name = "SID-FIRST (Inh)",
+ .action = dtx_fsm_f1_inh,
+ },
+ /* SID-UPDATE Inhibited: incoming SPEECH (only for AMR HR) */
+ [ST_U_INH]= {
+ .in_event_mask = X(E_COMPL),
+ .out_state_mask = X(ST_U_INH_REC),
+ .name = "SID-UPDATE (Inh)",
+ .action = dtx_fsm_u_inh,
+ },
+ /* SID-FIRST Inhibition recursion in progress:
+ Inhibit itself was already sent, now have to send the voice that caused it */
+ [ST_F1_INH_REC]= {
+ .in_event_mask = X(E_COMPL),
+ .out_state_mask = X(ST_VOICE),
+ .name = "SID-FIRST (Inh, Rec)",
+ .action = dtx_fsm_f1_inh_rec,
+ },
+ /* SID-UPDATE Inhibition recursion in progress:
+ Inhibit itself was already sent, now have to send the voice that caused it */
+ [ST_U_INH_REC]= {
+ .in_event_mask = X(E_COMPL),
+ .out_state_mask = X(ST_VOICE),
+ .name = "SID-UPDATE (Inh, Rec)",
+ .action = dtx_fsm_u_inh_rec,
+ },
+ /* Silence period with periodic comfort noise data updates */
+ [ST_SID_U]= {
+ .in_event_mask = X(E_FACCH) | X(E_VOICE) | X(E_INHIB) | X(E_SID_U) | X(E_SID_F) | X(E_ONSET),
+ .out_state_mask = X(ST_ONSET_F) | X(ST_VOICE) | X(ST_U_INH) | X(ST_SID_U) | X(ST_ONSET_V),
+ .name = "SID-UPDATE",
+ .action = dtx_fsm_sid_upd,
+ },
+ /* ONSET - end of silent period due to incoming SPEECH frame */
+ [ST_ONSET_V]= {
+ .in_event_mask = X(E_COMPL),
+ .out_state_mask = X(ST_ONSET_V_REC),
+ .name = "ONSET (SPEECH)",
+ .action = dtx_fsm_onset_v,
+ },
+ /* ONSET - end of silent period due to incoming FACCH frame */
+ [ST_ONSET_F]= {
+ .in_event_mask = X(E_COMPL),
+ .out_state_mask = X(ST_ONSET_F_REC),
+ .name = "ONSET (FACCH)",
+ .action = dtx_fsm_onset_f,
+ },
+ /* ONSET recursion in progress:
+ ONSET itself was already sent, now have to send the voice that caused it */
+ [ST_ONSET_V_REC]= {
+ .in_event_mask = X(E_COMPL),
+ .out_state_mask = X(ST_VOICE),
+ .name = "ONSET (SPEECH, Rec)",
+ .action = dtx_fsm_onset_v_rec,
+ },
+ /* ONSET recursion in progress:
+ ONSET itself was already sent, now have to send the data that caused it */
+ [ST_ONSET_F_REC]= {
+ .in_event_mask = X(E_COMPL),
+ .out_state_mask = X(ST_FACCH),
+ .name = "ONSET (FACCH, Rec)",
+ .action = dtx_fsm_onset_f_rec,
+ },
+ /* FACCH sending state: no SPEECH was observed before so once we're done
+ FSM should get back to silent period via SID-FIRST */
+ [ST_FACCH]= {
+ .in_event_mask = X(E_FACCH) | X(E_VOICE) | X(E_COMPL) | X(E_SID_U) | X(E_SID_F),
+ .out_state_mask = X(ST_VOICE) | X(ST_SID_F1),
+ .name = "FACCH",
+ .action = dtx_fsm_facch,
+ },
+};
+
+const struct value_string dtx_dl_amr_fsm_event_names[] = {
+ { E_VOICE, "Voice" },
+ { E_ONSET, "ONSET" },
+ { E_FACCH, "FACCH" },
+ { E_COMPL, "Complete" },
+ { E_INHIB, "Inhibit" },
+ { E_SID_F, "SID-FIRST" },
+ { E_SID_U, "SID-UPDATE" },
+ { 0, NULL }
+};
+
+struct osmo_fsm dtx_dl_amr_fsm = {
+ .name = "DTX DL AMR FSM",
+ .states = dtx_dl_amr_fsm_states,
+ .num_states = ARRAY_SIZE(dtx_dl_amr_fsm_states),
+ .event_names = dtx_dl_amr_fsm_event_names,
+ .log_subsys = DL1C,
+};
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index a0d886f2..fa09ca84 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -39,6 +39,7 @@
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/l1sap.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/bts.h>
@@ -73,25 +74,32 @@ get_active_lchan_by_chan_nr(struct gsm_bts_trx *trx, unsigned int chan_nr)
static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
-static uint32_t fn_ms_adj(uint32_t fn, uint32_t last_fn)
+static uint32_t fn_ms_adj(uint32_t fn, const struct gsm_lchan *lchan)
{
- if (last_fn != 0xFFFFFFFF) {
- uint32_t samples_passed = GSM_FN_TO_SAMPLES(fn - last_fn);
+ uint32_t samples_passed, r;
+
+ /* don't adjust duration:
+ - when no DTX enabled at all
+ - for ONSET RTP packet to avoid timestamp gap with subsequent SPEECH
+ RTP packet*/
+ if (lchan->rtp_tx_marker ||
+ lchan->ts->trx->bts->dtxu == GSM48_DTX_SHALL_NOT_BE_USED)
+ return GSM_RTP_DURATION;
+
+ if (lchan->tch.last_fn != LCHAN_FN_DUMMY) {
+ /* 12/13 frames usable for audio in TCH,
+ 160 samples per RTP packet,
+ 1 RTP packet per 4 frames */
+ samples_passed = (fn - lchan->tch.last_fn) * 12 * 160 / (13 * 4);
/* round number of samples to the nearest multiple of
GSM_RTP_DURATION */
- uint32_t r = samples_passed + GSM_RTP_DURATION / 2;
+ r = samples_passed + GSM_RTP_DURATION / 2;
r -= r % GSM_RTP_DURATION;
return r;
}
return GSM_RTP_DURATION;
}
-static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
- 0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
- 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
- 0x2B, 0x2B, 0x2B
-};
-
/* allocate a msgb containing a osmo_phsap_prim + optional l2 data
* in order to wrap femtobts header arround l2 data, there must be enough space
* in front and behind data pointer */
@@ -210,15 +218,14 @@ const struct value_string gsmtap_sapi_names[] = {
/* send primitive as gsmtap */
static int gsmtap_ph_data(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
- uint8_t *tn, uint8_t *ss, uint32_t *fn, uint8_t **data, int *len)
+ uint8_t *ss, uint32_t fn, uint8_t **data, int *len,
+ uint8_t num_agch)
{
struct msgb *msg = l1sap->oph.msg;
uint8_t chan_nr, link_id;
*data = msg->data + sizeof(struct osmo_phsap_prim);
*len = msg->len - sizeof(struct osmo_phsap_prim);
- *fn = l1sap->u.data.fn;
- *tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
chan_nr = l1sap->u.data.chan_nr;
link_id = l1sap->u.data.link_id;
@@ -236,10 +243,9 @@ static int gsmtap_ph_data(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
} else if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
*chan_type = GSMTAP_CHANNEL_BCCH;
} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
-#warning Set BS_AG_BLKS_RES
/* The sapi depends on DSP configuration, not
* on the actual SYSTEM INFORMATION 3. */
- if (L1SAP_FN2CCCHBLOCK(*fn) >= 1)
+ if (L1SAP_FN2CCCHBLOCK(fn) >= num_agch)
*chan_type = GSMTAP_CHANNEL_PCH;
else
*chan_type = GSMTAP_CHANNEL_AGCH;
@@ -251,18 +257,16 @@ static int gsmtap_ph_data(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
}
static int gsmtap_pdch(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
- uint8_t *tn, uint8_t *ss, uint32_t *fn, uint8_t **data, int *len)
+ uint8_t *ss, uint32_t fn, uint8_t **data, int *len)
{
struct msgb *msg = l1sap->oph.msg;
*data = msg->data + sizeof(struct osmo_phsap_prim);
*len = msg->len - sizeof(struct osmo_phsap_prim);
- *fn = l1sap->u.data.fn;
- *tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
- if (L1SAP_IS_PTCCH(*fn)) {
+ if (L1SAP_IS_PTCCH(fn)) {
*chan_type = GSMTAP_CHANNEL_PTCCH;
- *ss = L1SAP_FN2PTCCHBLOCK(*fn);
+ *ss = L1SAP_FN2PTCCHBLOCK(fn);
if (l1sap->oph.primitive
== PRIM_OP_INDICATION) {
if ((*data[0]) == 7)
@@ -314,12 +318,14 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
uplink = 0;
/* fall through */
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
+ fn = l1sap->u.data.fn;
+ tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
if (ts_is_pdch(&trx->ts[tn]))
- rc = gsmtap_pdch(l1sap, &chan_type, &tn, &ss, &fn, &data,
- &len);
+ rc = gsmtap_pdch(l1sap, &chan_type, &ss, fn, &data,
+ &len);
else
- rc = gsmtap_ph_data(l1sap, &chan_type, &tn, &ss, &fn,
- &data, &len);
+ rc = gsmtap_ph_data(l1sap, &chan_type, &ss, fn, &data,
+ &len, num_agch(trx, "GSMTAP"));
break;
case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
rc = gsmtap_ph_rach(l1sap, &chan_type, &tn, &ss, &fn, &data,
@@ -530,6 +536,7 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
uint8_t *p, *si;
struct lapdm_entity *le;
struct osmo_phsap_prim pp;
+ bool dtxd_facch = false;
int rc;
chan_nr = rts_ind->chan_nr;
@@ -584,9 +591,11 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
p[0] = lchan->ms_power_ctrl.current;
p[1] = lchan->rqd_ta;
le = &lchan->lapdm_ch.lapdm_acch;
- } else
+ } else {
+ if (lchan->ts->trx->bts->dtxd)
+ dtxd_facch = true;
le = &lchan->lapdm_ch.lapdm_dcch;
-
+ }
rc = lapdm_phsap_dequeue_prim(le, &pp);
if (rc < 0) {
if (L1SAP_IS_LINK_SACCH(link_id)) {
@@ -612,15 +621,16 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
memcpy(p, pp.oph.msg->data, GSM_MACBLOCK_LEN);
/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
check_for_ciph_cmd(pp.oph.msg, lchan, chan_nr);
+ if (dtxd_facch)
+ dtx_dispatch(lchan, E_FACCH);
}
msgb_free(pp.oph.msg);
}
} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
-#warning "TODO: Yet another assumption that BS_AG_BLKS_RES=1"
- /* if CCCH block is 0, it is AGCH */
rc = bts_ccch_copy_msg(trx->bts, p, &g_time,
- (L1SAP_FN2CCCHBLOCK(fn) < 1));
+ (L1SAP_FN2CCCHBLOCK(fn) <
+ num_agch(trx, "PH-RTS-IND")));
if (rc <= 0)
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
}
@@ -664,7 +674,8 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
struct gsm_time g_time;
struct gsm_lchan *lchan;
uint8_t chan_nr, marker = 0;
- uint32_t fn;
+ uint16_t seq;
+ uint32_t fn, timestamp;
int rc;
chan_nr = rts_ind->chan_nr;
@@ -698,6 +709,10 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
} else {
/* Obtain RTP header Marker bit from control buffer */
marker = rtpmsg_marker_bit(resp_msg);
+ /* Obtain RTP header Sequence Number from control buffer */
+ seq = rtpmsg_seq(resp_msg);
+ /* Obtain RTP header Timestamp from control buffer */
+ timestamp = rtpmsg_ts(resp_msg);
resp_msg->l2h = resp_msg->data;
msgb_push(resp_msg, sizeof(*resp_l1sap));
@@ -921,7 +936,7 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
/* hand msg to RTP code for transmission */
if (lchan->abis_ip.rtp_socket)
osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
- msg->data, msg->len, fn_ms_adj(fn, lchan->tch.last_fn), lchan->rtp_tx_marker);
+ msg->data, msg->len, fn_ms_adj(fn, lchan), lchan->rtp_tx_marker);
/* if loopback is enabled, also queue received RTP data */
if (lchan->loopback) {
@@ -938,6 +953,9 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
}
msgb_enqueue(&lchan->dl_tch_queue, msg);
+
+ /* Return 1 to signal that we're still using msg and it should not be freed */
+ return 1;
}
lchan->rtp_tx_marker = false;
@@ -956,7 +974,7 @@ static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx,
DEBUGP(DL1P, "Rx PH-RA.ind");
- lc = &trx->ts[0].lchan[4].lapdm_ch;
+ lc = &trx->ts[0].lchan[CCCH_LCHAN].lapdm_ch;
/* check for under/overflow / sign */
if (!check_acc_delay(rach_ind, btsb, &acc_delay)) {
@@ -1073,7 +1091,8 @@ int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
/*! \brief call-back function for incoming RTP */
void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
- unsigned int rtp_pl_len, bool marker)
+ unsigned int rtp_pl_len, uint16_t seq_number,
+ uint32_t timestamp, bool marker)
{
struct gsm_lchan *lchan = rs->priv;
struct msgb *msg, *tmp;
@@ -1088,8 +1107,12 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
/* Store RTP header Marker bit in control buffer */
rtpmsg_marker_bit(msg) = marker;
+ /* Store RTP header Sequence Number in control buffer */
+ rtpmsg_seq(msg) = seq_number;
+ /* Store RTP header Timestamp in control buffer */
+ rtpmsg_ts(msg) = timestamp;
- /* make sure the queue doesn't get too long */
+ /* make sure the queue doesn't get too long */
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
count++;
while (count >= 2) {
@@ -1154,14 +1177,29 @@ int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *
rc = l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_ACTIVATE, 0);
if (rc)
return -RSL_ERR_EQUIPMENT_FAIL;
+
+ /* Init DTX DL FSM if necessary */
+ //FIXME: only do it for AMR TCH/*
+ if (trx->bts->dtxd)
+ lchan->tch.dtx.dl_amr_fsm = osmo_fsm_inst_alloc(&dtx_dl_amr_fsm,
+ tall_bts_ctx,
+ lchan,
+ LOGL_DEBUG,
+ lchan->name);
return 0;
}
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
+ struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
LOGP(DL1P, LOGL_INFO, "deactivating channel chan_nr=0x%02x trx=%d\n",
chan_nr, trx->nr);
+ if (lchan->tch.dtx.dl_amr_fsm) {
+ osmo_fsm_inst_free(lchan->tch.dtx.dl_amr_fsm);
+ lchan->tch.dtx.dl_amr_fsm = NULL;
+ }
+
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
0);
}
diff --git a/src/common/main.c b/src/common/main.c
index bdd902b8..780556ab 100644
--- a/src/common/main.c
+++ b/src/common/main.c
@@ -228,16 +228,15 @@ int bts_main(int argc, char **argv)
struct gsm_bts_role_bts *btsb;
struct gsm_bts_trx *trx;
struct e1inp_line *line;
- void *tall_msgb_ctx;
int rc, i;
printf("((*))\n |\n / \\ OsmoBTS\n");
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
- tall_msgb_ctx = talloc_pool(tall_bts_ctx, 100*1024);
- msgb_set_talloc_ctx(tall_msgb_ctx);
+ msgb_talloc_ctx_init(tall_bts_ctx, 100*1024);
bts_log_init(NULL);
+ vty_init(&bts_vty_info);
handle_options(argc, argv);
@@ -253,7 +252,6 @@ int bts_main(int argc, char **argv)
exit(1);
}
}
- vty_init(&bts_vty_info);
e1inp_vty_init();
bts_vty_init(bts, &bts_log_info);
diff --git a/src/common/msg_utils.c b/src/common/msg_utils.c
index 967b10d9..9de9b6d6 100644
--- a/src/common/msg_utils.c
+++ b/src/common/msg_utils.c
@@ -17,18 +17,24 @@
*
*/
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <osmo-bts/msg_utils.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/oml.h>
+#include <osmo-bts/amr.h>
+#include <osmo-bts/rsl.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmocom/gsm/abis_nm.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/fsm.h>
#include <osmocom/trau/osmo_ortp.h>
#include <arpa/inet.h>
+#include <errno.h>
+#define STI_BIT_MASK 16
static int check_fom(struct abis_om_hdr *omh, size_t len)
{
@@ -91,74 +97,214 @@ static int check_manuf(struct msgb *msg, struct abis_om_hdr *omh, size_t msg_siz
void lchan_set_marker(bool t, struct gsm_lchan *lchan)
{
if (t)
- lchan->tch.ul_sid = true;
- else if (lchan->tch.ul_sid) {
- lchan->tch.ul_sid = false;
+ lchan->tch.dtx.ul_sid = true;
+ else if (lchan->tch.dtx.ul_sid) {
+ lchan->tch.dtx.ul_sid = false;
lchan->rtp_tx_marker = true;
}
}
-/* store the last SID frame in lchan context */
-void save_last_sid(struct gsm_lchan *lchan, uint8_t *l1_payload, size_t length,
- uint32_t fn, bool update)
+/*! \brief Store the last SID frame in lchan context
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \param[in] l1_payload buffer with SID data
+ * \param[in] length length of l1_payload
+ * \param[in] fn Frame Number for which we check scheduling
+ * \param[in] update 0 if SID_FIRST, 1 if SID_UPDATE, -1 if not AMR SID
+ */
+void dtx_cache_payload(struct gsm_lchan *lchan, const uint8_t *l1_payload,
+ size_t length, uint32_t fn, int update)
{
- size_t copy_len = OSMO_MIN(length + 1,
- ARRAY_SIZE(lchan->tch.last_sid.buf));
-
- lchan->tch.last_sid.len = copy_len;
- lchan->tch.last_sid.fn = fn;
- lchan->tch.last_sid.is_update = update;
+ size_t amr = (update < 0) ? 0 : 2,
+ copy_len = OSMO_MIN(length + 1,
+ ARRAY_SIZE(lchan->tch.dtx.cache) - amr);
+
+ lchan->tch.dtx.len = copy_len + amr;
+ /* SID FIRST is special because it's both sent and cached: */
+ if (update == 0) {
+ lchan->tch.dtx.is_update = false; /* Mark SID FIRST explicitly */
+ /* for non-AMR case - always update FN for incoming SID FIRST */
+ if (!amr ||
+ (dtx_dl_amr_enabled(lchan) &&
+ lchan->tch.dtx.dl_amr_fsm->state != ST_SID_U))
+ lchan->tch.dtx.fn = fn;
+ /* for AMR case - do not update FN if SID FIRST arrives in a
+ middle of silence: this should not be happening according to
+ the spec */
+ }
- memcpy(lchan->tch.last_sid.buf, l1_payload, copy_len);
+ memcpy(lchan->tch.dtx.cache + amr, l1_payload, copy_len);
}
-/* repeat last SID if possible, returns SID length + 1 or 0 */
-/*! \brief Repeat last SID if possible in case of DTX
+/*! \brief Check current state of DTX DL AMR FSM and dispatch necessary events
* \param[in] lchan Logical channel on which we check scheduling
- * \param[in] dst Buffer to copy last SID into
- * \returns Number of bytes copied + 1 (to accommodate for extra byte with
- * payload type) or 0 if there's nothing to copy
+ * \param[in] rtp_pl buffer with RTP data
+ * \param[in] rtp_pl_len length of rtp_pl
+ * \param[in] fn Frame Number for which we check scheduling
+ * \param[in] l1_payload buffer where CMR and CMI prefix should be added
+ * \param[in] marker RTP Marker bit
+ * \param[out] len Length of expected L1 payload
+ * \param[out] ft_out Frame Type to be populated after decoding
+ * \returns 0 in case of success; negative on error
*/
-uint8_t repeat_last_sid(struct gsm_lchan *lchan, uint8_t *dst, uint32_t fn)
+int dtx_dl_amr_fsm_step(struct gsm_lchan *lchan, const uint8_t *rtp_pl,
+ size_t rtp_pl_len, uint32_t fn, uint8_t *l1_payload,
+ bool marker, uint8_t *len, uint8_t *ft_out)
{
- if (lchan->tch.last_sid.len) {
- memcpy(dst, lchan->tch.last_sid.buf, lchan->tch.last_sid.len);
- lchan->tch.last_sid.fn = fn;
- return lchan->tch.last_sid.len + 1;
+ uint8_t cmr;
+ enum osmo_amr_type ft;
+ enum osmo_amr_quality bfi;
+ int8_t sti, cmi;
+ int rc;
+
+ if (dtx_dl_amr_enabled(lchan)) {
+ if (lchan->type == GSM_LCHAN_TCH_H &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2 && !rtp_pl) {
+ *len = 3; /* SID-FIRST P1 -> P2 completion */
+ memcpy(l1_payload, lchan->tch.dtx.cache, 2);
+ dtx_dispatch(lchan, E_SID_U);
+ return 0;
+ }
}
- LOGP(DL1C, LOGL_NOTICE, "Have to send %s frame on TCH but SID buffer "
- "is empty - sent nothing\n",
- get_value_string(gsm48_chan_mode_names, lchan->tch_mode));
+
+ if (!rtp_pl_len)
+ return -EBADMSG;
+
+ rc = osmo_amr_rtp_dec(rtp_pl, rtp_pl_len, &cmr, &cmi, &ft, &bfi, &sti);
+ if (rc < 0) {
+ LOGP(DRTP, LOGL_ERROR, "failed to decode AMR RTP (length %zu)\n",
+ rtp_pl_len);
+ return rc;
+ }
+
+ /* only needed for old sysmo firmware: */
+ *ft_out = ft;
+
+ /* CMI in downlink tells the L1 encoder which encoding function
+ * it will use, so we have to use the frame type */
+ if (osmo_amr_is_speech(ft))
+ cmi = ft;
+
+ /* populate L1 payload with CMR/CMI - might be ignored by caller: */
+ amr_set_mode_pref(l1_payload, &lchan->tch.amr_mr, cmi, cmr);
+
+ /* populate DTX cache with CMR/CMI - overwrite cache which will be
+ either updated or invalidated by caller anyway: */
+ amr_set_mode_pref(lchan->tch.dtx.cache, &lchan->tch.amr_mr, cmi, cmr);
+ *len = 3 + rtp_pl_len;
+
+ /* DTX DL is not enabled, move along */
+ if (!lchan->ts->trx->bts->dtxd)
+ return 0;
+
+ if (osmo_amr_is_speech(ft)) {
+ /* AMR HR - Inhibition */
+ if (lchan->type == GSM_LCHAN_TCH_H && marker &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1)
+ return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ E_INHIB, (void *)lchan);
+ /* AMR FR & HR - generic */
+ if (marker && (lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1 ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2 ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_U ))
+ return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ E_ONSET, (void *)lchan);
+ return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_VOICE,
+ (void *)lchan);
+ }
+
+ if (ft == AMR_SID) {
+ if (lchan->tch.dtx.dl_amr_fsm->state == ST_VOICE) {
+ /* SID FIRST/UPDATE scheduling logic relies on SID FIRST
+ being sent first hence we have to force caching of SID
+ as FIRST regardless of actually decoded type */
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, false);
+ return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ E_SID_F, (void *)lchan);
+ } else
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, sti);
+ return osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ sti ? E_SID_U : E_SID_F,
+ (void *)lchan);
+ }
+
+ if (ft != AMR_NO_DATA) {
+ LOGP(DRTP, LOGL_ERROR, "unsupported AMR FT 0x%02x\n", ft);
+ return -ENOTSUP;
+ }
+
+ if (marker)
+ osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, E_VOICE,
+ (void *)lchan);
+ *len = 0;
return 0;
}
+/* STI is located in payload byte 6, cache contains 2 byte prefix (CMR/CMI)
+ * STI set = SID UPDATE, STI unset = SID FIRST
+ */
+static inline void dtx_sti_set(struct gsm_lchan *lchan)
+{
+ lchan->tch.dtx.cache[6 + 2] |= STI_BIT_MASK;
+}
+
+static inline void dtx_sti_unset(struct gsm_lchan *lchan)
+{
+ lchan->tch.dtx.cache[6 + 2] &= ~STI_BIT_MASK;
+}
+
/*! \brief Check if enough time has passed since last SID (if any) to repeat it
* \param[in] lchan Logical channel on which we check scheduling
* \param[in] fn Frame Number for which we check scheduling
* \returns true if transmission can be omitted, false otherwise
*/
-bool dtx_amr_sid_optional(const struct gsm_lchan *lchan, uint32_t fn)
+static inline bool dtx_amr_sid_optional(struct gsm_lchan *lchan, uint32_t fn)
{
- /* Compute approx. time delta based on Fn duration */
- uint32_t delta = GSM_FN_TO_MS(fn - lchan->tch.last_sid.fn);
+ if (!dtx_dl_amr_enabled(lchan))
+ return true;
- /* according to 3GPP TS 26.093 A.5.1.1: */
- if (lchan->tch.last_sid.is_update) {
- /* SID UPDATE should be repeated every 8th RTP frame */
- if (delta < GSM_RTP_FRAME_DURATION_MS * 8)
+ /* Compute approx. time delta x26 based on Fn duration */
+ uint32_t dx26 = 120 * (fn - lchan->tch.dtx.fn);
+
+ /* We're resuming after FACCH interruption */
+ if (lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH) {
+ /* force STI bit to 0 so cache is treated as SID FIRST */
+ dtx_sti_unset(lchan);
+ lchan->tch.dtx.is_update = false;
+ /* check that this FN has not been used for FACCH message
+ already: we rely here on the order of RTS arrival from L1 - we
+ expect that PH-DATA.req ALWAYS comes before PH-TCH.req for the
+ same FN */
+ if (lchan->tch.dtx.fn != LCHAN_FN_DUMMY) {
+ /* FACCH interruption is over */
+ dtx_dispatch(lchan, E_COMPL);
+ return false;
+ } else
+ lchan->tch.dtx.fn = fn;
+ /* this FN was already used for FACCH or ONSET message so we just
+ prepare things for next one */
+ return true;
+ }
+
+ if (lchan->tch.dtx.dl_amr_fsm->state == ST_VOICE)
+ return true;
+
+ /* according to 3GPP TS 26.093 A.5.1.1:
+ (*26) to avoid float math, add 1 FN tolerance (-120) */
+ if (lchan->tch.dtx.is_update) { /* SID UPDATE: every 8th RTP frame */
+ if (dx26 < GSM_RTP_FRAME_DURATION_MS * 8 * 26 - 120)
return true;
return false;
}
/* 3rd frame after SID FIRST should be SID UPDATE */
- if (delta < GSM_RTP_FRAME_DURATION_MS * 3)
+ if (dx26 < GSM_RTP_FRAME_DURATION_MS * 3 * 26 - 120)
return true;
return false;
}
-static inline bool fn_chk(const uint8_t *t, uint32_t fn)
+static inline bool fn_chk(const uint8_t *t, uint32_t fn, uint8_t len)
{
uint8_t i;
- for (i = 0; i < ARRAY_SIZE(t); i++)
+ for (i = 0; i < len; i++)
if (fn % 104 == t[i])
return false;
return true;
@@ -169,7 +315,7 @@ static inline bool fn_chk(const uint8_t *t, uint32_t fn)
* \param[in] fn Frame Number for which we check scheduling
* \returns true if transmission can be omitted, false otherwise
*/
-bool dtx_sched_optional(struct gsm_lchan *lchan, uint32_t fn)
+static inline bool dtx_sched_optional(struct gsm_lchan *lchan, uint32_t fn)
{
/* According to 3GPP TS 45.008 § 8.3: */
static const uint8_t f[] = { 52, 53, 54, 55, 56, 57, 58, 59 },
@@ -177,13 +323,128 @@ bool dtx_sched_optional(struct gsm_lchan *lchan, uint32_t fn)
h1[] = { 14, 16, 18, 20, 66, 68, 70, 72 };
if (lchan->tch_mode == GSM48_CMODE_SPEECH_V1) {
if (lchan->type == GSM_LCHAN_TCH_F)
- return fn_chk(f, fn);
+ return fn_chk(f, fn, ARRAY_SIZE(f));
else
- return fn_chk(lchan->nr ? h1 : h0, fn);
+ return fn_chk(lchan->nr ? h1 : h0, fn,
+ lchan->nr ? ARRAY_SIZE(h1) :
+ ARRAY_SIZE(h0));
}
return false;
}
+/*! \brief Check if DTX DL AMR is enabled for a given lchan (it have proper type,
+ * FSM is allocated etc.)
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \returns true if DTX DL AMR is enabled, false otherwise
+ */
+bool dtx_dl_amr_enabled(const struct gsm_lchan *lchan)
+{
+ if (lchan->ts->trx->bts->dtxd &&
+ lchan->tch.dtx.dl_amr_fsm &&
+ lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+ return true;
+ return false;
+}
+
+/*! \brief Check if DTX DL AMR FSM state is recursive: requires secondary
+ * response to a single RTS request from L1.
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \returns true if DTX DL AMR FSM state is recursive, false otherwise
+ */
+bool dtx_recursion(const struct gsm_lchan *lchan)
+{
+ if (!dtx_dl_amr_enabled(lchan))
+ return false;
+
+ if (lchan->tch.dtx.dl_amr_fsm->state == ST_U_INH ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_F1_INH ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_F ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_V ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_F_REC ||
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_V_REC)
+ return true;
+
+ return false;
+}
+
+/*! \brief Send signal to FSM: with proper check if DIX is enabled for this lchan
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \param[in] e DTX DL AMR FSM Event
+ */
+void dtx_dispatch(struct gsm_lchan *lchan, enum dtx_dl_amr_fsm_events e)
+{
+ if (dtx_dl_amr_enabled(lchan))
+ osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm, e,
+ (void *)lchan);
+}
+
+/*! \brief Send internal signal to FSM: check that DTX is enabled for this chan,
+ * check that current FSM and lchan states are permitting such signal.
+ * Note: this should be the only way to dispatch E_COMPL to FSM from
+ * BTS code.
+ * \param[in] lchan Logical channel on which we check scheduling
+ */
+void dtx_int_signal(struct gsm_lchan *lchan)
+{
+ if (!dtx_dl_amr_enabled(lchan))
+ return;
+
+ if ((lchan->type == GSM_LCHAN_TCH_H &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1) ||
+ dtx_recursion(lchan))
+ dtx_dispatch(lchan, E_COMPL);
+}
+
+/*! \brief Repeat last SID if possible in case of DTX
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \param[in] dst Buffer to copy last SID into
+ * \returns Number of bytes copied + 1 (to accommodate for extra byte with
+ * payload type), 0 if there's nothing to copy
+ */
+uint8_t repeat_last_sid(struct gsm_lchan *lchan, uint8_t *dst, uint32_t fn)
+{
+ /* FIXME: add EFR support */
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_EFR)
+ return 0;
+
+ if (lchan->tch_mode != GSM48_CMODE_SPEECH_AMR) {
+ if (dtx_sched_optional(lchan, fn))
+ return 0;
+ } else
+ if (dtx_amr_sid_optional(lchan, fn))
+ return 0;
+
+ if (lchan->tch.dtx.len) {
+ if (dtx_dl_amr_enabled(lchan)) {
+ if ((lchan->type == GSM_LCHAN_TCH_H &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F2) ||
+ (lchan->type == GSM_LCHAN_TCH_F &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_SID_F1)) {
+ /* advance FSM in case we've just sent SID FIRST
+ to restore silence after FACCH interruption */
+ osmo_fsm_inst_dispatch(lchan->tch.dtx.dl_amr_fsm,
+ E_SID_U, (void *)lchan);
+ dtx_sti_unset(lchan);
+ } else if (lchan->tch.dtx.dl_amr_fsm->state ==
+ ST_SID_U) {
+ /* enforce SID UPDATE for next repetition: it
+ might have been altered by FACCH handling */
+ dtx_sti_set(lchan);
+ lchan->tch.dtx.is_update = true;
+ }
+ }
+ memcpy(dst, lchan->tch.dtx.cache, lchan->tch.dtx.len);
+ lchan->tch.dtx.fn = fn;
+ return lchan->tch.dtx.len + 1;
+ }
+
+ LOGP(DL1C, LOGL_DEBUG, "Have to send %s frame on TCH but SID buffer "
+ "is empty - sent nothing\n",
+ get_value_string(gsm48_chan_mode_names, lchan->tch_mode));
+
+ return 0;
+}
+
/**
* Return 0 in case the IPA structure is okay and in this
* case the l2h will be set to the beginning of the data.
diff --git a/src/common/oml.c b/src/common/oml.c
index d6468d81..43017fb3 100644
--- a/src/common/oml.c
+++ b/src/common/oml.c
@@ -843,7 +843,7 @@ int conf_lchans_as_pchan(struct gsm_bts_trx_ts *ts,
}
/* fallthrough */
case GSM_PCHAN_CCCH:
- lchan = &ts->lchan[4];
+ lchan = &ts->lchan[CCCH_LCHAN];
lchan->type = GSM_LCHAN_CCCH;
break;
case GSM_PCHAN_TCH_F:
diff --git a/src/common/paging.c b/src/common/paging.c
index f75f12dc..957d609b 100644
--- a/src/common/paging.c
+++ b/src/common/paging.c
@@ -538,12 +538,6 @@ static int paging_signal_cbfn(unsigned int subsys, unsigned int signal, void *hd
struct paging_state *ps = btsb->paging_state;
struct gsm48_system_information_type_3 *si3 = (void *) bts->si_buf[SYSINFO_TYPE_3];
-#warning "TODO: Remove this when setting u8NbrOfAgch is implemented properly"
- if (si3->control_channel_desc.bs_ag_blks_res != 1)
- LOGP(DPAG, LOGL_ERROR,
- "Paging: BS_AG_BLKS_RES = %d != 1 not fully supported\n",
- si3->control_channel_desc.bs_ag_blks_res);
-
paging_si_update(ps, &si3->control_channel_desc);
}
return 0;
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 49b01586..ea6b482a 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -296,6 +296,14 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg)
LOGP(DRSL, LOGL_INFO, " Rx RSL BCCH INFO (SI%s)\n",
get_value_string(osmo_sitype_strs, osmo_si));
+ if (SYSINFO_TYPE_3 == osmo_si && trx->nr == 0 &&
+ num_agch(trx, "RSL") != 1) {
+ lchan_deactivate(&trx->bts->c0->ts[0].lchan[CCCH_LCHAN]);
+ /* will be reactivated by sapi_deactivate_cb() */
+ trx->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
+ LCHAN_REL_ACT_REACT;
+ }
+
if (SYSINFO_TYPE_2quater == osmo_si) {
si2q = (struct gsm48_system_information_type_2quater *)
bts->si_buf[SYSINFO_TYPE_2quater];
@@ -1607,21 +1615,34 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
return tx_ipac_XXcx_nack(lchan, RSL_ERR_MAND_IE_ERROR,
0, dch->c.msg_type);
- /* any of these can be NULL!! */
- speech_mode = TLVP_VAL(&tp, RSL_IE_IPAC_SPEECH_MODE);
- payload_type = TLVP_VAL(&tp, RSL_IE_IPAC_RTP_PAYLOAD);
- payload_type2 = TLVP_VAL(&tp, RSL_IE_IPAC_RTP_PAYLOAD2);
-
- if (TLVP_PRESENT(&tp, RSL_IE_IPAC_REMOTE_IP))
+ if (TLVP_PRESENT(&tp, RSL_IE_IPAC_REMOTE_IP)) {
connect_ip = tlvp_val32_unal(&tp, RSL_IE_IPAC_REMOTE_IP);
+ LOGP(DRSL, LOGL_NOTICE, "connect_ip %d \n", connect_ip );
+ }
else
LOGP(DRSL, LOGL_NOTICE, "CRCX does not specify a remote IP\n");
- if (TLVP_PRESENT(&tp, RSL_IE_IPAC_REMOTE_PORT))
+ if (TLVP_PRESENT(&tp, RSL_IE_IPAC_REMOTE_PORT)) {
connect_port = tlvp_val16_unal(&tp, RSL_IE_IPAC_REMOTE_PORT);
+ LOGP(DRSL, LOGL_NOTICE, "connect_port %d \n", connect_port );
+ }
else
LOGP(DRSL, LOGL_NOTICE, "CRCX does not specify a remote port\n");
+ speech_mode = TLVP_VAL(&tp, RSL_IE_IPAC_SPEECH_MODE);
+ if (speech_mode)
+ LOGP(DRSL, LOGL_NOTICE, "speech mode: %d\n", *speech_mode);
+ else
+ LOGP(DRSL, LOGL_NOTICE, "speech mode: none\n");
+
+ payload_type = TLVP_VAL(&tp, RSL_IE_IPAC_RTP_PAYLOAD);
+ if (payload_type)
+ LOGP(DRSL, LOGL_NOTICE, "payload type: %d\n",*payload_type);
+ else
+ LOGP(DRSL, LOGL_NOTICE, "payload type: none\n");
+
+ payload_type2 = TLVP_VAL(&tp, RSL_IE_IPAC_RTP_PAYLOAD2);
+
if (dch->c.msg_type == RSL_MT_IPAC_CRCX && connect_ip && connect_port)
inc_ip_port = 1;
@@ -1648,6 +1669,7 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
/* FIXME: select default value depending on speech_mode */
//if (!payload_type)
+ lchan->tch.last_fn = LCHAN_FN_DUMMY;
lchan->abis_ip.rtp_socket = osmo_rtp_socket_create(lchan->ts->trx,
OSMO_RTP_F_POLL);
if (!lchan->abis_ip.rtp_socket) {
@@ -1662,9 +1684,19 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL,
inc_ip_port, dch->c.msg_type);
}
- osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
- OSMO_RTP_P_JITBUF,
- btsb->rtp_jitter_buf_ms);
+ rc = osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
+ btsb->rtp_jitter_adaptive ?
+ OSMO_RTP_P_JIT_ADAP :
+ OSMO_RTP_P_JITBUF,
+ btsb->rtp_jitter_buf_ms);
+ if (rc < 0)
+ LOGP(DRSL, LOGL_ERROR,
+ "%s IPAC Failed to set RTP socket parameters: %s\n",
+ gsm_lchan_name(lchan), strerror(-rc));
+ else
+ LOGP(DRSL, LOGL_INFO,
+ "%s IPAC set RTP socket parameters: %d\n",
+ gsm_lchan_name(lchan), rc);
lchan->abis_ip.rtp_socket->priv = lchan;
lchan->abis_ip.rtp_socket->rx_cb = &l1sap_rtp_rx_cb;
@@ -2503,9 +2535,9 @@ static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len)
msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
size_t ie_len = gsm0858_rsl_ul_meas_enc(&lchan->meas.ul_res,
- lchan->tch.dtxd_active,
+ lchan->tch.dtx.dl_active,
meas_res);
- lchan->tch.dtxd_active = false;
+ lchan->tch.dtx.dl_active = false;
if (ie_len >= 3) {
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;
@@ -2945,6 +2977,12 @@ static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg)
return ret;
}
+int lchan_deactivate(struct gsm_lchan *lchan)
+{
+ lchan->ciph_state = 0;
+ return bts_model_lchan_deactivate(lchan);
+}
+
int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg)
{
struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
diff --git a/src/common/scheduler.c b/src/common/scheduler.c
index ec66cfc4..fd5c5840 100644
--- a/src/common/scheduler.c
+++ b/src/common/scheduler.c
@@ -116,44 +116,44 @@ const ubit_t _sched_sch_train[64] = {
*/
const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
- { 0, TRXC_IDLE, 0, 0, "IDLE", NULL, tx_idle_fn, NULL, 1 },
- { 0, TRXC_FCCH, 0, 0, "FCCH", NULL, tx_fcch_fn, NULL, 1 },
- { 0, TRXC_SCH, 0, 0, "SCH", NULL, tx_sch_fn, NULL, 1 },
- { 0, TRXC_BCCH, 0x80, 0x00, "BCCH", rts_data_fn, tx_data_fn, NULL, 1 },
- { 0, TRXC_RACH, 0x88, 0x00, "RACH", NULL, NULL, rx_rach_fn, 1 },
- { 0, TRXC_CCCH, 0x90, 0x00, "CCCH", rts_data_fn, tx_data_fn, NULL, 1 },
- { 0, TRXC_TCHF, 0x08, 0x00, "TCH/F", rts_tchf_fn, tx_tchf_fn, rx_tchf_fn, 0 },
- { 0, TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tchh_fn, tx_tchh_fn, rx_tchh_fn, 0 },
- { 0, TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tchh_fn, tx_tchh_fn, rx_tchh_fn, 0 },
- { 0, TRXC_SDCCH4_0, 0x20, 0x00, "SDCCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH4_1, 0x28, 0x00, "SDCCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH4_2, 0x30, 0x00, "SDCCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH4_3, 0x38, 0x00, "SDCCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH8_0, 0x40, 0x00, "SDCCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH8_1, 0x48, 0x00, "SDCCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH8_2, 0x50, 0x00, "SDCCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH8_3, 0x58, 0x00, "SDCCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH8_4, 0x60, 0x00, "SDCCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH8_5, 0x68, 0x00, "SDCCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH8_6, 0x70, 0x00, "SDCCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SDCCH8_7, 0x78, 0x00, "SDCCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCHTF, 0x08, 0x40, "SACCH/TF", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCHTH_0, 0x10, 0x40, "SACCH/TH(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCHTH_1, 0x18, 0x40, "SACCH/TH(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH4_0, 0x20, 0x40, "SACCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH4_1, 0x28, 0x40, "SACCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH4_2, 0x30, 0x40, "SACCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH4_3, 0x38, 0x40, "SACCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH8_0, 0x40, 0x40, "SACCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH8_1, 0x48, 0x40, "SACCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH8_2, 0x50, 0x40, "SACCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH8_3, 0x58, 0x40, "SACCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH8_4, 0x60, 0x40, "SACCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH8_5, 0x68, 0x40, "SACCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH8_6, 0x70, 0x40, "SACCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 0, TRXC_SACCH8_7, 0x78, 0x40, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
- { 1, TRXC_PDTCH, 0x08, 0x00, "PDTCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 },
- { 1, TRXC_PTCCH, 0x08, 0x00, "PTCCH", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_IDLE, 0, LID_DEDIC, "IDLE", NULL, tx_idle_fn, NULL, 1 },
+ { 0, TRXC_FCCH, 0, LID_DEDIC, "FCCH", NULL, tx_fcch_fn, NULL, 1 },
+ { 0, TRXC_SCH, 0, LID_DEDIC, "SCH", NULL, tx_sch_fn, NULL, 1 },
+ { 0, TRXC_BCCH, 0x80, LID_DEDIC, "BCCH", rts_data_fn, tx_data_fn, NULL, 1 },
+ { 0, TRXC_RACH, 0x88, LID_DEDIC, "RACH", NULL, NULL, rx_rach_fn, 1 },
+ { 0, TRXC_CCCH, 0x90, LID_DEDIC, "CCCH", rts_data_fn, tx_data_fn, NULL, 1 },
+ { 0, TRXC_TCHF, 0x08, LID_DEDIC, "TCH/F", rts_tchf_fn, tx_tchf_fn, rx_tchf_fn, 0 },
+ { 0, TRXC_TCHH_0, 0x10, LID_DEDIC, "TCH/H(0)", rts_tchh_fn, tx_tchh_fn, rx_tchh_fn, 0 },
+ { 0, TRXC_TCHH_1, 0x18, LID_DEDIC, "TCH/H(1)", rts_tchh_fn, tx_tchh_fn, rx_tchh_fn, 0 },
+ { 0, TRXC_SDCCH4_0, 0x20, LID_DEDIC, "SDCCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH4_1, 0x28, LID_DEDIC, "SDCCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH4_2, 0x30, LID_DEDIC, "SDCCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH4_3, 0x38, LID_DEDIC, "SDCCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_0, 0x40, LID_DEDIC, "SDCCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_1, 0x48, LID_DEDIC, "SDCCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_2, 0x50, LID_DEDIC, "SDCCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_3, 0x58, LID_DEDIC, "SDCCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_4, 0x60, LID_DEDIC, "SDCCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_5, 0x68, LID_DEDIC, "SDCCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_6, 0x70, LID_DEDIC, "SDCCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_7, 0x78, LID_DEDIC, "SDCCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCHTF, 0x08, LID_SACCH, "SACCH/TF", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCHTH_0, 0x10, LID_SACCH, "SACCH/TH(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCHTH_1, 0x18, LID_SACCH, "SACCH/TH(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH4_0, 0x20, LID_SACCH, "SACCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH4_1, 0x28, LID_SACCH, "SACCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH4_2, 0x30, LID_SACCH, "SACCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH4_3, 0x38, LID_SACCH, "SACCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_0, 0x40, LID_SACCH, "SACCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_1, 0x48, LID_SACCH, "SACCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_2, 0x50, LID_SACCH, "SACCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_3, 0x58, LID_SACCH, "SACCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_4, 0x60, LID_SACCH, "SACCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_5, 0x68, LID_SACCH, "SACCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_6, 0x70, LID_SACCH, "SACCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_7, 0x78, LID_SACCH, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 1, TRXC_PDTCH, 0x08, LID_DEDIC, "PDTCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 },
+ { 1, TRXC_PTCCH, 0x08, LID_DEDIC, "PTCCH", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
};
@@ -166,6 +166,9 @@ int trx_sched_init(struct l1sched_trx *l1t, struct gsm_bts_trx *trx)
uint8_t tn;
unsigned int i;
+ if (!trx)
+ return -EINVAL;
+
l1t->trx = trx;
LOGP(DL1C, LOGL_NOTICE, "Init scheduler for trx=%u\n", l1t->trx->nr);
diff --git a/src/common/sysinfo.c b/src/common/sysinfo.c
index ee42da25..d0a476d3 100644
--- a/src/common/sysinfo.c
+++ b/src/common/sysinfo.c
@@ -22,6 +22,7 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/sysinfo.h>
+#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#define BTS_HAS_SI(bts, sinum) ((bts)->si_valid & (1 << sinum))
@@ -132,6 +133,19 @@ uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time)
return NULL;
}
+uint8_t num_agch(struct gsm_bts_trx *trx, const char * arg)
+{
+ struct gsm_bts *b = trx->bts;
+ struct gsm48_system_information_type_3 *si3;
+ if (BTS_HAS_SI(b, SYSINFO_TYPE_3)) {
+ si3 = GSM_BTS_SI(b, SYSINFO_TYPE_3);
+ return si3->control_channel_desc.bs_ag_blks_res;
+ }
+ LOGP(DL1P, LOGL_ERROR, "%s: Unable to determine actual BS_AG_BLKS_RES "
+ "value as SI3 is not available yet, fallback to 1\n", arg);
+ return 1;
+}
+
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan)
{
uint32_t tmp;
diff --git a/src/common/vty.c b/src/common/vty.c
index b4aa6166..b48afa43 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -179,13 +179,13 @@ struct gsm_network *gsmnet_from_vty(struct vty *v)
static struct cmd_node bts_node = {
BTS_NODE,
- "%s(bts)#",
+ "%s(bts)# ",
1,
};
static struct cmd_node trx_node = {
TRX_NODE,
- "%s(trx)#",
+ "%s(trx)# ",
1,
};
@@ -276,8 +276,10 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " ipa unit-id %u %u%s",
bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
vty_out(vty, " oml remote-ip %s%s", btsb->bsc_oml_host, VTY_NEWLINE);
- vty_out(vty, " rtp jitter-buffer %u%s", btsb->rtp_jitter_buf_ms,
- VTY_NEWLINE);
+ vty_out(vty, " rtp jitter-buffer %u", btsb->rtp_jitter_buf_ms);
+ if (btsb->rtp_jitter_adaptive)
+ vty_out(vty, " adaptive");
+ vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " paging queue-size %u%s", paging_get_queue_max(btsb->paging_state),
VTY_NEWLINE);
vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(btsb->paging_state),
@@ -487,13 +489,15 @@ DEFUN_HIDDEN(cfg_bts_rtp_bind_ip,
DEFUN(cfg_bts_rtp_jitbuf,
cfg_bts_rtp_jitbuf_cmd,
- "rtp jitter-buffer <0-10000>",
+ "rtp jitter-buffer <0-10000> [adaptive]",
RTP_STR "RTP jitter buffer\n" "jitter buffer in ms\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
btsb->rtp_jitter_buf_ms = atoi(argv[0]);
+ if (argc > 1)
+ btsb->rtp_jitter_adaptive = true;
return CMD_SUCCESS;
}
@@ -869,6 +873,7 @@ DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
int sapi;
sapi = get_string_value(gsmtap_sapi_names, argv[0]);
+ OSMO_ASSERT(sapi >= 0);
if (sapi == GSMTAP_CHANNEL_ACCH)
gsmtap_sapi_acch = 1;
@@ -884,6 +889,7 @@ DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
int sapi;
sapi = get_string_value(gsmtap_sapi_names, argv[0]);
+ OSMO_ASSERT(sapi >= 0);
if (sapi == GSMTAP_CHANNEL_ACCH)
gsmtap_sapi_acch = 0;
@@ -895,13 +901,13 @@ DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
static struct cmd_node phy_node = {
PHY_NODE,
- "%s(phy)#",
+ "%s(phy)# ",
1,
};
static struct cmd_node phy_inst_node = {
PHY_INST_NODE,
- "%s(phy-inst)#",
+ "%s(phy-inst)# ",
1,
};
@@ -1001,7 +1007,8 @@ DEFUN(bts_t_t_l_jitter_buf,
{
struct gsm_network *net = gsmnet_from_vty(vty);
struct gsm_lchan *lchan;
- int jitbuf_ms = atoi(argv[4]);
+ struct gsm_bts_role_bts *btsb;
+ int jitbuf_ms = atoi(argv[4]), rc;
lchan = resolve_lchan(net, argv, 0);
if (!lchan) {
@@ -1013,8 +1020,16 @@ DEFUN(bts_t_t_l_jitter_buf,
VTY_NEWLINE);
return CMD_WARNING;
}
- osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
- OSMO_RTP_P_JITBUF, jitbuf_ms);
+ btsb = bts_role_bts(lchan->ts->trx->bts);
+ rc = osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
+ btsb->rtp_jitter_adaptive ?
+ OSMO_RTP_P_JIT_ADAP : OSMO_RTP_P_JITBUF,
+ jitbuf_ms);
+ if (rc < 0)
+ vty_out(vty, "%% error setting jitter parameters: %s%s",
+ strerror(-rc), VTY_NEWLINE);
+ else
+ vty_out(vty, "%% jitter parameters set: %d%s", rc, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1055,6 +1070,64 @@ DEFUN(no_bts_t_t_l_loopback,
return CMD_SUCCESS;
}
+DEFUN(bts_t_t_l_activate,
+ bts_t_t_l_activate_cmd,
+ "bts <0-0> trx <0-0> ts <0-7> lchan <0-1> activate",
+ BTS_T_T_L_STR "Manually activate a logical channel (FOR TEST USE ONLY! Will disrupt normal operation of the channel)\n")
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ struct gsm_lchan *lchan;
+ int rc;
+
+ lchan = resolve_lchan(net, argv, 0);
+ if (!lchan) {
+ vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* set channel configuration */
+ /* TODO: let user choose speech mode */
+ lchan->tch_mode = GSM48_CMODE_SPEECH_V1;
+ lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
+ /* no encryption */
+ memset(&lchan->encr, 0, sizeof(lchan->encr));
+
+ /* activate the channel */
+ lchan->rel_act_kind = LCHAN_REL_ACT_OML;
+ rc = l1sap_chan_act(lchan->ts->trx, gsm_lchan2chan_nr(lchan), NULL);
+ if (rc < 0) {
+ vty_out(vty, "%% can't activate channel%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(bts_t_t_l_deactivate,
+ bts_t_t_l_deactivate_cmd,
+ "bts <0-0> trx <0-0> ts <0-7> lchan <0-1> deactivate",
+ BTS_T_T_L_STR "Deactivate a manually activated channel (DO NOT apply to channels activated by BSC or NITB)\n")
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ struct gsm_lchan *lchan;
+ int rc;
+
+ lchan = resolve_lchan(net, argv, 0);
+ if (!lchan) {
+ vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* deactivate the channel */
+ rc = l1sap_chan_rel(lchan->ts->trx, gsm_lchan2chan_nr(lchan));
+ if (rc < 0) {
+ vty_out(vty, "%% can't deactivate channel%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat)
{
cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
@@ -1113,6 +1186,8 @@ int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat)
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
install_element(ENABLE_NODE, &bts_t_t_l_loopback_cmd);
install_element(ENABLE_NODE, &no_bts_t_t_l_loopback_cmd);
+ install_element(ENABLE_NODE, &bts_t_t_l_activate_cmd);
+ install_element(ENABLE_NODE, &bts_t_t_l_deactivate_cmd);
install_element(CONFIG_NODE, &cfg_phy_cmd);
install_node(&phy_node, config_write_phy);
diff --git a/src/osmo-bts-litecell15/l1_if.c b/src/osmo-bts-litecell15/l1_if.c
index 8cb16e18..1f2d7f22 100644
--- a/src/osmo-bts-litecell15/l1_if.c
+++ b/src/osmo-bts-litecell15/l1_if.c
@@ -53,6 +53,8 @@
#include <osmo-bts/cbch.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/l1sap.h>
+#include <osmo-bts/msg_utils.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <nrw/litecell15/litecell15.h>
#include <nrw/litecell15/gsml1prim.h>
@@ -287,12 +289,6 @@ empty_req_from_rts_ind(GsmL1_Prim_t *l1p,
return empty_req;
}
-static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
- 0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
- 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
- 0x2B, 0x2B, 0x2B
-};
-
/* fill PH-DATA.req from l1sap primitive */
static GsmL1_PhDataReq_t *
data_req_from_l1sap(GsmL1_Prim_t *l1p, struct lc15l1_hdl *fl1,
@@ -337,10 +333,11 @@ empty_req_from_l1sap(GsmL1_Prim_t *l1p, struct lc15l1_hdl *fl1,
}
static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
- struct osmo_phsap_prim *l1sap)
+ struct osmo_phsap_prim *l1sap, bool use_cache)
{
struct lc15l1_hdl *fl1 = trx_lc15l1_hdl(trx);
struct msgb *l1msg = l1p_msgb_alloc();
+ struct gsm_lchan *lchan;
uint32_t u32Fn;
uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi = 0;
uint8_t chan_nr, link_id;
@@ -359,6 +356,7 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
u32Fn = l1sap->u.data.fn;
u8Tn = L1SAP_CHAN2TS(chan_nr);
subCh = 0x1f;
+ lchan = get_lchan_by_chan_nr(trx, chan_nr);
if (L1SAP_IS_LINK_SACCH(link_id)) {
sapi = GsmL1_Sapi_Sacch;
if (!L1SAP_IS_CHAN_TCHF(chan_nr))
@@ -400,6 +398,7 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d "
"chan_nr %d link_id %d\n", l1sap->oph.primitive,
l1sap->oph.operation, chan_nr, link_id);
+ msgb_free(l1msg);
return -EINVAL;
}
@@ -407,14 +406,50 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
if (len) {
/* data request */
GsmL1_Prim_t *l1p = msgb_l1prim(l1msg);
-
data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr, len);
-
- OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer));
- memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h, msgb_l2len(msg));
+ if (use_cache)
+ memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer,
+ lchan->tch.dtx.facch, msgb_l2len(msg));
+ else if (dtx_dl_amr_enabled(lchan) &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_F) {
+ if (sapi == GsmL1_Sapi_FacchF) {
+ sapi = GsmL1_Sapi_TchF;
+ }
+ if (sapi == GsmL1_Sapi_FacchH) {
+ sapi = GsmL1_Sapi_TchH;
+ }
+ if (sapi == GsmL1_Sapi_TchH || sapi == GsmL1_Sapi_TchF) {
+ /* FACCH interruption of DTX silence */
+ /* cache FACCH data */
+ memcpy(lchan->tch.dtx.facch, msg->l2h,
+ msgb_l2len(msg));
+ /* prepare ONSET message */
+ l1p->u.phDataReq.msgUnitParam.u8Buffer[0] =
+ GsmL1_TchPlType_Amr_Onset;
+ /* ignored CMR/CMI pair */
+ l1p->u.phDataReq.msgUnitParam.u8Buffer[1] = 0;
+ l1p->u.phDataReq.msgUnitParam.u8Buffer[2] = 0;
+ /* update length */
+ data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi,
+ subCh, u8BlockNbr, 3);
+ /* update FN so it can be checked by TCH silence
+ resume handler */
+ lchan->tch.dtx.fn = LCHAN_FN_DUMMY;
+ }
+ } else if (dtx_dl_amr_enabled(lchan) &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH) {
+ /* update FN so it can be checked by TCH silence
+ resume handler */
+ lchan->tch.dtx.fn = LCHAN_FN_DUMMY;
+ }
+ else {
+ OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer));
+ memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h,
+ msgb_l2len(msg));
+ }
LOGP(DL1P, LOGL_DEBUG, "PH-DATA.req(%s)\n",
- osmo_hexdump(l1p->u.phDataReq.msgUnitParam.u8Buffer,
- l1p->u.phDataReq.msgUnitParam.u8Size));
+ osmo_hexdump(l1p->u.phDataReq.msgUnitParam.u8Buffer,
+ l1p->u.phDataReq.msgUnitParam.u8Size));
} else {
/* empty frame */
GsmL1_Prim_t *l1p = msgb_l1prim(l1msg);
@@ -422,20 +457,20 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
empty_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr);
}
- /* free the msgb holding the L1SAP primitive */
- msgb_free(msg);
-
/* send message to DSP's queue */
if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], l1msg) != 0) {
LOGP(DL1P, LOGL_ERROR, "MQ_L1_WRITE queue full. Dropping msg.\n");
- msgb_free(msg);
- }
+ msgb_free(l1msg);
+ } else
+ dtx_int_signal(lchan);
+ if (dtx_recursion(lchan))
+ ph_data_req(trx, msg, l1sap, true);
return 0;
}
static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
- struct osmo_phsap_prim *l1sap)
+ struct osmo_phsap_prim *l1sap, bool use_cache, bool marker)
{
struct lc15l1_hdl *fl1 = trx_lc15l1_hdl(trx);
struct gsm_lchan *lchan;
@@ -444,6 +479,7 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
uint8_t chan_nr;
GsmL1_Prim_t *l1p;
struct msgb *nmsg = NULL;
+ int rc = -1;
chan_nr = l1sap->u.tch.chan_nr;
u32Fn = l1sap->u.tch.fn;
@@ -467,17 +503,20 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
if (!nmsg)
return -ENOMEM;
l1p = msgb_l1prim(nmsg);
- if (!l1if_tch_encode(lchan,
+ rc = l1if_tch_encode(lchan,
l1p->u.phDataReq.msgUnitParam.u8Buffer,
&l1p->u.phDataReq.msgUnitParam.u8Size,
- msg->data, msg->len, u32Fn,
- l1sap->u.tch.marker)) {
+ msg->data, msg->len, u32Fn, use_cache,
+ l1sap->u.tch.marker);
+ if (rc < 0) {
+ /* no data encoded for L1: smth will be generated below */
msgb_free(nmsg);
nmsg = NULL;
}
}
- /* no message/data, we generate an empty traffic msg */
+ /* no message/data, we might generate an empty traffic msg or re-send
+ cached SID in case of DTX */
if (!nmsg)
nmsg = gen_empty_tch_msg(lchan, u32Fn);
@@ -499,19 +538,16 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
} else {
/* empty frame */
if (trx->bts->dtxd && trx != trx->bts->c0)
- lchan->tch.dtxd_active = true;
+ lchan->tch.dtx.dl_active = true;
empty_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr);
}
/* send message to DSP's queue */
osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
+ dtx_int_signal(lchan);
- if (l1sap->u.tch.marker) { /* Send voice after ONSET was sent */
- l1sap->u.tch.marker = 0;
- return ph_tch_req(trx, l1sap->oph.msg, l1sap);
- }
+ if (dtx_recursion(lchan)) /* DTX: send voice after ONSET was sent */
+ return ph_tch_req(trx, l1sap->oph.msg, l1sap, true, false);
- if (msg)
- msgb_free(msg);
return 0;
}
@@ -555,7 +591,6 @@ static int mph_info_req(struct gsm_bts_trx *trx, struct msgb *msg,
l1if_rsl_deact_sacch(lchan);
else
l1if_rsl_chan_rel(lchan);
- msgb_free(msg);
break;
default:
LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n",
@@ -575,12 +610,14 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
struct msgb *msg = l1sap->oph.msg;
int rc = 0;
+ /* called functions MUST NOT take ownership of msgb, as it is
+ * free()d below */
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
- rc = ph_data_req(trx, msg, l1sap);
+ rc = ph_data_req(trx, msg, l1sap, false);
break;
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
- rc = ph_tch_req(trx, msg, l1sap);
+ rc = ph_tch_req(trx, msg, l1sap, false, l1sap->u.tch.marker);
break;
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
rc = mph_info_req(trx, msg, l1sap);
@@ -595,13 +632,14 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
rc = -EINVAL;
}
- if (rc)
- msgb_free(msg);
+ msgb_free(msg);
+
return rc;
}
static int handle_mph_time_ind(struct lc15l1_hdl *fl1,
- GsmL1_MphTimeInd_t *time_ind)
+ GsmL1_MphTimeInd_t *time_ind,
+ struct msgb *msg)
{
struct gsm_bts_trx *trx = lc15l1_hdl_trx(fl1);
struct gsm_bts *bts = trx->bts;
@@ -624,6 +662,8 @@ static int handle_mph_time_ind(struct lc15l1_hdl *fl1,
l1sap.u.info.type = PRIM_INFO_TIME;
l1sap.u.info.u.time_ind.fn = fn;
+ msgb_free(msg);
+
return l1sap_up(trx, &l1sap);
}
@@ -763,9 +803,11 @@ static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1,
if (chan_nr) {
fn = rts_ind->u32Fn;
if (rts_ind->sapi == GsmL1_Sapi_Sacch)
- link_id = 0x40;
+ link_id = LID_SACCH;
else
- link_id = 0;
+ link_id = LID_DEDIC;
+ /* recycle the msgb and use it for the L1 primitive,
+ * which means that we (or our caller) must not free it */
rc = msgb_trim(l1p_msg, sizeof(*l1sap));
if (rc < 0)
MSGB_ABORT(l1p_msg, "No room for primitive\n");
@@ -832,6 +874,8 @@ tx:
msgb_free(resp_msg);
}
+ /* free the msgb, as we have not handed it to l1sap and thus
+ * need to release its memory */
msgb_free(l1p_msg);
return 0;
@@ -862,6 +906,8 @@ static int process_meas_res(struct gsm_bts_trx *trx, uint8_t chan_nr,
l1sap.u.info.u.meas_ind.ber10k = (unsigned int) (m->fBer * 100);
l1sap.u.info.u.meas_ind.inv_rssi = (uint8_t) (m->fRssi * -1);
+ /* l1sap wants to take msgb ownership. However, as there is no
+ * msg, it will msgb_free(l1sap.oph.msg == NULL) */
return l1sap_up(trx, &l1sap);
}
@@ -886,7 +932,7 @@ static int handle_ph_data_ind(struct lc15l1_hdl *fl1, GsmL1_PhDataInd_t *data_in
return ENOTSUP;
}
fn = data_ind->u32Fn;
- link_id = (data_ind->sapi == GsmL1_Sapi_Sacch) ? 0x40 : 0x00;
+ link_id = (data_ind->sapi == GsmL1_Sapi_Sacch) ? LID_SACCH : LID_DEDIC;
process_meas_res(trx, chan_nr, &data_ind->measParam);
@@ -1008,7 +1054,8 @@ static int handle_ph_ra_ind(struct lc15l1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind,
l1sap->u.rach_ind.fn = fn;
l1sap->u.rach_ind.is_11bit = is_11bit; /* no of bits in 11 bit RACH */
- /*mapping of the burst type, the values are specific to osmo-bts-litecell15*/
+ /* mapping of the burst type, the values are specific to
+ * osmo-bts-litecell15 */
switch (burst_type) {
case GsmL1_BurstType_Access_0:
l1sap->u.rach_ind.burst_type =
@@ -1043,30 +1090,29 @@ static int l1if_handle_ind(struct lc15l1_hdl *fl1, struct msgb *msg)
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
int rc = 0;
+ /* all the below called functions must take ownership of the msgb */
switch (l1p->id) {
case GsmL1_PrimId_MphTimeInd:
- rc = handle_mph_time_ind(fl1, &l1p->u.mphTimeInd);
+ rc = handle_mph_time_ind(fl1, &l1p->u.mphTimeInd, msg);
break;
case GsmL1_PrimId_MphSyncInd:
break;
case GsmL1_PrimId_PhConnectInd:
break;
case GsmL1_PrimId_PhReadyToSendInd:
- return handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd,
+ rc = handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd,
msg);
+ break;
case GsmL1_PrimId_PhDataInd:
- return handle_ph_data_ind(fl1, &l1p->u.phDataInd, msg);
+ rc = handle_ph_data_ind(fl1, &l1p->u.phDataInd, msg);
+ break;
case GsmL1_PrimId_PhRaInd:
- return handle_ph_ra_ind(fl1, &l1p->u.phRaInd, msg);
+ rc = handle_ph_ra_ind(fl1, &l1p->u.phRaInd, msg);
break;
default:
break;
}
- /* Special return value '1' means: do not free */
- if (rc != 1)
- msgb_free(msg);
-
return rc;
}
@@ -1100,10 +1146,12 @@ int l1if_handle_l1prim(int wq, struct lc15l1_hdl *fl1h, struct msgb *msg)
llist_for_each_entry(wlc, &fl1h->wlc_list, list) {
if (is_prim_compat(l1p, wlc)) {
llist_del(&wlc->list);
- if (wlc->cb)
+ if (wlc->cb) {
+ /* call-back function must take
+ * ownership of msgb */
rc = wlc->cb(lc15l1_hdl_trx(fl1h), msg,
wlc->cb_data);
- else {
+ } else {
rc = 0;
msgb_free(msg);
}
@@ -1131,10 +1179,12 @@ int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg)
* sending the same primitive */
if (wlc->is_sys_prim && sysp->id == wlc->conf_prim_id) {
llist_del(&wlc->list);
- if (wlc->cb)
+ if (wlc->cb) {
+ /* call-back function must take
+ * ownership of msgb */
rc = wlc->cb(lc15l1_hdl_trx(fl1h), msg,
wlc->cb_data);
- else {
+ } else {
rc = 0;
msgb_free(msg);
}
@@ -1146,22 +1196,6 @@ int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg)
return l1if_handle_ind(fl1h, msg);
}
-#if 0
-/* called by RSL if the BCCH SI has been modified */
-int sysinfo_has_changed(struct gsm_bts *bts, int si)
-{
- /* FIXME: Determine BS_AG_BLKS_RES and
- * * set cfgParams.u.agch.u8NbrOfAgch
- * * determine implications on paging
- */
- /* FIXME: Check for Extended BCCH presence */
- /* FIXME: Check for CCCH_CONF */
- /* FIXME: Check for BS_PA_MFRMS: update paging */
-
- return 0;
-}
-#endif
-
static int activate_rf_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp,
void *data)
{
diff --git a/src/osmo-bts-litecell15/l1_if.h b/src/osmo-bts-litecell15/l1_if.h
index c8ab872f..6c7b6eda 100644
--- a/src/osmo-bts-litecell15/l1_if.h
+++ b/src/osmo-bts-litecell15/l1_if.h
@@ -96,9 +96,9 @@ uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
/* tch.c */
-bool l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
- const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
- bool marker);
+int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
+ bool use_cache, bool marker);
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn);
diff --git a/src/osmo-bts-litecell15/lc15bts_vty.c b/src/osmo-bts-litecell15/lc15bts_vty.c
index 9a0b1217..b9076a27 100644
--- a/src/osmo-bts-litecell15/lc15bts_vty.c
+++ b/src/osmo-bts-litecell15/lc15bts_vty.c
@@ -46,15 +46,14 @@
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/phy_link.h>
#include <osmo-bts/logging.h>
+#include <osmo-bts/bts_model.h>
#include <osmo-bts/vty.h>
#include "lc15bts.h"
#include "l1_if.h"
#include "utils.h"
-
extern int lchan_activate(struct gsm_lchan *lchan);
-extern int lchan_deactivate(struct gsm_lchan *lchan);
#define TRX_STR "Transceiver related commands\n" "TRX number\n"
diff --git a/src/osmo-bts-litecell15/misc/lc15bts_mgr.c b/src/osmo-bts-litecell15/misc/lc15bts_mgr.c
index c97525c8..3a7d3a1f 100644
--- a/src/osmo-bts-litecell15/misc/lc15bts_mgr.c
+++ b/src/osmo-bts-litecell15/misc/lc15bts_mgr.c
@@ -222,13 +222,11 @@ static int mgr_log_init(void)
int main(int argc, char **argv)
{
- void *tall_msgb_ctx;
int rc;
tall_mgr_ctx = talloc_named_const(NULL, 1, "bts manager");
- tall_msgb_ctx = talloc_named_const(tall_mgr_ctx, 1, "msgb");
- msgb_set_talloc_ctx(tall_msgb_ctx);
+ msgb_talloc_ctx_init(tall_mgr_ctx, 0);
mgr_log_init();
diff --git a/src/osmo-bts-litecell15/oml.c b/src/osmo-bts-litecell15/oml.c
index 4a3cafe3..d6267b29 100644
--- a/src/osmo-bts-litecell15/oml.c
+++ b/src/osmo-bts-litecell15/oml.c
@@ -287,8 +287,9 @@ static int opstart_compl(struct gsm_abis_mo *mo, struct msgb *l1_msg)
mo->obj_inst.ts_nr == 0) {
struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
- mo->bts->c0->ts[0].lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
- lchan_activate(&mo->bts->c0->ts[0].lchan[4]);
+ mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
+ LCHAN_REL_ACT_OML;
+ lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
if (cbch) {
cbch->rel_act_kind = LCHAN_REL_ACT_OML;
lchan_activate(cbch);
@@ -1027,8 +1028,7 @@ static int mph_send_activate_req(struct gsm_lchan *lchan, struct sapi_cmd *cmd)
lch_par->rach.u8Bsic = lchan->ts->trx->bts->bsic;
break;
case GsmL1_Sapi_Agch:
-#warning Set BS_AG_BLKS_RES
- lch_par->agch.u8NbrOfAgch = 1;
+ lch_par->agch.u8NbrOfAgch = num_agch(lchan->ts->trx, lchan->name);
break;
case GsmL1_Sapi_TchH:
case GsmL1_Sapi_TchF:
@@ -1568,6 +1568,12 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
lchan_set_state(lchan, LCHAN_S_NONE);
mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
+
+ /* Reactivate CCCH due to SI3 update in RSL */
+ if (lchan->rel_act_kind == LCHAN_REL_ACT_REACT) {
+ lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
+ lchan_activate(lchan);
+ }
return 0;
}
@@ -1658,10 +1664,9 @@ static void enqueue_rel_marker(struct gsm_lchan *lchan)
queue_sapi_command(lchan, cmd);
}
-int lchan_deactivate(struct gsm_lchan *lchan)
+int bts_model_lchan_deactivate(struct gsm_lchan *lchan)
{
lchan_set_state(lchan, LCHAN_S_REL_REQ);
- lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */
enqueue_rel_marker(lchan);
return 0;
}
@@ -1676,7 +1681,7 @@ static void enqueue_sacch_rel_marker(struct gsm_lchan *lchan)
queue_sapi_command(lchan, cmd);
}
-static int lchan_deactivate_sacch(struct gsm_lchan *lchan)
+int bts_model_lchan_deactivate_sacch(struct gsm_lchan *lchan)
{
enqueue_sacch_rel_marker(lchan);
return 0;
@@ -1860,7 +1865,7 @@ int l1if_rsl_deact_sacch(struct gsm_lchan *lchan)
/* Only de-activate the SACCH if the lchan is active */
if (lchan->state != LCHAN_S_ACTIVE)
return 0;
- return lchan_deactivate_sacch(lchan);
+ return bts_model_lchan_deactivate_sacch(lchan);
}
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
diff --git a/src/osmo-bts-litecell15/tch.c b/src/osmo-bts-litecell15/tch.c
index 5020f63c..de3c7e35 100644
--- a/src/osmo-bts-litecell15/tch.c
+++ b/src/osmo-bts-litecell15/tch.c
@@ -35,7 +35,6 @@
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/bits.h>
-#include <osmocom/codec/codec.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/trau/osmo_ortp.h>
@@ -46,6 +45,7 @@
#include <osmo-bts/measurement.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/l1sap.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <nrw/litecell15/litecell15.h>
#include <nrw/litecell15/gsml1prim.h>
@@ -199,86 +199,10 @@ static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_le
* \returns number of \a l1_payload bytes filled
*/
static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
- uint8_t payload_len,
- struct gsm_lchan *lchan, uint32_t fn)
+ uint8_t payload_len, uint8_t ft)
{
- struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
- enum osmo_amr_type ft;
- enum osmo_amr_quality bfi;
- uint8_t cmr;
- int8_t sti, cmi;
- uint8_t *l1_cmi_idx = l1_payload;
- uint8_t *l1_cmr_idx = l1_payload+1;
- int rc;
-
- osmo_amr_rtp_dec(rtp_payload, payload_len, &cmr, &cmi, &ft, &bfi, &sti);
- memcpy(l1_payload+2, rtp_payload, payload_len);
-
- /* CMI in downlink tells the L1 encoder which encoding function
- * it will use, so we have to use the frame type */
- switch (ft) {
- case 0: case 1: case 2: case 3:
- case 4: case 5: case 6: case 7:
- cmi = ft;
- LOGP(DRTP, LOGL_DEBUG, "SPEECH frame with CMI %u\n", cmi);
- break;
- case AMR_NO_DATA:
- LOGP(DRTP, LOGL_DEBUG, "SPEECH frame AMR NO_DATA\n");
- break;
- case AMR_SID:
- LOGP(DRTP, LOGL_DEBUG, "SID %s frame with CMI %u\n",
- sti ? "UPDATE" : "FIRST", cmi);
- break;
- default:
- LOGP(DRTP, LOGL_ERROR, "unsupported AMR FT 0x%02x\n", ft);
- return -EINVAL;
- break;
- }
-
- rc = get_amr_mode_idx(amr_mrc, cmi);
- if (rc < 0) {
- LOGP(DRTP, LOGL_ERROR, "AMR CMI %u not part of AMR MR set\n",
- cmi);
- *l1_cmi_idx = 0;
- } else
- *l1_cmi_idx = rc;
-
- /* Codec Mode Request is in upper 4 bits of RTP payload header,
- * and we simply copy the CMR into the CMC */
- if (cmr == 0xF) {
- /* FIXME: we need some state about the last codec mode */
- *l1_cmr_idx = 0;
- } else {
- rc = get_amr_mode_idx(amr_mrc, cmr);
- if (rc < 0) {
- /* FIXME: we need some state about the last codec mode */
- LOGP(DRTP, LOGL_INFO, "RTP->L1: overriding CMR %u\n", cmr);
- *l1_cmr_idx = 0;
- } else
- *l1_cmr_idx = rc;
- }
-#if 0
- /* check for bad quality indication */
- if (bfi == AMR_GOOD) {
- /* obtain frame type from AMR FT */
- l1_payload[2] = ft;
- } else {
- /* bad quality, we should indicate that... */
- if (ft == AMR_SID) {
- /* FIXME: Should we do GsmL1_TchPlType_Amr_SidBad? */
- l1_payload[2] = ft;
- } else {
- l1_payload[2] = ft;
- }
- }
-#endif
-
- if (ft == AMR_SID) {
- save_last_sid(lchan, l1_payload, payload_len, fn, sti);
- return -EALREADY;
- }
-
- return payload_len+2;
+ memcpy(l1_payload, rtp_payload, payload_len);
+ return payload_len;
}
#define RTP_MSGB_ALLOC_SIZE 512
@@ -286,8 +210,11 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
/*! \brief function for incoming RTP via TCH.req
* \param[in] rtp_pl buffer containing RTP payload
* \param[in] rtp_pl_len length of \a rtp_pl
+ * \param[in] use_cache Use cached payload instead of parsing RTP
* \param[in] marker RTP header Marker bit (indicates speech onset)
- * \returns true if encoding result can be sent further to L1, false otherwise
+ * \returns 0 if encoding result can be sent further to L1 without extra actions
+ * positive value if data is ready AND extra actions are required
+ * negative value otherwise (no data for L1 encoded)
*
* This function prepares a msgb with a L1 PH-DATA.req primitive and
* queues it into lchan->dl_tch_queue.
@@ -296,15 +223,14 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
* yet, as things like the frame number, etc. are unknown at the time we
* pre-fill the primtive.
*/
-bool l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
- const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn, bool marker)
+int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
+ bool use_cache, bool marker)
{
uint8_t *payload_type;
- uint8_t *l1_payload, cmr;
- enum osmo_amr_type ft;
- enum osmo_amr_quality bfi;
- int8_t sti, cmi;
- int rc;
+ uint8_t *l1_payload, ft;
+ int rc = 0;
+ bool is_sid = false;
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(rtp_pl, rtp_pl_len));
@@ -318,32 +244,91 @@ bool l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
*payload_type = GsmL1_TchPlType_Fr;
rc = rtppayload_to_l1_fr(l1_payload,
rtp_pl, rtp_pl_len);
+ if (rc && lchan->ts->trx->bts->dtxd)
+ is_sid = osmo_fr_check_sid(rtp_pl, rtp_pl_len);
} else{
*payload_type = GsmL1_TchPlType_Hr;
rc = rtppayload_to_l1_hr(l1_payload,
rtp_pl, rtp_pl_len);
+ if (rc && lchan->ts->trx->bts->dtxd)
+ is_sid = osmo_hr_check_sid(rtp_pl, rtp_pl_len);
}
+ if (is_sid)
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, -1);
break;
case GSM48_CMODE_SPEECH_EFR:
*payload_type = GsmL1_TchPlType_Efr;
rc = rtppayload_to_l1_efr(l1_payload, rtp_pl,
rtp_pl_len);
+ /* FIXME: detect and save EFR SID */
break;
case GSM48_CMODE_SPEECH_AMR:
- if (marker) {
- *payload_type = GsmL1_TchPlType_Amr_Onset;
- rc = 0;
- osmo_amr_rtp_dec(rtp_pl, rtp_pl_len, &cmr, &cmi, &ft,
- &bfi, &sti);
- LOGP(DRTP, LOGL_ERROR, "Marker SPEECH frame AMR %s\n",
- get_value_string(osmo_amr_type_names, ft));
+ if (use_cache) {
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload, lchan->tch.dtx.cache,
+ lchan->tch.dtx.len, ft);
+ *len = lchan->tch.dtx.len + 1;
+ return 0;
}
- else {
+
+ rc = dtx_dl_amr_fsm_step(lchan, rtp_pl, rtp_pl_len, fn,
+ l1_payload, marker, len, &ft);
+ if (rc < 0)
+ return rc;
+ if (!dtx_dl_amr_enabled(lchan)) {
*payload_type = GsmL1_TchPlType_Amr;
- rc = rtppayload_to_l1_amr(l1_payload, rtp_pl,
- rtp_pl_len, lchan, fn);
- if (-EALREADY == rc)
- return false;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ }
+
+ /* DTX DL-specific logic below: */
+ switch (lchan->tch.dtx.dl_amr_fsm->state) {
+ case ST_ONSET_V:
+ *payload_type = GsmL1_TchPlType_Amr_Onset;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ *len = 3;
+ return 1;
+ case ST_VOICE:
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_SID_F1:
+ if (lchan->type == GSM_LCHAN_TCH_H) { /* AMR HR */
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstP1;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl,
+ rtp_pl_len, ft);
+ return 0;
+ }
+ /* AMR FR */
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_SID_F2:
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_F1_INH:
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstInH;
+ *len = 3;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ return 1;
+ case ST_U_INH:
+ *payload_type = GsmL1_TchPlType_Amr_SidUpdateInH;
+ *len = 3;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ return 1;
+ case ST_SID_U:
+ return -EAGAIN;
+ case ST_FACCH:
+ return -EBADMSG;
+ default:
+ LOGP(DRTP, LOGL_ERROR, "Unhandled DTX DL AMR FSM state "
+ "%d\n", lchan->tch.dtx.dl_amr_fsm->state);
+ return -EINVAL;
}
break;
default:
@@ -355,14 +340,14 @@ bool l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
if (rc < 0) {
LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
gsm_lchan_name(lchan));
- return false;
+ return -EBADMSG;
}
*len = rc + 1;
DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(data, *len));
- return true;
+ return 0;
}
static int is_recv_only(uint8_t speech_mode)
@@ -412,7 +397,7 @@ int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg)
goto err_payload_match;
/* according to 3GPP TS 26.093 ONSET frames precede the first
speech frame of a speech burst - set the marker for next RTP
- frame and drop last SID */
+ frame */
lchan->rtp_tx_marker = true;
break;
case GsmL1_TchPlType_Amr_SidFirstP1:
@@ -492,6 +477,7 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn)
GsmL1_MsgUnitParam_t *msu_param;
uint8_t *payload_type;
uint8_t *l1_payload;
+ int rc;
msg = l1p_msgb_alloc();
if (!msg)
@@ -505,44 +491,39 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn)
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_AMR:
- *payload_type = GsmL1_TchPlType_Amr;
- if (dtx_amr_sid_optional(lchan, fn)) {
- msgb_free(msg);
- return NULL;
+ if (lchan->type == GSM_LCHAN_TCH_H &&
+ dtx_dl_amr_enabled(lchan)) {
+ /* we have to explicitly handle sending SID FIRST P2 for
+ AMR HR in here */
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstP2;
+ rc = dtx_dl_amr_fsm_step(lchan, NULL, 0, fn, l1_payload,
+ false, &(msu_param->u8Size),
+ NULL);
+ if (rc == 0)
+ return msg;
}
- msu_param->u8Size = repeat_last_sid(lchan, l1_payload, fn);
- if (!msu_param->u8Size)
- osmo_amr_rtp_enc(l1_payload, 0, AMR_NO_DATA, AMR_GOOD);
+ *payload_type = GsmL1_TchPlType_Amr;
break;
case GSM48_CMODE_SPEECH_V1:
if (lchan->type == GSM_LCHAN_TCH_F)
*payload_type = GsmL1_TchPlType_Fr;
else
*payload_type = GsmL1_TchPlType_Hr;
- /* unlike AMR, FR & HR schedued based on absolute FN value */
- if (dtx_sched_optional(lchan, fn)) {
- msgb_free(msg);
- return NULL;
- }
- msu_param->u8Size = repeat_last_sid(lchan, l1_payload, fn);
- if (!msu_param->u8Size)
- return NULL;
break;
case GSM48_CMODE_SPEECH_EFR:
*payload_type = GsmL1_TchPlType_Efr;
- if (dtx_sched_optional(lchan, fn)) {
- msgb_free(msg);
- return NULL;
- }
- msu_param->u8Size = repeat_last_sid(lchan, l1_payload, fn);
- if (!msu_param->u8Size)
- return NULL;
break;
default:
msgb_free(msg);
- msg = NULL;
- break;
+ return NULL;
+ }
+
+ rc = repeat_last_sid(lchan, l1_payload, fn);
+ if (!rc) {
+ msgb_free(msg);
+ return NULL;
}
+ msu_param->u8Size = rc;
return msg;
}
diff --git a/src/osmo-bts-octphy/Makefile.am b/src/osmo-bts-octphy/Makefile.am
index e0df9afc..ac6a83ea 100644
--- a/src/osmo-bts-octphy/Makefile.am
+++ b/src/osmo-bts-octphy/Makefile.am
@@ -1,6 +1,6 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) -I$(OCTSDR2G_INCDIR)
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(ORTP_CFLAGS)
-COMMON_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) $(ORTP_LIBS)
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(ORTP_CFLAGS)
+COMMON_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) $(ORTP_LIBS)
EXTRA_DIST = l1_if.h l1_oml.h l1_utils.h octphy_hw_api.h octpkt.h
diff --git a/src/osmo-bts-octphy/l1_if.c b/src/osmo-bts-octphy/l1_if.c
index c4105ac6..0fc51fcc 100644
--- a/src/osmo-bts-octphy/l1_if.c
+++ b/src/osmo-bts-octphy/l1_if.c
@@ -862,9 +862,9 @@ static int handle_ph_readytosend_ind(struct octphy_hdl *fl1,
chan_nr = chan_nr_by_sapi(trx->ts[ts_num].pchan, sapi, sc, ts_num, fn);
if (chan_nr) {
if (sapi == cOCTVC1_GSM_SAPI_ENUM_SACCH)
- link_id = 0x40;
+ link_id = LID_SACCH;
else
- link_id = 0;
+ link_id = LID_DEDIC;
rc = msgb_trim(l1p_msg, sizeof(*l1sap));
if (rc < 0)
@@ -985,9 +985,9 @@ static int handle_ph_data_ind(struct octphy_hdl *fl1,
}
if (sapi == cOCTVC1_GSM_SAPI_ENUM_SACCH)
- link_id = 0x40;
+ link_id = LID_SACCH;
else
- link_id = 0;
+ link_id = LID_DEDIC;
memset(&l1sap, 0, sizeof(l1sap));
diff --git a/src/osmo-bts-octphy/l1_oml.c b/src/osmo-bts-octphy/l1_oml.c
index 77352612..58656256 100644
--- a/src/osmo-bts-octphy/l1_oml.c
+++ b/src/osmo-bts-octphy/l1_oml.c
@@ -43,12 +43,14 @@
#include "l1_oml.h"
#include "l1_utils.h"
#include "octphy_hw_api.h"
+#include "btsconfig.h"
#include <octphy/octvc1/octvc1_rc2string.h>
#include <octphy/octvc1/gsm/octvc1_gsm_api_swap.h>
#include <octphy/octvc1/gsm/octvc1_gsm_default.h>
#include <octphy/octvc1/gsm/octvc1_gsm_id.h>
#include <octphy/octvc1/main/octvc1_main_default.h>
+#include <octphy/octvc1/main/octvc1_main_version.h>
/* Map OSMOCOM logical channel type to OctPHY Logical channel type */
static tOCTVC1_GSM_LOGICAL_CHANNEL_COMBINATION_ENUM pchan_to_logChComb[_GSM_PCHAN_MAX] =
@@ -181,8 +183,9 @@ static int opstart_compl(struct gsm_abis_mo *mo)
if (mo->obj_class == NM_OC_CHANNEL && mo->obj_inst.trx_nr == 0 &&
mo->obj_inst.ts_nr == 0) {
struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
- mo->bts->c0->ts[0].lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
- lchan_activate(&mo->bts->c0->ts[0].lchan[4]);
+ mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
+ LCHAN_REL_ACT_OML;
+ lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
if (cbch) {
cbch->rel_act_kind = LCHAN_REL_ACT_OML;
lchan_activate(cbch);
@@ -881,7 +884,7 @@ static void enqueue_sacch_rel_marker(struct gsm_lchan *lchan)
queue_sapi_command(lchan, cmd);
}
-static int lchan_deactivate_sacch(struct gsm_lchan *lchan)
+int bts_model_lchan_deactivate_sacch(struct gsm_lchan *lchan)
{
enqueue_sacch_rel_marker(lchan);
return 0;
@@ -892,7 +895,7 @@ int l1if_rsl_deact_sacch(struct gsm_lchan *lchan)
/* Only de-activate the SACCH if the lchan is active */
if (lchan->state != LCHAN_S_ACTIVE)
return 0;
- return lchan_deactivate_sacch(lchan);
+ return bts_model_lchan_deactivate_sacch(lchan);
}
@@ -910,10 +913,9 @@ static void enqueue_rel_marker(struct gsm_lchan *lchan)
queue_sapi_command(lchan, cmd);
}
-static int lchan_deactivate(struct gsm_lchan *lchan)
+int bts_model_lchan_deactivate(struct gsm_lchan *lchan)
{
lchan_set_state(lchan, LCHAN_S_REL_REQ);
- lchan->ciph_state = 0; /* FIXME: do this in common *.c */
enqueue_rel_marker(lchan);
return 0;
}
@@ -1206,29 +1208,42 @@ int l1if_check_app_sys_version(struct gsm_bts_trx *trx)
return l1if_req_compl(fl1h, msg, app_info_sys_compl_cb, 0);
}
-static int app_info_compl_cb(struct octphy_hdl *fl1h, struct msgb *resp, void *data)
+static int app_info_compl_cb(struct octphy_hdl *fl1h, struct msgb *resp,
+ void *data)
{
+ char ver_hdr[32];
+
tOCTVC1_MAIN_MSG_APPLICATION_INFO_RSP *air =
(tOCTVC1_MAIN_MSG_APPLICATION_INFO_RSP *) resp->l2h;
- /* in a completion call-back, we take msgb ownership and must
- * release it before returning */
+ sprintf(ver_hdr, "%02i.%02i.%02i-B%i", cOCTVC1_MAIN_VERSION_MAJOR,
+ cOCTVC1_MAIN_VERSION_MINOR, cOCTVC1_MAIN_VERSION_MAINTENANCE,
+ cOCTVC1_MAIN_VERSION_BUILD);
mOCTVC1_MAIN_MSG_APPLICATION_INFO_RSP_SWAP(air);
- LOGP(DL1C, LOGL_INFO, "Rx APP-INFO.resp (name='%s', desc='%s', ver='%s')\n",
- air->szName, air->szDescription, air->szVersion);
+ LOGP(DL1C, LOGL_INFO,
+ "Rx APP-INFO.resp (name='%s', desc='%s', ver='%s', ver_hdr='%s')\n",
+ air->szName, air->szDescription, air->szVersion, ver_hdr);
+
+ /* Bail if dsp firmware does not match up the header version info */
+ if (strcmp(air->szVersion, ver_hdr) != 0) {
+ LOGP(DL1C, LOGL_ERROR,
+ "Invalid header-file / dsp-firmware combination, exiting...\n");
+ exit(1);
+ }
talloc_replace(fl1h->info.app.name, fl1h, air->szName);
talloc_replace(fl1h->info.app.description, fl1h, air->szDescription);
talloc_replace(fl1h->info.app.version, fl1h, air->szVersion);
+ /* in a completion call-back, we take msgb ownership and must
+ * release it before returning */
msgb_free(resp);
return 0;
}
-
int l1if_check_app_version(struct gsm_bts_trx *trx)
{
struct phy_instance *pinst = trx_phy_instance(trx);
@@ -1345,17 +1360,34 @@ int l1if_trx_open(struct gsm_bts_trx *trx)
oc->TrxId.byTrxId = pinst->u.octphy.trx_id;
oc->Config.ulBand = osmocom_to_octphy_band(trx->bts->band, trx->arfcn);
oc->Config.usArfcn = trx->arfcn;
- oc->Config.usTsc = trx->bts->bsic & 0x7;
+
+#if OCTPHY_MULTI_TRX == 1
+ if (pinst->u.octphy.trx_id)
+ oc->Config.usCentreArfcn = plink->u.octphy.center_arfcn;
+ else {
+ oc->Config.usCentreArfcn = trx->arfcn;
+ plink->u.octphy.center_arfcn = trx->arfcn;
+ }
oc->Config.usBcchArfcn = trx->bts->c0->arfcn;
+#endif
+ oc->Config.usTsc = trx->bts->bsic & 0x7;
oc->RfConfig.ulRxGainDb = plink->u.octphy.rx_gain_db;
/* FIXME: compute this based on nominal transmit power, etc. */
oc->RfConfig.ulTxAttndB = plink->u.octphy.tx_atten_db;
+#if OCTPHY_MULTI_TRX == 1
+ LOGP(DL1C, LOGL_INFO, "Tx TRX-OPEN.req(trx=%u, rf_port=%u, arfcn=%u, "
+ "center=%u, tsc=%u, rx_gain=%u, tx_atten=%u)\n",
+ oc->TrxId.byTrxId, oc->ulRfPortIndex, oc->Config.usArfcn,
+ oc->Config.usCentreArfcn, oc->Config.usTsc, oc->RfConfig.ulRxGainDb,
+ oc->RfConfig.ulTxAttndB);
+#else
LOGP(DL1C, LOGL_INFO, "Tx TRX-OPEN.req(trx=%u, rf_port=%u, arfcn=%u, "
"tsc=%u, rx_gain=%u, tx_atten=%u)\n",
oc->TrxId.byTrxId, oc->ulRfPortIndex, oc->Config.usArfcn,
oc->Config.usTsc, oc->RfConfig.ulRxGainDb,
oc->RfConfig.ulTxAttndB);
+#endif
mOCTVC1_GSM_MSG_TRX_OPEN_CMD_SWAP(oc);
diff --git a/src/osmo-bts-octphy/l1_utils.c b/src/osmo-bts-octphy/l1_utils.c
index 682865bb..ac5e217d 100644
--- a/src/osmo-bts-octphy/l1_utils.c
+++ b/src/osmo-bts-octphy/l1_utils.c
@@ -62,7 +62,15 @@ const struct value_string octphy_dir_names[5] =
const struct value_string octphy_clkmgr_state_vals[8] = {
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNINITIALIZE, "UNINITIALIZED" },
+
+/* Note: Octasic renamed cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNUSED to
+ * cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_IDLE. The following ifdef
+ * statement ensures that older headers still work. */
+#ifdef cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNUSED
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNUSED, "UNUSED" },
+#else
+ { cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_IDLE, "IDLE" },
+#endif
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_NO_EXT_CLOCK, "NO_EXT_CLOCK" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_LOCKED, "LOCKED" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNLOCKED, "UNLOCKED" },
diff --git a/src/osmo-bts-octphy/octphy_hw_api.c b/src/osmo-bts-octphy/octphy_hw_api.c
index d0d5ec74..dc23676c 100644
--- a/src/osmo-bts-octphy/octphy_hw_api.c
+++ b/src/osmo-bts-octphy/octphy_hw_api.c
@@ -279,7 +279,14 @@ static const struct value_string clocksync_source_state_vals[] = {
static const struct value_string clocksync_state_vals[] = {
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNINITIALIZE,
"Uninitialized" },
+/* Note: Octasic renamed cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNUSED to
+ * cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_IDLE. The following ifdef
+ * statement ensures that older headers still work. */
+#ifdef cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNUSED
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNUSED, "Unused" },
+#else
+ { cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_IDLE, "Idle" },
+#endif
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_NO_EXT_CLOCK,
"No External Clock" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_LOCKED, "Locked" },
diff --git a/src/osmo-bts-sysmo/Makefile.am b/src/osmo-bts-sysmo/Makefile.am
index 34f4bb0d..8e39a3a2 100644
--- a/src/osmo-bts-sysmo/Makefile.am
+++ b/src/osmo-bts-sysmo/Makefile.am
@@ -29,7 +29,7 @@ sysmobts_mgr_SOURCES = \
misc/sysmobts_mgr_temp.c \
misc/sysmobts_mgr_calib.c \
eeprom.c
-sysmobts_mgr_LDADD = $(LIBGPS_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCTRL_LIBS) $(top_builddir)/src/common/libbts.a
+sysmobts_mgr_LDADD = $(LIBGPS_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCTRL_LIBS) $(top_builddir)/src/common/libbts.a $(COMMON_LDADD)
sysmobts_util_SOURCES = misc/sysmobts_util.c misc/sysmobts_par.c eeprom.c
sysmobts_util_LDADD = $(LIBOSMOCORE_LIBS)
diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c
index 3c6db43d..2a3caf95 100644
--- a/src/osmo-bts-sysmo/l1_if.c
+++ b/src/osmo-bts-sysmo/l1_if.c
@@ -49,6 +49,8 @@
#include <osmo-bts/cbch.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/l1sap.h>
+#include <osmo-bts/msg_utils.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
@@ -281,12 +283,6 @@ empty_req_from_rts_ind(GsmL1_Prim_t *l1p,
return empty_req;
}
-static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
- 0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
- 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
- 0x2B, 0x2B, 0x2B
-};
-
/* fill PH-DATA.req from l1sap primitive */
static GsmL1_PhDataReq_t *
data_req_from_l1sap(GsmL1_Prim_t *l1p, struct femtol1_hdl *fl1,
@@ -331,10 +327,11 @@ empty_req_from_l1sap(GsmL1_Prim_t *l1p, struct femtol1_hdl *fl1,
}
static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
- struct osmo_phsap_prim *l1sap)
+ struct osmo_phsap_prim *l1sap, bool use_cache)
{
struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
struct msgb *l1msg = l1p_msgb_alloc();
+ struct gsm_lchan *lchan;
uint32_t u32Fn;
uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi = 0;
uint8_t chan_nr, link_id;
@@ -353,6 +350,7 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
u32Fn = l1sap->u.data.fn;
u8Tn = L1SAP_CHAN2TS(chan_nr);
subCh = 0x1f;
+ lchan = get_lchan_by_chan_nr(trx, chan_nr);
if (L1SAP_IS_LINK_SACCH(link_id)) {
sapi = GsmL1_Sapi_Sacch;
if (!L1SAP_IS_CHAN_TCHF(chan_nr))
@@ -402,14 +400,50 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
if (len) {
/* data request */
GsmL1_Prim_t *l1p = msgb_l1prim(l1msg);
-
data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr, len);
-
- OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer));
- memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h, msgb_l2len(msg));
+ if (use_cache)
+ memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer,
+ lchan->tch.dtx.facch, msgb_l2len(msg));
+ else if (dtx_dl_amr_enabled(lchan) &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_ONSET_F) {
+ if (sapi == GsmL1_Sapi_FacchF) {
+ sapi = GsmL1_Sapi_TchF;
+ }
+ if (sapi == GsmL1_Sapi_FacchH) {
+ sapi = GsmL1_Sapi_TchH;
+ }
+ if (sapi == GsmL1_Sapi_TchH || sapi == GsmL1_Sapi_TchF) {
+ /* FACCH interruption of DTX silence */
+ /* cache FACCH data */
+ memcpy(lchan->tch.dtx.facch, msg->l2h,
+ msgb_l2len(msg));
+ /* prepare ONSET message */
+ l1p->u.phDataReq.msgUnitParam.u8Buffer[0] =
+ GsmL1_TchPlType_Amr_Onset;
+ /* ignored CMR/CMI pair */
+ l1p->u.phDataReq.msgUnitParam.u8Buffer[1] = 0;
+ l1p->u.phDataReq.msgUnitParam.u8Buffer[2] = 0;
+ /* update length */
+ data_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi,
+ subCh, u8BlockNbr, 3);
+ /* update FN so it can be checked by TCH silence
+ resume handler */
+ lchan->tch.dtx.fn = LCHAN_FN_DUMMY;
+ }
+ } else if (dtx_dl_amr_enabled(lchan) &&
+ lchan->tch.dtx.dl_amr_fsm->state == ST_FACCH) {
+ /* update FN so it can be checked by TCH silence
+ resume handler */
+ lchan->tch.dtx.fn = LCHAN_FN_DUMMY;
+ }
+ else {
+ OSMO_ASSERT(msgb_l2len(msg) <= sizeof(l1p->u.phDataReq.msgUnitParam.u8Buffer));
+ memcpy(l1p->u.phDataReq.msgUnitParam.u8Buffer, msg->l2h,
+ msgb_l2len(msg));
+ }
LOGP(DL1P, LOGL_DEBUG, "PH-DATA.req(%s)\n",
- osmo_hexdump(l1p->u.phDataReq.msgUnitParam.u8Buffer,
- l1p->u.phDataReq.msgUnitParam.u8Size));
+ osmo_hexdump(l1p->u.phDataReq.msgUnitParam.u8Buffer,
+ l1p->u.phDataReq.msgUnitParam.u8Size));
} else {
/* empty frame */
GsmL1_Prim_t *l1p = msgb_l1prim(l1msg);
@@ -421,13 +455,16 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], l1msg) != 0) {
LOGP(DL1P, LOGL_ERROR, "MQ_L1_WRITE queue full. Dropping msg.\n");
msgb_free(l1msg);
- }
+ } else
+ dtx_int_signal(lchan);
+ if (dtx_recursion(lchan))
+ ph_data_req(trx, msg, l1sap, true);
return 0;
}
static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
- struct osmo_phsap_prim *l1sap)
+ struct osmo_phsap_prim *l1sap, bool use_cache, bool marker)
{
struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
struct gsm_lchan *lchan;
@@ -436,6 +473,7 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
uint8_t chan_nr;
GsmL1_Prim_t *l1p;
struct msgb *nmsg = NULL;
+ int rc = -1;
chan_nr = l1sap->u.tch.chan_nr;
u32Fn = l1sap->u.tch.fn;
@@ -459,17 +497,20 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
if (!nmsg)
return -ENOMEM;
l1p = msgb_l1prim(nmsg);
- if (!l1if_tch_encode(lchan,
+ rc = l1if_tch_encode(lchan,
l1p->u.phDataReq.msgUnitParam.u8Buffer,
&l1p->u.phDataReq.msgUnitParam.u8Size,
- msg->data, msg->len, u32Fn,
- l1sap->u.tch.marker)) {
+ msg->data, msg->len, u32Fn, use_cache,
+ l1sap->u.tch.marker);
+ if (rc < 0) {
+ /* no data encoded for L1: smth will be generated below */
msgb_free(nmsg);
nmsg = NULL;
}
}
- /* no message/data, we generate an empty traffic msg */
+ /* no message/data, we might generate an empty traffic msg or re-send
+ cached SID in case of DTX */
if (!nmsg)
nmsg = gen_empty_tch_msg(lchan, u32Fn);
@@ -491,16 +532,15 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
} else {
/* empty frame */
if (trx->bts->dtxd && trx != trx->bts->c0)
- lchan->tch.dtxd_active = true;
+ lchan->tch.dtx.dl_active = true;
empty_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr);
}
/* send message to DSP's queue */
osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
+ dtx_int_signal(lchan);
- if (l1sap->u.tch.marker) { /* Send voice after ONSET was sent */
- l1sap->u.tch.marker = 0;
- return ph_tch_req(trx, l1sap->oph.msg, l1sap);
- }
+ if (dtx_recursion(lchan)) /* DTX: send voice after ONSET was sent */
+ return ph_tch_req(trx, l1sap->oph.msg, l1sap, true, false);
return 0;
}
@@ -565,10 +605,10 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
* free()d below */
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
- rc = ph_data_req(trx, msg, l1sap);
+ rc = ph_data_req(trx, msg, l1sap, false);
break;
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
- rc = ph_tch_req(trx, msg, l1sap);
+ rc = ph_tch_req(trx, msg, l1sap, false, l1sap->u.tch.marker);
break;
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
rc = mph_info_req(trx, msg, l1sap);
@@ -750,9 +790,9 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
if (chan_nr) {
fn = rts_ind->u32Fn;
if (rts_ind->sapi == GsmL1_Sapi_Sacch)
- link_id = 0x40;
+ link_id = LID_SACCH;
else
- link_id = 0;
+ link_id = LID_DEDIC;
/* recycle the msgb and use it for the L1 primitive,
* which means that we (or our caller) must not free it */
rc = msgb_trim(l1p_msg, sizeof(*l1sap));
@@ -878,7 +918,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
return ENOTSUP;
}
fn = data_ind->u32Fn;
- link_id = (data_ind->sapi == GsmL1_Sapi_Sacch) ? 0x40 : 0x00;
+ link_id = (data_ind->sapi == GsmL1_Sapi_Sacch) ? LID_SACCH : LID_DEDIC;
process_meas_res(trx, chan_nr, &data_ind->measParam);
@@ -1136,22 +1176,6 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
return l1if_handle_ind(fl1h, msg);
}
-#if 0
-/* called by RSL if the BCCH SI has been modified */
-int sysinfo_has_changed(struct gsm_bts *bts, int si)
-{
- /* FIXME: Determine BS_AG_BLKS_RES and
- * * set cfgParams.u.agch.u8NbrOfAgch
- * * determine implications on paging
- */
- /* FIXME: Check for Extended BCCH presence */
- /* FIXME: Check for CCCH_CONF */
- /* FIXME: Check for BS_PA_MFRMS: update paging */
-
- return 0;
-}
-#endif
-
static int activate_rf_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp,
void *data)
{
diff --git a/src/osmo-bts-sysmo/l1_if.h b/src/osmo-bts-sysmo/l1_if.h
index a90c39b2..0dc919bd 100644
--- a/src/osmo-bts-sysmo/l1_if.h
+++ b/src/osmo-bts-sysmo/l1_if.h
@@ -109,9 +109,9 @@ uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
/* tch.c */
-bool l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
- const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
- bool marker);
+int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
+ bool use_cache, bool marker);
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn);
diff --git a/src/osmo-bts-sysmo/misc/sysmobts_mgr.c b/src/osmo-bts-sysmo/misc/sysmobts_mgr.c
index ccb84d8a..f126db2d 100644
--- a/src/osmo-bts-sysmo/misc/sysmobts_mgr.c
+++ b/src/osmo-bts-sysmo/misc/sysmobts_mgr.c
@@ -245,13 +245,11 @@ static int mgr_log_init(void)
int main(int argc, char **argv)
{
- void *tall_msgb_ctx;
int rc;
tall_mgr_ctx = talloc_named_const(NULL, 1, "bts manager");
- tall_msgb_ctx = talloc_named_const(tall_mgr_ctx, 1, "msgb");
- msgb_set_talloc_ctx(tall_msgb_ctx);
+ msgb_talloc_ctx_init(tall_mgr_ctx, 0);
mgr_log_init();
if (classify_bts() != 0)
diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c
index c1f1e0b2..01752d18 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -286,8 +286,9 @@ static int opstart_compl(struct gsm_abis_mo *mo, struct msgb *l1_msg)
mo->obj_inst.ts_nr == 0) {
struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
- mo->bts->c0->ts[0].lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
- lchan_activate(&mo->bts->c0->ts[0].lchan[4]);
+ mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
+ LCHAN_REL_ACT_OML;
+ lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
if (cbch) {
cbch->rel_act_kind = LCHAN_REL_ACT_OML;
lchan_activate(cbch);
@@ -1039,8 +1040,7 @@ static int mph_send_activate_req(struct gsm_lchan *lchan, struct sapi_cmd *cmd)
lch_par->rach.u8Bsic = lchan->ts->trx->bts->bsic;
break;
case GsmL1_Sapi_Agch:
-#warning Set BS_AG_BLKS_RES
- lch_par->agch.u8NbrOfAgch = 1;
+ lch_par->agch.u8NbrOfAgch = num_agch(lchan->ts->trx, lchan->name);
break;
case GsmL1_Sapi_TchH:
case GsmL1_Sapi_TchF:
@@ -1585,6 +1585,12 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
lchan_set_state(lchan, LCHAN_S_NONE);
mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
+
+ /* Reactivate CCCH due to SI3 update in RSL */
+ if (lchan->rel_act_kind == LCHAN_REL_ACT_REACT) {
+ lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
+ lchan_activate(lchan);
+ }
return 0;
}
@@ -1675,10 +1681,9 @@ static void enqueue_rel_marker(struct gsm_lchan *lchan)
queue_sapi_command(lchan, cmd);
}
-int lchan_deactivate(struct gsm_lchan *lchan)
+int bts_model_lchan_deactivate(struct gsm_lchan *lchan)
{
lchan_set_state(lchan, LCHAN_S_REL_REQ);
- lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */
enqueue_rel_marker(lchan);
return 0;
}
@@ -1693,7 +1698,7 @@ static void enqueue_sacch_rel_marker(struct gsm_lchan *lchan)
queue_sapi_command(lchan, cmd);
}
-static int lchan_deactivate_sacch(struct gsm_lchan *lchan)
+int bts_model_lchan_deactivate_sacch(struct gsm_lchan *lchan)
{
enqueue_sacch_rel_marker(lchan);
return 0;
@@ -1877,7 +1882,7 @@ int l1if_rsl_deact_sacch(struct gsm_lchan *lchan)
/* Only de-activate the SACCH if the lchan is active */
if (lchan->state != LCHAN_S_ACTIVE)
return 0;
- return lchan_deactivate_sacch(lchan);
+ return bts_model_lchan_deactivate_sacch(lchan);
}
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
diff --git a/src/osmo-bts-sysmo/sysmobts_vty.c b/src/osmo-bts-sysmo/sysmobts_vty.c
index c829c497..e67d8be0 100644
--- a/src/osmo-bts-sysmo/sysmobts_vty.c
+++ b/src/osmo-bts-sysmo/sysmobts_vty.c
@@ -42,15 +42,14 @@
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/phy_link.h>
#include <osmo-bts/logging.h>
+#include <osmo-bts/bts_model.h>
#include <osmo-bts/vty.h>
#include "femtobts.h"
#include "l1_if.h"
#include "utils.h"
-
extern int lchan_activate(struct gsm_lchan *lchan);
-extern int lchan_deactivate(struct gsm_lchan *lchan);
#define TRX_STR "Transceiver related commands\n" "TRX number\n"
diff --git a/src/osmo-bts-sysmo/tch.c b/src/osmo-bts-sysmo/tch.c
index b1ab0866..16c2cf3a 100644
--- a/src/osmo-bts-sysmo/tch.c
+++ b/src/osmo-bts-sysmo/tch.c
@@ -32,7 +32,6 @@
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/bits.h>
-#include <osmocom/codec/codec.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/trau/osmo_ortp.h>
@@ -43,6 +42,7 @@
#include <osmo-bts/measurement.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/l1sap.h>
+#include <osmo-bts/dtx_dl_amr_fsm.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
@@ -282,22 +282,10 @@ static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_le
* \returns number of \a l1_payload bytes filled
*/
static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
- uint8_t payload_len,
- struct gsm_lchan *lchan, uint32_t fn)
+ uint8_t payload_len, uint8_t ft)
{
- struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
- enum osmo_amr_type ft;
- enum osmo_amr_quality bfi;
- uint8_t cmr;
- int8_t sti, cmi;
- uint8_t *l1_cmi_idx = l1_payload;
- uint8_t *l1_cmr_idx = l1_payload+1;
- int rc;
-
- osmo_amr_rtp_dec(rtp_payload, payload_len, &cmr, &cmi, &ft, &bfi, &sti);
-
#ifdef USE_L1_RTP_MODE
- memcpy(l1_payload+2, rtp_payload, payload_len);
+ memcpy(l1_payload, rtp_payload, payload_len);
#else
uint8_t amr_if2_core_len = payload_len - 2;
@@ -310,72 +298,7 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
/* lower 4 bit of first FR2 byte contains FT */
l1_payload[2] |= ft;
#endif /* USE_L1_RTP_MODE */
-
- /* CMI in downlink tells the L1 encoder which encoding function
- * it will use, so we have to use the frame type */
- switch (ft) {
- case 0: case 1: case 2: case 3:
- case 4: case 5: case 6: case 7:
- cmi = ft;
- LOGP(DRTP, LOGL_DEBUG, "SPEECH frame with CMI %u\n", cmi);
- break;
- case AMR_NO_DATA:
- LOGP(DRTP, LOGL_DEBUG, "SPEECH frame AMR NO_DATA\n");
- break;
- case AMR_SID:
- LOGP(DRTP, LOGL_DEBUG, "SID %s frame with CMI %u\n",
- sti ? "UPDATE" : "FIRST", cmi);
- break;
- default:
- LOGP(DRTP, LOGL_ERROR, "unsupported AMR FT 0x%02x\n", ft);
- return -EINVAL;
- break;
- }
-
- rc = get_amr_mode_idx(amr_mrc, cmi);
- if (rc < 0) {
- LOGP(DRTP, LOGL_ERROR, "AMR CMI %u not part of AMR MR set\n",
- cmi);
- *l1_cmi_idx = 0;
- } else
- *l1_cmi_idx = rc;
-
- /* Codec Mode Request is in upper 4 bits of RTP payload header,
- * and we simply copy the CMR into the CMC */
- if (cmr == 0xF) {
- /* FIXME: we need some state about the last codec mode */
- *l1_cmr_idx = 0;
- } else {
- rc = get_amr_mode_idx(amr_mrc, cmr);
- if (rc < 0) {
- /* FIXME: we need some state about the last codec mode */
- LOGP(DRTP, LOGL_INFO, "RTP->L1: overriding CMR %u\n", cmr);
- *l1_cmr_idx = 0;
- } else
- *l1_cmr_idx = rc;
- }
-#if 0
- /* check for bad quality indication */
- if (bfi == AMR_GOOD) {
- /* obtain frame type from AMR FT */
- l1_payload[2] = ft;
- } else {
- /* bad quality, we should indicate that... */
- if (ft == AMR_SID) {
- /* FIXME: Should we do GsmL1_TchPlType_Amr_SidBad? */
- l1_payload[2] = ft;
- } else {
- l1_payload[2] = ft;
- }
- }
-#endif
-
- if (ft == AMR_SID) {
- save_last_sid(lchan, l1_payload, payload_len, fn, sti);
- return -EALREADY;
- }
-
- return payload_len+1;
+ return payload_len;
}
#define RTP_MSGB_ALLOC_SIZE 512
@@ -383,8 +306,11 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
/*! \brief function for incoming RTP via TCH.req
* \param[in] rtp_pl buffer containing RTP payload
* \param[in] rtp_pl_len length of \a rtp_pl
+ * \param[in] use_cache Use cached payload instead of parsing RTP
* \param[in] marker RTP header Marker bit (indicates speech onset)
- * \returns true if encoding result can be sent further to L1, false otherwise
+ * \returns 0 if encoding result can be sent further to L1 without extra actions
+ * positive value if data is ready AND extra actions are required
+ * negative value otherwise (no data for L1 encoded)
*
* This function prepares a msgb with a L1 PH-DATA.req primitive and
* queues it into lchan->dl_tch_queue.
@@ -393,15 +319,14 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
* yet, as things like the frame number, etc. are unknown at the time we
* pre-fill the primtive.
*/
-bool l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
- const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn, bool marker)
+int l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
+ const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint32_t fn,
+ bool use_cache, bool marker)
{
uint8_t *payload_type;
- uint8_t *l1_payload, cmr;
- enum osmo_amr_type ft;
- enum osmo_amr_quality bfi;
- int8_t sti, cmi;
- int rc;
+ uint8_t *l1_payload, ft;
+ int rc = 0;
+ bool is_sid = false;
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(rtp_pl, rtp_pl_len));
@@ -415,34 +340,93 @@ bool l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
*payload_type = GsmL1_TchPlType_Fr;
rc = rtppayload_to_l1_fr(l1_payload,
rtp_pl, rtp_pl_len);
+ if (rc && lchan->ts->trx->bts->dtxd)
+ is_sid = osmo_fr_check_sid(rtp_pl, rtp_pl_len);
} else{
*payload_type = GsmL1_TchPlType_Hr;
rc = rtppayload_to_l1_hr(l1_payload,
rtp_pl, rtp_pl_len);
+ if (rc && lchan->ts->trx->bts->dtxd)
+ is_sid = osmo_hr_check_sid(rtp_pl, rtp_pl_len);
}
+ if (is_sid)
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, -1);
break;
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
case GSM48_CMODE_SPEECH_EFR:
*payload_type = GsmL1_TchPlType_Efr;
rc = rtppayload_to_l1_efr(l1_payload, rtp_pl,
rtp_pl_len);
+ /* FIXME: detect and save EFR SID */
break;
#endif
case GSM48_CMODE_SPEECH_AMR:
- if (marker) {
- *payload_type = GsmL1_TchPlType_Amr_Onset;
- rc = 0;
- osmo_amr_rtp_dec(rtp_pl, rtp_pl_len, &cmr, &cmi, &ft,
- &bfi, &sti);
- LOGP(DRTP, LOGL_ERROR, "Marker SPEECH frame AMR %s\n",
- get_value_string(osmo_amr_type_names, ft));
+ if (use_cache) {
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload, lchan->tch.dtx.cache,
+ lchan->tch.dtx.len, ft);
+ *len = lchan->tch.dtx.len + 1;
+ return 0;
}
- else {
+
+ rc = dtx_dl_amr_fsm_step(lchan, rtp_pl, rtp_pl_len, fn,
+ l1_payload, marker, len, &ft);
+ if (rc < 0)
+ return rc;
+ if (!dtx_dl_amr_enabled(lchan)) {
*payload_type = GsmL1_TchPlType_Amr;
- rc = rtppayload_to_l1_amr(l1_payload, rtp_pl,
- rtp_pl_len, lchan, fn);
- if (-EALREADY == rc)
- return false;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ }
+
+ /* DTX DL-specific logic below: */
+ switch (lchan->tch.dtx.dl_amr_fsm->state) {
+ case ST_ONSET_V:
+ *payload_type = GsmL1_TchPlType_Amr_Onset;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ *len = 3;
+ return 1;
+ case ST_VOICE:
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_SID_F1:
+ if (lchan->type == GSM_LCHAN_TCH_H) { /* AMR HR */
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstP1;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl,
+ rtp_pl_len, ft);
+ return 0;
+ }
+ /* AMR FR */
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_SID_F2:
+ *payload_type = GsmL1_TchPlType_Amr;
+ rtppayload_to_l1_amr(l1_payload + 2, rtp_pl, rtp_pl_len,
+ ft);
+ return 0;
+ case ST_F1_INH:
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstInH;
+ *len = 3;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ return 1;
+ case ST_U_INH:
+ *payload_type = GsmL1_TchPlType_Amr_SidUpdateInH;
+ *len = 3;
+ dtx_cache_payload(lchan, rtp_pl, rtp_pl_len, fn, 0);
+ return 1;
+ case ST_SID_U:
+ return -EAGAIN;
+ case ST_FACCH:
+ return -EBADMSG;
+ default:
+ LOGP(DRTP, LOGL_ERROR, "Unhandled DTX DL AMR FSM state "
+ "%d\n", lchan->tch.dtx.dl_amr_fsm->state);
+ return -EINVAL;
}
break;
default:
@@ -454,14 +438,14 @@ bool l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
if (rc < 0) {
LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
gsm_lchan_name(lchan));
- return false;
+ return -EBADMSG;
}
*len = rc + 1;
DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(data, *len));
- return true;
+ return 0;
}
static int is_recv_only(uint8_t speech_mode)
@@ -513,7 +497,7 @@ int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg)
goto err_payload_match;
/* according to 3GPP TS 26.093 ONSET frames precede the first
speech frame of a speech burst - set the marker for next RTP
- frame and drop last SID */
+ frame */
lchan->rtp_tx_marker = true;
break;
case GsmL1_TchPlType_Amr_SidFirstP1:
@@ -595,6 +579,7 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn)
GsmL1_MsgUnitParam_t *msu_param;
uint8_t *payload_type;
uint8_t *l1_payload;
+ int rc;
msg = l1p_msgb_alloc();
if (!msg)
@@ -608,44 +593,39 @@ struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan, uint32_t fn)
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_AMR:
- *payload_type = GsmL1_TchPlType_Amr;
- if (dtx_amr_sid_optional(lchan, fn)) {
- msgb_free(msg);
- return NULL;
+ if (lchan->type == GSM_LCHAN_TCH_H &&
+ dtx_dl_amr_enabled(lchan)) {
+ /* we have to explicitly handle sending SID FIRST P2 for
+ AMR HR in here */
+ *payload_type = GsmL1_TchPlType_Amr_SidFirstP2;
+ rc = dtx_dl_amr_fsm_step(lchan, NULL, 0, fn, l1_payload,
+ false, &(msu_param->u8Size),
+ NULL);
+ if (rc == 0)
+ return msg;
}
- msu_param->u8Size = repeat_last_sid(lchan, l1_payload, fn);
- if (!msu_param->u8Size)
- osmo_amr_rtp_enc(l1_payload, 0, AMR_NO_DATA, AMR_GOOD);
+ *payload_type = GsmL1_TchPlType_Amr;
break;
case GSM48_CMODE_SPEECH_V1:
if (lchan->type == GSM_LCHAN_TCH_F)
*payload_type = GsmL1_TchPlType_Fr;
else
*payload_type = GsmL1_TchPlType_Hr;
- /* unlike AMR, FR & HR schedued based on absolute FN value */
- if (dtx_sched_optional(lchan, fn)) {
- msgb_free(msg);
- return NULL;
- }
- msu_param->u8Size = repeat_last_sid(lchan, l1_payload, fn);
- if (!msu_param->u8Size)
- return NULL;
break;
case GSM48_CMODE_SPEECH_EFR:
*payload_type = GsmL1_TchPlType_Efr;
- if (dtx_sched_optional(lchan, fn)) {
- msgb_free(msg);
- return NULL;
- }
- msu_param->u8Size = repeat_last_sid(lchan, l1_payload, fn);
- if (!msu_param->u8Size)
- return NULL;
break;
default:
msgb_free(msg);
- msg = NULL;
- break;
+ return NULL;
+ }
+
+ rc = repeat_last_sid(lchan, l1_payload, fn);
+ if (!rc) {
+ msgb_free(msg);
+ return NULL;
}
+ msu_param->u8Size = rc;
return msg;
}
diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c
index b89a3594..a42d39a0 100644
--- a/src/osmo-bts-trx/l1_if.c
+++ b/src/osmo-bts-trx/l1_if.c
@@ -71,19 +71,22 @@ struct trx_l1h *l1if_open(struct phy_instance *pinst)
return NULL;
l1h->phy_inst = pinst;
- trx_sched_init(&l1h->l1s, pinst->trx);
+ rc = trx_sched_init(&l1h->l1s, pinst->trx);
+ if (rc < 0) {
+ LOGP(DL1C, LOGL_FATAL, "Cannot initialize scheduler for phy "
+ "instance %d\n", pinst->num);
+ return NULL;
+ }
rc = trx_if_open(l1h);
if (rc < 0) {
- LOGP(DL1C, LOGL_FATAL, "Cannot initialize scheduler\n");
- goto err;
+ LOGP(DL1C, LOGL_FATAL, "Cannot open TRX interface for phy "
+ "instance %d\n", pinst->num);
+ l1if_close(l1h);
+ return NULL;
}
return l1h;
-
-err:
- l1if_close(l1h);
- return NULL;
}
void l1if_close(struct trx_l1h *l1h)
@@ -144,6 +147,25 @@ int check_transceiver_availability(struct gsm_bts *bts, int avail)
return 0;
}
+int bts_model_lchan_deactivate(struct gsm_lchan *lchan)
+{
+ struct phy_instance *pinst = trx_phy_instance(lchan->ts->trx);
+ struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
+
+ /* set lchan inactive */
+ lchan_set_state(lchan, LCHAN_S_NONE);
+
+ return trx_sched_set_lchan(&l1h->l1s, gsm_lchan2chan_nr(lchan),
+ LID_DEDIC, 0);
+}
+
+int bts_model_lchan_deactivate_sacch(struct gsm_lchan *lchan)
+{
+ struct phy_instance *pinst = trx_phy_instance(lchan->ts->trx);
+ struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
+ return trx_sched_set_lchan(&l1h->l1s, gsm_lchan2chan_nr(lchan),
+ LID_SACCH, 0);
+}
/*
* transceiver provisioning
@@ -197,6 +219,10 @@ int l1if_provision_transceiver_trx(struct trx_l1h *l1h)
trx_if_cmd_setmaxdly(l1h, l1h->config.maxdly);
l1h->config.maxdly_sent = 1;
}
+ if (l1h->config.maxdlynb_valid && !l1h->config.maxdlynb_sent) {
+ trx_if_cmd_setmaxdlynb(l1h, l1h->config.maxdlynb);
+ l1h->config.maxdlynb_sent = 1;
+ }
for (tn = 0; tn < TRX_NR_TS; tn++) {
if (l1h->config.slottype_valid[tn]
@@ -217,6 +243,7 @@ int l1if_provision_transceiver_trx(struct trx_l1h *l1h)
plink->u.osmotrx.power_sent = 0;
}
l1h->config.maxdly_sent = 0;
+ l1h->config.maxdlynb_sent = 0;
for (tn = 0; tn < TRX_NR_TS; tn++)
l1h->config.slottype_sent[tn] = 0;
}
@@ -267,7 +294,7 @@ static int trx_init(struct gsm_bts_trx *trx)
}
if (trx == trx->bts->c0)
- lchan_init_lapdm(&trx->ts[0].lchan[4]);
+ lchan_init_lapdm(&trx->ts[0].lchan[CCCH_LCHAN]);
/* Set to Operational State: Enabled */
oml_mo_state_chg(&trx->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
@@ -288,7 +315,7 @@ int bts_model_trx_close(struct gsm_bts_trx *trx)
/* deactivate lchan for CCCH */
if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) {
- lchan_set_state(&trx->ts[0].lchan[4], LCHAN_S_INACTIVE);
+ lchan_set_state(&trx->ts[0].lchan[CCCH_LCHAN], LCHAN_S_INACTIVE);
}
/* power off transceiver, if not already */
@@ -408,8 +435,8 @@ static uint8_t trx_set_ts_as_pchan(struct gsm_bts_trx_ts *ts,
/* activate lchan for CCCH */
if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) {
- ts->lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
- lchan_set_state(&ts->lchan[4], LCHAN_S_ACTIVE);
+ ts->lchan[CCCH_LCHAN].rel_act_kind = LCHAN_REL_ACT_OML;
+ lchan_set_state(&ts->lchan[CCCH_LCHAN], LCHAN_S_ACTIVE);
}
slottype = transceiver_chan_types[pchan];
@@ -591,9 +618,9 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
break;
}
/* activate dedicated channel */
- trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x00, 1);
+ trx_sched_set_lchan(&l1h->l1s, chan_nr, LID_DEDIC, 1);
/* activate associated channel */
- trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x40, 1);
+ trx_sched_set_lchan(&l1h->l1s, chan_nr, LID_SACCH, 1);
/* set mode */
trx_sched_set_mode(&l1h->l1s, chan_nr,
lchan->rsl_cmode, lchan->tch_mode,
@@ -641,16 +668,13 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
break;
}
/* deactivate associated channel */
- trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x40, 0);
+ bts_model_lchan_deactivate_sacch(lchan);
if (!l1sap->u.info.u.act_req.sacch_only) {
- /* set lchan inactive */
- lchan_set_state(lchan, LCHAN_S_NONE);
/* deactivate dedicated channel */
- trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x00, 0);
+ lchan_deactivate(lchan);
/* confirm only on dedicated channel */
mph_info_chan_confirm(l1h, chan_nr,
PRIM_INFO_DEACTIVATE, 0);
- lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */
}
break;
default:
diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h
index 187303c6..f0b2e67a 100644
--- a/src/osmo-bts-trx/l1_if.h
+++ b/src/osmo-bts-trx/l1_if.h
@@ -24,6 +24,10 @@ struct trx_config {
int maxdly;
int maxdly_sent;
+ int maxdlynb_valid;
+ int maxdlynb;
+ int maxdlynb_sent;
+
uint8_t slotmask;
int slottype_valid[TRX_NR_TS];
diff --git a/src/osmo-bts-trx/scheduler_trx.c b/src/osmo-bts-trx/scheduler_trx.c
index 02e7aba8..d946ad5f 100644
--- a/src/osmo-bts-trx/scheduler_trx.c
+++ b/src/osmo-bts-trx/scheduler_trx.c
@@ -32,8 +32,6 @@
#include <osmocom/core/bits.h>
#include <osmocom/gsm/a5.h>
-#include <osmocom/netif/rtp.h>
-
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/rsl.h>
@@ -788,6 +786,10 @@ int rx_rach_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
l1sap.u.rach_ind.acc_delay = (toa >= 0) ? toa : 0;
l1sap.u.rach_ind.fn = fn;
+ /* 11bit RACH is not supported for osmo-trx */
+ l1sap.u.rach_ind.is_11bit = 0;
+ l1sap.u.rach_ind.burst_type = GSM_L1_BURST_TYPE_ACCESS_0;
+
/* forward primitive */
l1sap_up(l1t->trx, &l1sap);
@@ -1123,7 +1125,7 @@ bfi:
/* indicate bad frame */
switch (tch_mode) {
case GSM48_CMODE_SPEECH_V1: /* FR */
- if (lchan->tch.ul_sid)
+ if (lchan->tch.dtx.ul_sid)
return 0; /* DTXu: pause in progress */
memset(tch_data, 0, GSM_FR_BYTES);
rc = GSM_FR_BYTES;
@@ -1297,7 +1299,7 @@ bfi:
/* indicate bad frame */
switch (tch_mode) {
case GSM48_CMODE_SPEECH_V1: /* HR */
- if (lchan->tch.ul_sid)
+ if (lchan->tch.dtx.ul_sid)
return 0; /* DTXu: pause in progress */
tch_data[0] = 0x70; /* F = 0, FT = 111 */
memset(tch_data + 1, 0, 14);
diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c
index 42d383c3..989e77a7 100644
--- a/src/osmo-bts-trx/trx_if.c
+++ b/src/osmo-bts-trx/trx_if.c
@@ -281,6 +281,11 @@ int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly)
return trx_ctrl_cmd(l1h, 0, "SETMAXDLY", "%d", dly);
}
+int trx_if_cmd_setmaxdlynb(struct trx_l1h *l1h, int dly)
+{
+ return trx_ctrl_cmd(l1h, 0, "SETMAXDLYNB", "%d", dly);
+}
+
int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type)
{
return trx_ctrl_cmd(l1h, 1, "SETSLOT", "%d %d", tn, type);
diff --git a/src/osmo-bts-trx/trx_if.h b/src/osmo-bts-trx/trx_if.h
index 8659c4a8..fdc8a8d3 100644
--- a/src/osmo-bts-trx/trx_if.h
+++ b/src/osmo-bts-trx/trx_if.h
@@ -22,6 +22,7 @@ int trx_if_cmd_setbsic(struct trx_l1h *l1h, uint8_t bsic);
int trx_if_cmd_setrxgain(struct trx_l1h *l1h, int db);
int trx_if_cmd_setpower(struct trx_l1h *l1h, int db);
int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly);
+int trx_if_cmd_setmaxdlynb(struct trx_l1h *l1h, int dly);
int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type);
int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn);
int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn);
diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c
index 3aec8bae..ca347e8d 100644
--- a/src/osmo-bts-trx/trx_vty.c
+++ b/src/osmo-bts-trx/trx_vty.c
@@ -105,6 +105,11 @@ static void show_phy_inst_single(struct vty *vty, struct phy_instance *pinst)
VTY_NEWLINE);
else
vty_out(vty, " maxdly : undefined%s", VTY_NEWLINE);
+ if (l1h->config.maxdlynb_valid)
+ vty_out(vty, " maxdlynb : %d%s", l1h->config.maxdlynb,
+ VTY_NEWLINE);
+ else
+ vty_out(vty, " maxdlynb : undefined%s", VTY_NEWLINE);
for (tn = 0; tn < TRX_NR_TS; tn++) {
if (!((1 << tn) & l1h->config.slotmask))
vty_out(vty, " slot #%d: unsupported%s", tn,
@@ -239,7 +244,13 @@ DEFUN(cfg_bts_no_setbsic, cfg_bts_no_setbsic_cmd,
DEFUN(cfg_phyinst_maxdly, cfg_phyinst_maxdly_cmd,
"osmotrx maxdly <0-31>",
- "Set the maximum delay of GSM symbols\n"
+ "Set the maximum acceptable delay of an Access Burst (in GSM symbols)."
+ " Access Burst is the first burst a mobile transmits in order to establish"
+ " a connection and it is used to estimate Timing Advance (TA) which is"
+ " then applied to Normal Bursts to compensate for signal delay due to"
+ " distance. So changing this setting effectively changes maximum range of"
+ " the cell, because if we receive an Access Burst with a delay higher than"
+ " this value, it will be ignored and connection is dropped.\n"
"GSM symbols (approx. 1.1km per symbol)\n")
{
struct phy_instance *pinst = vty->index;
@@ -253,6 +264,31 @@ DEFUN(cfg_phyinst_maxdly, cfg_phyinst_maxdly_cmd,
return CMD_SUCCESS;
}
+
+DEFUN(cfg_phyinst_maxdlynb, cfg_phyinst_maxdlynb_cmd,
+ "osmotrx maxdlynb <0-31>",
+ "Set the maximum acceptable delay of a Normal Burst (in GSM symbols)."
+ " USE FOR TESTING ONLY, DON'T CHANGE IN PRODUCTION USE!"
+ " During normal operation, Normal Bursts delay are controled by a Timing"
+ " Advance control loop and thus Normal Bursts arrive to a BTS with no more"
+ " than a couple GSM symbols, which is already taken into account in osmo-trx."
+ " So changing this setting will have no effect in production installations"
+ " except increasing osmo-trx CPU load. This setting is only useful when"
+ " testing with a transmitter which can't precisely synchronize to the BTS"
+ " downlink signal, like e.g. R&S CMD57.\n"
+ "GSM symbols (approx. 1.1km per symbol)\n")
+{
+ struct phy_instance *pinst = vty->index;
+ struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
+
+ l1h->config.maxdlynb = atoi(argv[0]);
+ l1h->config.maxdlynb_valid = 1;
+ l1h->config.maxdlynb_sent = 0;
+ l1if_provision_transceiver_trx(l1h);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_phyinst_slotmask, cfg_phyinst_slotmask_cmd,
"slotmask (1|0) (1|0) (1|0) (1|0) (1|0) (1|0) (1|0) (1|0)",
"Set the supported slots\n"
@@ -400,6 +436,18 @@ DEFUN(cfg_phyinst_no_maxdly, cfg_phyinst_no_maxdly_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_phyinst_no_maxdlynb, cfg_phyinst_no_maxdlynb_cmd,
+ "no osmotrx maxdlynb",
+ NO_STR "Unset the maximum delay of GSM symbols\n")
+{
+ struct phy_instance *pinst = vty->index;
+ struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
+
+ l1h->config.maxdlynb_valid = 0;
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_phy_transc_ip, cfg_phy_transc_ip_cmd,
"osmotrx ip HOST",
OSMOTRX_STR
@@ -458,6 +506,8 @@ void bts_model_config_write_phy_inst(struct vty *vty, struct phy_instance *pinst
if (l1h->config.maxdly_valid)
vty_out(vty, " maxdly %d%s", l1h->config.maxdly, VTY_NEWLINE);
+ if (l1h->config.maxdlynb_valid)
+ vty_out(vty, " maxdlynb %d%s", l1h->config.maxdlynb, VTY_NEWLINE);
if (l1h->config.slotmask != 0xff)
vty_out(vty, " slotmask %d %d %d %d %d %d %d %d%s",
l1h->config.slotmask & 1,
@@ -520,6 +570,8 @@ int bts_model_vty_init(struct gsm_bts *bts)
install_element(PHY_INST_NODE, &cfg_phy_power_on_cmd);
install_element(PHY_INST_NODE, &cfg_phyinst_maxdly_cmd);
install_element(PHY_INST_NODE, &cfg_phyinst_no_maxdly_cmd);
+ install_element(PHY_INST_NODE, &cfg_phyinst_maxdlynb_cmd);
+ install_element(PHY_INST_NODE, &cfg_phyinst_no_maxdlynb_cmd);
return 0;
}
diff --git a/tests/agch/Makefile.am b/tests/agch/Makefile.am
index 39cb83b8..46b55e9f 100644
--- a/tests/agch/Makefile.am
+++ b/tests/agch/Makefile.am
@@ -1,6 +1,6 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(ORTP_CFLAGS)
-LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(ORTP_LIBS)
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(ORTP_CFLAGS)
+LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCODEC_LIBS) $(ORTP_LIBS)
noinst_PROGRAMS = agch_test
EXTRA_DIST = agch_test.ok
diff --git a/tests/agch/agch_test.c b/tests/agch/agch_test.c
index 4175bdd6..e275c644 100644
--- a/tests/agch/agch_test.c
+++ b/tests/agch/agch_test.c
@@ -224,11 +224,8 @@ static void test_agch_queue_length_computation(void)
int main(int argc, char **argv)
{
- void *tall_msgb_ctx;
-
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
- tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
- msgb_set_talloc_ctx(tall_msgb_ctx);
+ msgb_talloc_ctx_init(tall_bts_ctx, 0);
bts_log_init(NULL);
diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c
index 43764ab3..d4bb3e0b 100644
--- a/tests/bursts/bursts_test.c
+++ b/tests/bursts/bursts_test.c
@@ -349,6 +349,8 @@ static void test_pdtch(uint8_t *l2, int len)
int n_errors, n_bits_total;
int rc;
+ memset(result, 0xff, len);
+
/* zero the not coded tail bits */
switch (len) {
case 34:
diff --git a/tests/cipher/Makefile.am b/tests/cipher/Makefile.am
index b80713ac..d76dcd57 100644
--- a/tests/cipher/Makefile.am
+++ b/tests/cipher/Makefile.am
@@ -1,6 +1,6 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(ORTP_CFLAGS)
-LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(ORTP_LIBS)
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(ORTP_CFLAGS)
+LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCODEC_LIBS) $(ORTP_LIBS)
noinst_PROGRAMS = cipher_test
EXTRA_DIST = cipher_test.ok
diff --git a/tests/cipher/cipher_test.c b/tests/cipher/cipher_test.c
index a175012f..c913925e 100644
--- a/tests/cipher/cipher_test.c
+++ b/tests/cipher/cipher_test.c
@@ -66,11 +66,8 @@ static void test_cipher_parsing(void)
int main(int argc, char **argv)
{
- void *tall_msgb_ctx;
-
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
- tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
- msgb_set_talloc_ctx(tall_msgb_ctx);
+ msgb_talloc_ctx_init(tall_bts_ctx, 0);
bts_log_init(NULL);
diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c
index e1f4d86d..a7a66d06 100644
--- a/tests/handover/handover_test.c
+++ b/tests/handover/handover_test.c
@@ -58,7 +58,6 @@ int main(int argc, char **argv)
{
struct gsm_bts_role_bts *btsb;
void *tall_bts_ctx;
- void *tall_msgb_ctx;
struct e1inp_line *line;
struct gsm_lchan *lchan;
struct osmo_phsap_prim nl1sap;
@@ -67,8 +66,7 @@ int main(int argc, char **argv)
int i;
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
- tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
- msgb_set_talloc_ctx(tall_msgb_ctx);
+ msgb_talloc_ctx_init(tall_bts_ctx, 0);
bts_log_init(NULL);
osmo_stderr_target->categories[DHO].loglevel = LOGL_DEBUG;
@@ -277,3 +275,5 @@ void trx_get_hlayer1(void) {}
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan) { return 0; }
int bts_model_ts_disconnect(struct gsm_bts_trx_ts *ts) { return 0; }
int bts_model_ts_connect(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config as_pchan) { return 0; }
+int bts_model_lchan_deactivate(struct gsm_lchan *lchan) { return 0; }
+int bts_model_lchan_deactivate_sacch(struct gsm_lchan *lchan) { return 0; }
diff --git a/tests/misc/Makefile.am b/tests/misc/Makefile.am
index f60325b0..d5acb18a 100644
--- a/tests/misc/Makefile.am
+++ b/tests/misc/Makefile.am
@@ -1,6 +1,6 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS)
-LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS)
+LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS)
noinst_PROGRAMS = misc_test
EXTRA_DIST = misc_test.ok
diff --git a/tests/paging/Makefile.am b/tests/paging/Makefile.am
index b6d6d78e..be9de745 100644
--- a/tests/paging/Makefile.am
+++ b/tests/paging/Makefile.am
@@ -1,6 +1,6 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(ORTP_CFLAGS)
-LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(ORTP_LIBS)
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(ORTP_CFLAGS)
+LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCODEC_LIBS) $(ORTP_LIBS)
noinst_PROGRAMS = paging_test
EXTRA_DIST = paging_test.ok
diff --git a/tests/paging/paging_test.c b/tests/paging/paging_test.c
index de839e47..1d5f2162 100644
--- a/tests/paging/paging_test.c
+++ b/tests/paging/paging_test.c
@@ -112,11 +112,8 @@ static void test_paging_sleep(void)
int main(int argc, char **argv)
{
- void *tall_msgb_ctx;
-
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
- tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
- msgb_set_talloc_ctx(tall_msgb_ctx);
+ msgb_talloc_ctx_init(tall_bts_ctx, 0);
bts_log_init(NULL);
diff --git a/tests/stubs.c b/tests/stubs.c
index c680db04..f969cb3c 100644
--- a/tests/stubs.c
+++ b/tests/stubs.c
@@ -40,6 +40,8 @@ int bts_model_oml_estab(struct gsm_bts *bts)
int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power)
{ return 0; }
+int bts_model_lchan_deactivate(struct gsm_lchan *lchan) { return 0; }
+int bts_model_lchan_deactivate_sacch(struct gsm_lchan *lchan) { return 0; }
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
{ return 0; }