aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--TODO-RELEASE4
-rw-r--r--configure.ac67
-rwxr-xr-xcontrib/jenkins_bts_trx.sh1
-rwxr-xr-xcontrib/jenkins_oct_and_bts_trx.sh1
-rwxr-xr-xcontrib/jenkins_sysmobts.sh1
-rw-r--r--contrib/osmo-bts.spec.in16
-rw-r--r--debian/changelog372
-rw-r--r--debian/control26
-rw-r--r--doc/examples/trx/osmo-bts-trx-calypso.cfg9
-rw-r--r--doc/manuals/chapters/architecture.adoc1
-rw-r--r--doc/manuals/vty/bts_vty_additions.xml27
-rw-r--r--include/osmo-bts/Makefile.am1
-rw-r--r--include/osmo-bts/abis.h16
-rw-r--r--include/osmo-bts/bts.h18
-rw-r--r--include/osmo-bts/bts_shutdown_fsm.h4
-rw-r--r--include/osmo-bts/bts_trx.h2
-rw-r--r--include/osmo-bts/cbch.h2
-rw-r--r--include/osmo-bts/gsm_data.h395
-rw-r--r--include/osmo-bts/l1sap.h3
-rw-r--r--include/osmo-bts/lchan.h371
-rw-r--r--include/osmo-bts/measurement.h2
-rw-r--r--include/osmo-bts/nm_common_fsm.h10
-rw-r--r--include/osmo-bts/oml.h3
-rw-r--r--include/osmo-bts/pcu_if.h3
-rw-r--r--include/osmo-bts/phy_link.h5
-rw-r--r--include/osmo-bts/power_control.h85
-rw-r--r--include/osmo-bts/rsl.h4
-rw-r--r--include/osmo-bts/scheduler.h4
-rw-r--r--include/osmo-bts/scheduler_backend.h2
-rw-r--r--include/osmo-bts/signal.h6
-rw-r--r--include/osmo-bts/ta_control.h2
-rw-r--r--include/osmo-bts/tx_power.h1
-rw-r--r--src/common/Makefile.am33
-rw-r--r--src/common/abis.c382
-rw-r--r--src/common/bts.c79
-rw-r--r--src/common/bts_shutdown_fsm.c50
-rw-r--r--src/common/bts_trx.c52
-rw-r--r--src/common/cbch.c30
-rw-r--r--src/common/gsm_data.c263
-rw-r--r--src/common/handover.c6
-rw-r--r--src/common/l1sap.c431
-rw-r--r--src/common/lchan.c491
-rw-r--r--src/common/load_indication.c10
-rw-r--r--src/common/main.c28
-rw-r--r--src/common/measurement.c267
-rw-r--r--src/common/nm_bb_transc_fsm.c75
-rw-r--r--src/common/nm_bts_fsm.c77
-rw-r--r--src/common/nm_bts_sm_fsm.c54
-rw-r--r--src/common/nm_channel_fsm.c57
-rw-r--r--src/common/nm_common_fsm.c4
-rw-r--r--src/common/nm_radio_carrier_fsm.c63
-rw-r--r--src/common/oml.c42
-rw-r--r--src/common/pcu_sock.c28
-rw-r--r--src/common/phy_link.c10
-rw-r--r--src/common/power_control.c499
-rw-r--r--src/common/probes.d2
-rw-r--r--src/common/rsl.c455
-rw-r--r--src/common/scheduler.c96
-rw-r--r--src/common/sysinfo.c16
-rw-r--r--src/common/ta_control.c88
-rw-r--r--src/common/tx_power.c8
-rw-r--r--src/common/vty.c161
-rw-r--r--src/osmo-bts-lc15/l1_if.c7
-rw-r--r--src/osmo-bts-lc15/lc15bts_vty.c2
-rw-r--r--src/osmo-bts-lc15/oml.c51
-rw-r--r--src/osmo-bts-oc2g/l1_if.c6
-rw-r--r--src/osmo-bts-oc2g/oc2gbts_vty.c2
-rw-r--r--src/osmo-bts-oc2g/oml.c51
-rw-r--r--src/osmo-bts-octphy/l1_if.c6
-rw-r--r--src/osmo-bts-octphy/l1_oml.c38
-rw-r--r--src/osmo-bts-omldummy/bts_model.c30
-rw-r--r--src/osmo-bts-omldummy/main.c12
-rw-r--r--src/osmo-bts-sysmo/oml.c47
-rw-r--r--src/osmo-bts-sysmo/sysmobts_vty.c2
-rw-r--r--src/osmo-bts-trx/Makefile.am13
-rw-r--r--src/osmo-bts-trx/l1_if.c96
-rw-r--r--src/osmo-bts-trx/main.c2
-rw-r--r--src/osmo-bts-trx/probes.d7
-rw-r--r--src/osmo-bts-trx/sched_lchan_pdtch.c35
-rw-r--r--src/osmo-bts-trx/sched_lchan_tchf.c56
-rw-r--r--src/osmo-bts-trx/sched_lchan_tchh.c48
-rw-r--r--src/osmo-bts-trx/sched_lchan_xcch.c10
-rw-r--r--src/osmo-bts-trx/sched_utils.h76
-rw-r--r--src/osmo-bts-trx/scheduler_trx.c118
-rw-r--r--src/osmo-bts-trx/trx_if.c59
-rw-r--r--src/osmo-bts-trx/trx_provision_fsm.c184
-rw-r--r--src/osmo-bts-trx/trx_vty.c41
-rw-r--r--src/osmo-bts-virtual/bts_model.c30
-rw-r--r--src/osmo-bts-virtual/l1_if.c10
-rw-r--r--tests/Makefile.am30
-rw-r--r--tests/amr/Makefile.am11
-rw-r--r--tests/amr/amr_test.c151
-rw-r--r--tests/amr/amr_test.ok152
-rw-r--r--tests/meas/meas_testcases.h2
-rw-r--r--tests/osmo-bts.vty297
-rw-r--r--tests/power/bs_power_loop_test.c67
-rw-r--r--tests/power/bs_power_loop_test.err189
-rw-r--r--tests/power/bs_power_loop_test.ok25
-rw-r--r--tests/power/ms_power_loop_test.c272
-rw-r--r--tests/power/ms_power_loop_test.err116
-rw-r--r--tests/power/ms_power_loop_test.ok46
-rw-r--r--tests/ta_control/ta_control_test.c19
-rw-r--r--tests/ta_control/ta_control_test.ok784
-rw-r--r--tests/testsuite.at6
105 files changed, 5835 insertions, 2693 deletions
diff --git a/.gitignore b/.gitignore
index f76bdac7..2e6351db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,7 @@ src/osmo-bts-oc2g/misc/.dirstamp
tests/atconfig
tests/package.m4
+tests/amr/amr_test
tests/agch/agch_test
tests/paging/paging_test
tests/cipher/cipher_test
diff --git a/TODO-RELEASE b/TODO-RELEASE
index aa0b2fd3..6b2de141 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -1,6 +1,2 @@
# When cleaning up this file: bump API version(s) in the following files:
# configure.ac, debian/control, and contrib/osmo-bts.spec.in.
-update libosmo-abis dependency to > 1.1.1 for osmo_rtp_socket_set_priority()
-update libosmo-abis dependency to > 1.1.1 for new e1_input vty commands for DSCP + priority
-update libosmocore dependency to > 1.5.1-73-g524b4f80 for osmo_bts_features_desc()
-update libosmocore dependency to > 1.5.1-133-g09f075fa for name in (struct rate_ctr_group)
diff --git a/configure.ac b/configure.ac
index 2b5a3169..8e3a44c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -69,14 +69,14 @@ then
fi
dnl checks for libraries
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
-PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
-PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
-PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
-PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.5.0)
-PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.5.0)
-PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.1.0)
-PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.1.0)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.2.0)
+PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0)
AC_MSG_CHECKING([whether to enable support for sysmobts calibration tool])
AC_ARG_ENABLE(sysmobts-calib,
@@ -349,6 +349,56 @@ then
AC_SUBST([OSMO_GSM_MANUALS_DIR])
fi
+AC_ARG_ENABLE([external_tests],
+ AC_HELP_STRING([--enable-external-tests],
+ [Include the VTY/CTRL tests in make check [default=no]]),
+ [enable_ext_tests="$enableval"],[enable_ext_tests="no"])
+if test "x$enable_ext_tests" = "xyes" ; then
+ AC_CHECK_PROG(PYTHON3_AVAIL,python3,yes)
+ if test "x$PYTHON3_AVAIL" != "xyes" ; then
+ AC_MSG_ERROR([Please install python3 to run the VTY/CTRL tests.])
+ fi
+ AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
+ if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
+ AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
+ fi
+fi
+AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
+AC_MSG_RESULT([$enable_ext_tests])
+AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
+
+#
+# SystemTap support
+#
+AC_MSG_CHECKING([whether to include systemtap tracing support])
+AC_ARG_ENABLE([systemtap],
+ [AS_HELP_STRING([--enable-systemtap],
+ [Enable inclusion of systemtap trace support])],
+ [ENABLE_SYSTEMTAP="${enableval}"], [ENABLE_SYSTEMTAP='no'])
+AM_CONDITIONAL([ENABLE_SYSTEMTAP], [test x$ENABLE_SYSTEMTAP = xyes])
+AC_MSG_RESULT(${ENABLE_SYSTEMTAP})
+
+if test "x${ENABLE_SYSTEMTAP}" = xyes; then
+ # Additional configuration for --enable-systemtap is HERE
+ AC_CHECK_PROGS(DTRACE, dtrace)
+ if test -z "$DTRACE"; then
+ AC_MSG_ERROR([dtrace not found])
+ fi
+ AC_CHECK_HEADER([sys/sdt.h], [SDT_H_FOUND='yes'],
+ [SDT_H_FOUND='no';
+ AC_MSG_ERROR([systemtap support needs sys/sdt.h header])])
+ AC_DEFINE([HAVE_SYSTEMTAP], [1], [Define to 1 if using SystemTap probes.])
+ AC_ARG_WITH([tapset-install-dir],
+ [AS_HELP_STRING([--with-tapset-install-dir],
+ [The absolute path where the tapset dir will be installed])],
+ [if test "x${withval}" = x; then
+ ABS_TAPSET_DIR="\$(datadir)/systemtap/tapset"
+ else
+ ABS_TAPSET_DIR="${withval}"
+ fi], [ABS_TAPSET_DIR="\$(datadir)/systemtap/tapset"])
+ AC_SUBST(ABS_TAPSET_DIR)
+fi
+
# https://www.freedesktop.org/software/systemd/man/daemon.html
AC_ARG_WITH([systemdsystemunitdir],
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
@@ -393,6 +443,7 @@ AC_OUTPUT(
tests/tx_power/Makefile
tests/power/Makefile
tests/meas/Makefile
+ tests/amr/Makefile
doc/Makefile
doc/examples/Makefile
doc/manuals/Makefile
diff --git a/contrib/jenkins_bts_trx.sh b/contrib/jenkins_bts_trx.sh
index 54efa56f..cb041a58 100755
--- a/contrib/jenkins_bts_trx.sh
+++ b/contrib/jenkins_bts_trx.sh
@@ -17,6 +17,7 @@ configure_flags="\
--enable-sanitize \
--enable-werror \
--enable-trx \
+ --enable-external-tests \
"
build_bts "osmo-bts-trx" "$configure_flags"
diff --git a/contrib/jenkins_oct_and_bts_trx.sh b/contrib/jenkins_oct_and_bts_trx.sh
index 67f67aa4..ead2f567 100755
--- a/contrib/jenkins_oct_and_bts_trx.sh
+++ b/contrib/jenkins_oct_and_bts_trx.sh
@@ -20,6 +20,7 @@ configure_flags="\
--with-octsdr-2g=$deps/layer1-headers/ \
--enable-octphy \
--enable-trx \
+ --enable-external-tests \
"
build_bts "osmo-bts-octphy+trx" "$configure_flags"
diff --git a/contrib/jenkins_sysmobts.sh b/contrib/jenkins_sysmobts.sh
index d0d05ae6..6376e922 100755
--- a/contrib/jenkins_sysmobts.sh
+++ b/contrib/jenkins_sysmobts.sh
@@ -21,6 +21,7 @@ configure_flags="\
--enable-werror \
--enable-sysmocom-bts \
--with-sysmobts=$inst/include/ \
+ --enable-external-tests \
"
# This will not work for the femtobts
diff --git a/contrib/osmo-bts.spec.in b/contrib/osmo-bts.spec.in
index a6d5e861..5fed7d6f 100644
--- a/contrib/osmo-bts.spec.in
+++ b/contrib/osmo-bts.spec.in
@@ -27,14 +27,14 @@ BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
-BuildRequires: pkgconfig(libosmoabis) >= 1.1.0
-BuildRequires: pkgconfig(libosmocodec) >= 1.5.0
-BuildRequires: pkgconfig(libosmocoding) >= 1.5.0
-BuildRequires: pkgconfig(libosmocore) >= 1.5.0
-BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
-BuildRequires: pkgconfig(libosmogsm) >= 1.5.0
-BuildRequires: pkgconfig(libosmotrau) >= 1.1.0
-BuildRequires: pkgconfig(libosmovty) >= 1.5.0
+BuildRequires: pkgconfig(libosmocodec) >= 1.6.0
+BuildRequires: pkgconfig(libosmocoding) >= 1.6.0
+BuildRequires: pkgconfig(libosmocore) >= 1.6.0
+BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
+BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
+BuildRequires: pkgconfig(libosmovty) >= 1.6.0
+BuildRequires: pkgconfig(libosmoabis) >= 1.2.0
+BuildRequires: pkgconfig(libosmotrau) >= 1.2.0
### FIXME: DependencyHACK to include osmocom/gprs/protocol/gsm_04_60.h
BuildRequires: pkgconfig(libosmogb)
%{?systemd_requires}
diff --git a/debian/changelog b/debian/changelog
index bc44837b..3ad028be 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,375 @@
+osmo-bts (1.4.0) unstable; urgency=medium
+
+ [ Philipp Maier ]
+ * l1sap: Store status of SRR in an lchan struct memeber
+ * l1sap: add logging and VTY introspection for ACCH repetition
+ * sched_lchan_tchh: fix frame number and fill FACCH gap
+ * main,abis: change model name from sysmoBTS to osmo-bts
+ * paging: prioritize CS related paging over PS related pagings.
+ * allow to configure multiple oml remote-ip addresses
+ * sched_lchan_tch_x: do not use cmr as ft
+ * sched_lchan_tch_x: use functions to determine AMR tranmssion phase
+ * sched_lchan_tch_x: use ul_cmr and ul_ft when generating RTP bad frame
+ * rsl: simplfy parse_repeated_acch_capability
+ * rsl: parse temporary overpower value RSL CHAN ACT / MODIFY
+
+ [ Vadim Yanitskiy ]
+ * doc/examples: remove obsolete power control parameters
+ * doc/examples: enable stderr logging for osmo-bts-virtual.cfg
+ * osmo-bts-trx: fix: do not call trx_if_close() two times
+ * osmo-bts-trx: fix segfault on trx_phy_inst_open() failure
+ * l1sap: use the passed 'trx' pointer in l1sap_chan_act()
+ * l1sap: use TLVP_PRES_LEN() macro in l1sap_chan_act()
+ * l1sap: check BTS_FEAT_MULTI_TSC in l1sap_chan_act()
+ * l1sap: fix wrong IEI and parsing in l1sap_chan_act()
+ * manuals: fix wrong VTY node for 'gsmtap-sapi' command
+ * manuals: document GSMTAP 'enable-all' / 'disable-all'
+ * osmo-bts-trx: correct definition of 'osmotrx rx-gain' command
+ * rsl: do not blindly ignore unhandled/unknown Channel Mode
+ * manuals: remove deprecated command line parameters
+ * manuals: document new 'gsmtap-remote-host' command
+ * l1sap: fix incorrect pointer cast in l1sap_chan_act()
+ * rsl: rename, fix and refactor lchan_tchmode_from_cmode()
+ * rsl: add missing Channel Mode values to rsl_handle_chan_mod_ie()
+ * lchan2lch_par(): fix missing default branch in switch
+ * osmo-bts-trx: cosmetic: TRXD 'header version' -> 'PDU version'
+ * osmo-bts-trx: remove outdated TRXD protocol documentation
+ * osmo-bts-trx: cosmetic: use '#pragma once' in trx_if.h
+ * osmo-bts-trx: define TRXC/TRXD message buffer size
+ * osmo-bts-trx: 'burst type' is actually modulation type
+ * osmo-bts-trx: move MTS parser into trx_data_parse_mts()
+ * osmo-bts-trx: discard TRXD PDUs with unexpected version
+ * osmo-bts-trx: move TDMA frame number check to trx_data_read_cb()
+ * osmo-bts-trx: cosmetic: get rid of TRX_CHDR_LEN macro
+ * osmo-bts-trx: generalize checking of TRXD header length
+ * osmo-bts-trx: pass 'struct phy_instance' to TRXD dissectors
+ * osmo-bts-trx: refactor handling of version specific TRXD parts
+ * osmo-bts-trx: enlarge and share TRXD message buffer
+ * osmo-bts-trx: assert PDU version in trx_if_send_burst()
+ * osmo-bts-trx: reduce code nasting in trx_if_send_burst()
+ * vty: fix the use of deprecated osmo_bts_feature_name()
+ * common/abis: fix the use of deprecated e1inp_line_get() API
+ * osmo-bts-trx: refactor parse_rsp(), fix compilation warnings
+ * rsl: fix wrong value printed in rsl_handle_chan_mod_ie()
+ * struct gsm_bts_trx: remove unused leftovers from openbsc
+ * common/sysinfo: make struct gsm_bts_trx const in num_agch()
+ * osmo-bts-{lc15,oc2g}: drop redundant checks in VTY commands
+ * [VAMOS] struct gsm_bts_trx: fix the PHY instance pointer
+ * [VAMOS] Merge bts_trx_init() into gsm_bts_trx_alloc()
+ * [VAMOS] osmo-bts-trx: move {chan,bid} to trx_{dl,ul}_burst_{req,ind}
+ * osmo-bts-trx: implement TRXDv2 protocol support
+ * scheduler.h: cosmetic: use #pragma once
+ * osmo-bts-trx: cosmetic: s/trx_sched_fn/bts_sched_fn/g
+ * osmo-bts-trx: remove redundant assert() in bts_sched_fn()
+ * osmo-bts-trx: fix hopping pointer bug in bts_sched_fn()
+ * [VAMOS] Re-organize osmo-bts-trx specific structures
+ * osmo-bts-trx: clarify logging messages in trx_if_{open,close}()
+ * osmo-bts-{trx,virtual}: fix: pinst->trx may be NULL
+ * common: make the arguments of phy_{link,instance}_name() const
+ * [VAMOS] common: make 'struct gsm_bts_trx_ts' pointers const
+ * [VAMOS] gsm_data.h: fix wrong bit-mask in BSIC2BCC macro
+ * [VAMOS] gsm_data.h: introduce and use BTS_TSC macro
+ * common: phy_links_open(): warn about dangling PHY instances
+ * [VAMOS] osmo-bts-trx: rework and split up bts_sched_fn()
+ * Fix regression in 'osmo-bts-trx: rework and split up bts_sched_fn()'
+ * [VAMOS] osmo-bts-trx: implement and enable PDU batching for TRXDv2
+ * [VAMOS] osmo-bts-trx: indicate MTS in Downlink TRXDv2 PDUs
+ * [VAMOS] rsl_rx_mode_modif(): handle Channel Identification IE
+ * [VAMOS] rsl: call bts_supports_cm() from rsl_handle_chan_mod_ie()
+ * [VAMOS] bts_supports_cm(): handle RSL_CMOD_CRT_OSMO_TCH_VAMOS_{Bm,Lm}
+ * [VAMOS] common/scheduler: unify symbol names for training sequences
+ * [VAMOS] osmo-bts-trx: rework handling of Training Sequence
+ * [VAMOS] osmo-bts-trx: properly handle per-timeslot TSC values
+ * [VAMOS] scheduler: add new GMSK training sequences from 3GPP 45.002
+ * [VAMOS] l1sap_chan_act(): handle Osmocom specific TSC IE
+ * [VAMOS] common/oml: generalize checking BTS_FEAT_MULTI_TSC
+ * [VAMOS] gsm_pchan2chan_nr(): use ABIS_RSL_CHAN_NR_CBITS_* macros
+ * [VAMOS] rsl_lchan_lookup(): use ABIS_RSL_CHAN_NR_CBITS_* macros
+ * [VAMOS] rsl_lchan_lookup(): make it more readable
+ * [VAMOS] gsm_data: rework and rename gsm_lchan_name_compute()
+ * [VAMOS] l1sap: get_lchan_by_chan_nr() may return NULL
+ * [VAMOS] oml_rx_set_chan_attr(): clarify NM_ATT_CHAN_COMB handling
+ * manuals/abis/rsl.adoc: s/TS 08.58/TS 48.058/
+ * manuals/abis/rsl.adoc: rework Channel Number description
+ * manuals/abis/rsl.adoc: add missing CBCH Channel Number values
+ * manuals/abis/rsl.adoc: add VAMOS specific Channel Number values
+ * osmo-bts-trx: fix NULL pointer dereference in trx_if_send_burst()
+ * trx_sched_is_sacch_fn(): fix handling of dynamic timeslots
+ * [VAMOS] scheduler: drop meaningless channel number checks
+ * [VAMOS] conf_lchans_as_pchan(): improve readability
+ * [VAMOS] Implement the concept of 'shadow' timeslots
+ * [VAMOS] osmo-bts-trx: schedule bursts on 'shadow' timeslots
+ * l1sap: fix TDMA frame number wrap in l1sap_info_time_ind()
+ * conf_lchans_as_pchan(): fix GSM_LCHAN_{CCCH->CBCH} regression
+ * conf_lchans_as_pchan(): initialize all lchans with GSM_LCHAN_NONE
+ * measurement: remove over-defensive checks in is_meas_complete()
+ * [VAMOS] trx_sched_init_ts(): assign names to per-timeslot counters
+ * common/vty: facilitate finding duplicate PHY/TRX associations
+ * vty: ensure all warning messages are prefixed with '%%'
+ * osmo-bts-octphy: drop talloc_replace(), use osmo_talloc_replace_string()
+ * l1sap: fix TDMA frame number arithmetic in fn_ms_adj()
+ * osmo-bts-trx: fix typo: s/bisc/bsic/ in 'show transceiver'
+ * osmo-bts-trx: fix copy-pasted comment: s/sysmoBTS/osmo-bts-trx/
+ * oml: fix handling of NM_ATT_INTERF_BOUND attribute
+ * Report interference levels in RSL RF RESource INDication
+ * scheduler: reorder enum trx_chan_type, add TRX_CHAN_IS_DEDIC()
+ * osmo-bts-trx: report interference levels to the upper layers
+ * osmo-bts-{trx,virtual}: get rid of dummy tx_idle_fn()
+ * scheduler: unset TRX_CHAN_FLAG_AUTO_ACTIVE for TRXC_IDLE
+ * osmo-bts-trx: print timeslot brief info in 'show transceiver'
+ * osmo-bts-trx: measure interference levels on TRXC_IDLE
+ * osmo-bts-trx: report PDCH interference levels to the PCU
+ * scheduler: fix wrong union field in trx_sched_tch_req()
+ * scheduler: fix: use ts_pchan() in trx_sched_set_cipher()
+ * Revert "power_control: BS power shall not be reduced on C0"
+ * osmo-bts-omldummy: indicate BTS_FEAT_BCCH_POWER_RED as supported
+ * osmo-bts-trx: implement BCCH carrier power reduction mode
+ * power_control: constrain BS power reduction on BCCH carrier
+ * manuals/abis/rsl.adoc: clarify RF Resource Indication conformance
+ * rsl: use tlvp_val16be() in rsl_rx_ipac_XXcx()
+ * gsm_lchan_interf_meas_calc_band(): also print number of AVG samples
+ * osmo-bts-trx: send dummy FACCH in the absense of RTP frames
+ * osmo-bts-trx: return -ENODEV if 'bursts_p' is NULL
+ * l1sap: unify channel (de)activation/modification messages
+ * gsm_lchan2chan_nr(): separate RSL specific variant of this API
+ * osmo-bts-trx: bts_model_l1sap_down(): remove chan_nr patching
+ * trx_sched_set_lchan(): use LOGL_INFO for logging messages
+ * osmo-bts-trx: remove an 'else' branch in _sched_dl_burst()
+ * osmo-bts-trx: implement Temporary Overpower for SACCH/FACCH
+ * scheduler: fix comments explaining the interleaving of TCH/H
+ * fix handle_ms_meas_report(): properly count measurement reports
+ * abis: fix memory leak in abis_oml_sendmsg()
+ * rsl: remove redundant logging in rsl_rx_chan_activ()
+ * .gitignore: add tests/amr/amr_test
+ * rsl: prevent race condition during timeslot re-configuration
+ * rsl_tx_rf_res(): separate interference AVG / band calculation
+ * rsl_tx_rf_res(): also report noise levels for PDTCH
+ * osmo-bts-trx: report PDCH interference levels to L1SAP
+ * l1sap: check if BTS model supports interference reporting
+ * vty: show interference level / band in 'show lchan'
+ * trx_sched_clean_ts(): also free() the associated 'struct l1sched_ts'
+ * trx_sched_clean(): also free() the shadow timeslot
+ * osmo-bts-trx: refactor 'maxdly' / 'maxdlynb' commands
+ * rsl: rsl_tx_meas_res() does not change l3, make it const
+ * rsl: send NACK if BTS_FEAT_ACCH_REP is not supported
+ * measurement: handle_ms_meas_report() accepts const gh
+ * measurement: move repeated_dl_facch_active_decision() here
+ * measurement: make sure that DL measurements are valid
+ * cosmetic: s/repeated_acch_capability/rep_acch_cap/g
+ * struct gsm_lchan: group ACCH repetition state fields
+ * struct gsm_lchan: move tch.rep_facch to rep_acch.dl_facch
+ * measurement: fix wrong operator used in handle_ms_meas_report()
+ * osmo-bts-trx: fix potential NULL pointer dereference
+ * lchan_set_state(): also free pending messages if any
+ * lchan: introduce and use lchan_is_tch() helper
+ * [overpower] rsl: store full content of RSL_IE_OSMO_TEMP_OVP_ACCH_CAP
+ * [overpower] lchan_dump_full_vty(): print overpower state
+ * [overpower] scheduler: handle {sacch,facch}_enabled flags
+ * l1sap: fix handling of lchan->pending_rel_ind_msg
+ * l1sap: move false PTCCH/U detection into PDCH branch
+ * l1sap: use designated initializers in process_l1sap_meas_data()
+ * l1sap: process_l1sap_meas_data() accepts pointer to lchan
+ * l1sap: make 'l1sap' argument of process_l1sap_meas_data() const
+ * rsl: fix a memory leak in handle_gprs_susp_req()
+ * l1sap: rework handling of DATA.ind on SACCH
+ * lchan_meas_handle_sacch(): check if Measurement Result is valid
+ * measurement: get rid of *le in lchan_meas_handle_sacch()
+ * measurement: pass *mr to repeated_dl_facch_active_decision()
+ * measurement: pass *mr to lchan_bs_pwr_ctrl()
+ * [overpower] Turn it on and off depending on DL RxQual
+ * measurement: make use of gsm48_meas_res_is_valid()
+ * common/Makefile.am: reformat {AM_CPPFLAGS,AM_CFLAGS,LDADD}
+ * rsl: exclude disabled timeslots from interference reports
+ * oml: use ARRAY_SIZE() in oml_rx_set_bts_attr()
+ * gsm_lchan_interf_meas_calc_avg(): fix band calculation
+
+ [ Pau Espin Pedrol ]
+ * l1sap: Transmit pdtch invalid MAC blocks to PCU
+ * bts-trx: Always submit rx PDTCH DATA.ind to l1sap
+ * bts-trx: Avoid submitting first data_ind with FN=0 to upper layers
+ * bts-trx: Drop duplicate set of last_clk_ind
+ * bts-trx: reorder first timerfd schedule to decrease first timeout skew
+ * sysmo,oc2g,lc15: Make RadioChannel MO depend on RadioCarrier MO
+ * bts: Clean up TS selection in sign_link_up
+ * Fix regression in 'bts: Clean up TS selection in sign_link_up'
+ * Add missing value_string for NM_EV_* introduced recently
+ * pcuif: Set missing bsic field during Tx of info_ind
+ * Use new stat item/ctr getter APIs
+ * rsl: Use switch statement in rsl_rx_bcch_info()
+ * pcu_sock: Transmit SI2
+ * doc: rsl.adoc: Fix trailing whitespace
+ * gsm_data: Drop unused function gsm_pchan_parse()
+ * pcuif_proto.h: Add new container messages
+ * Support forwarding proto IPAC_PROTO_EXT_PCU BSC<->PCU
+ * Rename osmo dyn ts enums to contain SDCCH8
+ * Support SDCCH8 in osmo dyn ts
+ * Make gcc 11.1.0 false positivies happy
+ * rsl: Fix rx of multiple RSL_IPAC_EIE_MEAS_AVG_CFG IEs
+ * rsl: Support parsing up to 3 RSL_IPAC_EIE_MEAS_AVG_CFG IEs
+ * MS Power Control Loop: Take C/I into account
+ * MS Power Control Loop: Support EWMA algorithm for C/I measurements
+ * MS Power Control Loop: Improve logging
+ * BS Power Control Loop: refactor lchan_bs_pwr_ctrl() to look similar to lchan_ms_pwr_ctrl()
+ * BS Power Control Loop: Support EWMA average algo for RxQual meas
+ * BS Power Control Loop: Increase attenuation if RxQual is better than upper threshold
+ * MS/BS Power Control Loop: Do RxLEV meas avg & delta calculations directly on RxLevels
+ * MS/BS Power Control Loop: Fix downscaling averaging bug
+ * Power Control Loop: Move skip loop logic to function helper
+ * comsetic: measurement.c: fix typo in comment
+ * l1sap: Take L1SACCH MS_PWR from bitfield instead of manual parsing
+ * TA loop: Take into account UL SACCH 'Actual Timing advance' field
+ * ta_control: Allow switching TA quicker
+ * lchan: Move TA CTRL param to its own substruct
+ * MS Power Control Loop: Feed UL RSSI from correct measurement period
+ * MS Power Control Loop: Feed UL C/I from correct measurement period
+ * TA Control Loop: Change toa256 switch threshold to 75% of a symbol
+ * Power Control Loop: Set P_CON_INTERVAL to 1 by default
+ * Support configuring TA loop SACCH block rate
+ * MS Power Control Loop: Fix sub vs full being passed to algo
+ * abis: Clear code and drop code not executed
+ * abis.h: Drop unused state
+ * cosmetic: fix typo in comment
+ * abis.c: Rearrange code to follow logic state order
+ * abis.c: Convert early return to assert()
+ * power_control: Drop unused param in function
+ * tests: MS Power Control Loop: Show oscillation among good power levels
+ * cosmetic: Fix formatting of conditional operator
+ * abis: Move FSM registration to constructor function
+ * abis: Shorten string names of events
+ * abis.c: Transition to CONNECTED state only when OML link is up
+ * abis.c: Fix mess with priv->bsc_oml_host
+ * abis.c: Loop over list of BSCs until connection succeeds
+ * trx_provision_fsm: Add missing state transition OPEN_WAIT_POWEROFF_CNF => OPEN_POWEROFF
+ * nm_*_fsm: Add missing item in event mask list for state ENABLED
+ * Allow setting administrative state through oml_mo_state_chg()
+ * nm_*_fsm: Set adminsitrative state 'shutting down' when shutdown procedure starts
+ * MS Power Control Loop: Fix oscillations within good MS Power Levels
+ * nm_*_fsm: Move to state Disabled NotInstalled Locked when shtudown proc ends
+ * abis: Drop internal OML msg queue
+ * nm_*fsm: Make FSMs aware of object being properly configured or not
+ * bts_shutdown_fsm: Fix event name
+ * trx_if: Set pointer to null after freeing it
+ * trx_if: Allow calling trx_if_flush/close from within TRXC callback
+ * trx_if: delete retrans timer when flushing the Tx queue
+ * trx_provision_fsm: Properly reset FSM state upon starting listening for events
+ * bts-trx: Submit TRX_PROV_EV_CFG_ARFCN for C0 during SetBtsAttr
+ * bts-trx: Get rid of check_transceiver_availability_trx()
+ * MS Power Control Loop: Disable threshold comparison on {LOWER,UPPER}_CMP_N=0
+ * l1sap: Support rx of empty rlcmac blocks from PCU
+ * bts-trx: Avoid race condition configuring TS-specific TSC values
+ * bts-trx: Submit TRX SW_ACT when PHY becomes connected
+ * trx_sched_clean_ts: Clean VAMOS shadow TS too
+ * phy_link: Introduce bts_model_phy_link_close() and use it in bts-trx
+ * nm_bts_fsm: Make sure PHYs are opened when SW_ACTivating it
+ * bts_shutdown_fsm: Allow configuring FSM to shutdown without exiting process
+ * abis: Call bts_model_abis_close() when Abis link goes down
+ * bts_trx: Drop non-executed path in trx_link_estab()
+ * Avoid sending Load Indications when BTS is not RSL-connected
+ * abis: Fix memory leak of bts->osmo_link upon link going down
+ * abis: Fix line leaked & recreated upon every reconnect
+ * bts-trx: Keep the process ongoing trying to reconnect on Abis link down
+ * Revert "bts-trx: Keep the process ongoing trying to reconnect on Abis link down"
+ * Revert "abis: Fix line leaked & recreated upon every reconnect"
+ * osmo-bts-omldummy: Fix crash accessing null phy
+ * bts-trx: Fix rxgain & maxdly VTY values being reset
+ * Decouple handling of Measurement Report from lapdm
+ * Move TA & Power Loops further up the stack, take DTXu flag into account
+ * scheduler: Fix lqual_cb not populated for TCH.ind
+ * abis: Fix line leaked & recreated upon every reconnect
+ * trx_provision_fsm: Fix TRX!=0 never going back to CLOSED state
+ * trx_provision_fsm: Support OPEN_POWEROFF->CLOSED transition
+ * bts-trx: Delay power ramp up until RCARRIER is ENABLED
+ * Delay abis reconnect while bts is shutting down
+ * bts-trx: Keep the process ongoing trying to reconnect on Abis link down
+ * trx_provision_fsm: Drop unneeded reset of fields
+ * trx_provision_fsm: Drop impossible paths
+ * trx_provision_fsm: poweronoff_sent flag: track POWERON and POWEROFF separately
+ * trx_provision_fsm: Fix shutdown while POWERON in transit
+ * rsl: NACK Chan Activation for lchans on disabled TS
+ * Introduce gsm_lchan_init() function helper
+ * MS Power Control Loop: Use P_CON_INTERVAL=2 by default
+ * load_indication.c: Avoid sending if CCCH is still not operational
+ * Move lchan,power_ctrl specific code from gsm_data.h to their own files
+ * Move lchan,power_control related code from gsm_data.c to their own files
+ * lchan.h: Add related ticket info to FIXME comment
+ * Introduce gsm_lchan_release function helper
+ * nm_channel_fsm: Release lchans after BTS shutdown
+ * nm_bts_fsm: Reset si_valid bitmask when BTS is shut down
+ * nm_*_fsm: Move reset state code to st_op_disabled_notinstalled_on_enter
+ * nm_*_fsm: reset mo.nm_attr from previous runs when entering state NOT_INSTALLED
+ * Add new gsm_bts_trx_free_shadow_ts() function
+ * Make sure lchan allocated memory from shadow_ts is properly freed
+ * rsl: Fix all shadow TS being Chan Act NACKed
+ * bts-trx: Guard call to trx_sched_clean with NULL trx ptr
+ * lchan: Setup early_rr_ia timer only once during init
+ * Move lchan related code to lchan.{c,h}
+ * lchan: Update log line level to use macro and level INFO
+ * lchan: Avoid applying transition changes if state new==old
+ * Move lchan_deactivate() to lchan.c
+ * Move lchan_init_lapdm inside lchan_set_state(LCHAN_S_ACTIVE)
+ * lchan: Call lapdm_channel_exit() when state changes to NONE
+ * bts_shutdown_fsm: Make sure pending power ramping are aborted before closing TRX
+ * gsm_pchan2chan_nr(): Properly assert if unexpected pchan is passed
+ * Reset CBCH state after BTS shutdown
+ * bts-trx: sched_lchan_pdtch: Refactor tx_pdtch_fn to get rid of goto tag
+ * bts-trx: sched: tx_pdtch_fn: Handle PCU idle blocks properly
+ * Revert "bts-trx: sched: tx_pdtch_fn: Handle PCU idle blocks properly"
+ * scheduler: Fix check against empty PDCH blocks
+ * bts-trx: sched: tx_pdtch_fn: Drop log line clogging logs
+ * l1sap: Avoid re-(de)activating already (de)active lchans
+ * scheduler: Avoid crash upon call to trx_sched_set_lchan if l1ts is uninitialized
+ * bts-trx: sched_lchan_tchf: Drop impossible code path
+ * scheduler: Fix FACCH msg with l2len==0 going to lower layers and logging errors
+ * bts-trx: sched_lchan_tchf: Change log level to debug for line informing about missing dl prim
+ * abis: Drop unneded if condition in else clause
+ * abis: Try one reconnect to previously connected BSC before trying next one
+ * gsm_ts_release(): Make sure pchan{,is_want} is reset to NONE
+
+ [ Neels Hofmeyr ]
+ * osmobts-abis.adoc: add missing bibliography
+ * Abis manual: s/TS 12.21/TS 52.021
+ * Abis manual: add Get Attributes, add BTS features
+ * Abis manual: add VAMOS to BTS features
+ * Abis manual: add RSL_IE_OSMO_TRAINING_SEQUENCE
+ * omldummy: introduce using getopt_long
+ * omldummy: add cmdline arg --features
+ * [VAMOS] osmo-bts-omldummy: allocate shadow timeslots
+ * remove unused LCHAN_S_INACTIVE
+ * enable Early Immediate Assignment
+ * add VTY transcript testing
+ * jenkins: enable new flag --enable-external-tests
+ * add osmo_tdef groups, exposing T timers on VTY config
+ * early IMM ASS: add configurable delay for RR IMM ASS
+ * early IA: change default X15 timer to 0 ms
+ * gsm_lchan_interf_meas_calc_avg(): adapt to the order of boundaries
+
+ [ Harald Welte ]
+ * Introduce ability to set socket priority of RTP sockets
+ * manual: Include QoS chapter and add osmo-bts specific example
+ * manual: Remove manual revision history; we don't use it anywawy
+ * manuals: Update copyright years
+ * l1sap/gsmtap: Don't log UI fill frames [zero information field]
+ * rsl: fix handling of REL IND in lapdm_rll_tx_cb()
+ * initial support for static userspace probes via systemtap
+
+ [ Keith ]
+ * sysmobts-mgr: Fix path to hwmon in /sys
+
+ [ Eric Wild ]
+ * osmo-bts-trx: indicate A5/4 support, handle Kc128
+
+ [ Oliver Smith ]
+ * debian/control: remove dh-systemd build-depend
+
+ [ Eric ]
+ * lc15, oc2g, sysmo: fix show dsp-trace-flags
+ * osmo-trx: fix maxdly
+
+ [ Martin Hauke ]
+ * osmo-bts-trx-calypso.cfg: Adjust settings to work with current osmo-bts versions
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 16:40:05 +0100
+
osmo-bts (1.3.0) unstable; urgency=medium
[ Michael McTernan ]
diff --git a/debian/control b/debian/control
index 5a42d194..2cb152d8 100644
--- a/debian/control
+++ b/debian/control
@@ -5,22 +5,37 @@ Priority: optional
Build-Depends: debhelper (>= 9),
pkg-config,
dh-autoreconf,
- dh-systemd (>= 1.5),
autotools-dev,
pkg-config,
- libosmocore-dev (>= 1.5.0),
- libosmo-abis-dev (>= 1.1.0),
+ libosmocore-dev (>= 1.6.0),
+ libosmo-abis-dev (>= 1.2.0),
libgps-dev,
txt2man,
- osmo-gsm-manuals-dev (>= 1.1.0)
+ osmo-gsm-manuals-dev (>= 1.2.0)
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
+Architecture: any
+Depends: osmo-bts-trx, osmo-bts-virtual, ${misc:Depends}
+Description: Base Transceiver Station for GSM
+ OsmoBTS is a software implementation of Layer2/3 of a BTS. It implements the
+ following protocols/interfaces:
+ LAPDm (GSM 04.06)
+ RTP
+ A-bis/IP in IPA multiplex
+ OML (GSM TS 12.21)
+ RSL (GSM TS 08.58)
+ .
+ OsmoBTS is modular and has support for multiple back-ends. A back-end talks to
+ a specific L1/PHY implementation of the respective BTS hardware. Based on this
+ architecture, it should be relatively easy to add a new back-end to support
+ so-far unsupported GSM PHY/L1 and associated hardware.
+
Package: osmo-bts-trx
Architecture: any
-Conflicts: osmo-bts
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: osmo-bts-trx GSM BTS with osmo-trx
osmo-bts-trx to be used with the osmo-trx application
@@ -35,7 +50,6 @@ Description: Debug symbols for the osmo-bts-trx
Package: osmo-bts-virtual
Architecture: any
-Conflicts: osmo-bts
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Virtual Osmocom GSM BTS (no RF hardware; GSMTAP/UDP)
This version of OsmoBTS doesn't use actual GSM PHY/Hardware/RF, but
diff --git a/doc/examples/trx/osmo-bts-trx-calypso.cfg b/doc/examples/trx/osmo-bts-trx-calypso.cfg
index 3f2e7781..d6cc43a1 100644
--- a/doc/examples/trx/osmo-bts-trx-calypso.cfg
+++ b/doc/examples/trx/osmo-bts-trx-calypso.cfg
@@ -1,6 +1,6 @@
!
! OsmoBTS configuration example for CalypsoBTS
-! http://osmocom.org/projects/baseband/wiki/CalypsoBTS
+! https://osmocom.org/projects/baseband/wiki/CalypsoBTS
!!
!
log stderr
@@ -24,14 +24,15 @@ phy 0
instance 0
osmotrx ip local 127.0.0.1
osmotrx ip remote 127.0.0.1
- osmotrx timing-advance-loop
- osmotrx ms-power-loop -65
osmotrx legacy-setbsic
+ osmotrx fn-advance 20
+ osmotrx rts-advance 5
bts 0
oml remote-ip 127.0.0.1
- ipa unit-id 1801 0
+ ipa unit-id 6969 0
gsmtap-sapi pdtch
gsmtap-sapi ccch
band 900
trx 0
phy 0 instance 0
+ nominal-tx-power 23
diff --git a/doc/manuals/chapters/architecture.adoc b/doc/manuals/chapters/architecture.adoc
index a0e66cd0..30eab7a4 100644
--- a/doc/manuals/chapters/architecture.adoc
+++ b/doc/manuals/chapters/architecture.adoc
@@ -88,6 +88,7 @@ order to specify which PHY instance is allocated to this specific TRX.
| common | abis_open() | Start of the A-bis connection to BSC
| common | phy_links_open() | Iterate over list of configured PHY links
| bts-specific | bts_model_phy_link_open() | Open each of the configured PHY links
+| bts-specific | bts_model_phy_link_close() | Close each of the configured PHY links
| common | write_pid_file() | Generate the pid file
| common | osmo_daemonize() | Fork as daemon in background (if configured)
| common | bts_main() | Run main loop until global variable quit >= 2
diff --git a/doc/manuals/vty/bts_vty_additions.xml b/doc/manuals/vty/bts_vty_additions.xml
index 519c4b48..2d22b41d 100644
--- a/doc/manuals/vty/bts_vty_additions.xml
+++ b/doc/manuals/vty/bts_vty_additions.xml
@@ -1 +1,26 @@
-<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'/>
+<!-- ex:ts=2:sw=2:et -->
+<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
+ <node id='phy-inst'>
+ <!-- FIXME: This command appears twice for some reason. -->
+ <command id='osmotrx maxdly &lt;0-63&gt;'>
+ <description>
+ 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 Access Bursts with a delay higher than this value will be ignored.
+ </description>
+ </command>
+ <!-- FIXME: This command appears unconditionally, despite being hidden. -->
+ <command id='osmotrx maxdlynb &lt;0-63&gt;'>
+ <description>
+ USE FOR TESTING ONLY, DO NOT CHANGE IN PRODUCTION USE!
+ During the normal operation, delay of Normal Bursts is controlled by the Timing Advance 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. 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 cannot precisely synchronize to the BTS downlink
+ signal, like R&amp;S CMD57.
+ </description>
+ </command>
+ </node>
+</vtydoc>
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index d52c9aa4..247e43e0 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -22,6 +22,7 @@ noinst_HEADERS = \
control_if.h \
cbch.h \
l1sap.h \
+ lchan.h \
power_control.h \
scheduler.h \
scheduler_backend.h \
diff --git a/include/osmo-bts/abis.h b/include/osmo-bts/abis.h
index 62407ece..40707cd1 100644
--- a/include/osmo-bts/abis.h
+++ b/include/osmo-bts/abis.h
@@ -6,19 +6,15 @@
#include <osmo-bts/gsm_data.h>
-#define OML_RETRY_TIMER 5
-#define OML_PING_TIMER 20
-
-enum {
- LINK_STATE_IDLE = 0,
- LINK_STATE_RETRYING,
- LINK_STATE_CONNECTING,
- LINK_STATE_CONNECT,
+enum abis_link_fsm_event {
+ ABIS_LINK_EV_SIGN_LINK_OML_UP,
+ ABIS_LINK_EV_SIGN_LINK_DOWN,
+ ABIS_LINK_EV_VTY_RM_ADDR, /* data: struct bsc_oml_host* being removed */
};
void abis_init(struct gsm_bts *bts);
-struct e1inp_line *abis_open(struct gsm_bts *bts, char *dst_host,
- char *model_name);
+int abis_open(struct gsm_bts *bts, char *model_name);
+
int abis_oml_sendmsg(struct msgb *msg);
diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h
index 6a61d015..8832588e 100644
--- a/include/osmo-bts/bts.h
+++ b/include/osmo-bts/bts.h
@@ -62,6 +62,8 @@ const char *btsvariant2str(enum gsm_bts_type_variant v);
/* Whether the BTS model requires RadioCarrier MO to be in Enabled state
* (OPSTARTed) before OPSTARTing the RadioChannel MOs. See OS#5157 */
#define BTS_INTERNAL_FLAG_NM_RCHANNEL_DEPENDS_RCARRIER (1 << 2)
+/* Whether the BTS model reports interference measurements to L1SAP. */
+#define BTS_INTERNAL_FLAG_INTERF_MEAS (1 << 3)
/* BTS implementation flags (internal use, not exposed via OML) */
#define bts_internal_flag_get(bts, flag) \
@@ -133,6 +135,12 @@ struct gsm_bts_sm {
struct gsm_abis_mo mo;
};
+/* Struct that holds one OML-Address (Address of the BSC) */
+struct bsc_oml_host {
+ struct llist_head list;
+ char *addr;
+};
+
/* One BTS */
struct gsm_bts {
/* list header in net->bts_list */
@@ -295,8 +303,7 @@ struct gsm_bts {
} etws;
struct paging_state *paging_state;
- char *bsc_oml_host;
- struct llist_head oml_queue;
+ struct llist_head bsc_oml_hosts;
unsigned int rtp_jitter_buf_ms;
bool rtp_jitter_adaptive;
@@ -363,6 +370,8 @@ struct gsm_bts {
} gsmtap;
struct osmo_fsm_inst *shutdown_fi; /* FSM instance to manage shutdown procedure during process exit */
+ bool shutdown_fi_exit_proc; /* exit process when shutdown_fsm is finished? */
+ struct osmo_fsm_inst *abis_link_fi; /* FSM instance to manage abis connection during process startup and link failure */
struct osmo_tdef *T_defs; /* Timer defines */
void *model_priv; /* Allocated by bts_model, contains model specific data pointer */
@@ -384,6 +393,7 @@ struct gsm_bts *gsm_bts_num(const struct gsm_network *net, int num);
int bts_init(struct gsm_bts *bts);
void bts_shutdown(struct gsm_bts *bts, const char *reason);
+void bts_shutdown_ext(struct gsm_bts *bts, const char *reason, bool exit_proc);
int bts_link_estab(struct gsm_bts *bts);
@@ -397,10 +407,10 @@ uint8_t *bts_sysinfo_get(struct gsm_bts *bts, const struct gsm_time *g_time);
void regenerate_si3_restoctets(struct gsm_bts *bts);
void regenerate_si4_restoctets(struct gsm_bts *bts);
int get_si4_ro_offset(const uint8_t *si4_buf);
-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);
+void load_timer_stop(struct gsm_bts *bts);
+bool load_timer_is_running(const struct gsm_bts *bts);
void bts_update_status(enum bts_global_status which, int on);
struct gsm_time *get_time(struct gsm_bts *bts);
diff --git a/include/osmo-bts/bts_shutdown_fsm.h b/include/osmo-bts/bts_shutdown_fsm.h
index 1e74ac6b..fe526253 100644
--- a/include/osmo-bts/bts_shutdown_fsm.h
+++ b/include/osmo-bts/bts_shutdown_fsm.h
@@ -24,7 +24,6 @@
#include <osmocom/core/fsm.h>
-/* 3GPP TS 24.008 § 4.1.3.3 GMM mobility management states on the network side */
enum bts_shutdown_fsm_states {
BTS_SHUTDOWN_ST_NONE,
BTS_SHUTDOWN_ST_WAIT_RAMP_DOWN_COMPL,
@@ -39,3 +38,6 @@ enum bts_shutdown_fsm_events {
};
extern struct osmo_fsm bts_shutdown_fsm;
+
+struct gsm_bts;
+bool bts_shutdown_in_progress(const struct gsm_bts *bts);
diff --git a/include/osmo-bts/bts_trx.h b/include/osmo-bts/bts_trx.h
index f033573f..c0dcb1ce 100644
--- a/include/osmo-bts/bts_trx.h
+++ b/include/osmo-bts/bts_trx.h
@@ -30,6 +30,7 @@ struct gsm_bts_trx {
uint8_t max_power_backoff_8psk; /* in actual dB OC-2G only */
uint8_t c0_idle_power_red; /* in actual dB OC-2G only */
+ uint8_t ta_ctrl_interval; /* 1 step is 2 SACCH periods */
struct trx_power_params power_params;
struct gsm_power_ctrl_params *bs_dpc_params; /* BS Dynamic Power Control */
@@ -49,6 +50,7 @@ static inline struct gsm_bts_trx *gsm_bts_bb_trx_get_trx(struct gsm_bts_bb_trx *
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts);
struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num);
void gsm_bts_trx_init_shadow_ts(struct gsm_bts_trx *trx);
+void gsm_bts_trx_free_shadow_ts(struct gsm_bts_trx *trx);
char *gsm_trx_name(const struct gsm_bts_trx *trx);
const char *gsm_trx_unit_id(struct gsm_bts_trx *trx);
diff --git a/include/osmo-bts/cbch.h b/include/osmo-bts/cbch.h
index 6bba5fa2..d5521f06 100644
--- a/include/osmo-bts/cbch.h
+++ b/include/osmo-bts/cbch.h
@@ -21,3 +21,5 @@ int bts_process_smscb_cmd(struct gsm_bts *bts, struct rsl_ie_cb_cmd_type cmd_typ
/* call-back from bts model specific code when it wants to obtain a CBCH
* block for a given gsm_time. outbuf must have 23 bytes of space. */
int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time);
+
+void bts_cbch_reset(struct gsm_bts *bts);
diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h
index 0ed63612..dcb357f1 100644
--- a/include/osmo-bts/gsm_data.h
+++ b/include/osmo-bts/gsm_data.h
@@ -9,15 +9,13 @@
#include <osmocom/core/statistics.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
-#include <osmocom/codec/ecu.h>
-#include <osmocom/gsm/lapdm.h>
+#include <osmocom/core/tdef.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm0502.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/rxlev_stat.h>
#include <osmocom/gsm/sysinfo.h>
-#include <osmocom/gsm/meas_rep.h>
#include <osmocom/gsm/bts_features.h>
#include <osmocom/gsm/gsm48_rest_octets.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
@@ -29,6 +27,7 @@
#include <osmo-bts/paging.h>
#include <osmo-bts/tx_power.h>
#include <osmo-bts/oml.h>
+#include <osmo-bts/lchan.h>
#define GSM_FR_BITS 260
#define GSM_EFR_BITS 244
@@ -42,8 +41,6 @@
#define GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT 41
#define GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT 91
-#define LOGPLCHAN(lchan, ss, lvl, fmt, args...) LOGP(ss, lvl, "%s " fmt, gsm_lchan_name(lchan), ## args)
-
struct gsm_network {
struct llist_head bts_list;
unsigned int num_bts;
@@ -51,15 +48,6 @@ struct gsm_network {
struct pcu_sock_state *pcu_state;
};
-enum lchan_ciph_state {
- LCHAN_CIPH_NONE,
- LCHAN_CIPH_RX_REQ,
- LCHAN_CIPH_RX_CONF,
- LCHAN_CIPH_RXTX_REQ,
- LCHAN_CIPH_RX_CONF_TX_REQ,
- LCHAN_CIPH_RXTX_CONF,
-};
-
/* 16 is the max. number of SI2quater messages according to 3GPP TS 44.018 Table 10.5.2.33b.1:
4-bit index is used (2#1111 = 10#15) */
#define SI2Q_MAX_NUM 16
@@ -76,350 +64,6 @@ enum lchan_ciph_state {
#define MAX_VERSION_LENGTH 64
-struct gsm_lchan;
-struct osmo_rtp_socket;
-struct pcu_sock_state;
-struct smscb_msg;
-
-#define MAX_A5_KEY_LEN (128/8)
-#define RSL_ENC_ALG_A5(x) (x+1)
-
-/* state of a logical channel */
-enum gsm_lchan_state {
- LCHAN_S_NONE, /* channel is not active */
- LCHAN_S_ACT_REQ, /* channel activation requested */
- LCHAN_S_ACTIVE, /* channel is active and operational */
- LCHAN_S_REL_REQ, /* channel release has been requested */
- LCHAN_S_REL_ERR, /* channel is in an error state */
- LCHAN_S_BROKEN, /* channel is somehow unusable */
- LCHAN_S_INACTIVE, /* channel is set inactive */
-};
-
-#define MAX_NUM_UL_MEAS 104
-#define LC_UL_M_F_L1_VALID (1 << 0)
-#define LC_UL_M_F_RES_VALID (1 << 1)
-#define LC_UL_M_F_OSMO_EXT_VALID (1 << 2)
-
-struct bts_ul_meas {
- /* BER in units of 0.01%: 10.000 == 100% ber, 0 == 0% ber */
- uint16_t ber10k;
- /* timing advance offset (in 1/256 bits) */
- int16_t ta_offs_256bits;
- /* C/I ratio in dB */
- float c_i;
- /* flags */
- uint8_t is_sub:1;
- /* RSSI in dBm * -1 */
- uint8_t inv_rssi;
-};
-
-struct amr_mode {
- uint8_t mode;
- uint8_t threshold;
- uint8_t hysteresis;
-};
-
-struct amr_multirate_conf {
- uint8_t gsm48_ie[2];
- struct amr_mode ms_mode[4];
- struct amr_mode bts_mode[4];
- uint8_t num_modes;
-};
-
-enum lchan_csd_mode {
- LCHAN_CSD_M_NT,
- LCHAN_CSD_M_T_1200_75,
- LCHAN_CSD_M_T_600,
- LCHAN_CSD_M_T_1200,
- LCHAN_CSD_M_T_2400,
- LCHAN_CSD_M_T_9600,
- LCHAN_CSD_M_T_14400,
- LCHAN_CSD_M_T_29000,
- LCHAN_CSD_M_T_32000,
-};
-
-/* State of the SAPIs in the lchan */
-enum lchan_sapi_state {
- LCHAN_SAPI_S_NONE,
- LCHAN_SAPI_S_REQ,
- LCHAN_SAPI_S_ASSIGNED,
- LCHAN_SAPI_S_REL,
- LCHAN_SAPI_S_ERROR,
-};
-
-/* What kind of release/activation is done? A silent one for
- * the PDCH or one triggered through RSL? */
-enum lchan_rel_act_kind {
- LCHAN_REL_ACT_RSL,
- LCHAN_REL_ACT_PCU,
- LCHAN_REL_ACT_OML,
- LCHAN_REL_ACT_REACT, /* remove once auto-activation hack is removed from opstart_compl() */
-};
-
-struct gsm_rep_facch {
- struct msgb *msg;
- uint32_t fn;
-};
-
-/* MS/BS Power related measurement averaging algo */
-enum gsm_power_ctrl_meas_avg_algo {
- GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE = 0x00,
- GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED = 0x01,
- GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED = 0x02,
- GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN = 0x03,
- /* EWMA is an Osmocom specific algo */
- GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA = 0x04,
-};
-
-/* MS/BS Power related measurement parameters */
-struct gsm_power_ctrl_meas_params {
- /* Thresholds (see 3GPP TS 45.008, section A.3.2.1) */
- uint8_t lower_thresh; /* lower (decreasing) direction */
- uint8_t upper_thresh; /* upper (increasing) direction */
-
- /* Threshold Comparators for lower (decreasing) direction */
- uint8_t lower_cmp_p; /* P1 for RxLev, P3 for RxQual */
- uint8_t lower_cmp_n; /* N1 for RxLev, N3 for RxQual */
- /* Threshold Comparators for upper (increasing) direction */
- uint8_t upper_cmp_p; /* P2 for RxLev, P4 for RxQual */
- uint8_t upper_cmp_n; /* N2 for RxLev, N4 for RxQual */
-
- /* Hreqave and Hreqt (see 3GPP TS 45.008, Annex A) */
- uint8_t h_reqave;
- uint8_t h_reqt;
-
- /* AVG algorithm and its specific parameters */
- enum gsm_power_ctrl_meas_avg_algo algo;
- union {
- /* Exponentially Weighted Moving Average */
- struct {
- /* Smoothing factor: higher the value - less smoothing */
- uint8_t alpha; /* 1 .. 99 (in %) */
- } ewma;
- };
-};
-
-/* MS/BS Power Control parameters */
-struct gsm_power_ctrl_params {
- /* Minimum interval between power level changes */
- uint8_t ctrl_interval; /* 1 step is 2 SACCH periods */
-
- /* Power change step size (maximum) */
- uint8_t inc_step_size_db; /* increasing direction */
- uint8_t red_step_size_db; /* reducing direction */
-
- /* Measurement averaging parameters for RxLev & RxQual */
- struct gsm_power_ctrl_meas_params rxqual_meas;
- struct gsm_power_ctrl_meas_params rxlev_meas;
-};
-
-/* Default MS/BS Power Control parameters */
-extern const struct gsm_power_ctrl_params power_ctrl_params_def;
-
-/* Measurement pre-processing state */
-struct gsm_power_ctrl_meas_proc_state {
- /* Number of measurements processed */
- unsigned int meas_num;
- /* Algorithm specific data */
- union {
- struct {
- /* Scaled up 100 times average value */
- int Avg100;
- } ewma;
- };
-};
-
-struct lchan_power_ctrl_state {
- /* Dynamic Power Control parameters (NULL in static mode) */
- const struct gsm_power_ctrl_params *dpc_params;
- /* Measurement pre-processing state (for dynamic mode) */
- struct gsm_power_ctrl_meas_proc_state rxlev_meas_proc;
- /* Number of SACCH blocks to skip (for dynamic mode) */
- int skip_block_num;
-
- /* Depending on the context (MS or BS power control), fields 'current' and 'max'
- * reflect either the MS power level (magic numbers), or BS Power reduction level
- * (attenuation, in dB). */
- uint8_t current;
- uint8_t max;
-};
-
-struct gsm_lchan {
- /* The TS that we're part of */
- struct gsm_bts_trx_ts *ts;
- /* The logical subslot number in the TS */
- uint8_t nr;
- /* The logical channel type */
- enum gsm_chan_t type;
- /* RSL channel mode */
- enum rsl_cmod_spd rsl_cmode;
- /* If TCH, traffic channel mode */
- enum gsm48_chan_mode tch_mode;
- enum lchan_csd_mode csd_mode;
- /* State */
- enum gsm_lchan_state state;
- const char *broken_reason;
- /* Encryption information */
- struct {
- uint8_t alg_id;
- uint8_t key_len;
- uint8_t key[MAX_A5_KEY_LEN];
- } encr;
-
- struct {
- uint32_t bound_ip;
- uint32_t connect_ip;
- uint16_t bound_port;
- uint16_t connect_port;
- uint16_t conn_id;
- uint8_t rtp_payload;
- uint8_t rtp_payload2;
- uint8_t speech_mode;
- struct osmo_rtp_socket *rtp_socket;
- } abis_ip;
-
- uint8_t rqd_ta;
-
- char *name;
-
- /* For handover, activation is described in 3GPP TS 48.058 4.1.3 and 4.1.4:
- *
- * | | Access || transmit | activate
- * | MS Power | Delay || on main channel | dl SACCH
- * ----------------------------------------------------------------------
- * async ho no * --> yes no
- * async ho yes * --> yes may be started
- * sync ho no no --> yes no
- * sync ho yes no --> yes may be started
- * sync ho yes yes --> yes shall be started
- *
- * Always start the main channel immediately.
- * want_dl_sacch_active indicates whether dl SACCH should be activated on CHAN ACT.
- */
- bool want_dl_sacch_active;
-
- /* Number of different GsmL1_Sapi_t used in osmo_bts_sysmo is 23.
- * Currently we don't share these headers so this is a magic number. */
- struct llist_head sapi_cmds;
- uint8_t sapis_dl[23];
- uint8_t sapis_ul[23];
- struct lapdm_channel lapdm_ch;
- struct llist_head dl_tch_queue;
- struct {
- /* bitmask of all SI that are present/valid in si_buf */
- uint32_t valid;
- /* bitmask of all SI that do not mirror the BTS-global SI values */
- uint32_t overridden;
- uint32_t last;
- /* buffers where we put the pre-computed SI:
- SI2Q_MAX_NUM is the max number of SI2quater messages (see 3GPP TS 44.018) */
- sysinfo_buf_t buf[_MAX_SYSINFO_TYPE][SI2Q_MAX_NUM];
- } si;
- struct {
- uint8_t flags;
- /* RSL measurement result number, 0 at lchan_act */
- uint8_t res_nr;
- /* number of measurements stored in array below */
- uint8_t num_ul_meas;
- struct bts_ul_meas uplink[MAX_NUM_UL_MEAS];
- /* last L1 header from the MS */
- struct rsl_l1_info l1_info;
- struct gsm_meas_rep_unidir ul_res;
- int16_t ms_toa256;
- /* Frame number of the last measurement indication receceived */
- uint32_t last_fn;
- /* Osmocom extended measurement results, see LC_UL_M_F_EXTD_VALID */
- struct {
- /* minimum value of toa256 during measurement period */
- int16_t toa256_min;
- /* maximum value of toa256 during measurement period */
- int16_t toa256_max;
- /* standard deviation of toa256 value during measurement period */
- uint16_t toa256_std_dev;
- } ext;
- /* Interference levels reported by PHY (in dBm) */
- int16_t interf_meas_dbm[31]; /* Intave max is 31 */
- uint8_t interf_meas_num;
- } meas;
- struct {
- struct amr_multirate_conf amr_mr;
- struct {
- struct osmo_fsm_inst *dl_amr_fsm;
- /* TCH cache */
- uint8_t cache[20];
- /* FACCH cache */
- uint8_t facch[GSM_MACBLOCK_LEN];
- uint8_t len;
- uint32_t fn;
- bool is_update;
- /* set for each SID frame to detect talkspurt for codecs
- without explicit ONSET event */
- bool ul_sid;
- /* indicates if DTXd was active during DL measurement
- period */
- bool dl_active;
- /* last UL SPEECH resume flag */
- bool is_speech_resume;
- } dtx;
- uint8_t last_cmr;
- uint32_t last_fn;
-
- /* SLOT #0 and #1 to store FACCH for repetition */
- struct gsm_rep_facch rep_facch[2];
-
- } tch;
-
- /* 3GPP TS 48.058 § 9.3.37: [0; 255] ok, -1 means invalid*/
- int16_t ms_t_offs;
- /* 3GPP TS 45.010 § 1.2 round trip propagation delay (in symbols) or -1 */
- int16_t p_offs;
-
- /* BTS-side ciphering state (rx only, bi-directional, ...) */
- uint8_t ciph_state;
- uint8_t ciph_ns;
- uint8_t loopback;
- struct {
- uint8_t active;
- uint8_t ref;
- /* T3105: PHYS INF retransmission */
- struct osmo_timer_list t3105;
- /* counts up to Ny1 */
- unsigned int phys_info_count;
- } ho;
- /* S counter for link loss */
- int s;
- /* Kind of the release/activation. E.g. RSL or PCU */
- enum lchan_rel_act_kind rel_act_kind;
- /* RTP header Marker bit to indicate beginning of speech after pause */
- bool rtp_tx_marker;
-
- /* MS/BS power control state */
- struct lchan_power_ctrl_state ms_power_ctrl;
- struct lchan_power_ctrl_state bs_power_ctrl;
-
- /* MS/BS Dynamic Power Control parameters */
- struct gsm_power_ctrl_params ms_dpc_params;
- struct gsm_power_ctrl_params bs_dpc_params;
-
- struct msgb *pending_rel_ind_msg;
-
- /* ECU (Error Concealment Unit) state */
- struct osmo_ecu_state *ecu_state;
-
- struct abis_rsl_osmo_rep_acch_cap repeated_acch_capability;
- bool repeated_dl_facch_active;
- bool repeated_ul_sacch_active;
- bool repeated_dl_sacch_active;
-
- /* Message buffer to store DL-SACCH repeation candidate */
- struct msgb *rep_sacch;
-};
-
-extern const struct value_string lchan_ciph_state_names[];
-static inline const char *lchan_ciph_state_name(uint8_t state) {
- return get_value_string(lchan_ciph_state_names, state);
-}
-
enum gsm_bts_trx_ts_flags {
TS_F_PDCH_ACTIVE = 0x1000,
TS_F_PDCH_ACT_PENDING = 0x2000,
@@ -439,7 +83,6 @@ struct gsm_bts_trx_ts {
struct {
enum gsm_phys_chan_config pchan_is;
enum gsm_phys_chan_config pchan_want;
- struct msgb *pending_chan_activ;
} dyn;
unsigned int flags;
@@ -479,8 +122,6 @@ struct gsm_bts_trx_ts {
struct gsm_lchan lchan[TS_MAX_LCHAN];
};
-#define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0])
-
enum gprs_rlc_par {
RLC_T3142,
RLC_T3169,
@@ -517,6 +158,9 @@ enum gprs_cs {
* OML connection will cause a special warning to be logged. */
#define OSMO_BTS_OML_CONN_EARLY_DISCONNECT 10 /* in seconds */
+extern struct osmo_tdef_group bts_tdef_groups[];
+extern struct osmo_tdef bts_T_defs[];
+extern struct osmo_tdef abis_T_defs[];
extern const struct value_string gsm_pchant_names[13];
extern const struct value_string gsm_pchant_descs[13];
@@ -524,8 +168,6 @@ const char *gsm_pchan_name(enum gsm_phys_chan_config c);
const char *gsm_lchant_name(enum gsm_chan_t c);
char *gsm_ts_name(const struct gsm_bts_trx_ts *ts);
char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts);
-void gsm_lchan_name_update(struct gsm_lchan *lchan);
-const char *gsm_lchans_name(enum gsm_lchan_state s);
#define GSM_TS_NAME_FMT \
"bts=%u,trx=%u,ts=%u" "%s"
@@ -533,18 +175,6 @@ const char *gsm_lchans_name(enum gsm_lchan_state s);
(ts)->trx->bts->nr, (ts)->trx->nr, (ts)->nr, \
(ts)->vamos.is_shadow ? ",shadow" : ""
-static inline char *gsm_lchan_name(const struct gsm_lchan *lchan)
-{
- return lchan->name;
-}
-
-uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan);
-uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan,
- enum gsm_phys_chan_config as_pchan);
-
-void gsm_lchan_interf_meas_push(struct gsm_lchan *lchan, int dbm);
-int gsm_lchan_interf_meas_calc_band(struct gsm_lchan *lchan);
-
#define BSIC2BCC(bsic) ((bsic) & 0x07)
#define BTS_TSC(bts) BSIC2BCC((bts)->bsic)
@@ -555,9 +185,6 @@ enum gsm_phys_chan_config ts_pchan(const struct gsm_bts_trx_ts *ts);
uint8_t ts_subslots(const struct gsm_bts_trx_ts *ts);
bool ts_is_tch(const struct gsm_bts_trx_ts *ts);
-int lchan2ecu_codec(const struct gsm_lchan *lchan);
-
-void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state);
int conf_lchans_as_pchan(struct gsm_bts_trx_ts *ts,
enum gsm_phys_chan_config pchan);
@@ -566,16 +193,6 @@ int conf_lchans_as_pchan(struct gsm_bts_trx_ts *ts,
bool ts_is_pdch(const struct gsm_bts_trx_ts *ts);
-static inline bool lchan_is_dcch(const struct gsm_lchan *lchan)
-{
- switch (lchan->type) {
- case GSM_LCHAN_SDCCH:
- case GSM_LCHAN_TCH_F:
- case GSM_LCHAN_TCH_H:
- return true;
- default:
- return false;
- }
-}
+void gsm_ts_release(struct gsm_bts_trx_ts *ts);
#endif /* _GSM_DATA_H */
diff --git a/include/osmo-bts/l1sap.h b/include/osmo-bts/l1sap.h
index f78d1143..93c532f8 100644
--- a/include/osmo-bts/l1sap.h
+++ b/include/osmo-bts/l1sap.h
@@ -144,7 +144,4 @@ int bts_check_for_first_ciphrd(struct gsm_lchan *lchan,
int is_ccch_for_agch(struct gsm_bts_trx *trx, uint32_t fn);
-void repeated_dl_facch_active_decision(struct gsm_lchan *lchan,
- const uint8_t *l3, size_t l3_len);
-
#endif /* L1SAP_H */
diff --git a/include/osmo-bts/lchan.h b/include/osmo-bts/lchan.h
new file mode 100644
index 00000000..3aaa75ab
--- /dev/null
+++ b/include/osmo-bts/lchan.h
@@ -0,0 +1,371 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <osmocom/core/timer.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/codec/ecu.h>
+#include <osmocom/gsm/lapdm.h>
+#include <osmocom/gsm/sysinfo.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/gsm/gsm48_rest_octets.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/meas_rep.h>
+
+#include <osmo-bts/power_control.h>
+
+#define LOGPLCHAN(lchan, ss, lvl, fmt, args...) LOGP(ss, lvl, "%s " fmt, gsm_lchan_name(lchan), ## args)
+
+enum lchan_ciph_state {
+ LCHAN_CIPH_NONE,
+ LCHAN_CIPH_RX_REQ,
+ LCHAN_CIPH_RX_CONF,
+ LCHAN_CIPH_RXTX_REQ,
+ LCHAN_CIPH_RX_CONF_TX_REQ,
+ LCHAN_CIPH_RXTX_CONF,
+};
+
+/* state of a logical channel */
+enum gsm_lchan_state {
+ LCHAN_S_NONE, /* channel is not active */
+ LCHAN_S_ACT_REQ, /* channel activation requested */
+ LCHAN_S_ACTIVE, /* channel is active and operational */
+ LCHAN_S_REL_REQ, /* channel release has been requested */
+ LCHAN_S_REL_ERR, /* channel is in an error state */
+ LCHAN_S_BROKEN, /* channel is somehow unusable */
+};
+
+#define MAX_NUM_UL_MEAS 104
+#define LC_UL_M_F_L1_VALID (1 << 0)
+#define LC_UL_M_F_RES_VALID (1 << 1)
+#define LC_UL_M_F_OSMO_EXT_VALID (1 << 2)
+
+#define MAX_A5_KEY_LEN (128/8)
+#define RSL_ENC_ALG_A5(x) (x+1)
+
+struct bts_ul_meas {
+ /* BER in units of 0.01%: 10.000 == 100% ber, 0 == 0% ber */
+ uint16_t ber10k;
+ /* timing advance offset (in 1/256 bits) */
+ int16_t ta_offs_256bits;
+ /* C/I ratio in cB */
+ int16_t c_i;
+ /* flags */
+ uint8_t is_sub:1;
+ /* RSSI in dBm * -1 */
+ uint8_t inv_rssi;
+};
+
+struct amr_mode {
+ uint8_t mode;
+ uint8_t threshold;
+ uint8_t hysteresis;
+};
+
+struct amr_multirate_conf {
+ uint8_t gsm48_ie[2];
+ struct amr_mode ms_mode[4];
+ struct amr_mode bts_mode[4];
+ uint8_t num_modes;
+};
+
+enum lchan_csd_mode {
+ LCHAN_CSD_M_NT,
+ LCHAN_CSD_M_T_1200_75,
+ LCHAN_CSD_M_T_600,
+ LCHAN_CSD_M_T_1200,
+ LCHAN_CSD_M_T_2400,
+ LCHAN_CSD_M_T_9600,
+ LCHAN_CSD_M_T_14400,
+ LCHAN_CSD_M_T_29000,
+ LCHAN_CSD_M_T_32000,
+};
+
+/* State of the SAPIs in the lchan */
+enum lchan_sapi_state {
+ LCHAN_SAPI_S_NONE,
+ LCHAN_SAPI_S_REQ,
+ LCHAN_SAPI_S_ASSIGNED,
+ LCHAN_SAPI_S_REL,
+ LCHAN_SAPI_S_ERROR,
+};
+
+/* What kind of release/activation is done? A silent one for
+ * the PDCH or one triggered through RSL? */
+enum lchan_rel_act_kind {
+ LCHAN_REL_ACT_RSL,
+ LCHAN_REL_ACT_PCU,
+ LCHAN_REL_ACT_OML,
+ LCHAN_REL_ACT_REACT, /* FIXME: remove once auto-activation hack is removed from opstart_compl() (OS#1575) */
+};
+
+struct gsm_rep_facch {
+ struct msgb *msg;
+ uint32_t fn;
+};
+
+
+struct lchan_power_ctrl_state {
+ /* Dynamic Power Control parameters (NULL in static mode) */
+ const struct gsm_power_ctrl_params *dpc_params;
+ /* Measurement pre-processing state (for dynamic mode) */
+ struct gsm_power_ctrl_meas_proc_state rxlev_meas_proc;
+ struct gsm_power_ctrl_meas_proc_state rxqual_meas_proc;
+ struct gsm_power_ctrl_meas_proc_state ci_meas_proc;
+ /* Number of SACCH blocks to skip (for dynamic mode) */
+ int skip_block_num;
+
+ /* Depending on the context (MS or BS power control), fields 'current' and 'max'
+ * reflect either the MS power level (magic numbers), or BS Power reduction level
+ * (attenuation, in dB). */
+ uint8_t current;
+ uint8_t max;
+};
+
+struct lchan_ta_ctrl_state {
+ /* Number of SACCH blocks to skip */
+ int skip_block_num;
+ /* Currently requested TA */
+ uint8_t current;
+};
+
+struct gsm_lchan {
+ /* The TS that we're part of */
+ struct gsm_bts_trx_ts *ts;
+ /* The logical subslot number in the TS */
+ uint8_t nr;
+ /* The logical channel type */
+ enum gsm_chan_t type;
+ /* RSL channel mode */
+ enum rsl_cmod_spd rsl_cmode;
+ /* If TCH, traffic channel mode */
+ enum gsm48_chan_mode tch_mode;
+ enum lchan_csd_mode csd_mode;
+ /* State */
+ enum gsm_lchan_state state;
+ const char *broken_reason;
+ /* Encryption information */
+ struct {
+ uint8_t alg_id;
+ uint8_t key_len;
+ uint8_t key[MAX_A5_KEY_LEN];
+ } encr;
+
+ struct {
+ uint32_t bound_ip;
+ uint32_t connect_ip;
+ uint16_t bound_port;
+ uint16_t connect_port;
+ uint16_t conn_id;
+ uint8_t rtp_payload;
+ uint8_t rtp_payload2;
+ uint8_t speech_mode;
+ struct osmo_rtp_socket *rtp_socket;
+ } abis_ip;
+
+ char *name;
+
+ /* For handover, activation is described in 3GPP TS 48.058 4.1.3 and 4.1.4:
+ *
+ * | | Access || transmit | activate
+ * | MS Power | Delay || on main channel | dl SACCH
+ * ----------------------------------------------------------------------
+ * async ho no * --> yes no
+ * async ho yes * --> yes may be started
+ * sync ho no no --> yes no
+ * sync ho yes no --> yes may be started
+ * sync ho yes yes --> yes shall be started
+ *
+ * Always start the main channel immediately.
+ * want_dl_sacch_active indicates whether dl SACCH should be activated on CHAN ACT.
+ */
+ bool want_dl_sacch_active;
+
+ /* Number of different GsmL1_Sapi_t used in osmo_bts_sysmo is 23.
+ * Currently we don't share these headers so this is a magic number. */
+ struct llist_head sapi_cmds;
+ uint8_t sapis_dl[23];
+ uint8_t sapis_ul[23];
+ struct lapdm_channel lapdm_ch;
+ struct llist_head dl_tch_queue;
+ struct {
+ /* bitmask of all SI that are present/valid in si_buf */
+ uint32_t valid;
+ /* bitmask of all SI that do not mirror the BTS-global SI values */
+ uint32_t overridden;
+ uint32_t last;
+ /* buffers where we put the pre-computed SI:
+ SI2Q_MAX_NUM is the max number of SI2quater messages (see 3GPP TS 44.018) */
+ sysinfo_buf_t buf[_MAX_SYSINFO_TYPE][SI2Q_MAX_NUM];
+ } si;
+ struct {
+ uint8_t flags;
+ /* RSL measurement result number, 0 at lchan_act */
+ uint8_t res_nr;
+ /* number of measurements stored in array below */
+ uint8_t num_ul_meas;
+ struct bts_ul_meas uplink[MAX_NUM_UL_MEAS];
+ /* last L1 header from the MS */
+ struct rsl_l1_info l1_info;
+ struct gsm_meas_rep_unidir ul_res;
+ int16_t ms_toa256;
+ int16_t ul_ci_cb_full;
+ int16_t ul_ci_cb_sub;
+ /* Frame number of the last measurement indication receceived */
+ uint32_t last_fn;
+ /* Osmocom extended measurement results, see LC_UL_M_F_EXTD_VALID */
+ struct {
+ /* minimum value of toa256 during measurement period */
+ int16_t toa256_min;
+ /* maximum value of toa256 during measurement period */
+ int16_t toa256_max;
+ /* standard deviation of toa256 value during measurement period */
+ uint16_t toa256_std_dev;
+ } ext;
+ /* Interference levels reported by PHY (in dBm) */
+ int16_t interf_meas_avg_dbm; /* Average value */
+ int16_t interf_meas_dbm[31]; /* Intave max is 31 */
+ uint8_t interf_meas_num;
+ uint8_t interf_band;
+ } meas;
+ struct {
+ struct amr_multirate_conf amr_mr;
+ struct {
+ struct osmo_fsm_inst *dl_amr_fsm;
+ /* TCH cache */
+ uint8_t cache[20];
+ /* FACCH cache */
+ uint8_t facch[GSM_MACBLOCK_LEN];
+ uint8_t len;
+ uint32_t fn;
+ bool is_update;
+ /* set for each SID frame to detect talkspurt for codecs
+ without explicit ONSET event */
+ bool ul_sid;
+ /* indicates if DTXd was active during DL measurement
+ period */
+ bool dl_active;
+ /* last UL SPEECH resume flag */
+ bool is_speech_resume;
+ } dtx;
+ uint8_t last_cmr;
+ uint32_t last_fn;
+
+ } tch;
+
+ /* 3GPP TS 48.058 § 9.3.37: [0; 255] ok, -1 means invalid*/
+ int16_t ms_t_offs;
+ /* 3GPP TS 45.010 § 1.2 round trip propagation delay (in symbols) or -1 */
+ int16_t p_offs;
+
+ /* BTS-side ciphering state (rx only, bi-directional, ...) */
+ uint8_t ciph_state;
+ uint8_t ciph_ns;
+ uint8_t loopback;
+ struct {
+ uint8_t active;
+ uint8_t ref;
+ /* T3105: PHYS INF retransmission */
+ struct osmo_timer_list t3105;
+ /* counts up to Ny1 */
+ unsigned int phys_info_count;
+ } ho;
+ /* S counter for link loss */
+ int s;
+ /* Kind of the release/activation. E.g. RSL or PCU */
+ enum lchan_rel_act_kind rel_act_kind;
+ /* Pending RSL CHANnel ACTIVation message */
+ struct msgb *pending_chan_activ;
+ /* RTP header Marker bit to indicate beginning of speech after pause */
+ bool rtp_tx_marker;
+
+ /* TA Control Loop */
+ struct lchan_ta_ctrl_state ta_ctrl;
+
+ /* MS/BS power control state */
+ struct lchan_power_ctrl_state ms_power_ctrl;
+ struct lchan_power_ctrl_state bs_power_ctrl;
+
+ /* MS/BS Dynamic Power Control parameters */
+ struct gsm_power_ctrl_params ms_dpc_params;
+ struct gsm_power_ctrl_params bs_dpc_params;
+
+ /* Temporary ACCH overpower capabilities and state */
+ struct abis_rsl_osmo_temp_ovp_acch_cap top_acch_cap;
+ bool top_acch_active;
+
+ struct msgb *pending_rel_ind_msg;
+
+ /* ECU (Error Concealment Unit) state */
+ struct osmo_ecu_state *ecu_state;
+
+ /* Repeated ACCH capabilities and current state */
+ struct abis_rsl_osmo_rep_acch_cap rep_acch_cap;
+ struct {
+ bool dl_facch_active;
+ bool ul_sacch_active;
+ bool dl_sacch_active;
+
+ /* Message buffers to store repeation candidates */
+ struct gsm_rep_facch dl_facch[2];
+ struct msgb *dl_sacch_msg;
+ } rep_acch;
+
+ /* Cached early Immediate Assignment message: if the Immediate Assignment arrives before the channel is
+ * confirmed active, then cache it here and send it once the channel is confirmed to be active. This is related
+ * to the Early IA feature, see OsmoBSC config option 'immediate-assignment pre-chan-ack'. */
+ struct msgb *early_rr_ia;
+ struct osmo_timer_list early_rr_ia_delay;
+};
+
+extern const struct value_string lchan_ciph_state_names[];
+static inline const char *lchan_ciph_state_name(uint8_t state)
+{
+ return get_value_string(lchan_ciph_state_names, state);
+}
+
+#define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0])
+
+void gsm_lchan_init(struct gsm_lchan *lchan, struct gsm_bts_trx_ts *ts, unsigned int lchan_nr);
+void gsm_lchan_name_update(struct gsm_lchan *lchan);
+int lchan_init_lapdm(struct gsm_lchan *lchan);
+void gsm_lchan_release(struct gsm_lchan *lchan, enum lchan_rel_act_kind rel_kind);
+int lchan_deactivate(struct gsm_lchan *lchan);
+const char *gsm_lchans_name(enum gsm_lchan_state s);
+
+static inline char *gsm_lchan_name(const struct gsm_lchan *lchan)
+{
+ return lchan->name;
+}
+
+uint8_t *lchan_sacch_get(struct gsm_lchan *lchan);
+
+uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan);
+uint8_t gsm_lchan2chan_nr_rsl(const struct gsm_lchan *lchan);
+uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan,
+ enum gsm_phys_chan_config as_pchan);
+
+void gsm_lchan_interf_meas_push(struct gsm_lchan *lchan, int dbm);
+void gsm_lchan_interf_meas_calc_avg(struct gsm_lchan *lchan);
+
+int lchan2ecu_codec(const struct gsm_lchan *lchan);
+
+void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state);
+
+static inline bool lchan_is_dcch(const struct gsm_lchan *lchan)
+{
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ case GSM_LCHAN_TCH_F:
+ case GSM_LCHAN_TCH_H:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#define lchan_is_tch(lchan) \
+ ((lchan)->type == GSM_LCHAN_TCH_F || (lchan)->type == GSM_LCHAN_TCH_H)
diff --git a/include/osmo-bts/measurement.h b/include/osmo-bts/measurement.h
index 45f275f1..ad86d8de 100644
--- a/include/osmo-bts/measurement.h
+++ b/include/osmo-bts/measurement.h
@@ -20,4 +20,6 @@ bool ts45008_83_is_sub(struct gsm_lchan *lchan, uint32_t fn);
int is_meas_complete(struct gsm_lchan *lchan, uint32_t fn);
+void lchan_meas_handle_sacch(struct gsm_lchan *lchan, struct msgb *msg);
+
#endif
diff --git a/include/osmo-bts/nm_common_fsm.h b/include/osmo-bts/nm_common_fsm.h
index 4679b235..1f0accc5 100644
--- a/include/osmo-bts/nm_common_fsm.h
+++ b/include/osmo-bts/nm_common_fsm.h
@@ -25,12 +25,17 @@
#include <osmocom/core/fsm.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
/* Common */
enum nm_fsm_events {
NM_EV_SW_ACT,
+ NM_EV_SETATTR_ACK, /* data: struct nm_fsm_ev_setattr_data */
+ NM_EV_SETATTR_NACK, /* data: struct nm_fsm_ev_setattr_data */
NM_EV_OPSTART_ACK,
NM_EV_OPSTART_NACK,
+ NM_EV_SHUTDOWN_START,
+ NM_EV_SHUTDOWN_FINISH,
NM_EV_RSL_UP, /* RadioCarrier and BaseBand Transceiver only */
NM_EV_RSL_DOWN, /* RadioCarrier and BaseBand Transceiver only */
NM_EV_PHYLINK_UP, /* RadioCarrier and BaseBand Transceiver only */
@@ -44,6 +49,11 @@ enum nm_fsm_events {
};
extern const struct value_string nm_fsm_event_names[];
+struct nm_fsm_ev_setattr_data {
+ struct msgb *msg; /* msgb ownership is transferred to FSM */
+ int cause;
+};
+
/* BTS SiteManager */
enum nm_bts_sm_op_fsm_states {
diff --git a/include/osmo-bts/oml.h b/include/osmo-bts/oml.h
index 27afc538..90c90770 100644
--- a/include/osmo-bts/oml.h
+++ b/include/osmo-bts/oml.h
@@ -32,6 +32,7 @@ struct gsm_abis_mo {
struct gsm_bts *bts;
/* NM BTS Site Manager FSM */
struct osmo_fsm_inst *fi;
+ bool setattr_success;
bool opstart_success;
};
@@ -47,7 +48,7 @@ int oml_mo_statechg_ack(const struct gsm_abis_mo *mo);
int oml_mo_statechg_nack(const struct gsm_abis_mo *mo, uint8_t nack_cause);
/* Change the state and send STATE CHG REP */
-int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state);
+int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state, int adm_state);
/* First initialization of MO, does _not_ generate state changes */
void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state);
diff --git a/include/osmo-bts/pcu_if.h b/include/osmo-bts/pcu_if.h
index 12a8abc9..bc30f341 100644
--- a/include/osmo-bts/pcu_if.h
+++ b/include/osmo-bts/pcu_if.h
@@ -19,8 +19,7 @@ int pcu_tx_rach_ind(uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
int16_t qta, uint16_t ra, uint32_t fn, uint8_t is_11bit,
enum ph_burst_type burst_type, uint8_t sapi);
int pcu_tx_time_ind(uint32_t fn);
-int pcu_tx_interf_ind(uint8_t bts_nr, uint8_t trx_nr, uint32_t fn,
- const uint8_t *pdch_interf);
+int pcu_tx_interf_ind(const struct gsm_bts_trx *trx, uint32_t fn);
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed);
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len);
int pcu_tx_susp_req(struct gsm_lchan *lchan, uint32_t tlli, const uint8_t *ra_id, uint8_t cause);
diff --git a/include/osmo-bts/phy_link.h b/include/osmo-bts/phy_link.h
index 4b8a8633..862ed48f 100644
--- a/include/osmo-bts/phy_link.h
+++ b/include/osmo-bts/phy_link.h
@@ -50,7 +50,8 @@ struct phy_link {
bool use_legacy_setbsic;
uint8_t trxd_pdu_ver_max; /* Maximum TRXD PDU version to negotiate */
bool powered; /* last POWERON (true) or POWEROFF (false) confirmed */
- bool poweronoff_sent; /* is there a POWERON/POWEROFF in transit? (one or the other based on ->powered) */
+ bool poweron_sent; /* is there a POWERON in transit? */
+ bool poweroff_sent; /* is there a POWEROFF in transit? */
} osmotrx;
struct {
char *mcast_dev; /* Network device for multicast */
@@ -112,7 +113,6 @@ struct phy_instance {
} sysmobts;
struct {
struct trx_l1h *hdl;
- bool sw_act_reported;
struct trx_dl_burst_req br[TRX_NR_TS];
} osmotrx;
struct {
@@ -175,6 +175,7 @@ static inline struct phy_instance *trx_phy_instance(const struct gsm_bts_trx *tr
}
int bts_model_phy_link_open(struct phy_link *plink);
+int bts_model_phy_link_close(struct phy_link *plink);
#define LOGPPHL(plink, section, lvl, fmt, args...) LOGP(section, lvl, "%s: " fmt, phy_link_name(plink), ##args)
#define LOGPPHI(pinst, section, lvl, fmt, args...) LOGP(section, lvl, "%s: " fmt, phy_instance_name(pinst), ##args)
diff --git a/include/osmo-bts/power_control.h b/include/osmo-bts/power_control.h
index f2e14cfe..0764ba76 100644
--- a/include/osmo-bts/power_control.h
+++ b/include/osmo-bts/power_control.h
@@ -1,11 +1,90 @@
#pragma once
#include <stdint.h>
-#include <osmo-bts/gsm_data.h>
+#include <stdbool.h>
+/* MS/BS Power related measurement averaging algo */
+enum gsm_power_ctrl_meas_avg_algo {
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE = 0x00,
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED = 0x01,
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED = 0x02,
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN = 0x03,
+ /* EWMA is an Osmocom specific algo */
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA = 0x04,
+};
+
+/* MS/BS Power related measurement parameters */
+struct gsm_power_ctrl_meas_params {
+ /* Thresholds (see 3GPP TS 45.008, section A.3.2.1) */
+ uint8_t lower_thresh; /* lower (decreasing) direction */
+ uint8_t upper_thresh; /* upper (increasing) direction */
+
+ /* Threshold Comparators for lower (decreasing) direction */
+ uint8_t lower_cmp_p; /* P1 for RxLev, P3 for RxQual */
+ uint8_t lower_cmp_n; /* N1 for RxLev, N3 for RxQual */
+ /* Threshold Comparators for upper (increasing) direction */
+ uint8_t upper_cmp_p; /* P2 for RxLev, P4 for RxQual */
+ uint8_t upper_cmp_n; /* N2 for RxLev, N4 for RxQual */
+
+ /* Hreqave and Hreqt (see 3GPP TS 45.008, Annex A) */
+ uint8_t h_reqave;
+ uint8_t h_reqt;
+
+ /* AVG algorithm and its specific parameters */
+ enum gsm_power_ctrl_meas_avg_algo algo;
+ union {
+ /* Exponentially Weighted Moving Average */
+ struct {
+ /* Smoothing factor: higher the value - less smoothing */
+ uint8_t alpha; /* 1 .. 99 (in %) */
+ } ewma;
+ };
+};
+
+/* MS/BS Power Control parameters */
+struct gsm_power_ctrl_params {
+ /* Minimum interval between power level changes */
+ uint8_t ctrl_interval; /* 1 step is 2 SACCH periods */
+
+ /* Power change step size (maximum) */
+ uint8_t inc_step_size_db; /* increasing direction */
+ uint8_t red_step_size_db; /* reducing direction */
+
+ /* Measurement averaging parameters for RxLev & RxQual */
+ struct gsm_power_ctrl_meas_params rxqual_meas;
+ struct gsm_power_ctrl_meas_params rxlev_meas;
+
+ /* Measurement averaging parameters for C/I, per chan type */
+ struct gsm_power_ctrl_meas_params ci_fr_meas;
+ struct gsm_power_ctrl_meas_params ci_hr_meas;
+ struct gsm_power_ctrl_meas_params ci_amr_fr_meas;
+ struct gsm_power_ctrl_meas_params ci_amr_hr_meas;
+ struct gsm_power_ctrl_meas_params ci_sdcch_meas;
+ struct gsm_power_ctrl_meas_params ci_gprs_meas;
+};
+
+/* Measurement pre-processing state */
+struct gsm_power_ctrl_meas_proc_state {
+ /* Number of measurements processed */
+ unsigned int meas_num;
+ /* Algorithm specific data */
+ union {
+ struct {
+ /* Scaled up 100 times average value */
+ int Avg100;
+ } ewma;
+ };
+};
+
+/* Default MS/BS Power Control parameters */
+extern const struct gsm_power_ctrl_params power_ctrl_params_def;
+void power_ctrl_params_def_reset(struct gsm_power_ctrl_params *params, bool is_bs_pwr);
+
+struct gsm_lchan;
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
const uint8_t ms_power_lvl,
- const int8_t ul_rssi_dbm);
+ const int8_t ul_rssi_dbm,
+ const int16_t ul_lqual_cb);
int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
- const struct gsm48_hdr *gh);
+ const struct gsm48_meas_res *mr);
diff --git a/include/osmo-bts/rsl.h b/include/osmo-bts/rsl.h
index 4e79de5e..dcd476c5 100644
--- a/include/osmo-bts/rsl.h
+++ b/include/osmo-bts/rsl.h
@@ -17,8 +17,6 @@ int rsl_tx_conn_fail(const 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);
@@ -34,6 +32,6 @@ void ipacc_dyn_pdch_complete(struct gsm_bts_trx_ts *ts, int rc);
int rsl_tx_cbch_load_indication(struct gsm_bts *bts, bool ext_cbch, bool overflow, uint8_t amount);
-int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const struct lapdm_entity *le);
+int rsl_tx_meas_res(struct gsm_lchan *lchan, const uint8_t *l3, int l3_len, int timing_offset);
#endif // _RSL_H */
diff --git a/include/osmo-bts/scheduler.h b/include/osmo-bts/scheduler.h
index 80a260fe..d6406474 100644
--- a/include/osmo-bts/scheduler.h
+++ b/include/osmo-bts/scheduler.h
@@ -128,6 +128,8 @@ struct l1sched_chan_state {
struct l1sched_meas_set meas_avg_facch; /* measurement results for last FACCH */
uint16_t ber10k_facch; /* bit error rate for last FACCH */
+ uint8_t dl_facch_bursts; /* number of remaining DL FACCH bursts */
+
/* encryption */
int ul_encr_algo; /* A5/x encry algo downlink */
int dl_encr_algo; /* A5/x encry algo uplink */
@@ -272,6 +274,8 @@ struct trx_ul_burst_ind {
size_t burst_len;
};
+#define TRX_BR_F_FACCH (1 << 0)
+
/*! DL burst request with the corresponding meta info */
struct trx_dl_burst_req {
uint8_t flags; /*!< see TRX_BR_F_* */
diff --git a/include/osmo-bts/scheduler_backend.h b/include/osmo-bts/scheduler_backend.h
index 50ba8228..3b1388f4 100644
--- a/include/osmo-bts/scheduler_backend.h
+++ b/include/osmo-bts/scheduler_backend.h
@@ -51,7 +51,7 @@ int _sched_compose_ph_data_ind(struct l1sched_ts *l1ts, uint32_t fn,
int _sched_compose_tch_ind(struct l1sched_ts *l1ts, uint32_t fn,
enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len,
int16_t ta_offs_256bits, uint16_t ber10k, float rssi,
- uint8_t is_sub);
+ int16_t link_qual_cb, uint8_t is_sub);
int tx_fcch_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br);
int tx_sch_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br);
diff --git a/include/osmo-bts/signal.h b/include/osmo-bts/signal.h
index c8168a26..8359f021 100644
--- a/include/osmo-bts/signal.h
+++ b/include/osmo-bts/signal.h
@@ -15,4 +15,10 @@ enum signals_global {
S_NEW_NSVC_ATTR,
};
+struct nm_statechg_signal_data {
+ struct gsm_abis_mo *mo;
+ uint8_t old_state;
+ uint8_t new_state;
+};
+
#endif
diff --git a/include/osmo-bts/ta_control.h b/include/osmo-bts/ta_control.h
index 168f14a7..bf993319 100644
--- a/include/osmo-bts/ta_control.h
+++ b/include/osmo-bts/ta_control.h
@@ -2,4 +2,4 @@
#include <osmo-bts/gsm_data.h>
-void lchan_ms_ta_ctrl(struct gsm_lchan *lchan);
+void lchan_ms_ta_ctrl(struct gsm_lchan *lchan, uint8_t ms_tx_ta, int16_t toa256);
diff --git a/include/osmo-bts/tx_power.h b/include/osmo-bts/tx_power.h
index 8f68d8a5..a7f846e8 100644
--- a/include/osmo-bts/tx_power.h
+++ b/include/osmo-bts/tx_power.h
@@ -78,6 +78,7 @@ int get_p_trxout_actual_mdBm(const struct gsm_bts_trx *trx, uint8_t bs_power_red
int get_p_trxout_actual_mdBm_lchan(const struct gsm_lchan *lchan);
int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass, ramp_compl_cb_t ramp_compl_cb);
+void power_ramp_abort(struct gsm_bts_trx *trx);
void power_trx_change_compl(struct gsm_bts_trx *trx, int p_trxout_cur_mdBm);
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 72438c67..35df73e6 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -1,6 +1,21 @@
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOCODEC_CFLAGS)
-LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOCODEC_LIBS)
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOTRAU_CFLAGS) \
+ $(LIBOSMOCODEC_CFLAGS) \
+ $(NULL)
+
+LDADD = \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOTRAU_LIBS) \
+ $(LIBOSMOCODEC_LIBS) \
+ $(NULL)
if ENABLE_LC15BTS
AM_CFLAGS += -DENABLE_LC15BTS
@@ -44,6 +59,18 @@ libbts_a_SOURCES = \
nm_bb_transc_fsm.c \
nm_channel_fsm.c \
nm_radio_carrier_fsm.c \
+ probes.d \
$(NULL)
libl1sched_a_SOURCES = scheduler.c
+
+if ENABLE_SYSTEMTAP
+probes.h: probes.d
+ $(DTRACE) -C -h -s $< -o $@
+
+probes.lo: probes.d
+ $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -C -G -s $< -o $@
+
+BUILT_SOURCES = probes.h probes.lo
+libbts_la_LDADD = probes.lo
+endif
diff --git a/src/common/abis.c b/src/common/abis.c
index abef8264..4afe81af 100644
--- a/src/common/abis.c
+++ b/src/common/abis.c
@@ -38,11 +38,13 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/macaddr.h>
+#include <osmocom/core/fsm.h>
#include <osmocom/abis/abis.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/ipa.h>
+#include <osmo-bts/abis.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/bts.h>
@@ -51,35 +53,296 @@
#include <osmo-bts/abis_osmo.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/bts_trx.h>
+#include <osmo-bts/bts_shutdown_fsm.h>
static struct gsm_bts *g_bts;
-int abis_oml_sendmsg(struct msgb *msg)
+static struct e1inp_line_ops line_ops;
+
+static struct ipaccess_unit bts_dev_info;
+
+#define S(x) (1 << (x))
+#define OML_RETRY_TIMER 5
+
+enum abis_link_fsm_state {
+ ABIS_LINK_ST_WAIT_RECONNECT, /* OML link has not yet been established */
+ ABIS_LINK_ST_CONNECTING, /* OML link in process of been established */
+ ABIS_LINK_ST_CONNECTED, /* OML link is established, RSL links may be established or not */
+ ABIS_LINK_ST_FAILED, /* There used to be an active OML connection but it became broken */
+};
+
+static const struct value_string abis_link_fsm_event_names[] = {
+ { ABIS_LINK_EV_SIGN_LINK_OML_UP, "SIGN_LINK_OML_UP", },
+ { ABIS_LINK_EV_SIGN_LINK_DOWN, "SIGN_LINK_DOWN" },
+ { ABIS_LINK_EV_VTY_RM_ADDR, "VTY_RM_ADDR" },
+ {}
+};
+
+struct abis_link_fsm_priv {
+ struct bsc_oml_host *current_bsc;
+ struct gsm_bts *bts;
+ char *model_name;
+ bool reconnect_to_current_bsc;
+};
+
+static void reset_oml_link(struct gsm_bts *bts)
{
- struct gsm_bts *bts = msg->trx->bts;
+ if (bts->oml_link) {
+ struct timespec now;
- if (!bts->oml_link) {
- llist_add_tail(&msg->list, &bts->oml_queue);
+ e1inp_sign_link_destroy(bts->oml_link);
+
+ /* Log a special notice if the OML connection was dropped relatively quickly. */
+ if (bts->oml_conn_established_timestamp.tv_sec != 0 && clock_gettime(CLOCK_MONOTONIC, &now) == 0 &&
+ bts->oml_conn_established_timestamp.tv_sec + OSMO_BTS_OML_CONN_EARLY_DISCONNECT >= now.tv_sec) {
+ LOGP(DABIS, LOGL_FATAL, "OML link was closed early within %" PRIu64 " seconds. "
+ "If this situation persists, please check your BTS and BSC configuration files for errors. "
+ "A common error is a mismatch between unit_id configuration parameters of BTS and BSC.\n",
+ (uint64_t) (now.tv_sec - bts->oml_conn_established_timestamp.tv_sec));
+ }
+ bts->oml_link = NULL;
+ }
+ memset(&bts->oml_conn_established_timestamp, 0, sizeof(bts->oml_conn_established_timestamp));
+
+ /* Same for IPAC_PROTO_OSMO on the same ipa connection: */
+ if (bts->osmo_link) {
+ e1inp_sign_link_destroy(bts->osmo_link);
+ bts->osmo_link = NULL;
+ }
+
+}
+
+static int pick_next_bsc(struct osmo_fsm_inst *fi)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+ struct bsc_oml_host *last;
+
+ if (llist_empty(&bts->bsc_oml_hosts)) {
+ LOGPFSML(fi, LOGL_ERROR, "List of BSCs to connect to is empty!\n");
+ return -1;
+ }
+
+ /* Keep current pointer to priv->current_bsc: */
+ if (priv->reconnect_to_current_bsc) {
+ OSMO_ASSERT(priv->current_bsc);
+ priv->reconnect_to_current_bsc = false;
return 0;
- } else {
- /* osmo-bts uses msg->trx internally, but libosmo-abis uses
- * the signalling link at msg->dst */
- msg->dst = bts->oml_link;
- return abis_sendmsg(msg);
+ }
+
+ last = (struct bsc_oml_host *)llist_last_entry(&bts->bsc_oml_hosts, struct bsc_oml_host, list);
+
+ if (!priv->current_bsc || priv->current_bsc == last) /* Pick first one (wrap around): */
+ priv->current_bsc = (struct bsc_oml_host *)llist_first_entry(&bts->bsc_oml_hosts, struct bsc_oml_host, list);
+ else
+ priv->current_bsc = (struct bsc_oml_host *)llist_entry(priv->current_bsc->list.next, struct bsc_oml_host, list);
+
+ return 0;
+}
+
+static void abis_link_connecting_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct e1inp_line *line;
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+
+ if (bts_shutdown_in_progress(bts)) {
+ LOGPFSML(fi, LOGL_NOTICE, "BTS is shutting down, delaying A-bis connection establishment to BSC\n");
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_WAIT_RECONNECT, OML_RETRY_TIMER, 0);
+ return;
+ }
+
+ if (pick_next_bsc(fi) < 0) {
+ LOGPFSML(fi, LOGL_FATAL, "No BSC available, A-bis connection establishment failed\n");
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_FAILED, 0, 0);
+ return;
+ }
+
+ LOGP(DABIS, LOGL_NOTICE, "A-bis connection establishment to BSC (%s) in progress...\n", priv->current_bsc->addr);
+
+ /* patch in various data from VTY and other sources */
+ line_ops.cfg.ipa.addr = priv->current_bsc->addr;
+ osmo_get_macaddr(bts_dev_info.mac_addr, "eth0");
+ bts_dev_info.site_id = bts->ip_access.site_id;
+ bts_dev_info.bts_id = bts->ip_access.bts_id;
+ bts_dev_info.unit_name = priv->model_name;
+ if (bts->description)
+ bts_dev_info.unit_name = bts->description;
+ bts_dev_info.location2 = priv->model_name;
+
+ line = e1inp_line_find(0);
+ if (!line)
+ line = e1inp_line_create(0, "ipa");
+ if (!line) {
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_FAILED, 0, 0);
+ return;
+ }
+ /* Line always comes already with a "ctor" reference, enough to keep it alive forever. */
+
+ e1inp_line_bind_ops(line, &line_ops);
+ /* This will open the OML connection now */
+ if (e1inp_line_update(line) < 0) {
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_FAILED, 0, 0);
+ return;
+ }
+
+ /* The TCP connection to the BSC is now in progress.
+ * Wait for OML Link UP to transition to CONNECTED. */
+}
+
+static void abis_link_connecting(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+
+ switch (event) {
+ case ABIS_LINK_EV_SIGN_LINK_OML_UP:
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_CONNECTED, 0, 0);
+ break;
+ case ABIS_LINK_EV_SIGN_LINK_DOWN:
+ reset_oml_link(bts);
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_WAIT_RECONNECT, OML_RETRY_TIMER, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void abis_link_connected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ bts_link_estab(g_bts);
+}
+
+static void abis_link_connected(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+ struct gsm_bts_trx *trx;
+ OSMO_ASSERT(event == ABIS_LINK_EV_SIGN_LINK_DOWN);
+
+ /* First remove the OML signalling link */
+ reset_oml_link(bts);
+
+ /* Then iterate over the RSL signalling links */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx->rsl_link) {
+ e1inp_sign_link_destroy(trx->rsl_link);
+ trx->rsl_link = NULL;
+ if (trx == trx->bts->c0)
+ load_timer_stop(trx->bts);
+ }
+ /* Note: Here we could send NM_EV_RSL_DOWN to each
+ * trx->(bb_transc.)mo.fi, but we are starting shutdown of the
+ * entire BTS anyway through bts_model_abis_close(), so simply
+ * let bts_shutdown FSM take care of slowly powering down all
+ * the TRX. It would make sense to send NM_EV_RSL_DOWN only if a
+ * RSL link TRX!=C0 was going down, in order to selectively stop
+ * that TRX only. But libosmo-abis expects us to drop the entire
+ * line when something goes wrong... */
+ }
+ bts_model_abis_close(bts);
+
+ /* We want to try reconnecting to the current BSC at least once before switching to a new one: */
+ priv->reconnect_to_current_bsc = true;
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_WAIT_RECONNECT, OML_RETRY_TIMER, 0);
+}
+
+static void abis_link_failed_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+
+ /* None of the configured BSCs was reachable or there was an existing
+ * OML/RSL connection that broke. Initiate BTS process shut down now. */
+ bts_model_abis_close(bts);
+}
+
+static void abis_link_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+
+ OSMO_ASSERT(event == ABIS_LINK_EV_VTY_RM_ADDR);
+
+ if (priv->current_bsc == data) {
+ if (llist_count(&bts->bsc_oml_hosts) <= 1)
+ priv->current_bsc = NULL;
+ else
+ pick_next_bsc(fi);
}
}
-static void drain_oml_queue(struct gsm_bts *bts)
+int abis_link_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
- struct msgb *msg, *msg2;
-
- llist_for_each_entry_safe(msg, msg2, &bts->oml_queue, list) {
- /* osmo-bts uses msg->trx internally, but libosmo-abis uses
- * the signalling link at msg->dst */
- llist_del(&msg->list);
- msg->dst = bts->oml_link;
- abis_sendmsg(msg);
+ switch (fi->state) {
+ case ABIS_LINK_ST_WAIT_RECONNECT:
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_CONNECTING, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
}
+ return 0;
+}
+
+
+static struct osmo_fsm_state abis_link_fsm_states[] = {
+ [ABIS_LINK_ST_WAIT_RECONNECT] = {
+ .name = "WAIT_RECONNECT",
+ .out_state_mask =
+ S(ABIS_LINK_ST_CONNECTING),
+ },
+ [ABIS_LINK_ST_CONNECTING] = {
+ .name = "CONNECTING",
+ .in_event_mask =
+ S(ABIS_LINK_EV_SIGN_LINK_OML_UP) |
+ S(ABIS_LINK_EV_SIGN_LINK_DOWN),
+ .out_state_mask =
+ S(ABIS_LINK_ST_WAIT_RECONNECT) |
+ S(ABIS_LINK_ST_CONNECTED) |
+ S(ABIS_LINK_ST_FAILED),
+ .onenter = abis_link_connecting_onenter,
+ .action = abis_link_connecting,
+ },
+ [ABIS_LINK_ST_CONNECTED] = {
+ .name = "CONNECTED",
+ .in_event_mask =
+ S(ABIS_LINK_EV_SIGN_LINK_DOWN),
+ .out_state_mask =
+ S(ABIS_LINK_ST_WAIT_RECONNECT),
+ .onenter = abis_link_connected_onenter,
+ .action = abis_link_connected,
+ },
+ [ABIS_LINK_ST_FAILED] = {
+ .name = "FAILED",
+ .onenter = abis_link_failed_onenter,
+ },
+};
+
+static struct osmo_fsm abis_link_fsm = {
+ .name = "abis_link",
+ .states = abis_link_fsm_states,
+ .num_states = ARRAY_SIZE(abis_link_fsm_states),
+ .log_subsys = DABIS,
+ .event_names = abis_link_fsm_event_names,
+ .allstate_action = abis_link_allstate,
+ .allstate_event_mask = S(ABIS_LINK_EV_VTY_RM_ADDR),
+ .timer_cb = abis_link_fsm_timer_cb,
+};
+
+int abis_oml_sendmsg(struct msgb *msg)
+{
+ struct gsm_bts *bts = msg->trx->bts;
+
+ if (!bts->oml_link) {
+ LOGP(DABIS, LOGL_INFO, "Drop Tx OML msg, OML link is down\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ /* osmo-bts uses msg->trx internally, but libosmo-abis uses
+ * the signalling link at msg->dst */
+ msg->dst = bts->oml_link;
+ return abis_sendmsg(msg);
}
int abis_bts_rsl_sendmsg(struct msgb *msg)
@@ -116,8 +379,7 @@ static struct e1inp_sign_link *sign_link_up(void *unit, struct e1inp_line *line,
sizeof(g_bts->oml_conn_established_timestamp));
g_bts->osmo_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OSMO,
g_bts->c0, IPAC_PROTO_OSMO, 0);
- drain_oml_queue(g_bts);
- bts_link_estab(g_bts);
+ osmo_fsm_inst_dispatch(g_bts->abis_link_fi, ABIS_LINK_EV_SIGN_LINK_OML_UP, NULL);
return g_bts->oml_link;
case E1INP_SIGN_RSL:
@@ -144,41 +406,8 @@ static struct e1inp_sign_link *sign_link_up(void *unit, struct e1inp_line *line,
static void sign_link_down(struct e1inp_line *line)
{
- struct gsm_bts_trx *trx;
LOGPIL(line, DABIS, LOGL_ERROR, "Signalling link down\n");
-
- /* First remove the OML signalling link */
- if (g_bts->oml_link) {
- struct timespec now;
-
- e1inp_sign_link_destroy(g_bts->oml_link);
-
- /* Log a special notice if the OML connection was dropped relatively quickly. */
- if (g_bts->oml_conn_established_timestamp.tv_sec != 0 && clock_gettime(CLOCK_MONOTONIC, &now) == 0 &&
- g_bts->oml_conn_established_timestamp.tv_sec + OSMO_BTS_OML_CONN_EARLY_DISCONNECT >= now.tv_sec) {
- LOGP(DABIS, LOGL_FATAL, "OML link was closed early within %" PRIu64 " seconds. "
- "If this situation persists, please check your BTS and BSC configuration files for errors. "
- "A common error is a mismatch between unit_id configuration parameters of BTS and BSC.\n",
- (uint64_t)(now.tv_sec - g_bts->oml_conn_established_timestamp.tv_sec));
- }
- g_bts->oml_link = NULL;
- }
- memset(&g_bts->oml_conn_established_timestamp, 0, sizeof(g_bts->oml_conn_established_timestamp));
-
- if (g_bts->osmo_link) {
- e1inp_sign_link_destroy(g_bts->osmo_link);
- g_bts->osmo_link = NULL;
- }
-
- /* Then iterate over the RSL signalling links */
- llist_for_each_entry(trx, &g_bts->trx_list, list) {
- if (trx->rsl_link) {
- e1inp_sign_link_destroy(trx->rsl_link);
- trx->rsl_link = NULL;
- }
- }
-
- bts_model_abis_close(g_bts);
+ osmo_fsm_inst_dispatch(g_bts->abis_link_fi, ABIS_LINK_EV_SIGN_LINK_DOWN, NULL);
}
@@ -277,33 +506,30 @@ void abis_init(struct gsm_bts *bts)
osmo_signal_register_handler(SS_L_INPUT, &inp_s_cbfn, bts);
}
-struct e1inp_line *abis_open(struct gsm_bts *bts, char *dst_host,
- char *model_name)
+int abis_open(struct gsm_bts *bts, char *model_name)
{
- struct e1inp_line *line;
+ struct abis_link_fsm_priv *abis_link_fsm_priv;
- /* patch in various data from VTY and other sources */
- line_ops.cfg.ipa.addr = dst_host;
- osmo_get_macaddr(bts_dev_info.mac_addr, "eth0");
- bts_dev_info.site_id = bts->ip_access.site_id;
- bts_dev_info.bts_id = bts->ip_access.bts_id;
- bts_dev_info.unit_name = model_name;
- if (bts->description)
- bts_dev_info.unit_name = bts->description;
- bts_dev_info.location2 = model_name;
+ if (llist_empty(&bts->bsc_oml_hosts)) {
+ LOGP(DABIS, LOGL_FATAL, "No BSC configured, cannot start BTS without knowing BSC OML IP\n");
+ return -EINVAL;
+ }
- line = e1inp_line_find(0);
- if (line) {
- e1inp_line_get2(line, __FILE__); /* We want a new reference for returned line */
- } else
- line = e1inp_line_create(0, "ipa"); /* already comes with a reference */
- if (!line)
- return NULL;
- e1inp_line_bind_ops(line, &line_ops);
+ bts->abis_link_fi = osmo_fsm_inst_alloc(&abis_link_fsm, bts, NULL, LOGL_DEBUG, "abis_link");
+ OSMO_ASSERT(bts->abis_link_fi);
- /* This will open the OML connection now */
- if (e1inp_line_update(line) < 0)
- return NULL;
+ abis_link_fsm_priv = talloc_zero(bts->abis_link_fi, struct abis_link_fsm_priv);
+ OSMO_ASSERT(abis_link_fsm_priv);
+ abis_link_fsm_priv->bts = bts;
+ abis_link_fsm_priv->model_name = model_name;
+ bts->abis_link_fi->priv = abis_link_fsm_priv;
+
+ osmo_fsm_inst_state_chg(bts->abis_link_fi, ABIS_LINK_ST_CONNECTING, 0, 0);
- return line;
+ return 0;
+}
+
+static __attribute__((constructor)) void abis_link_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&abis_link_fsm) == 0);
}
diff --git a/src/common/bts.c b/src/common/bts.c
index 27c7f74c..93fb4007 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -128,7 +128,7 @@ static const struct rate_ctr_group_desc cbch_ctrg_desc = {
cbch_ctr_desc
};
-static struct osmo_tdef bts_T_defs[] = {
+struct osmo_tdef bts_T_defs[] = {
/* T-1: FIXME: Ideally should be dynamically calculated per trx at
* shutdown start based on params below, and highest trx value taken:
* + VTY's power-ramp step-interval.
@@ -143,6 +143,11 @@ static struct osmo_tdef bts_T_defs[] = {
{}
};
+struct osmo_tdef abis_T_defs[] = {
+ { .T=-15, .default_val=0, .unit=OSMO_TDEF_MS, .desc="Time to wait between Channel Activation and dispatching a cached early Immediate Assignment" },
+ {}
+};
+
static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 };
static const uint8_t bts_cell_timer_default[] =
{ 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 };
@@ -214,6 +219,10 @@ static int gsm_bts_talloc_destructor(struct gsm_bts *bts)
osmo_fsm_inst_free(bts->mo.fi);
bts->mo.fi = NULL;
}
+ if (bts->shutdown_fi) {
+ osmo_fsm_inst_free(bts->shutdown_fi);
+ bts->shutdown_fi = NULL;
+ }
return 0;
}
@@ -234,6 +243,7 @@ struct gsm_bts *gsm_bts_alloc(void *ctx, uint8_t bts_num)
bts->T_defs = bts_T_defs;
osmo_tdefs_reset(bts->T_defs);
+ osmo_tdefs_reset(abis_T_defs);
bts->shutdown_fi = osmo_fsm_inst_alloc(&bts_shutdown_fsm, bts, bts,
LOGL_INFO, NULL);
osmo_fsm_inst_update_id_f(bts->shutdown_fi, "bts%d", bts->nr);
@@ -334,12 +344,11 @@ int bts_init(struct gsm_bts *bts)
bts->rtp_priority = -1;
/* Default (fall-back) MS/BS Power control parameters */
- bts->bs_dpc_params = power_ctrl_params_def;
- bts->ms_dpc_params = power_ctrl_params_def;
+ power_ctrl_params_def_reset(&bts->bs_dpc_params, true);
+ power_ctrl_params_def_reset(&bts->ms_dpc_params, false);
/* configurable via OML */
bts->load.ccch.load_ind_period = 112;
- load_timer_start(bts);
bts->rtp_jitter_buf_ms = 100;
bts->max_ta = 63;
bts->ny1 = 4;
@@ -401,7 +410,7 @@ int bts_init(struct gsm_bts *bts)
bts->smscb_queue_tgt_len = 2;
bts->smscb_queue_hyst = 2;
- INIT_LLIST_HEAD(&bts->oml_queue);
+ INIT_LLIST_HEAD(&bts->bsc_oml_hosts);
/* register DTX DL FSM */
rc = osmo_fsm_register(&dtx_dl_amr_fsm);
@@ -449,66 +458,6 @@ int bts_link_estab(struct gsm_bts *bts)
return bts_model_oml_estab(bts);
}
-/* prepare the per-SAPI T200 arrays for a given lchan */
-static int t200_by_lchan(int *t200_ms_dcch, int *t200_ms_acch, struct gsm_lchan *lchan)
-{
- struct gsm_bts *bts = lchan->ts->trx->bts;
-
- /* we have to compensate for the "RTS advance" due to the asynchronous interface between
- * the BTS (LAPDm) and the PHY/L1 (OsmoTRX or DSP in case of osmo-bts-{sysmo,lc15,oc2g,octphy} */
- int32_t fn_advance = bts_get_avg_fn_advance(bts);
- int32_t fn_advance_us = fn_advance * 4615;
- int fn_advance_ms = fn_advance_us / 1000;
-
- t200_ms_acch[DL_SAPI0] = bts->t200_ms[T200_SACCH_SDCCH] + fn_advance_ms;
- t200_ms_acch[DL_SAPI3] = bts->t200_ms[T200_SACCH_SDCCH] + fn_advance_ms;
-
- if (lchan->repeated_acch_capability.dl_facch_all && (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)) {
- t200_ms_acch[DL_SAPI0] *= 2;
- t200_ms_acch[DL_SAPI3] *= 2;
- }
-
- switch (lchan->type) {
- case GSM_LCHAN_SDCCH:
- t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_SDCCH] + fn_advance_ms;
- t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_SDCCH_SAPI3] + fn_advance_ms;
- break;
- case GSM_LCHAN_TCH_F:
- t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_FACCH_F] + fn_advance_ms;
- t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_FACCH_F] + fn_advance_ms;
- break;
- case GSM_LCHAN_TCH_H:
- t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_FACCH_H] + fn_advance_ms;
- t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_FACCH_H] + fn_advance_ms;
- break;
- default:
- /* Channels such as CCCH don't use lapdm DL, and hence no T200 is needed */
- return -1;
- }
- return 0;
-}
-
-int lchan_init_lapdm(struct gsm_lchan *lchan)
-{
- struct lapdm_channel *lc = &lchan->lapdm_ch;
- int t200_ms_dcch[_NR_DL_SAPI], t200_ms_acch[_NR_DL_SAPI];
-
- if (t200_by_lchan(t200_ms_dcch, t200_ms_acch, lchan) == 0) {
- LOGPLCHAN(lchan, DLLAPD, LOGL_DEBUG,
- "Setting T200 D0=%u, D3=%u, S0=%u, S3=%u (all in ms)\n",
- t200_ms_dcch[DL_SAPI0], t200_ms_dcch[DL_SAPI3],
- t200_ms_acch[DL_SAPI0], t200_ms_acch[DL_SAPI3]);
- lapdm_channel_init3(lc, LAPDM_MODE_BTS, t200_ms_dcch, t200_ms_acch, lchan->type,
- gsm_lchan_name(lchan));
- lapdm_channel_set_flags(lc, LAPDM_ENT_F_POLLING_ONLY);
- lapdm_channel_set_l1(lc, NULL, lchan);
- }
- /* We still need to set Rx callback to receive RACH requests: */
- lapdm_channel_set_l3(lc, lapdm_rll_tx_cb, lchan);
-
- return 0;
-}
-
#define CCCH_RACH_RATIO_COMBINED256 (256*1/9)
#define CCCH_RACH_RATIO_SEPARATE256 (256*10/55)
diff --git a/src/common/bts_shutdown_fsm.c b/src/common/bts_shutdown_fsm.c
index 0ac30789..0c6d80c1 100644
--- a/src/common/bts_shutdown_fsm.c
+++ b/src/common/bts_shutdown_fsm.c
@@ -29,6 +29,7 @@
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/bts.h>
+#include <osmo-bts/nm_common_fsm.h>
#define X(s) (1 << (s))
@@ -58,6 +59,9 @@ static void st_none(struct osmo_fsm_inst *fi, uint32_t event, void *data)
unsigned int count;
switch(event) {
case BTS_SHUTDOWN_EV_START:
+ /* Firt announce to NM objects that we are starting a shutdown procedure: */
+ osmo_fsm_inst_dispatch(bts->site_mgr.mo.fi, NM_EV_SHUTDOWN_START, NULL);
+
count = count_trx_operational(bts);
if (count) {
bts_shutdown_fsm_state_chg(fi, BTS_SHUTDOWN_ST_WAIT_RAMP_DOWN_COMPL);
@@ -108,8 +112,15 @@ static void st_wait_ramp_down_compl(struct osmo_fsm_inst *fi, uint32_t event, vo
LOGPFSML(fi, LOGL_INFO, "%s Ramping down complete, %u TRX remaining\n",
gsm_trx_name(src_trx), remaining);
- if (remaining == 0)
+ if (remaining == 0) {
+ /* Make sure we end up any remaining ongoing power ramp
+ * down under target shutdown tx power level, then
+ * finally transit to next state:
+ */
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ power_ramp_abort(trx);
bts_shutdown_fsm_state_chg(fi, BTS_SHUTDOWN_ST_WAIT_TRX_CLOSED);
+ }
break;
}
}
@@ -148,8 +159,15 @@ static void st_wait_trx_closed(struct osmo_fsm_inst *fi, uint32_t event, void *d
static void st_exit_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
- LOGPFSML(fi, LOGL_NOTICE, "Shutdown process completed successfuly, exiting process\n");
- exit(0);
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+
+ osmo_fsm_inst_dispatch(bts->site_mgr.mo.fi, NM_EV_SHUTDOWN_FINISH, NULL);
+
+ if (bts->shutdown_fi_exit_proc) {
+ LOGPFSML(fi, LOGL_NOTICE, "Shutdown process completed successfully, exiting process\n");
+ exit(0);
+ }
+ bts_shutdown_fsm_state_chg(fi, BTS_SHUTDOWN_ST_NONE);
}
static struct osmo_fsm_state bts_shutdown_fsm_states[] = {
@@ -182,6 +200,8 @@ static struct osmo_fsm_state bts_shutdown_fsm_states[] = {
},
[BTS_SHUTDOWN_ST_EXIT] = {
.name = "EXIT",
+ .out_state_mask =
+ X(BTS_SHUTDOWN_ST_NONE),
.onenter = st_exit_on_enter,
}
};
@@ -189,7 +209,7 @@ static struct osmo_fsm_state bts_shutdown_fsm_states[] = {
const struct value_string bts_shutdown_fsm_event_names[] = {
OSMO_VALUE_STRING(BTS_SHUTDOWN_EV_START),
OSMO_VALUE_STRING(BTS_SHUTDOWN_EV_TRX_RAMP_COMPL),
- OSMO_VALUE_STRING(BTS_SHUTDOWN_ST_WAIT_TRX_CLOSED),
+ OSMO_VALUE_STRING(BTS_SHUTDOWN_EV_TRX_CLOSED),
{ 0, NULL }
};
@@ -224,18 +244,32 @@ static __attribute__((constructor)) void bts_shutdown_fsm_init(void)
OSMO_ASSERT(osmo_fsm_register(&bts_shutdown_fsm) == 0);
}
-void bts_shutdown(struct gsm_bts *bts, const char *reason)
+bool bts_shutdown_in_progress(const struct gsm_bts *bts)
+{
+ const struct osmo_fsm_inst *fi = bts->shutdown_fi;
+ return fi->state != BTS_SHUTDOWN_ST_NONE;
+}
+
+void bts_shutdown_ext(struct gsm_bts *bts, const char *reason, bool exit_proc)
{
struct osmo_fsm_inst *fi = bts->shutdown_fi;
- if (fi->state != BTS_SHUTDOWN_ST_NONE) {
+ if (bts_shutdown_in_progress(bts)) {
LOGPFSML(fi, LOGL_NOTICE, "BTS is already being shutdown.\n");
+ if (exit_proc)
+ bts->shutdown_fi_exit_proc = true;
return;
}
-
- LOGPFSML(fi, LOGL_NOTICE, "Shutting down BTS, reason: %s\n", reason);
+ bts->shutdown_fi_exit_proc = exit_proc;
+ LOGPFSML(fi, LOGL_NOTICE, "Shutting down BTS, exit %u, reason: %s\n",
+ exit_proc, reason);
osmo_fsm_inst_dispatch(fi, BTS_SHUTDOWN_EV_START, NULL);
}
+void bts_shutdown(struct gsm_bts *bts, const char *reason)
+{
+ bts_shutdown_ext(bts, reason, true);
+}
+
void bts_model_trx_close_cb(struct gsm_bts_trx *trx, int rc)
{
struct osmo_fsm_inst *fi = trx->bts->shutdown_fi;
diff --git a/src/common/bts_trx.c b/src/common/bts_trx.c
index a5d7ed3d..ff5c6181 100644
--- a/src/common/bts_trx.c
+++ b/src/common/bts_trx.c
@@ -59,14 +59,7 @@ static void gsm_bts_trx_ts_init_lchan(struct gsm_bts_trx_ts *ts)
for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
struct gsm_lchan *lchan = &ts->lchan[ln];
-
- lchan->ts = ts;
- lchan->nr = ln;
- lchan->type = GSM_LCHAN_NONE;
- gsm_lchan_name_update(lchan);
-
- INIT_LLIST_HEAD(&lchan->sapi_cmds);
- INIT_LLIST_HEAD(&lchan->dl_tch_queue);
+ gsm_lchan_init(lchan, ts, ln);
}
}
@@ -119,6 +112,27 @@ void gsm_bts_trx_init_shadow_ts(struct gsm_bts_trx *trx)
}
}
+void gsm_bts_trx_free_shadow_ts(struct gsm_bts_trx *trx)
+{
+ unsigned int tn;
+ unsigned int ln;
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ struct gsm_bts_trx_ts *shadow_ts = trx->ts[tn].vamos.peer;
+ if (!shadow_ts)
+ continue;
+
+ /* free lchan related mem allocated on the trx object: */
+ for (ln = 0; ln < ARRAY_SIZE(shadow_ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &shadow_ts->lchan[ln];
+ TALLOC_FREE(lchan->name);
+ }
+
+ talloc_free(shadow_ts);
+ trx->ts[tn].vamos.peer = NULL;
+ }
+}
+
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
{
struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
@@ -206,25 +220,19 @@ const char *gsm_trx_unit_id(struct gsm_bts_trx *trx)
/* RSL link is established, send status report */
int trx_link_estab(struct gsm_bts_trx *trx)
{
- struct e1inp_sign_link *link = trx->rsl_link;
int rc;
- LOGPTRX(trx, DSUM, LOGL_INFO, "RSL link %s\n",
- link ? "up" : "down");
+ LOGPTRX(trx, DSUM, LOGL_INFO, "RSL link up\n");
- osmo_fsm_inst_dispatch(trx->mo.fi, link ? NM_EV_RSL_UP : NM_EV_RSL_DOWN, NULL);
- osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, link ? NM_EV_RSL_UP : NM_EV_RSL_DOWN, NULL);
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_RSL_UP, NULL);
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_RSL_UP, NULL);
- if (link)
- rc = rsl_tx_rf_res(trx);
- else
- rc = bts_model_trx_deact_rf(trx);
- if (rc < 0) {
+ if ((rc = rsl_tx_rf_res(trx)) < 0)
oml_tx_failure_event_rep(&trx->bb_transc.mo, NM_SEVER_MAJOR, OSMO_EVT_MAJ_RSL_FAIL,
- link ?
- "Failed to establish RSL link (%d)" :
- "Failed to deactivate RF (%d)", rc);
- }
+ "Failed to establish RSL link (%d)", rc);
+
+ if (trx == trx->bts->c0)
+ load_timer_start(trx->bts);
return 0;
}
diff --git a/src/common/cbch.c b/src/common/cbch.c
index ebea60be..f65df0db 100644
--- a/src/common/cbch.c
+++ b/src/common/cbch.c
@@ -198,7 +198,7 @@ int bts_process_smscb_cmd(struct gsm_bts *bts, struct rsl_ie_cb_cmd_type cmd_typ
return -EINVAL;
}
- scm = talloc_zero_size(bts, sizeof(*scm));
+ scm = talloc_zero(bts, struct smscb_msg);
if (!scm)
return -1;
@@ -233,10 +233,10 @@ int bts_process_smscb_cmd(struct gsm_bts *bts, struct rsl_ie_cb_cmd_type cmd_typ
rate_ctr_inc2(bts_ss->ctrs, CBCH_CTR_RCVD_QUEUED);
break;
case RSL_CB_CMD_TYPE_DEFAULT:
- /* old default msg will be free'd in get_smscb_block() if it is currently in transit
- * and we set a new default_msg here */
+ /* clear the cur_msg pointer if it is the old default message */
if (bts_ss->cur_msg && bts_ss->cur_msg == bts_ss->default_msg)
- talloc_free(bts_ss->cur_msg);
+ bts_ss->cur_msg = NULL;
+ talloc_free(bts_ss->default_msg);
if (cmd_type.def_bcast == RSL_CB_CMD_DEFBCAST_NORMAL)
/* def_bcast == 0: normal message */
bts_ss->default_msg = scm;
@@ -322,3 +322,25 @@ int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time)
return rc;
}
+
+static void bts_smscb_state_reset(struct bts_smscb_state *bts_ss)
+{
+ struct smscb_msg *scm, *tmp;
+ llist_for_each_entry_safe(scm, tmp, &bts_ss->queue, list) {
+ llist_del(&scm->list);
+ talloc_free(scm);
+ }
+ bts_ss->queue_len = 0;
+ rate_ctr_group_reset(bts_ss->ctrs);
+ /* avoid double-free of default_msg in case cur_msg == default_msg */
+ if (bts_ss->cur_msg && bts_ss->cur_msg != bts_ss->default_msg)
+ talloc_free(bts_ss->cur_msg);
+ bts_ss->cur_msg = NULL;
+ TALLOC_FREE(bts_ss->default_msg);
+}
+
+void bts_cbch_reset(struct gsm_bts *bts)
+{
+ bts_smscb_state_reset(&bts->smscb_basic);
+ bts_smscb_state_reset(&bts->smscb_extended);
+}
diff --git a/src/common/gsm_data.c b/src/common/gsm_data.c
index 36a57115..e5dbf105 100644
--- a/src/common/gsm_data.c
+++ b/src/common/gsm_data.c
@@ -40,6 +40,12 @@
#include <osmo-bts/bts_trx.h>
#include <osmo-bts/logging.h>
+struct osmo_tdef_group bts_tdef_groups[] = {
+ { .name = "bts", .tdefs = bts_T_defs, .desc = "BTS process timers" },
+ { .name = "abis", .tdefs = abis_T_defs, .desc = "Abis (RSL) related timers" },
+ {}
+};
+
const struct value_string gsm_pchant_names[13] = {
{ GSM_PCHAN_NONE, "NONE" },
{ GSM_PCHAN_CCCH, "CCCH" },
@@ -84,22 +90,6 @@ const char *gsm_lchant_name(enum gsm_chan_t c)
return get_value_string(gsm_chan_t_names, c);
}
-static const struct value_string lchan_s_names[] = {
- { LCHAN_S_NONE, "NONE" },
- { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" },
- { LCHAN_S_ACTIVE, "ACTIVE" },
- { LCHAN_S_INACTIVE, "INACTIVE" },
- { LCHAN_S_REL_REQ, "RELEASE REQUESTED" },
- { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" },
- { LCHAN_S_BROKEN, "BROKEN UNUSABLE" },
- { 0, NULL }
-};
-
-const char *gsm_lchans_name(enum gsm_lchan_state s)
-{
- return get_value_string(lchan_s_names, s);
-}
-
static char ts2str[255];
char *gsm_ts_name(const struct gsm_bts_trx_ts *ts)
@@ -157,172 +147,6 @@ char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts)
return ts2str;
}
-void gsm_lchan_name_update(struct gsm_lchan *lchan)
-{
- const struct gsm_bts_trx_ts *ts = lchan->ts;
- const struct gsm_bts_trx *trx = ts->trx;
- char *name;
-
- name = talloc_asprintf(trx, "(" GSM_TS_NAME_FMT ",ss=%u)",
- GSM_TS_NAME_ARGS(ts), lchan->nr);
- if (lchan->name != NULL)
- talloc_free(lchan->name);
- lchan->name = name;
-}
-
-/* See Table 10.5.25 of GSM04.08 */
-static uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
- uint8_t ts_nr, uint8_t lchan_nr)
-{
- uint8_t cbits, chan_nr;
-
- OSMO_ASSERT(pchan != GSM_PCHAN_OSMO_DYN);
- OSMO_ASSERT(pchan != GSM_PCHAN_TCH_F_PDCH);
-
- switch (pchan) {
- case GSM_PCHAN_TCH_F:
- OSMO_ASSERT(lchan_nr == 0);
- cbits = ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs;
- break;
- case GSM_PCHAN_PDCH:
- OSMO_ASSERT(lchan_nr == 0);
- cbits = ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH;
- break;
- case GSM_PCHAN_TCH_H:
- OSMO_ASSERT(lchan_nr < 2);
- cbits = ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(lchan_nr);
- break;
- case GSM_PCHAN_CCCH_SDCCH4:
- case GSM_PCHAN_CCCH_SDCCH4_CBCH:
- /*
- * As a special hack for BCCH, lchan_nr == 4 may be passed
- * here. This should never be sent in an RSL message.
- * See osmo-bts-xxx/oml.c:opstart_compl().
- */
- if (lchan_nr == CCCH_LCHAN)
- cbits = ABIS_RSL_CHAN_NR_CBITS_BCCH;
- else {
- OSMO_ASSERT(lchan_nr < 4);
- cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(lchan_nr);
- }
- break;
- case GSM_PCHAN_SDCCH8_SACCH8C:
- case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
- OSMO_ASSERT(lchan_nr < 8);
- cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(lchan_nr);
- break;
- case GSM_PCHAN_CCCH:
- cbits = ABIS_RSL_CHAN_NR_CBITS_BCCH;
- break;
- case GSM_PCHAN_NONE:
- LOGP(DRSL, LOGL_ERROR, "Physical channel %s not expected!\n",
- gsm_pchan_name(pchan));
- cbits = 0x00;
- break;
- default:
- LOGP(DRSL, LOGL_ERROR, "Physical channel %s (0x%02x) not expected!\n",
- gsm_pchan_name(pchan), (int)pchan);
- /* OSMO_ASSERT(lchan_nr == 0);
- * FIXME: On octphy and litecell, we hit above assertion (see
- * Max's comment at https://gerrit.osmocom.org/589 ); disabled
- * for BTS until this is clarified; remove the #ifdef when it
- * is fixed. Tracked in OS#2906.
- */
-#pragma message "fix caller that passes lchan_nr != 0"
- cbits = 0x10;
- break;
- }
-
- chan_nr = (cbits << 3) | (ts_nr & 0x7);
-
- return chan_nr;
-}
-
-uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan)
-{
- uint8_t chan_nr;
-
- switch (lchan->ts->pchan) {
- case GSM_PCHAN_OSMO_DYN:
- /* Return chan_nr reflecting the current TS pchan, either a standard TCH kind, or the
- * nonstandard value reflecting PDCH for Osmocom style dyn TS. */
- chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, lchan->ts->dyn.pchan_is);
- break;
- case GSM_PCHAN_TCH_F_PDCH:
- /* For ip.access style dyn TS, we always want to use the chan_nr as if it was TCH/F.
- * We're using custom PDCH ACT and DEACT messages that use the usual chan_nr values. */
- chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_TCH_F);
- break;
- default:
- chan_nr = gsm_pchan2chan_nr(lchan->ts->pchan, lchan->ts->nr, lchan->nr);
- break;
- }
-
- /* VAMOS: if this lchan belongs to a shadow timeslot, we must reflect
- * this in the channel number. Convert it to Osmocom specific value. */
- if (lchan->ts->vamos.is_shadow)
- chan_nr |= RSL_CHAN_OSMO_VAMOS_MASK;
-
- return chan_nr;
-}
-
-uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan,
- enum gsm_phys_chan_config as_pchan)
-{
- if (lchan->ts->pchan == GSM_PCHAN_OSMO_DYN
- && as_pchan == GSM_PCHAN_PDCH)
- return RSL_CHAN_OSMO_PDCH | (lchan->ts->nr & ~RSL_CHAN_NR_MASK);
- return gsm_pchan2chan_nr(as_pchan, lchan->ts->nr, lchan->nr);
-}
-
-/* Called by the model specific code every 104 TDMA frames (SACCH period) */
-void gsm_lchan_interf_meas_push(struct gsm_lchan *lchan, int dbm)
-{
- const uint8_t meas_num = lchan->meas.interf_meas_num;
-
- if (meas_num >= ARRAY_SIZE(lchan->meas.interf_meas_dbm)) {
- LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Not enough room "
- "to store interference report (%ddBm)\n", dbm);
- return;
- }
-
- lchan->meas.interf_meas_dbm[meas_num] = dbm;
- lchan->meas.interf_meas_num++;
-}
-
-/* Called by the higher layers every Intave * 104 TDMA frames */
-int gsm_lchan_interf_meas_calc_band(struct gsm_lchan *lchan)
-{
- const uint8_t meas_num = lchan->meas.interf_meas_num;
- const struct gsm_bts *bts = lchan->ts->trx->bts;
- int b, meas_avg, meas_sum = 0;
-
- /* There must be at least one sample */
- if (meas_num == 0)
- return -EAGAIN;
-
- /* Calculate the sum of all collected samples (in -x dBm) */
- while (lchan->meas.interf_meas_num) {
- uint8_t i = --lchan->meas.interf_meas_num;
- meas_sum += lchan->meas.interf_meas_dbm[i];
- }
-
- /* Calculate the average of all collected samples */
- meas_avg = meas_sum / (int) meas_num;
-
- /* Determine the band using interference boundaries from BSC */
- for (b = 0; b < ARRAY_SIZE(bts->interference.boundary); b++) {
- if (meas_avg >= bts->interference.boundary[b])
- break; /* Current 'b' is the band value */
- }
-
- LOGPLCHAN(lchan, DL1C, LOGL_DEBUG,
- "Interference AVG: %ddBm (band %d, samples %u)\n",
- meas_avg, b, meas_num);
-
- return b;
-}
-
/* determine logical channel based on TRX and channel number IE */
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
int *rc)
@@ -466,61 +290,32 @@ bool ts_is_tch(const struct gsm_bts_trx_ts *ts)
return pchan_is_tch(ts_pchan(ts));
}
-const struct value_string lchan_ciph_state_names[] = {
- { LCHAN_CIPH_NONE, "NONE" },
- { LCHAN_CIPH_RX_REQ, "RX_REQ" },
- { LCHAN_CIPH_RX_CONF, "RX_CONF" },
- { LCHAN_CIPH_RXTX_REQ, "RXTX_REQ" },
- { LCHAN_CIPH_RX_CONF_TX_REQ, "RX_CONF_TX_REQ" },
- { LCHAN_CIPH_RXTX_CONF, "RXTX_CONF" },
- { 0, NULL }
-};
-
-/* determine the ECU codec constant for the codec used by given lchan */
-int lchan2ecu_codec(const struct gsm_lchan *lchan)
+bool ts_is_pdch(const struct gsm_bts_trx_ts *ts)
{
- const struct gsm_bts_trx_ts *ts = lchan->ts;
-
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_V1:
- if (ts_pchan(ts) == GSM_PCHAN_TCH_H)
- return OSMO_ECU_CODEC_HR;
- else
- return OSMO_ECU_CODEC_FR;
- break;
- case GSM48_CMODE_SPEECH_EFR:
- return OSMO_ECU_CODEC_EFR;
- case GSM48_CMODE_SPEECH_AMR:
- return OSMO_ECU_CODEC_AMR;
+ switch (ts->pchan) {
+ case GSM_PCHAN_PDCH:
+ return true;
+ case GSM_PCHAN_TCH_F_PDCH:
+ return (ts->flags & TS_F_PDCH_ACTIVE)
+ && !(ts->flags & TS_F_PDCH_PENDING_MASK);
+ case GSM_PCHAN_OSMO_DYN:
+ return ts->dyn.pchan_is == GSM_PCHAN_PDCH
+ && ts->dyn.pchan_want == ts->dyn.pchan_is;
default:
- return -1;
+ return false;
}
}
-/* Default MS/BS Power Control parameters (see 3GPP TS 45.008, table A.1) */
-const struct gsm_power_ctrl_params power_ctrl_params_def = {
- /* Power increasing/reducing step size (optimal defaults) */
- .inc_step_size_db = 4, /* quickly increase MS/BS power */
- .red_step_size_db = 2, /* slowly decrease MS/BS power */
-
- /* RxLev measurement parameters */
- .rxlev_meas = {
- /* Thresholds for RxLev (see 3GPP TS 45.008, A.3.2.1) */
- .lower_thresh = 32, /* L_RXLEV_XX_P (-78 dBm) */
- .upper_thresh = 38, /* U_RXLEV_XX_P (-72 dBm) */
-
- /* NOTE: only Osmocom specific EWMA is supported */
- .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA,
- .ewma.alpha = 50, /* Smoothing factor 50% */
- },
-
- /* RxQual measurement parameters */
- .rxqual_meas = {
- /* Thresholds for RxQual (see 3GPP TS 45.008, A.3.2.1) */
- .lower_thresh = 3, /* L_RXQUAL_XX_P (0.8% <= BER < 1.6%) */
- .upper_thresh = 0, /* U_RXQUAL_XX_P (BER < 0.2%) */
+void gsm_ts_release(struct gsm_bts_trx_ts *ts)
+{
+ unsigned int ln;
- /* FIXME: RxQual averaging is not yet implemented */
- .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
- },
-};
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &ts->lchan[ln];
+ gsm_lchan_release(lchan, LCHAN_REL_ACT_OML);
+ }
+ ts->pchan = GSM_PCHAN_NONE;
+ /* Make sure pchan_is is reset, since PCU act_req to release it will be
+ * ignored as the lchan will already be released. */
+ ts->dyn.pchan_is = ts->dyn.pchan_want = GSM_PCHAN_NONE;
+}
diff --git a/src/common/handover.c b/src/common/handover.c
index 888649db..922c5140 100644
--- a/src/common/handover.c
+++ b/src/common/handover.c
@@ -51,7 +51,7 @@ static int ho_tx_phys_info(struct gsm_lchan *lchan)
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_HANDO_INFO;
- msgb_put_u8(msg, lchan->rqd_ta);
+ msgb_put_u8(msg, lchan->ta_ctrl.current);
rsl_rll_push_l3(msg, RSL_MT_UNIT_DATA_REQ, gsm_lchan2chan_nr(lchan),
0x00, 0);
@@ -111,7 +111,7 @@ void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
"TA=%u, ref=%u\n", gsm_lchant_name(lchan->type), acc_delay, ra);
/* Set timing advance */
- lchan->rqd_ta = acc_delay;
+ lchan->ta_ctrl.current = acc_delay;
lchan->want_dl_sacch_active = true;
/* Stop handover detection, wait for valid frame */
@@ -123,7 +123,7 @@ void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
}
/* Send HANDover DETect to BSC */
- rsl_tx_hando_det(lchan, &lchan->rqd_ta);
+ rsl_tx_hando_det(lchan, &lchan->ta_ctrl.current);
/* Send PHYS INFO */
lchan->ho.phys_info_count = 1;
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index c028a2c7..502bcef5 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -33,7 +33,6 @@
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/rsl.h>
-#include <osmocom/gsm/protocol/gsm_44_004.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/utils.h>
@@ -52,7 +51,6 @@
#include <osmo-bts/abis.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/handover.h>
-#include <osmo-bts/power_control.h>
#include <osmo-bts/msg_utils.h>
#include <osmo-bts/pcuif_proto.h>
#include <osmo-bts/cbch.h>
@@ -474,6 +472,9 @@ static bool is_fill_frame(uint8_t chan_type, const uint8_t *data, unsigned int l
switch (chan_type) {
case GSMTAP_CHANNEL_AGCH:
+ case GSMTAP_CHANNEL_SDCCH:
+ case GSMTAP_CHANNEL_TCH_F:
+ case GSMTAP_CHANNEL_TCH_H:
if (!memcmp(data, fill_frame, GSM_MACBLOCK_LEN))
return true;
break;
@@ -481,6 +482,7 @@ static bool is_fill_frame(uint8_t chan_type, const uint8_t *data, unsigned int l
if (!memcmp(data, paging_fill, GSM_MACBLOCK_LEN))
return true;
break;
+ /* FIXME: implement the same for GSMTAP_CHANNEL_PDTCH from/to PCU */
/* don't use 'default' case here as the above only conditionally return true */
}
return false;
@@ -588,6 +590,34 @@ static unsigned int calc_exprd_rach_frames(struct gsm_bts *bts, uint32_t fn)
return rach_frames_expired;
}
+static void l1sap_interf_meas_calc_avg(struct gsm_bts_trx *trx)
+{
+ unsigned int tn, ln;
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (ts->mo.nm_state.availability != NM_AVSTATE_OK)
+ continue;
+
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &ts->lchan[ln];
+
+ lchan->meas.interf_meas_avg_dbm = 0;
+ lchan->meas.interf_band = 0;
+
+ /* There must be at least one sample */
+ if (lchan->meas.interf_meas_num == 0)
+ continue;
+
+ /* Average all collected samples */
+ gsm_lchan_interf_meas_calc_avg(lchan);
+ }
+ }
+}
+
static void l1sap_interf_meas_report(struct gsm_bts *bts)
{
const uint32_t period = bts->interference.intave * 104;
@@ -598,8 +628,14 @@ static void l1sap_interf_meas_report(struct gsm_bts *bts)
if (bts->gsm_time.fn % period != 0)
return;
- llist_for_each_entry(trx, &bts->trx_list, list)
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ /* Calculate the average of all received samples */
+ l1sap_interf_meas_calc_avg(trx);
+ /* Report to the BSC over the A-bis/RSL */
rsl_tx_rf_res(trx);
+ /* Report to the PCU over the PCUIF */
+ pcu_tx_interf_ind(trx, bts->gsm_time.fn);
+ }
}
/* time information received from bts model */
@@ -635,7 +671,8 @@ static int l1sap_info_time_ind(struct gsm_bts *bts,
}
/* Report interference levels to the BSC */
- l1sap_interf_meas_report(bts);
+ if (bts_internal_flag_get(bts, BTS_INTERNAL_FLAG_INTERF_MEAS))
+ l1sap_interf_meas_report(bts);
return 0;
}
@@ -661,89 +698,70 @@ static inline void set_ms_to_data(struct gsm_lchan *lchan, int16_t data, bool se
}
/* measurement information received from bts model */
-static void process_l1sap_meas_data(struct gsm_bts_trx *trx,
- struct osmo_phsap_prim *l1sap,
+static void process_l1sap_meas_data(struct gsm_lchan *lchan,
+ const struct osmo_phsap_prim *l1sap,
enum osmo_ph_prim ind_type)
{
struct bts_ul_meas ulm;
- struct gsm_lchan *lchan;
- struct info_meas_ind_param *info_meas_ind;
- struct ph_data_param *ph_data_ind;
- struct ph_tch_param *ph_tch_ind;
- uint8_t chan_nr;
+ const struct info_meas_ind_param *info_meas_ind;
+ const struct ph_data_param *ph_data_ind;
+ const struct ph_tch_param *ph_tch_ind;
uint32_t fn;
- uint8_t inv_rssi;
- uint8_t is_sub;
- int16_t ta_offs_256bits;
- uint16_t ber10k;
const char *ind_name;
switch (ind_type) {
case PRIM_MPH_INFO:
/* (legacy way, see also OS#2977) */
info_meas_ind = &l1sap->u.info.u.meas_ind;
- chan_nr = info_meas_ind->chan_nr;
fn = info_meas_ind->fn;
- inv_rssi = info_meas_ind->inv_rssi;
- is_sub = info_meas_ind->is_sub;
- ta_offs_256bits = info_meas_ind->ta_offs_256bits;
- ber10k = info_meas_ind->ber10k;
ind_name = "MPH INFO";
+ ulm = (struct bts_ul_meas) {
+ .ta_offs_256bits = info_meas_ind->ta_offs_256bits,
+ .inv_rssi = info_meas_ind->inv_rssi,
+ .ber10k = info_meas_ind->ber10k,
+ .c_i = info_meas_ind->c_i_cb,
+ .is_sub = info_meas_ind->is_sub,
+ };
break;
case PRIM_TCH:
ph_tch_ind = &l1sap->u.tch;
if (ph_tch_ind->rssi == 0)
return;
- chan_nr = ph_tch_ind->chan_nr;
fn = ph_tch_ind->fn;
- inv_rssi = abs(ph_tch_ind->rssi);
- is_sub = ph_tch_ind->is_sub;
- ta_offs_256bits = ph_tch_ind->ta_offs_256bits;
- ber10k = ph_tch_ind->ber10k;
ind_name = "TCH";
+ ulm = (struct bts_ul_meas) {
+ .ta_offs_256bits = ph_tch_ind->ta_offs_256bits,
+ .inv_rssi = abs(ph_tch_ind->rssi),
+ .ber10k = ph_tch_ind->ber10k,
+ .c_i = ph_tch_ind->lqual_cb,
+ .is_sub = ph_tch_ind->is_sub,
+ };
break;
case PRIM_PH_DATA:
ph_data_ind = &l1sap->u.data;
if (ph_data_ind->rssi == 0)
return;
- chan_nr = ph_data_ind->chan_nr;
fn = ph_data_ind->fn;
- inv_rssi = abs(ph_data_ind->rssi);
- is_sub = ph_data_ind->is_sub;
- ta_offs_256bits = ph_data_ind->ta_offs_256bits;
- ber10k = ph_data_ind->ber10k;
ind_name = "DATA";
+ ulm = (struct bts_ul_meas) {
+ .ta_offs_256bits = ph_data_ind->ta_offs_256bits,
+ .inv_rssi = abs(ph_data_ind->rssi),
+ .ber10k = ph_data_ind->ber10k,
+ .c_i = ph_data_ind->lqual_cb,
+ .is_sub = ph_data_ind->is_sub,
+ };
break;
default:
OSMO_ASSERT(false);
}
- lchan = get_active_lchan_by_chan_nr(trx, chan_nr);
- if (!lchan) {
- LOGPFN(DL1P, LOGL_ERROR, fn,
- "No lchan for %s MEAS IND (chan_nr=%s)\n",
- ind_name, rsl_chan_nr_str(chan_nr));
- return;
- }
-
DEBUGPFN(DL1P, fn,
- "%s %s meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u\n",
- gsm_lchan_name(lchan), ind_name, ta_offs_256bits, ber10k,
- inv_rssi);
-
- /* in the GPRS case we are not interested in measurement
- * processing. The PCU will take care of it */
- if (lchan->type == GSM_LCHAN_PDTCH)
- return;
-
- memset(&ulm, 0, sizeof(ulm));
- ulm.ta_offs_256bits = ta_offs_256bits;
- ulm.ber10k = ber10k;
- ulm.inv_rssi = inv_rssi;
- ulm.is_sub = is_sub;
+ "%s %s meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u, C/I=%d cB\n",
+ gsm_lchan_name(lchan), ind_name, ulm.ta_offs_256bits,
+ ulm.ber10k, ulm.inv_rssi, ulm.c_i);
/* we assume that symbol period is 1 bit: */
- set_ms_to_data(lchan, ta_offs_256bits / 256, true);
+ set_ms_to_data(lchan, ulm.ta_offs_256bits / 256, true);
lchan_meas_process_measurement(lchan, &ulm, fn);
@@ -754,6 +772,8 @@ static void process_l1sap_meas_data(struct gsm_bts_trx *trx,
static int l1sap_mph_info_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct mph_info_param *info)
{
+ const struct info_meas_ind_param *meas_ind;
+ struct gsm_lchan *lchan;
int rc = 0;
switch (info->type) {
@@ -773,7 +793,17 @@ static int l1sap_mph_info_ind(struct gsm_bts_trx *trx,
if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
OSMO_ASSERT(false);
- process_l1sap_meas_data(trx, l1sap, PRIM_MPH_INFO);
+ meas_ind = &l1sap->u.info.u.meas_ind;
+
+ lchan = get_active_lchan_by_chan_nr(trx, meas_ind->chan_nr);
+ if (!lchan) {
+ LOGPFN(DL1P, LOGL_ERROR, meas_ind->fn,
+ "No lchan for chan_nr=%s\n",
+ rsl_chan_nr_str(meas_ind->chan_nr));
+ return 0;
+ }
+
+ process_l1sap_meas_data(lchan, l1sap, PRIM_MPH_INFO);
break;
default:
LOGP(DL1P, LOGL_NOTICE, "unknown MPH_INFO ind type %d\n",
@@ -949,14 +979,14 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_facch(struct gsm_lchan *lchan
/* Note: The repeated version of the FACCH block must be scheduled 8 or 9 bursts after the original
* transmission. see 3GPP TS 44.006, section 10.2 for a more detailed explaination. */
- if (lchan->tch.rep_facch[0].msg && GSM_TDMA_FN_SUB(fn, lchan->tch.rep_facch[0].fn) >= 8) {
+ if (lchan->rep_acch.dl_facch[0].msg && GSM_TDMA_FN_SUB(fn, lchan->rep_acch.dl_facch[0].fn) >= 8) {
/* Re-use stored FACCH message buffer from SLOT #0 for repetition. */
- msg = lchan->tch.rep_facch[0].msg;
- lchan->tch.rep_facch[0].msg = NULL;
- } else if (lchan->tch.rep_facch[1].msg && GSM_TDMA_FN_SUB(fn, lchan->tch.rep_facch[1].fn) >= 8) {
+ msg = lchan->rep_acch.dl_facch[0].msg;
+ lchan->rep_acch.dl_facch[0].msg = NULL;
+ } else if (lchan->rep_acch.dl_facch[1].msg && GSM_TDMA_FN_SUB(fn, lchan->rep_acch.dl_facch[1].fn) >= 8) {
/* Re-use stored FACCH message buffer from SLOT #1 for repetition. */
- msg = lchan->tch.rep_facch[1].msg;
- lchan->tch.rep_facch[1].msg = NULL;
+ msg = lchan->rep_acch.dl_facch[1].msg;
+ lchan->rep_acch.dl_facch[1].msg = NULL;
} else {
/* Fetch new FACCH from queue ... */
if (lapdm_phsap_dequeue_prim(le, &pp) < 0)
@@ -968,16 +998,16 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_facch(struct gsm_lchan *lchan
* If the MS explicitly indicated that repeated ACCH is
* supported, than all FACCH frames may be repeated
* see also: 3GPP TS 44.006, section 10.3). */
- if (!(lchan->repeated_acch_capability.dl_facch_all || msg->data[0] & 0x02))
+ if (!(lchan->rep_acch_cap.dl_facch_all || msg->data[0] & 0x02))
return msg;
/* ... and store the message buffer for repetition. */
- if (lchan->tch.rep_facch[0].msg == NULL) {
- lchan->tch.rep_facch[0].msg = msgb_copy(msg, "rep_facch_0");
- lchan->tch.rep_facch[0].fn = fn;
- } else if (lchan->tch.rep_facch[1].msg == NULL) {
- lchan->tch.rep_facch[1].msg = msgb_copy(msg, "rep_facch_1");
- lchan->tch.rep_facch[1].fn = fn;
+ if (lchan->rep_acch.dl_facch[0].msg == NULL) {
+ lchan->rep_acch.dl_facch[0].msg = msgb_copy(msg, "rep_facch_0");
+ lchan->rep_acch.dl_facch[0].fn = fn;
+ } else if (lchan->rep_acch.dl_facch[1].msg == NULL) {
+ lchan->rep_acch.dl_facch[1].msg = msgb_copy(msg, "rep_facch_1");
+ lchan->rep_acch.dl_facch[1].fn = fn;
} else {
/* By definition 3GPP TS 05.02 does not allow more than two (for TCH/H only one) FACCH blocks
* to be transmitted simultaniously. */
@@ -988,80 +1018,6 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_facch(struct gsm_lchan *lchan
return msg;
}
-/* Decide if repeated FACCH should be applied or not. If RXQUAL level, that the
- * MS reports is high enough, FACCH repetition is not needed. */
-void repeated_dl_facch_active_decision(struct gsm_lchan *lchan, const uint8_t *l3,
- size_t l3_len)
-{
- const struct gsm48_meas_res *meas_res;
- uint8_t upper;
- uint8_t lower;
- uint8_t rxqual;
- bool prev_repeated_dl_facch_active = lchan->repeated_dl_facch_active;
-
- /* This is an optimization so that we exit as quickly as possible if
- * there are no FACCH repetition capabilities present. However If the
- * repeated FACCH capabilities vanish for whatever reason, we must be
- * sure that FACCH repetition is disabled. */
- if (!lchan->repeated_acch_capability.dl_facch_cmd
- && !lchan->repeated_acch_capability.dl_facch_all) {
- lchan->repeated_dl_facch_active = false;
- goto out;
- }
-
- /* Threshold disabled (always on) */
- if (lchan->repeated_acch_capability.rxqual == 0) {
- lchan->repeated_dl_facch_active = true;
- goto out;
- }
-
- /* When the MS sets the SRR bit in the UL-SACCH L1 header
- * (repeated SACCH requested) then it makes sense to enable
- * FACCH repetition too. */
- if (lchan->meas.l1_info.srr_sro) {
- lchan->repeated_dl_facch_active = true;
- goto out;
- }
-
- /* Parse MS measurement results */
- if (l3_len <= sizeof(struct gsm48_meas_res *) + 2)
- goto out;
- if (l3[0] != GSM48_PDISC_RR)
- goto out;
- if (l3[1] != GSM48_MT_RR_MEAS_REP)
- goto out;
- l3 += 2;
- meas_res = (struct gsm48_meas_res *)l3;
-
- /* If the RXQUAL level at the MS drops under a certain threshold
- * we enable FACCH repetition. */
- upper = lchan->repeated_acch_capability.rxqual;
- if (upper > 2)
- lower = lchan->repeated_acch_capability.rxqual - 2;
- else
- lower = 0;
-
- /* When downlink DTX is applied, use RXQUAL-SUB, otherwise use
- * RXQUAL-FULL. */
- if (meas_res->dtx_used)
- rxqual = meas_res->rxqual_sub;
- else
- rxqual = meas_res->rxqual_full;
-
- if (rxqual >= upper)
- lchan->repeated_dl_facch_active = true;
- else if (rxqual <= lower)
- lchan->repeated_dl_facch_active = false;
-
-out:
- if (lchan->repeated_dl_facch_active == prev_repeated_dl_facch_active)
- return;
- if (lchan->repeated_dl_facch_active)
- LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-FACCH repetition: inactive => active\n");
- else
- LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-FACCH repetition: active => inactive\n");
-}
-
/* Special dequeueing function with SACCH repetition (3GPP TS 44.006, section 11) */
static inline struct msgb *lapdm_phsap_dequeue_msg_sacch(struct gsm_lchan *lchan, struct lapdm_entity *le)
{
@@ -1073,15 +1029,15 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_sacch(struct gsm_lchan *lchan
* possible candidates in order to have one ready in case the MS enables
* SACCH repetition. */
- if (lchan->rep_sacch) {
+ if (lchan->rep_acch.dl_sacch_msg) {
if (lchan->meas.l1_info.srr_sro == 0) {
/* Toss previous repetition candidate */
- msgb_free(lchan->rep_sacch);
- lchan->rep_sacch = NULL;
+ msgb_free(lchan->rep_acch.dl_sacch_msg);
+ lchan->rep_acch.dl_sacch_msg = NULL;
} else {
/* Use previous repetition candidate */
- msg = lchan->rep_sacch;
- lchan->rep_sacch = NULL;
+ msg = lchan->rep_acch.dl_sacch_msg;
+ lchan->rep_acch.dl_sacch_msg = NULL;
return msg;
}
}
@@ -1095,7 +1051,7 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_sacch(struct gsm_lchan *lchan
/* Only LAPDm frames for SAPI 0 may become a repetition
* candidate. */
if (sapi == 0)
- lchan->rep_sacch = msgb_copy(msg, "rep_sacch");
+ lchan->rep_acch.dl_sacch_msg = msgb_copy(msg, "rep_sacch");
return msg;
}
@@ -1175,24 +1131,31 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
rsl_chan_nr_str(chan_nr));
return 0;
}
+ if (lchan->pending_rel_ind_msg) {
+ LOGPGT(DRSL, LOGL_INFO, &g_time,
+ "%s Forward RLL RELease INDication to the BSC\n",
+ gsm_lchan_name(lchan));
+ abis_bts_rsl_sendmsg(lchan->pending_rel_ind_msg);
+ lchan->pending_rel_ind_msg = NULL;
+ }
if (L1SAP_IS_LINK_SACCH(link_id)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
/* L1-header, if not set/modified by layer 1 */
p[0] = lchan->ms_power_ctrl.current;
- if (lchan->repeated_ul_sacch_active)
+ if (lchan->rep_acch.ul_sacch_active)
p[0] |= 0x40; /* See also: 3GPP TS 44.004, section 7.1 */
- p[1] = lchan->rqd_ta;
+ p[1] = lchan->ta_ctrl.current;
le = &lchan->lapdm_ch.lapdm_acch;
- if (lchan->repeated_acch_capability.dl_sacch) {
+ if (lchan->rep_acch_cap.dl_sacch) {
/* Check if MS requests SACCH repetition and update state accordingly */
if (lchan->meas.l1_info.srr_sro) {
- if (lchan->repeated_dl_sacch_active == false)
+ if (lchan->rep_acch.dl_sacch_active == false)
LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-SACCH repetition: inactive => active\n");
- lchan->repeated_dl_sacch_active = true;
+ lchan->rep_acch.dl_sacch_active = true;
} else {
- if (lchan->repeated_dl_sacch_active == true)
+ if (lchan->rep_acch.dl_sacch_active == true)
LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-SACCH repetition: active => inactive\n");
- lchan->repeated_dl_sacch_active = false;
+ lchan->rep_acch.dl_sacch_active = false;
}
pp_msg = lapdm_phsap_dequeue_msg_sacch(lchan, le);
} else {
@@ -1202,7 +1165,7 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
if (lchan->ts->trx->bts->dtxd)
dtxd_facch = true;
le = &lchan->lapdm_ch.lapdm_dcch;
- if (lchan->repeated_dl_facch_active && lchan->rsl_cmode != RSL_CMOD_SPD_SIGN)
+ if (lchan->rep_acch.dl_facch_active && lchan->rsl_cmode != RSL_CMOD_SPD_SIGN)
pp_msg = lapdm_phsap_dequeue_msg_facch(lchan, le, fn);
else
pp_msg = lapdm_phsap_dequeue_msg(le);
@@ -1310,7 +1273,6 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
struct gsm_lchan *lchan;
uint8_t chan_nr, marker = 0;
uint32_t fn;
- int rc;
chan_nr = rts_ind->chan_nr;
fn = rts_ind->fn;
@@ -1354,16 +1316,6 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
resp_l1sap = msgb_l1sap_prim(resp_msg);
}
- /* check for pending REL_IND */
- if (lchan->pending_rel_ind_msg) {
- LOGPGT(DRSL, LOGL_INFO, &g_time, "%s Forward REL_IND to L3\n", gsm_lchan_name(lchan));
- /* Forward it to L3 */
- rc = abis_bts_rsl_sendmsg(lchan->pending_rel_ind_msg);
- lchan->pending_rel_ind_msg = NULL;
- if (rc < 0)
- return rc;
- }
-
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
resp_msg);
@@ -1463,20 +1415,20 @@ static void repeated_ul_sacch_active_decision(struct gsm_lchan *lchan,
{
uint16_t upper = 0;
uint16_t lower = 0;
- bool prev_repeated_ul_sacch_active = lchan->repeated_ul_sacch_active;
+ bool prev_repeated_ul_sacch_active = lchan->rep_acch.ul_sacch_active;
/* This is an optimization so that we exit as quickly as possible if
* there are no uplink SACCH repetition capabilities present.
* However If the repeated UL-SACCH capabilities vanish for whatever
* reason, we must be sure that UL-SACCH repetition is disabled. */
- if (!lchan->repeated_acch_capability.ul_sacch) {
- lchan->repeated_ul_sacch_active = false;
+ if (!lchan->rep_acch_cap.ul_sacch) {
+ lchan->rep_acch.ul_sacch_active = false;
goto out;
}
/* Threshold disabled (repetition is always on) */
- if (lchan->repeated_acch_capability.rxqual == 0) {
- lchan->repeated_ul_sacch_active = true;
+ if (lchan->rep_acch_cap.rxqual == 0) {
+ lchan->rep_acch.ul_sacch_active = true;
goto out;
}
@@ -1490,19 +1442,19 @@ static void repeated_ul_sacch_active_decision(struct gsm_lchan *lchan,
* of the table in GSM 05.08, section 8.2.4. The lower vector is just
* the upper vector shifted by 2. */
- upper = ber10k_by_rxqual_upper[lchan->repeated_acch_capability.rxqual];
- lower = ber10k_by_rxqual_lower[lchan->repeated_acch_capability.rxqual];
+ upper = ber10k_by_rxqual_upper[lchan->rep_acch_cap.rxqual];
+ lower = ber10k_by_rxqual_lower[lchan->rep_acch_cap.rxqual];
/* If upper/rxqual == 0, then repeated UL-SACCH is always on */
if (ber10k >= upper)
- lchan->repeated_ul_sacch_active = true;
+ lchan->rep_acch.ul_sacch_active = true;
else if (ber10k <= lower)
- lchan->repeated_ul_sacch_active = false;
+ lchan->rep_acch.ul_sacch_active = false;
out:
- if (lchan->repeated_ul_sacch_active == prev_repeated_ul_sacch_active)
+ if (lchan->rep_acch.ul_sacch_active == prev_repeated_ul_sacch_active)
return;
- if (lchan->repeated_ul_sacch_active)
+ if (lchan->rep_acch.ul_sacch_active)
LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "UL-SACCH repetition: inactive => active\n");
else
LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "UL-SACCH repetition: active => inactive\n");
@@ -1521,11 +1473,8 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
uint8_t chan_nr, link_id;
uint8_t tn;
uint32_t fn;
- int8_t rssi;
enum osmo_ph_pres_info_type pr_info = data_ind->pdch_presence_info;
- struct gsm_sacch_l1_hdr *l1_hdr;
- rssi = data_ind->rssi;
chan_nr = data_ind->chan_nr;
link_id = data_ind->link_id;
fn = data_ind->fn;
@@ -1536,21 +1485,6 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
DEBUGPGT(DL1P, &g_time, "Rx PH-DATA.ind chan_nr=%s link_id=0x%02x len=%d\n",
rsl_chan_nr_str(chan_nr), link_id, len);
- /* Actually, there can be no DATA.ind on PTCCH/U (rather RACH.ind instead),
- * but some BTS models with buggy implementation may still be sending them
- * to us. Let's keep this for backwards compatibility. */
- if (L1SAP_IS_CHAN_PDCH(chan_nr) && L1SAP_IS_PTCCH(fn)) {
- LOGPGT(DL1P, LOGL_NOTICE, &g_time, "There can be no DATA.ind on PTCCH/U. "
- "This is probably a bug of the BTS model you're using, please fix!\n");
- return -EINVAL;
- }
-
- /* The ph_data_param contained in the l1sap primitive may contain
- * measurement data. If this data is present, forward it for
- * processing */
- if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
- process_l1sap_meas_data(trx, l1sap, PRIM_PH_DATA);
-
if (ts_is_pdch(&trx->ts[tn])) {
lchan = get_lchan_by_chan_nr(trx, chan_nr);
if (!lchan)
@@ -1567,13 +1501,21 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
return 1;
}
+ /* There can be no DATA.ind on PTCCH/U (rather RACH.ind instead), but some
+ * BTS models with buggy implementation may still be sending them to us. */
+ if (L1SAP_IS_PTCCH(fn)) {
+ LOGPGT(DL1P, LOGL_NOTICE, &g_time, "There can be no DATA.ind on PTCCH/U. "
+ "This is probably a bug of the BTS model you're using, please fix!\n");
+ return -EINVAL;
+ }
+
/* Drop all data from incomplete UL block */
if (pr_info != PRES_INFO_BOTH)
len = 0;
/* PDTCH / PACCH frame handling */
pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PDTCH, fn, trx->arfcn,
- L1SAP_FN2MACBLOCK(fn), data, len, rssi, data_ind->ber10k,
+ L1SAP_FN2MACBLOCK(fn), data, len, data_ind->rssi, data_ind->ber10k,
data_ind->ta_offs_256bits/64, data_ind->lqual_cb);
return 0;
}
@@ -1584,53 +1526,39 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
return 0;
}
- if (L1SAP_IS_LINK_SACCH(link_id))
- repeated_ul_sacch_active_decision(lchan, data_ind->ber10k);
+ /* The ph_data_param contained in the l1sap primitive may contain
+ * measurement data. If this data is present, forward it for
+ * processing */
+ if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
+ process_l1sap_meas_data(lchan, l1sap, PRIM_PH_DATA);
- /* bad frame */
- if (len == 0) {
- if (L1SAP_IS_LINK_SACCH(link_id)) {
- /* In case we loose a SACCH block, we must take care
- * that the related measurement report is sent via RSL.
- * This is a fallback method. The report will also
- * lack the measurement report from the MS side. See
- * also rsl.c:lapdm_rll_tx_cb() */
- LOGPGT(DL1P, LOGL_INFO, &g_time, "Lost SACCH block, faking meas reports and ms pwr\n");
- le = &lchan->lapdm_ch.lapdm_acch;
- rsl_tx_meas_res(lchan, NULL, 0, le);
+ if (L1SAP_IS_LINK_SACCH(link_id)) {
+ repeated_ul_sacch_active_decision(lchan, data_ind->ber10k);
+ /* Radio Link Timeout counter */
+ if (len == 0) {
+ LOGPGT(DL1P, LOGL_INFO, &g_time, "%s Lost SACCH block\n",
+ gsm_lchan_name(lchan));
radio_link_timeout(lchan, true);
- lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, data_ind->rssi);
+ } else {
+ radio_link_timeout(lchan, false);
}
- return -EINVAL;
+
+ /* Trigger the measurement reporting/processing logic */
+ lchan_meas_handle_sacch(lchan, msg);
}
+ /* bad frame */
+ if (len == 0)
+ return -EINVAL;
+
/* report first valid received frame to handover process */
if (lchan->ho.active == HANDOVER_WAIT_FRAME)
handover_frame(lchan);
- if (L1SAP_IS_LINK_SACCH(link_id)) {
- radio_link_timeout(lchan, false);
+ if (L1SAP_IS_LINK_SACCH(link_id))
le = &lchan->lapdm_ch.lapdm_acch;
- /* save the SACCH L1 header in the lchan struct for RSL MEAS RES */
- if (len != GSM_MACBLOCK_LEN) {
- LOGPGT(DL1P, LOGL_NOTICE, &g_time, "SACCH with odd len=%u!?!\n", len);
- return -EINVAL;
- }
- /* Some brilliant engineer decided that the ordering of
- * fields on the Um interface is different from the
- * order of fields in RSL. See 3GPP TS 44.004 (section 7.2)
- * vs. 3GPP TS 48.058 (section 9.3.10). */
- l1_hdr = (struct gsm_sacch_l1_hdr*)data;
- lchan->meas.l1_info.ms_pwr = l1_hdr->ms_pwr;
- lchan->meas.l1_info.fpc_epc = l1_hdr->fpc_epc;
- lchan->meas.l1_info.srr_sro = l1_hdr->srr_sro;
- lchan->meas.l1_info.ta = l1_hdr->ta;
- lchan->meas.flags |= LC_UL_M_F_L1_VALID;
-
- lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi);
- lchan_bs_pwr_ctrl(lchan, (const struct gsm48_hdr *) &data[5]);
- } else
+ else
le = &lchan->lapdm_ch.lapdm_dcch;
if (check_for_first_ciphrd(lchan, data, len))
@@ -1672,7 +1600,7 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
* measurement data. If this data is present, forward it for
* processing */
if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
- process_l1sap_meas_data(trx, l1sap, PRIM_TCH);
+ process_l1sap_meas_data(lchan, l1sap, PRIM_TCH);
msgb_pull_to_l2(msg);
@@ -1941,8 +1869,12 @@ int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
l1sap->u.data.chan_nr = RSL_CHAN_OSMO_PDCH | ts->nr;
l1sap->u.data.link_id = 0x00;
l1sap->u.data.fn = fn;
- msg->l2h = msgb_put(msg, len);
- memcpy(msg->l2h, data, len);
+ if (len) {
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, data, len);
+ } else {
+ msg->l2h = NULL; /* Idle block */
+ }
return l1sap_down(ts->trx, l1sap);
}
@@ -2000,7 +1932,13 @@ int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
int rc;
- LOGPLCHAN(lchan, DL1C, LOGL_INFO, "activating channel %s\n", rsl_chan_nr_str(chan_nr));
+ if (lchan->state == LCHAN_S_ACTIVE) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Trying to activate already active channel %s\n",
+ rsl_chan_nr_str(chan_nr));
+ return -1;
+ }
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Activating channel %s\n", rsl_chan_nr_str(chan_nr));
lchan->s = trx->bts->radio_link_timeout.current;
@@ -2033,8 +1971,15 @@ int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *
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);
- LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivating channel chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
+
+ if (lchan->state == LCHAN_S_NONE) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Trying to deactivate already deactivated channel %s\n",
+ rsl_chan_nr_str(chan_nr));
+ return -1;
+ }
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Deactivating channel %s\n",
+ rsl_chan_nr_str(chan_nr));
if (lchan->tch.dtx.dl_amr_fsm) {
osmo_fsm_inst_free(lchan->tch.dtx.dl_amr_fsm);
@@ -2049,8 +1994,8 @@ int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
- LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivating sacch chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Deactivating SACCH on channel %s\n",
+ rsl_chan_nr_str(chan_nr));
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
1);
@@ -2058,8 +2003,10 @@ int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr)
int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
- LOGP(DL1C, LOGL_INFO, "modifying channel chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
+ struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Modifying channel %s\n",
+ rsl_chan_nr_str(chan_nr));
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_MODIFY, 0);
}
diff --git a/src/common/lchan.c b/src/common/lchan.c
index 5a3f539a..fe5efd57 100644
--- a/src/common/lchan.c
+++ b/src/common/lchan.c
@@ -20,30 +20,493 @@
*/
#include <osmocom/core/logging.h>
+
+#include <osmocom/trau/osmo_ortp.h>
+
#include <osmo-bts/logging.h>
-#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/lchan.h>
+#include <osmo-bts/bts.h>
+#include <osmo-bts/rsl.h>
+#include <osmo-bts/pcu_if.h>
+#include <osmo-bts/handover.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/bts_model.h>
+#include <errno.h>
+
+static const struct value_string lchan_s_names[] = {
+ { LCHAN_S_NONE, "NONE" },
+ { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" },
+ { LCHAN_S_ACTIVE, "ACTIVE" },
+ { LCHAN_S_REL_REQ, "RELEASE REQUESTED" },
+ { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" },
+ { LCHAN_S_BROKEN, "BROKEN UNUSABLE" },
+ { 0, NULL }
+};
+
+const struct value_string lchan_ciph_state_names[] = {
+ { LCHAN_CIPH_NONE, "NONE" },
+ { LCHAN_CIPH_RX_REQ, "RX_REQ" },
+ { LCHAN_CIPH_RX_CONF, "RX_CONF" },
+ { LCHAN_CIPH_RXTX_REQ, "RXTX_REQ" },
+ { LCHAN_CIPH_RX_CONF_TX_REQ, "RX_CONF_TX_REQ" },
+ { LCHAN_CIPH_RXTX_CONF, "RXTX_CONF" },
+ { 0, NULL }
+};
+
+/* prepare the per-SAPI T200 arrays for a given lchan */
+static int t200_by_lchan(int *t200_ms_dcch, int *t200_ms_acch, struct gsm_lchan *lchan)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+
+ /* we have to compensate for the "RTS advance" due to the asynchronous interface between
+ * the BTS (LAPDm) and the PHY/L1 (OsmoTRX or DSP in case of osmo-bts-{sysmo,lc15,oc2g,octphy} */
+ int32_t fn_advance = bts_get_avg_fn_advance(bts);
+ int32_t fn_advance_us = fn_advance * 4615;
+ int fn_advance_ms = fn_advance_us / 1000;
+
+ t200_ms_acch[DL_SAPI0] = bts->t200_ms[T200_SACCH_SDCCH] + fn_advance_ms;
+ t200_ms_acch[DL_SAPI3] = bts->t200_ms[T200_SACCH_SDCCH] + fn_advance_ms;
+
+ if (lchan->rep_acch_cap.dl_facch_all && lchan_is_tch(lchan)) {
+ t200_ms_acch[DL_SAPI0] *= 2;
+ t200_ms_acch[DL_SAPI3] *= 2;
+ }
+
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_SDCCH] + fn_advance_ms;
+ t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_SDCCH_SAPI3] + fn_advance_ms;
+ break;
+ case GSM_LCHAN_TCH_F:
+ t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_FACCH_F] + fn_advance_ms;
+ t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_FACCH_F] + fn_advance_ms;
+ break;
+ case GSM_LCHAN_TCH_H:
+ t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_FACCH_H] + fn_advance_ms;
+ t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_FACCH_H] + fn_advance_ms;
+ break;
+ default:
+ /* Channels such as CCCH don't use lapdm DL, and hence no T200 is needed */
+ return -1;
+ }
+ return 0;
+}
+
+static void early_rr_ia_delay_cb(void *data)
+{
+ struct gsm_lchan *lchan = data;
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+
+ if (!lchan->early_rr_ia) {
+ /* The IA message has disappeared since the timer was started. */
+ return;
+ }
+
+ if (lchan->state != LCHAN_S_ACTIVE) {
+ /* Release has happened since the timer was started. */
+ msgb_free(lchan->early_rr_ia);
+ lchan->early_rr_ia = NULL;
+ return;
+ }
+
+ /* Activation is done, send the RR IA now. Put RR IA msg into the AGCH queue of the BTS. */
+ if (bts_agch_enqueue(bts, lchan->early_rr_ia) < 0) {
+ /* if there is no space in the queue: send DELETE IND */
+ rsl_tx_delete_ind(bts, lchan->early_rr_ia->data, lchan->early_rr_ia->len);
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_AGCH_DELETED);
+ msgb_free(lchan->early_rr_ia);
+ }
+ lchan->early_rr_ia = NULL;
+}
+
+void gsm_lchan_init(struct gsm_lchan *lchan, struct gsm_bts_trx_ts *ts, unsigned int lchan_nr)
+{
+ lchan->ts = ts;
+ lchan->nr = lchan_nr;
+ lchan->type = GSM_LCHAN_NONE;
+ gsm_lchan_name_update(lchan);
+
+ osmo_timer_setup(&lchan->early_rr_ia_delay, early_rr_ia_delay_cb, lchan);
+
+ INIT_LLIST_HEAD(&lchan->sapi_cmds);
+ INIT_LLIST_HEAD(&lchan->dl_tch_queue);
+}
+
+void gsm_lchan_name_update(struct gsm_lchan *lchan)
+{
+ const struct gsm_bts_trx_ts *ts = lchan->ts;
+ const struct gsm_bts_trx *trx = ts->trx;
+ char *name;
+
+ name = talloc_asprintf(trx, "(" GSM_TS_NAME_FMT ",ss=%u)",
+ GSM_TS_NAME_ARGS(ts), lchan->nr);
+ if (lchan->name != NULL)
+ talloc_free(lchan->name);
+ lchan->name = name;
+}
+
+int lchan_init_lapdm(struct gsm_lchan *lchan)
+{
+ struct lapdm_channel *lc = &lchan->lapdm_ch;
+ int t200_ms_dcch[_NR_DL_SAPI], t200_ms_acch[_NR_DL_SAPI];
+
+ if (t200_by_lchan(t200_ms_dcch, t200_ms_acch, lchan) == 0) {
+ LOGPLCHAN(lchan, DLLAPD, LOGL_DEBUG,
+ "Setting T200 D0=%u, D3=%u, S0=%u, S3=%u (all in ms)\n",
+ t200_ms_dcch[DL_SAPI0], t200_ms_dcch[DL_SAPI3],
+ t200_ms_acch[DL_SAPI0], t200_ms_acch[DL_SAPI3]);
+ lapdm_channel_init3(lc, LAPDM_MODE_BTS, t200_ms_dcch, t200_ms_acch, lchan->type,
+ gsm_lchan_name(lchan));
+ lapdm_channel_set_flags(lc, LAPDM_ENT_F_POLLING_ONLY);
+ lapdm_channel_set_l1(lc, NULL, lchan);
+ }
+ /* We still need to set Rx callback to receive RACH requests: */
+ lapdm_channel_set_l3(lc, lapdm_rll_tx_cb, lchan);
+
+ return 0;
+}
+
+static int dyn_ts_pdch_release(struct gsm_lchan *lchan)
+{
+ struct gsm_bts_trx_ts *ts = lchan->ts;
+
+ if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
+ LOGP(DRSL, LOGL_ERROR, "%s: PDCH release requested but already"
+ " in switchover\n", gsm_ts_and_pchan_name(ts));
+ return -EINVAL;
+ }
+
+ /*
+ * Indicate PDCH Disconnect in dyn_pdch.want, let pcu_tx_info_ind()
+ * pick it up and wait for PCU to disable the channel.
+ */
+ ts->dyn.pchan_want = GSM_PCHAN_NONE;
+
+ if (!pcu_connected()) {
+ /* PCU not connected yet. Just record the new type and done,
+ * the PCU will pick it up once connected. */
+ ts->dyn.pchan_is = GSM_PCHAN_NONE;
+ return 1;
+ }
+
+ return pcu_tx_info_ind();
+}
+
+void gsm_lchan_release(struct gsm_lchan *lchan, enum lchan_rel_act_kind rel_kind)
+{
+ int rc;
+
+ if (lchan->abis_ip.rtp_socket) {
+ rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
+ osmo_rtp_socket_log_stats(lchan->abis_ip.rtp_socket, DRTP, LOGL_INFO,
+ "Closing RTP socket on Channel Release ");
+ osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
+ lchan->abis_ip.rtp_socket = NULL;
+ msgb_queue_flush(&lchan->dl_tch_queue);
+ }
+
+ /* FIXME: right now we allow creating the rtp_socket even if chan is not
+ * activated... Once we check for that, we can move this check at the
+ * start of the function */
+ if (lchan->state == LCHAN_S_NONE)
+ return;
+
+ /* release handover state */
+ handover_reset(lchan);
+
+ lchan->rel_act_kind = rel_kind;
+
+ /* Dynamic channel in PDCH mode is released via PCU */
+ if (lchan->ts->pchan == GSM_PCHAN_OSMO_DYN
+ && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
+ rc = dyn_ts_pdch_release(lchan);
+ if (rc == 1) {
+ /* If the PCU is not connected, continue to rel ack right away. */
+ lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
+ rsl_tx_rf_rel_ack(lchan);
+ return;
+ }
+ /* Waiting for PDCH release */
+ return;
+ }
+
+ l1sap_chan_rel(lchan->ts->trx, gsm_lchan2chan_nr(lchan));
+}
+
+int lchan_deactivate(struct gsm_lchan *lchan)
+{
+ OSMO_ASSERT(lchan);
+
+ lchan->ciph_state = 0;
+ return bts_model_lchan_deactivate(lchan);
+}
+
+const char *gsm_lchans_name(enum gsm_lchan_state s)
+{
+ return get_value_string(lchan_s_names, s);
+}
+
+/* obtain the next to-be transmitted dowlink SACCH frame (L2 hdr + L3); returns pointer to lchan->si buffer */
+uint8_t *lchan_sacch_get(struct gsm_lchan *lchan)
+{
+ uint32_t tmp, i;
+
+ for (i = 0; i < _MAX_SYSINFO_TYPE; i++) {
+ tmp = (lchan->si.last + 1 + i) % _MAX_SYSINFO_TYPE;
+ if (!(lchan->si.valid & (1 << tmp)))
+ continue;
+ lchan->si.last = tmp;
+ return GSM_LCHAN_SI(lchan, tmp);
+ }
+ LOGPLCHAN(lchan, DL1P, LOGL_NOTICE, "SACCH no SI available\n");
+ return NULL;
+}
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state)
{
- DEBUGP(DL1C, "%s state %s -> %s\n",
- gsm_lchan_name(lchan),
- gsm_lchans_name(lchan->state),
- gsm_lchans_name(state));
+ if (lchan->state == state)
+ return;
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "state %s -> %s\n",
+ gsm_lchans_name(lchan->state), gsm_lchans_name(state));
lchan->state = state;
+
+ switch (lchan->state) {
+ case LCHAN_S_ACT_REQ:
+ /* Early Immediate Assignment: Activation is requested, keep the
+ * early IA until active. This allows the BSC to send the IA
+ * even before a dynamic timeslot is done switching to a
+ * different pchan kind (experimental). */
+ break;
+ case LCHAN_S_ACTIVE:
+ lchan_init_lapdm(lchan);
+ if (lchan->early_rr_ia) {
+ /* Early Immediate Assignment: Activation is done, send
+ * the RR IA now. Delay a bit more to give Um time to
+ * let the lchan light up for the MS */
+ osmo_timer_del(&lchan->early_rr_ia_delay);
+ osmo_timer_schedule(&lchan->early_rr_ia_delay, 0,
+ osmo_tdef_get(abis_T_defs, -15, OSMO_TDEF_US, -1));
+ }
+ break;
+ case LCHAN_S_NONE:
+ lapdm_channel_exit(&lchan->lapdm_ch);
+ /* Also ensure that there are no leftovers from repeated FACCH or
+ * repeated SACCH that might cause memory leakage. */
+ msgb_free(lchan->rep_acch.dl_facch[0].msg);
+ msgb_free(lchan->rep_acch.dl_facch[1].msg);
+ lchan->rep_acch.dl_facch[0].msg = NULL;
+ lchan->rep_acch.dl_facch[1].msg = NULL;
+ msgb_free(lchan->rep_acch.dl_sacch_msg);
+ lchan->rep_acch.dl_sacch_msg = NULL;
+ /* free() pending messages */
+ msgb_free(lchan->pending_rel_ind_msg);
+ lchan->pending_rel_ind_msg = NULL;
+ msgb_free(lchan->pending_chan_activ);
+ lchan->pending_chan_activ = NULL;
+ /* fall through */
+ default:
+ if (lchan->early_rr_ia) {
+ /* Early Immediate Assignment: Transition to any other
+ * state means whatever IA the BSC has sent shall now
+ * not be relevant anymore. */
+ osmo_timer_del(&lchan->early_rr_ia_delay);
+ msgb_free(lchan->early_rr_ia);
+ lchan->early_rr_ia = NULL;
+ }
+ break;
+ }
}
-bool ts_is_pdch(const struct gsm_bts_trx_ts *ts)
+/* See Table 10.5.25 of GSM04.08 */
+static uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
+ uint8_t ts_nr, uint8_t lchan_nr)
{
- switch (ts->pchan) {
+ uint8_t cbits, chan_nr;
+
+ OSMO_ASSERT(pchan != GSM_PCHAN_OSMO_DYN);
+ OSMO_ASSERT(pchan != GSM_PCHAN_TCH_F_PDCH);
+
+ switch (pchan) {
+ case GSM_PCHAN_TCH_F:
+ OSMO_ASSERT(lchan_nr == 0);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs;
+ break;
case GSM_PCHAN_PDCH:
- return true;
- case GSM_PCHAN_TCH_F_PDCH:
- return (ts->flags & TS_F_PDCH_ACTIVE)
- && !(ts->flags & TS_F_PDCH_PENDING_MASK);
+ OSMO_ASSERT(lchan_nr == 0);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH;
+ break;
+ case GSM_PCHAN_TCH_H:
+ OSMO_ASSERT(lchan_nr < 2);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(lchan_nr);
+ break;
+ case GSM_PCHAN_CCCH_SDCCH4:
+ case GSM_PCHAN_CCCH_SDCCH4_CBCH:
+ /*
+ * As a special hack for BCCH, lchan_nr == 4 may be passed
+ * here. This should never be sent in an RSL message.
+ * See osmo-bts-xxx/oml.c:opstart_compl().
+ */
+ if (lchan_nr == CCCH_LCHAN)
+ cbits = ABIS_RSL_CHAN_NR_CBITS_BCCH;
+ else {
+ OSMO_ASSERT(lchan_nr < 4);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(lchan_nr);
+ }
+ break;
+ case GSM_PCHAN_SDCCH8_SACCH8C:
+ case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
+ OSMO_ASSERT(lchan_nr < 8);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(lchan_nr);
+ break;
+ case GSM_PCHAN_CCCH:
+ cbits = ABIS_RSL_CHAN_NR_CBITS_BCCH;
+ break;
+ case GSM_PCHAN_NONE:
+ LOGP(DRSL, LOGL_ERROR, "Physical channel %s not expected!\n",
+ gsm_pchan_name(pchan));
+ cbits = 0x00;
+ break;
+ default:
+ LOGP(DRSL, LOGL_ERROR, "Physical channel %s (0x%02x) not expected!\n",
+ gsm_pchan_name(pchan), (int)pchan);
+ OSMO_ASSERT(0);
+ break;
+ }
+
+ chan_nr = (cbits << 3) | (ts_nr & 0x7);
+
+ return chan_nr;
+}
+
+static uint8_t _gsm_lchan2chan_nr(const struct gsm_lchan *lchan, bool rsl)
+{
+ uint8_t chan_nr;
+
+ switch (lchan->ts->pchan) {
case GSM_PCHAN_OSMO_DYN:
- return ts->dyn.pchan_is == GSM_PCHAN_PDCH
- && ts->dyn.pchan_want == ts->dyn.pchan_is;
+ /* Return chan_nr reflecting the current TS pchan, either a standard TCH kind, or the
+ * nonstandard value reflecting PDCH for Osmocom style dyn TS. */
+ chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, lchan->ts->dyn.pchan_is);
+ break;
+ case GSM_PCHAN_TCH_F_PDCH:
+ /* For ip.access style dyn TS, on RSL we want to use the chan_nr as if it was TCH/F.
+ * We're using custom PDCH ACT and DEACT messages that use the usual chan_nr values. */
+ if (rsl)
+ chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_TCH_F);
+ else if (~lchan->ts->flags & TS_F_PDCH_ACTIVE)
+ chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_TCH_F);
+ else
+ chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_PDCH);
+ break;
+ default:
+ chan_nr = gsm_pchan2chan_nr(lchan->ts->pchan, lchan->ts->nr, lchan->nr);
+ break;
+ }
+
+ /* VAMOS: if this lchan belongs to a shadow timeslot, we must reflect
+ * this in the channel number. Convert it to Osmocom specific value. */
+ if (lchan->ts->vamos.is_shadow)
+ chan_nr |= RSL_CHAN_OSMO_VAMOS_MASK;
+
+ return chan_nr;
+}
+
+uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan)
+{
+ return _gsm_lchan2chan_nr(lchan, false);
+}
+
+uint8_t gsm_lchan2chan_nr_rsl(const struct gsm_lchan *lchan)
+{
+ return _gsm_lchan2chan_nr(lchan, true);
+}
+
+uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan,
+ enum gsm_phys_chan_config as_pchan)
+{
+ if (lchan->ts->pchan == GSM_PCHAN_OSMO_DYN
+ && as_pchan == GSM_PCHAN_PDCH)
+ return RSL_CHAN_OSMO_PDCH | (lchan->ts->nr & ~RSL_CHAN_NR_MASK);
+ return gsm_pchan2chan_nr(as_pchan, lchan->ts->nr, lchan->nr);
+}
+
+/* Called by the model specific code every 104 TDMA frames (SACCH period) */
+void gsm_lchan_interf_meas_push(struct gsm_lchan *lchan, int dbm)
+{
+ const uint8_t meas_num = lchan->meas.interf_meas_num;
+
+ if (meas_num >= ARRAY_SIZE(lchan->meas.interf_meas_dbm)) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Not enough room "
+ "to store interference report (%ddBm)\n", dbm);
+ return;
+ }
+
+ lchan->meas.interf_meas_dbm[meas_num] = dbm;
+ lchan->meas.interf_meas_num++;
+}
+
+/* Called by the higher layers every Intave * 104 TDMA frames */
+void gsm_lchan_interf_meas_calc_avg(struct gsm_lchan *lchan)
+{
+ const uint8_t meas_num = lchan->meas.interf_meas_num;
+ const struct gsm_bts *bts = lchan->ts->trx->bts;
+ int b, meas_avg, meas_sum = 0;
+
+ /* There must be at least one sample */
+ OSMO_ASSERT(meas_num > 0);
+
+ /* Calculate the sum of all collected samples (in -x dBm) */
+ while (lchan->meas.interf_meas_num) {
+ uint8_t i = --lchan->meas.interf_meas_num;
+ meas_sum += lchan->meas.interf_meas_dbm[i];
+ }
+
+ /* Calculate the average of all collected samples */
+ meas_avg = meas_sum / (int) meas_num;
+
+ /* 3GPP TS 48.008 defines 5 interference bands, and 6 interference level
+ * boundaries (0, X1, ... X5). It's not clear how to handle values
+ * exceeding the outer boundaries (0 or X5), because bands 0 and 6 do
+ * not exist (sigh). Let's map such values to closest bands 1 and 5. */
+ if (bts->interference.boundary[0] < bts->interference.boundary[5]) {
+ /* Ascending order (band=1 indicates lowest interference) */
+ for (b = 1; b < ARRAY_SIZE(bts->interference.boundary) - 1; b++) {
+ if (meas_avg < bts->interference.boundary[b])
+ break; /* Current 'b' is the band value */
+ }
+ } else {
+ /* Descending order (band=1 indicates highest interference) */
+ for (b = 1; b < ARRAY_SIZE(bts->interference.boundary) - 1; b++) {
+ if (meas_avg >= bts->interference.boundary[b])
+ break; /* Current 'b' is the band value */
+ }
+ }
+
+ LOGPLCHAN(lchan, DL1C, LOGL_DEBUG,
+ "Interference AVG: %ddBm (band %d, samples %u)\n",
+ meas_avg, b, meas_num);
+
+ lchan->meas.interf_meas_avg_dbm = meas_avg;
+ lchan->meas.interf_band = b;
+}
+
+/* determine the ECU codec constant for the codec used by given lchan */
+int lchan2ecu_codec(const struct gsm_lchan *lchan)
+{
+ const struct gsm_bts_trx_ts *ts = lchan->ts;
+
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ if (ts_pchan(ts) == GSM_PCHAN_TCH_H)
+ return OSMO_ECU_CODEC_HR;
+ else
+ return OSMO_ECU_CODEC_FR;
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ return OSMO_ECU_CODEC_EFR;
+ case GSM48_CMODE_SPEECH_AMR:
+ return OSMO_ECU_CODEC_AMR;
default:
- return false;
+ return -1;
}
}
diff --git a/src/common/load_indication.c b/src/common/load_indication.c
index c9b26458..d5b76ea2 100644
--- a/src/common/load_indication.c
+++ b/src/common/load_indication.c
@@ -41,6 +41,10 @@ static void load_timer_cb(void *data)
struct gsm_bts *bts = data;
unsigned int pch_percent, rach_percent;
+ /* It makes no sense to send Load Indication if CCCH is still disabled...*/
+ if (bts->c0->ts[0].mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ goto retry_later;
+
/* compute percentages */
if (bts->load.ccch.pch_total == 0)
pch_percent = 0;
@@ -73,6 +77,7 @@ static void load_timer_cb(void *data)
bts->load.rach.access);
}
+retry_later:
reset_load_counters(bts);
/* re-schedule the timer */
@@ -94,3 +99,8 @@ void load_timer_stop(struct gsm_bts *bts)
{
osmo_timer_del(&bts->load.ccch.timer);
}
+
+bool load_timer_is_running(const struct gsm_bts *bts)
+{
+ return osmo_timer_pending(&bts->load.ccch.timer);
+}
diff --git a/src/common/main.c b/src/common/main.c
index 0b7d3fb7..b5ba21b8 100644
--- a/src/common/main.c
+++ b/src/common/main.c
@@ -65,6 +65,7 @@ static int daemonize = 0;
static int rt_prio = -1;
static char *gsmtap_ip = 0;
extern int g_vty_port_num;
+static bool vty_test_mode = false;
static void print_help()
{
@@ -80,6 +81,8 @@ static void print_help()
"\nVTY reference generation:\n"
" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
" --vty-ref-xml Generate the VTY reference XML output and exit.\n"
+ "\nRegression testing:\n"
+ " --vty-test VTY test mode. Do not connect to BSC, do not exit.\n"
);
bts_model_print_help();
}
@@ -103,6 +106,9 @@ static void handle_long_options(const char *prog_name, const int long_option)
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
exit(0);
+ case 3:
+ vty_test_mode = true;
+ break;
default:
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
exit(2);
@@ -140,6 +146,7 @@ static void handle_options(int argc, char **argv)
{ "realtime", 1, 0, 'r' },
{ "vty-ref-mode", 1, &long_option, 1 },
{ "vty-ref-xml", 0, &long_option, 2 },
+ { "vty-test", 0, &long_option, 3 },
{ 0, 0, 0, 0 }
};
@@ -272,7 +279,6 @@ static int write_pid_file(char *procname)
int bts_main(int argc, char **argv)
{
struct gsm_bts_trx *trx;
- struct e1inp_line *line;
int rc;
/* Track the use of talloc NULL memory contexts */
@@ -300,6 +306,8 @@ int bts_main(int argc, char **argv)
handle_options(argc, argv);
fprintf(stderr, "((*))\n |\n / \\ OsmoBTS\n");
+ if (vty_test_mode)
+ fprintf(stderr, "--- VTY test mode: not connecting to BSC, not exiting ---\n");
g_bts = gsm_bts_alloc(tall_bts_ctx, 0);
if (!g_bts) {
@@ -397,16 +405,18 @@ int bts_main(int argc, char **argv)
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
- if (!g_bts->bsc_oml_host) {
- fprintf(stderr, "Cannot start BTS without knowing BSC OML IP\n");
- exit(1);
+ if (vty_test_mode) {
+ /* Just select-loop without connecting to the BSC, don't exit. This allows running tests on the VTY
+ * telnet port. */
+ while (!quit) {
+ log_reset_context();
+ osmo_select_main(0);
+ }
+ return EXIT_SUCCESS;
}
- line = abis_open(g_bts, g_bts->bsc_oml_host, "osmo-bts");
- if (!line) {
- fprintf(stderr, "unable to connect to BSC\n");
- exit(2);
- }
+ if (abis_open(g_bts, "osmo-bts") != 0)
+ exit(1);
rc = phy_links_open();
if (rc < 0) {
diff --git a/src/common/measurement.c b/src/common/measurement.c
index b5869872..1a5992b2 100644
--- a/src/common/measurement.c
+++ b/src/common/measurement.c
@@ -2,14 +2,18 @@
#include <stdint.h>
#include <errno.h>
-#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/endian.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_44_004.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/scheduler.h>
#include <osmo-bts/rsl.h>
+#include <osmo-bts/power_control.h>
#include <osmo-bts/ta_control.h>
/* Tables as per TS 45.008 Section 8.3 */
@@ -342,7 +346,7 @@ int lchan_new_ul_meas(struct gsm_lchan *lchan,
if (!ulm->is_sub)
dest->is_sub = ts45008_83_is_sub(lchan, fn);
- DEBUGPFN(DMEAS, fn, "%s adding measurement (ber10k=%u, ta_offs=%d, ci=%0.2f, is_sub=%u, rssi=-%u), num_ul_meas=%d, fn_mod=%u\n",
+ DEBUGPFN(DMEAS, fn, "%s adding measurement (ber10k=%u, ta_offs=%d, ci_cB=%d, is_sub=%u, rssi=-%u), num_ul_meas=%d, fn_mod=%u\n",
gsm_lchan_name(lchan), ulm->ber10k, ulm->ta_offs_256bits,
ulm->c_i, dest->is_sub, ulm->inv_rssi, lchan->meas.num_ul_meas,
fn_mod);
@@ -556,8 +560,10 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
struct gsm_meas_rep_unidir *mru;
uint32_t ber_full_sum = 0;
uint32_t irssi_full_sum = 0;
+ int32_t ci_full_sum = 0;
uint32_t ber_sub_sum = 0;
uint32_t irssi_sub_sum = 0;
+ int32_t ci_sub_sum = 0;
int32_t ta256b_sum = 0;
unsigned int num_meas_sub = 0;
unsigned int num_meas_sub_actual = 0;
@@ -589,7 +595,7 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
/* When AMR is used, we expect at least one SUB frame, since
* the SACCH will always be SUB frame. There may occur more
* SUB frames but since DTX periods in AMR are dynamic, we
- * can not know how much exactly. */
+ * can not know how many exactly. */
num_meas_sub_expect = 1;
}
@@ -625,11 +631,13 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
m = &lchan->meas.uplink[i + num_ul_meas_excess];
if (m->is_sub) {
irssi_sub_sum += m->inv_rssi;
+ ci_sub_sum += m->c_i;
num_meas_sub_actual++;
is_sub = true;
}
irssi_full_sum += m->inv_rssi;
ta256b_sum += m->ta_offs_256bits;
+ ci_full_sum += m->c_i;
num_ul_meas_actual++;
} else {
@@ -698,27 +706,32 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
else
irssi_full_sum = irssi_full_sum / num_ul_meas_actual;
- if (!num_ul_meas_actual)
+ if (!num_ul_meas_actual) {
ta256b_sum = lchan->meas.ms_toa256;
- else
+ ci_full_sum = lchan->meas.ul_ci_cb_full;
+ } else {
ta256b_sum = ta256b_sum / (signed)num_ul_meas_actual;
+ ci_full_sum = ci_full_sum / (signed)num_ul_meas_actual;
+ }
if (!num_meas_sub)
ber_sub_sum = MEASUREMENT_DUMMY_BER;
else
ber_sub_sum = ber_sub_sum / num_meas_sub;
- if (!num_meas_sub_actual)
+ if (!num_meas_sub_actual) {
irssi_sub_sum = MEASUREMENT_DUMMY_IRSSI;
- else
+ ci_sub_sum = lchan->meas.ul_ci_cb_sub;
+ } else {
irssi_sub_sum = irssi_sub_sum / num_meas_sub_actual;
+ ci_sub_sum = ci_sub_sum / (signed)num_meas_sub_actual;
+ }
LOGPLCHAN(lchan, DMEAS, LOGL_INFO,
- "Computed TA256(% 4d) BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), "
- "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm)\n",
- ta256b_sum, ber_full_sum / 100, ber_full_sum % 100,
- irssi_full_sum, ber_sub_sum / 100, ber_sub_sum % 100,
- irssi_sub_sum);
+ "Computed TA256(% 4d), BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), C/I-FULL(% 4d cB), "
+ "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm), C/I-SUB(% 4d cB)\n",
+ ta256b_sum, ber_full_sum / 100, ber_full_sum % 100, irssi_full_sum, ci_full_sum,
+ ber_sub_sum / 100, ber_sub_sum % 100, irssi_sub_sum, ci_sub_sum);
/* store results */
mru = &lchan->meas.ul_res;
@@ -727,6 +740,8 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
mru->full.rx_qual = ber10k_to_rxqual(ber_full_sum);
mru->sub.rx_qual = ber10k_to_rxqual(ber_sub_sum);
lchan->meas.ms_toa256 = ta256b_sum;
+ lchan->meas.ul_ci_cb_full = ci_full_sum;
+ lchan->meas.ul_ci_cb_sub = ci_sub_sum;
LOGPLCHAN(lchan, DMEAS, LOGL_INFO,
"UL MEAS RXLEV_FULL(%u), RXLEV_SUB(%u), RXQUAL_FULL(%u), RXQUAL_SUB(%u), "
@@ -739,11 +754,6 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
lchan_meas_compute_extended(lchan);
- /* Compute new ta_req value. This has to be done here since the value
- * in lchan->meas.num_ul_meas together with lchan->meas.ms_toa256
- * is needed for the computation. */
- lchan_ms_ta_ctrl(lchan);
-
lchan->meas.num_ul_meas = 0;
/* return 1 to indicate that the computation has been done and the next
@@ -771,3 +781,226 @@ void lchan_meas_reset(struct gsm_lchan *lchan)
memset(&lchan->meas, 0, sizeof(lchan->meas));
lchan->meas.last_fn = LCHAN_FN_DUMMY;
}
+
+static inline uint8_t ms_to2rsl(const struct gsm_lchan *lchan, uint8_t ta)
+{
+ return (lchan->ms_t_offs >= 0) ? lchan->ms_t_offs : (lchan->p_offs - ta);
+}
+
+static inline bool ms_to_valid(const struct gsm_lchan *lchan)
+{
+ return (lchan->ms_t_offs >= 0) || (lchan->p_offs >= 0);
+}
+
+/* Decide if repeated FACCH should be applied or not. If RXQUAL level, that the
+ * MS reports is high enough, FACCH repetition is not needed. */
+static void repeated_dl_facch_active_decision(struct gsm_lchan *lchan,
+ const struct gsm48_meas_res *meas_res)
+{
+ uint8_t upper;
+ uint8_t lower;
+ uint8_t rxqual;
+ bool prev_repeated_dl_facch_active = lchan->rep_acch.dl_facch_active;
+
+ /* This is an optimization so that we exit as quickly as possible if
+ * there are no FACCH repetition capabilities present. However If the
+ * repeated FACCH capabilities vanish for whatever reason, we must be
+ * sure that FACCH repetition is disabled. */
+ if (!lchan->rep_acch_cap.dl_facch_cmd
+ && !lchan->rep_acch_cap.dl_facch_all) {
+ lchan->rep_acch.dl_facch_active = false;
+ goto out;
+ }
+
+ /* Threshold disabled (always on) */
+ if (lchan->rep_acch_cap.rxqual == 0) {
+ lchan->rep_acch.dl_facch_active = true;
+ goto out;
+ }
+
+ /* When the MS sets the SRR bit in the UL-SACCH L1 header
+ * (repeated SACCH requested) then it makes sense to enable
+ * FACCH repetition too. */
+ if (lchan->meas.l1_info.srr_sro) {
+ lchan->rep_acch.dl_facch_active = true;
+ goto out;
+ }
+
+ /* Parse MS measurement results */
+ if (meas_res == NULL)
+ goto out;
+ if (!gsm48_meas_res_is_valid(meas_res))
+ goto out;
+
+ /* If the RXQUAL level at the MS drops under a certain threshold
+ * we enable FACCH repetition. */
+ upper = lchan->rep_acch_cap.rxqual;
+ if (upper > 2)
+ lower = lchan->rep_acch_cap.rxqual - 2;
+ else
+ lower = 0;
+
+ /* When downlink DTX is applied, use RXQUAL-SUB, otherwise use
+ * RXQUAL-FULL. */
+ if (meas_res->dtx_used)
+ rxqual = meas_res->rxqual_sub;
+ else
+ rxqual = meas_res->rxqual_full;
+
+ if (rxqual >= upper)
+ lchan->rep_acch.dl_facch_active = true;
+ else if (rxqual <= lower)
+ lchan->rep_acch.dl_facch_active = false;
+
+out:
+ if (lchan->rep_acch.dl_facch_active == prev_repeated_dl_facch_active)
+ return;
+ if (lchan->rep_acch.dl_facch_active)
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-FACCH repetition: inactive => active\n");
+ else
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-FACCH repetition: active => inactive\n");
+}
+
+static void acch_overpower_active_decision(struct gsm_lchan *lchan,
+ const struct gsm48_meas_res *meas_res)
+{
+ const bool old = lchan->top_acch_active;
+ uint8_t upper, lower, rxqual;
+
+ /* ACCH overpower is not allowed => nothing to do */
+ if (lchan->top_acch_cap.overpower_db == 0)
+ return;
+ /* RxQual threshold is disabled => overpower is always on */
+ if (lchan->top_acch_cap.rxqual == 0)
+ return;
+
+ /* If DTx is active on Downlink, use the '-SUB' */
+ if (meas_res->dtx_used)
+ rxqual = meas_res->rxqual_sub;
+ else /* ... otherwise use the '-FULL' */
+ rxqual = meas_res->rxqual_full;
+
+ upper = lchan->top_acch_cap.rxqual;
+ if (upper > 2)
+ lower = upper - 2;
+ else
+ lower = 0;
+
+ if (rxqual >= upper)
+ lchan->top_acch_active = true;
+ else if (rxqual <= lower)
+ lchan->top_acch_active = false;
+
+ if (lchan->top_acch_active != old) {
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "Temporary ACCH overpower: %s\n",
+ lchan->top_acch_active ? "inactive => active"
+ : "active => inactive");
+ }
+}
+
+static bool data_is_rr_meas_rep(const uint8_t *data)
+{
+ const struct gsm48_hdr *gh = (void *)(data + 5);
+ const uint8_t *lapdm_hdr = (void *)(data + 2);
+
+ /* LAPDm address field: SAPI=0, C/R=0, EA=1 */
+ if (lapdm_hdr[0] != 0x01)
+ return false;
+ /* LAPDm control field: U, func=UI */
+ if (lapdm_hdr[1] != 0x03)
+ return false;
+ /* Protocol discriminator: RR */
+ if (gh->proto_discr != GSM48_PDISC_RR)
+ return false;
+
+ switch (gh->msg_type) {
+ case GSM48_MT_RR_EXT_MEAS_REP:
+ case GSM48_MT_RR_MEAS_REP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Called every time a SACCH block is received from lower layers */
+void lchan_meas_handle_sacch(struct gsm_lchan *lchan, struct msgb *msg)
+{
+ const struct gsm48_meas_res *mr = NULL;
+ const struct gsm48_hdr *gh = NULL;
+ int timing_offset, rc;
+ bool dtxu_used = true; /* safe default assumption */
+ uint8_t ms_pwr;
+ uint8_t ms_ta;
+ int8_t ul_rssi;
+ int16_t ul_ci_cb;
+
+ if (msgb_l2len(msg) == GSM_MACBLOCK_LEN) {
+ /* Some brilliant engineer decided that the ordering of
+ * fields on the Um interface is different from the
+ * order of fields in RSL. See 3GPP TS 44.004 (section 7.2)
+ * vs. 3GPP TS 48.058 (section 9.3.10). */
+ const struct gsm_sacch_l1_hdr *l1h = msgb_l2(msg);
+ lchan->meas.l1_info.ms_pwr = l1h->ms_pwr;
+ lchan->meas.l1_info.fpc_epc = l1h->fpc_epc;
+ lchan->meas.l1_info.srr_sro = l1h->srr_sro;
+ lchan->meas.l1_info.ta = l1h->ta;
+ lchan->meas.flags |= LC_UL_M_F_L1_VALID;
+
+ /* Check if this is a Measurement Report */
+ if (data_is_rr_meas_rep(msgb_l2(msg))) {
+ /* Skip both L1 SACCH and LAPDm headers */
+ msg->l3h = (void *)(msg->l2h + 2 + 3);
+ gh = msgb_l3(msg);
+ }
+
+ ms_pwr = lchan->meas.l1_info.ms_pwr;
+ ms_ta = lchan->meas.l1_info.ta;
+ } else {
+ lchan->meas.flags &= ~LC_UL_M_F_L1_VALID;
+ ms_pwr = lchan->ms_power_ctrl.current;
+ ms_ta = lchan->ta_ctrl.current;
+ }
+
+ timing_offset = ms_to_valid(lchan) ? ms_to2rsl(lchan, ms_ta) : -1;
+ rc = rsl_tx_meas_res(lchan, msgb_l3(msg), msgb_l3len(msg), timing_offset);
+ if (rc == 0) /* Count successful transmissions */
+ lchan->meas.res_nr++;
+
+ /* Run control loops now that we have all the information: */
+ /* 3GPP TS 45.008 sec 4.2: UL L1 SACCH Header contains TA and
+ * MS_PWR used "for the last burst of the previous SACCH
+ * period". Since MS must use the values provided in DL SACCH
+ * starting at next meas period, the value of the "last burst"
+ * is actually the value used in the entire meas period. Since
+ * it contains info about the previous meas period, we want to
+ * feed the Control Loop with the measurements for the same
+ * period (the previous one), which is stored in lchan->meas(.ul_res):
+ */
+ if (gh && gh->msg_type == GSM48_MT_RR_MEAS_REP) {
+ mr = (const struct gsm48_meas_res *)gh->data;
+ if (gsm48_meas_res_is_valid(mr))
+ dtxu_used = mr->dtx_used;
+ }
+
+ if (dtxu_used) {
+ ul_rssi = rxlev2dbm(lchan->meas.ul_res.sub.rx_lev);
+ ul_ci_cb = lchan->meas.ul_ci_cb_sub;
+ } else {
+ ul_rssi = rxlev2dbm(lchan->meas.ul_res.full.rx_lev);
+ ul_ci_cb = lchan->meas.ul_ci_cb_full;
+ }
+ lchan_ms_ta_ctrl(lchan, ms_ta, lchan->meas.ms_toa256);
+ lchan_ms_pwr_ctrl(lchan, ms_pwr, ul_rssi, ul_ci_cb);
+ if (mr && gsm48_meas_res_is_valid(mr)) {
+ lchan_bs_pwr_ctrl(lchan, mr);
+ acch_overpower_active_decision(lchan, mr);
+ }
+
+ repeated_dl_facch_active_decision(lchan, mr);
+
+ /* Reset state for next iteration */
+ lchan->tch.dtx.dl_active = false;
+ lchan->meas.flags &= ~LC_UL_M_F_OSMO_EXT_VALID;
+ lchan->ms_t_offs = -1;
+ lchan->p_offs = -1;
+}
diff --git a/src/common/nm_bb_transc_fsm.c b/src/common/nm_bb_transc_fsm.c
index 936451ff..ca78256c 100644
--- a/src/common/nm_bb_transc_fsm.c
+++ b/src/common/nm_bb_transc_fsm.c
@@ -41,6 +41,17 @@
#define nm_bb_transc_fsm_state_chg(fi, NEXT_STATE) \
osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+static void ev_dispatch_children(struct gsm_bts_bb_trx *bb_transc, uint32_t event)
+{
+ struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+ uint8_t tn;
+
+ for (tn = 0; tn < TRX_NR_TS; tn++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ osmo_fsm_inst_dispatch(ts->mo.fi, event, NULL);
+ }
+}
+
//////////////////////////
// FSM STATE ACTIONS
//////////////////////////
@@ -48,8 +59,12 @@
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+ /* Reset state: */
+ TALLOC_FREE(bb_transc->mo.nm_attr);
+
+ bb_transc->mo.setattr_success = false;
bb_transc->mo.opstart_success = false;
- oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -88,8 +103,9 @@ static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t p
struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
int i;
+ bb_transc->mo.setattr_success = false;
bb_transc->mo.opstart_success = false;
- oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
if (prev_state == NM_BBTRANSC_ST_OP_ENABLED) {
for (i = 0; i < TRX_NR_TS; i++) {
@@ -103,10 +119,17 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
{
struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+ struct nm_fsm_ev_setattr_data *setattr_data;
bool phy_state_connected;
bool rsl_link_connected;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ bb_transc->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
bb_transc->mo.opstart_success = true;
oml_mo_opstart_ack(&bb_transc->mo);
@@ -139,14 +162,15 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
rsl_link_connected = true;
}
+ /* We so far don't expect any SetAttributes for this NM object */
if (rsl_link_connected && phy_state_connected &&
bb_transc->mo.opstart_success) {
nm_bb_transc_fsm_state_chg(fi, NM_BBTRANSC_ST_OP_ENABLED);
} else {
LOGPFSML(fi, LOGL_INFO, "Delay switch to operative state Enabled, wait for:%s%s%s\n",
- rsl_link_connected ? "": " rsl",
- phy_state_connected ? "": " phy",
- bb_transc->mo.opstart_success ? "": " opstart");
+ rsl_link_connected ? "" : " rsl",
+ phy_state_connected ? "" : " phy",
+ bb_transc->mo.opstart_success ? "" : " opstart");
}
}
@@ -157,7 +181,7 @@ static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state
struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
uint8_t tn;
- oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
/* Mark Dependency TS as Offline (ready to be Opstarted) */
for (tn = 0; tn < TRX_NR_TS; tn++) {
struct gsm_bts_trx_ts *ts = &trx->ts[tn];
@@ -181,6 +205,28 @@ static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
nm_bb_transc_fsm_state_chg(fi, NM_BBTRANSC_ST_OP_DISABLED_OFFLINE);
}
+static void nm_bb_transc_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&bb_transc->mo, -1, -1, NM_STATE_SHUTDOWN);
+
+ /* Propagate event to children: */
+ ev_dispatch_children(bb_transc, event);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ /* Propagate event to children: */
+ ev_dispatch_children(bb_transc, event);
+ nm_bb_transc_fsm_state_chg(fi, NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_bb_transc_fsm_states[] = {
[NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
@@ -188,8 +234,10 @@ static struct osmo_fsm_state nm_bb_transc_fsm_states[] = {
X(NM_EV_RSL_UP) |
X(NM_EV_RSL_DOWN) |
X(NM_EV_PHYLINK_UP) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BBTRANSC_ST_OP_DISABLED_OFFLINE),
.name = "DISABLED_NOTINSTALLED",
.onenter = st_op_disabled_notinstalled_on_enter,
@@ -197,13 +245,17 @@ static struct osmo_fsm_state nm_bb_transc_fsm_states[] = {
},
[NM_BBTRANSC_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK) |
X(NM_EV_RSL_UP) |
X(NM_EV_RSL_DOWN) |
X(NM_EV_PHYLINK_UP) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BBTRANSC_ST_OP_ENABLED),
.name = "DISABLED_OFFLINE",
.onenter = st_op_disabled_offline_on_enter,
@@ -212,8 +264,10 @@ static struct osmo_fsm_state nm_bb_transc_fsm_states[] = {
[NM_BBTRANSC_ST_OP_ENABLED] = {
.in_event_mask =
X(NM_EV_RSL_DOWN) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BBTRANSC_ST_OP_DISABLED_OFFLINE),
.name = "ENABLED",
.onenter = st_op_enabled_on_enter,
@@ -226,6 +280,9 @@ struct osmo_fsm nm_bb_transc_fsm = {
.states = nm_bb_transc_fsm_states,
.num_states = ARRAY_SIZE(nm_bb_transc_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_bb_transc_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/nm_bts_fsm.c b/src/common/nm_bts_fsm.c
index 12f1a612..063ffe84 100644
--- a/src/common/nm_bts_fsm.c
+++ b/src/common/nm_bts_fsm.c
@@ -35,12 +35,22 @@
#include <osmo-bts/rsl.h>
#include <osmo-bts/nm_common_fsm.h>
#include <osmo-bts/phy_link.h>
+#include <osmo-bts/cbch.h>
#define X(s) (1 << (s))
#define nm_bts_fsm_state_chg(fi, NEXT_STATE) \
osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+static void ev_dispatch_children(struct gsm_bts *bts, uint32_t event)
+{
+ struct gsm_bts_trx *trx;
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ osmo_fsm_inst_dispatch(trx->mo.fi, event, NULL);
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, event, NULL);
+ }
+}
+
//////////////////////////
// FSM STATE ACTIONS
//////////////////////////
@@ -48,17 +58,38 @@
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ /* Reset state: */
+ bts->si_valid = 0;
+ TALLOC_FREE(bts->mo.nm_attr);
+ bts_cbch_reset(bts);
+
+ bts->mo.setattr_success = false;
bts->mo.opstart_success = false;
- oml_mo_state_chg(&bts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&bts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ struct gsm_bts_trx *trx;
switch (event) {
case NM_EV_SW_ACT:
oml_mo_tx_sw_act_rep(&bts->mo);
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx->bts->variant == BTS_OSMO_OMLDUMMY) /* In OMLDUMMY, phy=NULL */
+ continue;
+ /* During startup, phy_links are already opened, but if we are
+ * re-connecting, phy_link was closed when disconnected from
+ * previous BSC, so let's re-open it.
+ */
+ struct phy_instance *pinst = trx_phy_instance(trx);
+ struct phy_link *plink = pinst->phy_link;
+ if (phy_link_state_get(plink) == PHY_LINK_SHUTDOWN)
+ bts_model_phy_link_open(plink);
+ }
+
nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_OFFLINE);
return;
default:
@@ -69,15 +100,23 @@ static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event
static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ bts->mo.setattr_success = false;
bts->mo.opstart_success = false;
- oml_mo_state_chg(&bts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&bts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
}
static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ bts->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
bts->mo.opstart_success = true;
oml_mo_opstart_ack(&bts->mo);
@@ -95,18 +134,41 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
- oml_mo_state_chg(&bts->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bts->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
}
static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
}
+static void nm_bts_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&bts->mo, -1, -1, NM_STATE_SHUTDOWN);
+
+ /* Propagate event to children: */
+ ev_dispatch_children(bts, event);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ /* Propagate event to children: */
+ ev_dispatch_children(bts, event);
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_bts_fsm_states[] = {
[NM_BTS_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
X(NM_EV_SW_ACT),
.out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BTS_ST_OP_DISABLED_OFFLINE),
.name = "DISABLED_NOTINSTALLED",
.onenter = st_op_disabled_notinstalled_on_enter,
@@ -114,9 +176,12 @@ static struct osmo_fsm_state nm_bts_fsm_states[] = {
},
[NM_BTS_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK),
.out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BTS_ST_OP_ENABLED),
.name = "DISABLED_OFFLINE",
.onenter = st_op_disabled_offline_on_enter,
@@ -124,7 +189,8 @@ static struct osmo_fsm_state nm_bts_fsm_states[] = {
},
[NM_BTS_ST_OP_ENABLED] = {
.in_event_mask = 0,
- .out_state_mask = 0,
+ .out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_NOTINSTALLED),
.name = "ENABLED",
.onenter = st_op_enabled_on_enter,
.action = st_op_enabled,
@@ -136,6 +202,9 @@ struct osmo_fsm nm_bts_fsm = {
.states = nm_bts_fsm_states,
.num_states = ARRAY_SIZE(nm_bts_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_bts_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/nm_bts_sm_fsm.c b/src/common/nm_bts_sm_fsm.c
index dedbacbd..2d433153 100644
--- a/src/common/nm_bts_sm_fsm.c
+++ b/src/common/nm_bts_sm_fsm.c
@@ -41,6 +41,13 @@
#define nm_bts_sm_fsm_state_chg(fi, NEXT_STATE) \
osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+static void ev_dispatch_children(struct gsm_bts_sm *site_mgr, uint32_t event)
+{
+ struct gsm_bts *bts = gsm_bts_sm_get_bts(site_mgr);
+ osmo_fsm_inst_dispatch(bts->mo.fi, event, NULL);
+}
+
//////////////////////////
// FSM STATE ACTIONS
//////////////////////////
@@ -48,8 +55,9 @@
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+ site_mgr->mo.setattr_success = false;
site_mgr->mo.opstart_success = false;
- oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -69,15 +77,23 @@ static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event
static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+ site_mgr->mo.setattr_success = false;
site_mgr->mo.opstart_success = false;
- oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
}
static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ site_mgr->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
site_mgr->mo.opstart_success = true;
oml_mo_opstart_ack(&site_mgr->mo);
@@ -95,18 +111,41 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
- oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
}
static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
}
+static void nm_bts_sm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&site_mgr->mo, -1, -1, NM_STATE_SHUTDOWN);
+
+ /* Propagate event to children: */
+ ev_dispatch_children(site_mgr, event);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ /* Propagate event to children: */
+ ev_dispatch_children(site_mgr, event);
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_bts_sm_fsm_states[] = {
[NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
X(NM_EV_SW_ACT),
.out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BTS_SM_ST_OP_DISABLED_OFFLINE),
.name = "DISABLED_NOTINSTALLED",
.onenter = st_op_disabled_notinstalled_on_enter,
@@ -114,9 +153,12 @@ static struct osmo_fsm_state nm_bts_sm_fsm_states[] = {
},
[NM_BTS_SM_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK),
.out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BTS_SM_ST_OP_ENABLED),
.name = "DISABLED_OFFLINE",
.onenter = st_op_disabled_offline_on_enter,
@@ -124,7 +166,8 @@ static struct osmo_fsm_state nm_bts_sm_fsm_states[] = {
},
[NM_BTS_SM_ST_OP_ENABLED] = {
.in_event_mask = 0,
- .out_state_mask = 0,
+ .out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED),
.name = "ENABLED",
.onenter = st_op_enabled_on_enter,
.action = st_op_enabled,
@@ -136,6 +179,9 @@ struct osmo_fsm nm_bts_sm_fsm = {
.states = nm_bts_sm_fsm_states,
.num_states = ARRAY_SIZE(nm_bts_sm_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_bts_sm_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/nm_channel_fsm.c b/src/common/nm_channel_fsm.c
index 15be6c6a..503ddfb9 100644
--- a/src/common/nm_channel_fsm.c
+++ b/src/common/nm_channel_fsm.c
@@ -55,8 +55,15 @@ static bool ts_can_be_enabled(const struct gsm_bts_trx_ts *ts)
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+ /* Reset state: */
+ gsm_ts_release(ts);
+ if (ts->vamos.peer)
+ gsm_ts_release(ts->vamos.peer);
+ TALLOC_FREE(ts->mo.nm_attr);
+
+ ts->mo.setattr_success = false;
ts->mo.opstart_success = false;
- oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -80,14 +87,21 @@ static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
ts->mo.opstart_success = false;
- oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
+ oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY, -1);
}
static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ ts->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
LOGPFSML(fi, LOGL_NOTICE, "BSC trying to activate TS while still in avail=dependency. "
"Allowing it to stay backward-compatible with older osmo-bts versions, but BSC is wrong.\n");
@@ -117,14 +131,21 @@ static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t p
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
ts->mo.opstart_success = false;
- oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
}
static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ ts->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
ts->mo.opstart_success = true;
oml_mo_opstart_ack(&ts->mo);
@@ -147,7 +168,7 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
- oml_mo_state_chg(&ts->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&ts->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
}
static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -168,11 +189,29 @@ static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
}
}
+static void nm_chan_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&ts->mo, -1, -1, NM_STATE_SHUTDOWN);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_chan_fsm_states[] = {
[NM_CHAN_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
X(NM_EV_BBTRANSC_INSTALLED),
.out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY),
.name = "DISABLED_NOTINSTALLED",
@@ -181,6 +220,8 @@ static struct osmo_fsm_state nm_chan_fsm_states[] = {
},
[NM_CHAN_ST_OP_DISABLED_DEPENDENCY] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) | /* backward compatibility, buggy BSC */
X(NM_EV_OPSTART_NACK) |
X(NM_EV_BBTRANSC_ENABLED) |
@@ -188,6 +229,7 @@ static struct osmo_fsm_state nm_chan_fsm_states[] = {
X(NM_EV_BBTRANSC_DISABLED) |
X(NM_EV_RCARRIER_DISABLED),
.out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
X(NM_CHAN_ST_OP_ENABLED), /* backward compatibility, buggy BSC */
.name = "DISABLED_DEPENDENCY",
@@ -196,11 +238,14 @@ static struct osmo_fsm_state nm_chan_fsm_states[] = {
},
[NM_CHAN_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK) |
X(NM_EV_BBTRANSC_DISABLED) |
X(NM_EV_RCARRIER_DISABLED),
.out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_CHAN_ST_OP_ENABLED) |
X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY),
.name = "DISABLED_OFFLINE",
@@ -213,6 +258,7 @@ static struct osmo_fsm_state nm_chan_fsm_states[] = {
X(NM_EV_RCARRIER_DISABLED) |
X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY),
.name = "ENABLED",
@@ -226,6 +272,9 @@ struct osmo_fsm nm_chan_fsm = {
.states = nm_chan_fsm_states,
.num_states = ARRAY_SIZE(nm_chan_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_chan_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/nm_common_fsm.c b/src/common/nm_common_fsm.c
index 7273e22f..be11bef3 100644
--- a/src/common/nm_common_fsm.c
+++ b/src/common/nm_common_fsm.c
@@ -25,8 +25,12 @@
const struct value_string nm_fsm_event_names[] = {
{ NM_EV_SW_ACT, "SW_ACT" },
+ { NM_EV_SETATTR_ACK, "SETATTR_ACK" },
+ { NM_EV_SETATTR_NACK, "SETATTR_NACK" },
{ NM_EV_OPSTART_ACK, "OPSTART_ACK" },
{ NM_EV_OPSTART_NACK, "OPSTART_NACK" },
+ { NM_EV_SHUTDOWN_START, "SHUTDOWN_START" },
+ { NM_EV_SHUTDOWN_FINISH, "SHUTDOWN_FINISH" },
{ NM_EV_RSL_UP, "RSL_UP" },
{ NM_EV_RSL_DOWN, "RSL_DOWN" },
{ NM_EV_PHYLINK_UP, "PHYLINK_UP" },
diff --git a/src/common/nm_radio_carrier_fsm.c b/src/common/nm_radio_carrier_fsm.c
index 4cbdf682..88930dd7 100644
--- a/src/common/nm_radio_carrier_fsm.c
+++ b/src/common/nm_radio_carrier_fsm.c
@@ -48,8 +48,12 @@
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+ /* Reset state: */
+ TALLOC_FREE(trx->mo.nm_attr);
+
+ trx->mo.setattr_success = false;
trx->mo.opstart_success = false;
- oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -81,8 +85,9 @@ static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t p
struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
unsigned int i;
+ trx->mo.setattr_success = false;
trx->mo.opstart_success = false;
- oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
if (prev_state == NM_RCARRIER_ST_OP_ENABLED) {
for (i = 0; i < TRX_NR_TS; i++) {
@@ -95,10 +100,17 @@ static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t p
static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
bool phy_state_connected;
bool rsl_link_connected;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ trx->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
trx->mo.opstart_success = true;
oml_mo_opstart_ack(&trx->mo);
@@ -131,13 +143,14 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
}
if (rsl_link_connected && phy_state_connected &&
- trx->mo.opstart_success) {
+ trx->mo.setattr_success && trx->mo.opstart_success) {
nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_ENABLED);
} else {
- LOGPFSML(fi, LOGL_INFO, "Delay switch to operative state Enabled, wait for:%s%s%s\n",
- rsl_link_connected ? "": " rsl",
- phy_state_connected ? "": " phy",
- trx->mo.opstart_success ? "": " opstart");
+ LOGPFSML(fi, LOGL_INFO, "Delay switch to operative state Enabled, wait for:%s%s%s%s\n",
+ rsl_link_connected ? "" : " rsl",
+ phy_state_connected ? "" : " phy",
+ trx->mo.setattr_success ? "" : " setattr",
+ trx->mo.opstart_success ? "" : " opstart");
}
}
@@ -147,7 +160,7 @@ static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state
struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
unsigned int tn;
- oml_mo_state_chg(&trx->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&trx->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
/* Mark Dependency TS as Offline (ready to be Opstarted) */
for (tn = 0; tn < TRX_NR_TS; tn++) {
struct gsm_bts_trx_ts *ts = &trx->ts[tn];
@@ -171,6 +184,23 @@ static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_OFFLINE);
}
+static void nm_rcarrier_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&trx->mo, -1, -1, NM_STATE_SHUTDOWN);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_rcarrier_fsm_states[] = {
[NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
@@ -178,8 +208,10 @@ static struct osmo_fsm_state nm_rcarrier_fsm_states[] = {
X(NM_EV_RSL_UP) |
X(NM_EV_RSL_DOWN) |
X(NM_EV_PHYLINK_UP) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_RCARRIER_ST_OP_DISABLED_OFFLINE),
.name = "DISABLED_NOTINSTALLED",
.onenter = st_op_disabled_notinstalled_on_enter,
@@ -187,13 +219,17 @@ static struct osmo_fsm_state nm_rcarrier_fsm_states[] = {
},
[NM_RCARRIER_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK) |
X(NM_EV_RSL_UP) |
X(NM_EV_RSL_DOWN) |
X(NM_EV_PHYLINK_UP) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_RCARRIER_ST_OP_ENABLED),
.name = "DISABLED_OFFLINE",
.onenter = st_op_disabled_offline_on_enter,
@@ -202,8 +238,10 @@ static struct osmo_fsm_state nm_rcarrier_fsm_states[] = {
[NM_RCARRIER_ST_OP_ENABLED] = {
.in_event_mask =
X(NM_EV_RSL_DOWN) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_RCARRIER_ST_OP_DISABLED_OFFLINE),
.name = "ENABLED",
.onenter = st_op_enabled_on_enter,
@@ -216,6 +254,9 @@ struct osmo_fsm nm_rcarrier_fsm = {
.states = nm_rcarrier_fsm_states,
.num_states = ARRAY_SIZE(nm_rcarrier_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_rcarrier_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/oml.c b/src/common/oml.c
index 819b033a..724e4f81 100644
--- a/src/common/oml.c
+++ b/src/common/oml.c
@@ -340,12 +340,13 @@ void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state)
mo->nm_state.operational = op_state;
}
-int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
+int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state, int adm_state)
{
int rc = 0;
if ((op_state != -1 && mo->nm_state.operational != op_state) ||
- (avail_state != -1 && mo->nm_state.availability != avail_state)) {
+ (avail_state != -1 && mo->nm_state.availability != avail_state) ||
+ (adm_state != -1 && mo->nm_state.administrative != adm_state)) {
if (avail_state != -1) {
LOGP(DOML, LOGL_INFO, "%s AVAIL STATE %s -> %s\n",
gsm_abis_mo_name(mo),
@@ -354,14 +355,26 @@ int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
mo->nm_state.availability = avail_state;
}
if (op_state != -1) {
+ struct nm_statechg_signal_data nsd;
LOGP(DOML, LOGL_INFO, "%s OPER STATE %s -> %s\n",
gsm_abis_mo_name(mo),
abis_nm_opstate_name(mo->nm_state.operational),
abis_nm_opstate_name(op_state));
+ nsd.mo = mo;
+ nsd.old_state = mo->nm_state.operational;
+ nsd.new_state = op_state;
mo->nm_state.operational = op_state;
- osmo_signal_dispatch(SS_GLOBAL, S_NEW_OP_STATE, NULL);
+ osmo_signal_dispatch(SS_GLOBAL, S_NEW_OP_STATE, &nsd);
+ }
+ if (adm_state != -1) {
+ LOGP(DOML, LOGL_INFO, "%s ADMIN STATE %s -> %s\n",
+ gsm_abis_mo_name(mo),
+ abis_nm_admin_name(mo->nm_state.administrative),
+ abis_nm_admin_name(adm_state));
+ mo->nm_state.administrative = adm_state;
}
+
/* send state change report */
rc = oml_tx_state_changed(mo);
}
@@ -567,6 +580,7 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
/* merge existing BTS attributes with new attributes */
tp_merged = osmo_tlvp_copy(bts->mo.nm_attr, bts);
+ talloc_set_name_const(tp_merged, "oml_bts_attr");
osmo_tlvp_merge(tp_merged, &tp);
/* Ask BTS driver to validate new merged attributes */
@@ -585,7 +599,7 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
/* 9.4.25 Interference Level Boundaries */
if (TLVP_PRES_LEN(&tp, NM_ATT_INTERF_BOUND, 6)) {
payload = TLVP_VAL(&tp, NM_ATT_INTERF_BOUND);
- for (i = 0; i < 6; i++) {
+ for (i = 0; i < ARRAY_SIZE(bts->interference.boundary); i++) {
const int16_t boundary = payload[i];
bts->interference.boundary[i] = -1 * boundary;
}
@@ -661,7 +675,10 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
/* 9.4.11 CCCH Load Indication Period */
if (TLVP_PRES_LEN(&tp, NM_ATT_CCCH_L_I_P, 1)) {
bts->load.ccch.load_ind_period = *TLVP_VAL(&tp, NM_ATT_CCCH_L_I_P);
- load_timer_start(bts);
+ if (load_timer_is_running(bts)) {
+ load_timer_stop(bts);
+ load_timer_start(bts);
+ }
}
/* 9.4.44 RACH Busy Threshold */
@@ -719,8 +736,9 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
}
- /* merge existing BTS attributes with new attributes */
- tp_merged = osmo_tlvp_copy(trx->mo.nm_attr, trx->bts);
+ /* merge existing TRX attributes with new attributes */
+ tp_merged = osmo_tlvp_copy(trx->mo.nm_attr, trx);
+ talloc_set_name_const(tp_merged, "oml_trx_attr");
osmo_tlvp_merge(tp_merged, &tp);
/* Ask BTS driver to validate new merged attributes */
@@ -730,7 +748,7 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
return oml_fom_ack_nack(msg, -rc);
}
- /* Success: replace old BTS attributes with new */
+ /* Success: replace old TRX attributes with new */
talloc_free(trx->mo.nm_attr);
trx->mo.nm_attr = tp_merged;
@@ -933,8 +951,9 @@ static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg)
return oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP);
}
- /* merge existing BTS attributes with new attributes */
- tp_merged = osmo_tlvp_copy(ts->mo.nm_attr, bts);
+ /* merge existing CHAN attributes with new attributes */
+ tp_merged = osmo_tlvp_copy(ts->mo.nm_attr, ts->trx);
+ talloc_set_name_const(tp_merged, "oml_chan_attr");
osmo_tlvp_merge(tp_merged, &tp);
/* Call into BTS driver to check attribute values */
@@ -946,7 +965,7 @@ static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg)
return oml_fom_ack_nack(msg, -rc);
}
- /* Success: replace old BTS attributes with new */
+ /* Success: replace old CHAN attributes with new */
talloc_free(ts->mo.nm_attr);
ts->mo.nm_attr = tp_merged;
@@ -1379,6 +1398,7 @@ static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg)
/* Success: replace old MO attributes with new */
/* merge existing MO attributes with new attributes */
tp_merged = osmo_tlvp_copy(mo->nm_attr, bts);
+ talloc_set_name_const(tp_merged, "oml_ipa_attr");
osmo_tlvp_merge(tp_merged, &tp);
talloc_free(mo->nm_attr);
mo->nm_attr = tp_merged;
diff --git a/src/common/pcu_sock.c b/src/common/pcu_sock.c
index 03f1a05d..94b45f33 100644
--- a/src/common/pcu_sock.c
+++ b/src/common/pcu_sock.c
@@ -15,10 +15,6 @@
* 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -559,23 +555,35 @@ int pcu_tx_time_ind(uint32_t fn)
return pcu_sock_send(&bts_gsmnet, msg);
}
-int pcu_tx_interf_ind(uint8_t bts_nr, uint8_t trx_nr, uint32_t fn,
- const uint8_t *pdch_interf)
+int pcu_tx_interf_ind(const struct gsm_bts_trx *trx, uint32_t fn)
{
struct gsm_pcu_if_interf_ind *interf_ind;
struct gsm_pcu_if *pcu_prim;
struct msgb *msg;
+ unsigned int tn;
- msg = pcu_msgb_alloc(PCU_IF_MSG_INTERF_IND, bts_nr);
+ msg = pcu_msgb_alloc(PCU_IF_MSG_INTERF_IND, trx->bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
interf_ind = &pcu_prim->u.interf_ind;
- interf_ind->trx_nr = trx_nr;
+ interf_ind->trx_nr = trx->nr;
interf_ind->fn = fn;
- memcpy(&interf_ind->interf[0], &pdch_interf[0],
- sizeof(interf_ind->interf));
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ const struct gsm_lchan *lchan = &ts->lchan[0];
+
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (ts->mo.nm_state.availability != NM_AVSTATE_OK)
+ continue;
+ if (ts_pchan(ts) != GSM_PCHAN_PDCH)
+ continue;
+
+ interf_ind->interf[tn] = -1 * lchan->meas.interf_meas_avg_dbm;
+ }
return pcu_sock_send(&bts_gsmnet, msg);
}
diff --git a/src/common/phy_link.c b/src/common/phy_link.c
index 5ad72ac7..352d8f72 100644
--- a/src/common/phy_link.c
+++ b/src/common/phy_link.c
@@ -129,10 +129,12 @@ void phy_instance_destroy(struct phy_instance *pinst)
/* remove from list of instances in the link */
llist_del(&pinst->list);
- /* remove reverse link from TRX */
- OSMO_ASSERT(pinst->trx->pinst == pinst);
- pinst->trx->pinst = NULL;
- pinst->trx = NULL;
+ /* remove reverse link from TRX (if associated) */
+ if (pinst->trx != NULL) {
+ OSMO_ASSERT(pinst->trx->pinst == pinst);
+ pinst->trx->pinst = NULL;
+ pinst->trx = NULL;
+ }
talloc_free(pinst);
}
diff --git a/src/common/power_control.c b/src/common/power_control.c
index 4f5d15e3..42d9d07f 100644
--- a/src/common/power_control.c
+++ b/src/common/power_control.c
@@ -36,6 +36,8 @@
/* We don't want to deal with floating point, so we scale up */
#define EWMA_SCALE_FACTOR 100
+/* EWMA_SCALE_FACTOR/2 = +50: Round to nearest value when downscaling, otherwise floor() is applied. */
+#define EWMA_ROUND_FACTOR (EWMA_SCALE_FACTOR / 2)
/* Base Low-Pass Single-Pole IIR Filter (EWMA) formula:
*
@@ -84,49 +86,45 @@ static int do_pf_ewma(const struct gsm_power_ctrl_meas_params *mp,
return Val;
}
- *Avg100 += A * (Val - *Avg100 / EWMA_SCALE_FACTOR);
- return *Avg100 / EWMA_SCALE_FACTOR;
+ *Avg100 += A * (Val - (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR);
+ return (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR;
}
/* Calculate target RxLev value from lower/upper thresholds */
#define CALC_TARGET(mp) \
- (mp.lower_thresh + mp.upper_thresh) / 2
+ ((mp).lower_thresh + (mp).upper_thresh) / 2
-/* Calculate a 'delta' value (for the given MS/BS power control state and parameters)
- * to be applied to the current Tx power level to approach the target level. */
-static int calc_delta(const struct gsm_power_ctrl_params *params,
- struct lchan_power_ctrl_state *state,
- const int rxlev_dbm)
+static int do_avg_algo(const struct gsm_power_ctrl_meas_params *mp,
+ struct gsm_power_ctrl_meas_proc_state *mps,
+ const int val)
{
- int rxlev_dbm_avg;
- uint8_t rxlev_avg;
- int delta;
-
- /* Filter RxLev value to reduce unnecessary Tx power oscillations */
- switch (params->rxlev_meas.algo) {
+ int val_avg;
+ switch (mp->algo) {
case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:
- rxlev_dbm_avg = do_pf_ewma(&params->rxlev_meas,
- &state->rxlev_meas_proc,
- rxlev_dbm);
+ val_avg = do_pf_ewma(mp, mps, val);
break;
/* TODO: implement other pre-processing methods */
case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE:
default:
/* No filtering (pass through) */
- rxlev_dbm_avg = rxlev_dbm;
+ val_avg = val;
}
-
- /* FIXME: avoid this conversion, accept RxLev as-is */
- rxlev_avg = dbm2rxlev(rxlev_dbm_avg);
+ return val_avg;
+}
+/* Calculate a 'delta' value (for the given MS/BS power control parameters)
+ * to be applied to the current Tx power level to approach the target level. */
+static int calc_delta_rxlev(const struct gsm_power_ctrl_params *params, const uint8_t rxlev)
+{
+ int delta;
/* Check if RxLev is within the threshold window */
- if (rxlev_avg >= params->rxlev_meas.lower_thresh &&
- rxlev_avg <= params->rxlev_meas.upper_thresh)
+ if (rxlev >= params->rxlev_meas.lower_thresh &&
+ rxlev <= params->rxlev_meas.upper_thresh)
return 0;
/* How many dBs measured power should be increased (+) or decreased (-)
* to reach expected power. */
- delta = CALC_TARGET(params->rxlev_meas) - rxlev_avg;
+ delta = CALC_TARGET(params->rxlev_meas) - rxlev;
/* Don't ever change more than PWR_{LOWER,RAISE}_MAX_DBM during one loop
* iteration, i.e. reduce the speed at which the MS transmit power can
@@ -139,14 +137,57 @@ static int calc_delta(const struct gsm_power_ctrl_params *params,
return delta;
}
+/* Shall we skip current block based on configured interval? */
+static bool ctrl_interval_skip_block(const struct gsm_power_ctrl_params *params,
+ struct lchan_power_ctrl_state *state)
+{
+ /* Power control interval: how many blocks do we skip? */
+ if (state->skip_block_num-- > 0)
+ return true;
+
+ /* Reset the number of SACCH blocks to be skipped:
+ * ctrl_interval=0 => 0 blocks to skip,
+ * ctrl_interval=1 => 1 blocks to skip,
+ * ctrl_interval=2 => 3 blocks to skip,
+ * so basically ctrl_interval * 2 - 1. */
+ state->skip_block_num = params->ctrl_interval * 2 - 1;
+ return false;
+}
+
+static const struct gsm_power_ctrl_meas_params *lchan_get_ci_thresholds(const struct gsm_lchan *lchan)
+{
+ const struct gsm_power_ctrl_params *params = lchan->ms_power_ctrl.dpc_params;
+
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ return &params->ci_sdcch_meas;
+ case GSM_LCHAN_PDTCH:
+ return &params->ci_gprs_meas;
+ case GSM_LCHAN_TCH_F:
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+ return &params->ci_amr_fr_meas;
+ else
+ return &params->ci_fr_meas;
+ case GSM_LCHAN_TCH_H:
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+ return &params->ci_amr_hr_meas;
+ else
+ return &params->ci_hr_meas;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
/*! compute the new MS POWER LEVEL communicated to the MS and store it in lchan.
* \param lchan logical channel for which to compute (and in which to store) new power value.
* \param[in] ms_power_lvl MS Power Level received from Uplink L1 SACCH Header in SACCH block.
* \param[in] ul_rssi_dbm Signal level of the received SACCH block, in dBm.
+ * \param[in] ul_lqual_cb C/I of the received SACCH block, in dB.
*/
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
const uint8_t ms_power_lvl,
- const int8_t ul_rssi_dbm)
+ const int8_t ul_rssi_dbm,
+ const int16_t ul_lqual_cb)
{
struct lchan_power_ctrl_state *state = &lchan->ms_power_ctrl;
const struct gsm_power_ctrl_params *params = state->dpc_params;
@@ -155,23 +196,20 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
enum gsm_band band = bts->band;
int8_t new_power_lvl; /* TS 05.05 power level */
int8_t ms_dbm, new_dbm, current_dbm, bsc_max_dbm;
+ uint8_t rxlev_avg;
+ int16_t ul_lqual_cb_avg;
+ const struct gsm_power_ctrl_meas_params *ci_meas;
+ bool ignore, ci_on;
if (!trx_ms_pwr_ctrl_is_osmo(trx))
return 0;
if (params == NULL)
return 0;
- /* Power control interval: how many blocks do we skip? */
- if (state->skip_block_num-- > 0)
+ /* Shall we skip current block based on configured interval? */
+ if (ctrl_interval_skip_block(params, state))
return 0;
- /* Reset the number of SACCH blocks to be skipped:
- * ctrl_interval=0 => 0 blocks to skip,
- * ctrl_interval=1 => 1 blocks to skip,
- * ctrl_interval=2 => 3 blocks to skip,
- * so basically ctrl_interval * 2 - 1. */
- state->skip_block_num = params->ctrl_interval * 2 - 1;
-
ms_dbm = ms_pwr_dbm(band, ms_power_lvl);
if (ms_dbm < 0) {
LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE,
@@ -187,8 +225,24 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
return 0;
}
- /* Calculate the new Tx power value (in dBm) */
- new_dbm = ms_dbm + calc_delta(params, state, ul_rssi_dbm);
+ ci_meas = lchan_get_ci_thresholds(lchan);
+
+ /* Is C/I based algo enabled by config?
+ * FIXME: this can later be generalized when properly implementing P & N counting. */
+ ci_on = ci_meas->lower_cmp_n && ci_meas->upper_cmp_n;
+
+ ul_lqual_cb_avg = do_avg_algo(ci_meas, &state->ci_meas_proc, ul_lqual_cb);
+ rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, dbm2rxlev(ul_rssi_dbm));
+
+ /* If computed C/I is enabled and out of acceptable thresholds: */
+ if (ci_on && ul_lqual_cb_avg < ci_meas->lower_thresh * 10) {
+ new_dbm = ms_dbm + params->inc_step_size_db;
+ } else if (ci_on && ul_lqual_cb_avg > ci_meas->upper_thresh * 10) {
+ new_dbm = ms_dbm - params->red_step_size_db;
+ } else {
+ /* Calculate the new Tx power value (in dBm) */
+ new_dbm = ms_dbm + calc_delta_rxlev(params, rxlev_avg);
+ }
/* Make sure new_dbm is never negative. ms_pwr_ctl_lvl() can later on
cope with any unsigned dbm value, regardless of band minimal value. */
@@ -207,23 +261,46 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
return 0;
}
- /* FIXME: this is only needed for logging, print thresholds instead */
- int target_dbm = rxlev2dbm(CALC_TARGET(params->rxlev_meas));
+ current_dbm = ms_pwr_dbm(band, state->current);
- if (state->current == new_power_lvl) {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping MS power at control level %d, %d dBm "
- "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm)\n",
- new_power_lvl, new_dbm, ms_power_lvl, state->max,
- ul_rssi_dbm, target_dbm);
+ /* In this Power Control Loop, we infer a new good MS Power Level based
+ * on the previous MS Power Level announced by the MS (not the previous
+ * one we requested!) together with the related computed measurements.
+ * Hence, and since we allow for several good MS Power Levels falling into our
+ * thresholds, we could finally converge into an oscillation loop where
+ * the MS bounces between 2 different correct MS Power levels all the
+ * time, due to the fact that we "accept" and "request back" whatever
+ * good MS Power Level we received from the MS, but at that time the MS
+ * will be transmitting using the previous MS Power Level we
+ * requested, which we will later "accept" and "request back" on next loop
+ * iteration. As a result MS effectively bounces between those 2 MS
+ * Power Levels.
+ * In order to fix this permanent oscillation, if current MS_PWR used/announced
+ * by MS is good ("ms_dbm == new_dbm", hence within thresholds and no change
+ * required) but has higher Tx power than the one we last requested, we ignore
+ * it and keep requesting for one with lower Tx power. This way we converge to
+ * the lowest good Tx power avoiding oscillating over values within thresholds.
+ */
+ ignore = (ms_dbm == new_dbm && ms_dbm > current_dbm);
+
+ if (state->current == new_power_lvl || ignore) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping MS power at control level %d (%d dBm): "
+ "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm,"
+ " C/I[curr %d, avg %d, thresh %d..%d] dB\n",
+ new_power_lvl, new_dbm, ms_power_lvl, state->max, ul_rssi_dbm, rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh),
+ ul_lqual_cb/10, ul_lqual_cb_avg/10, ci_meas->lower_thresh, ci_meas->upper_thresh);
return 0;
}
- current_dbm = ms_pwr_dbm(band, state->current);
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s MS power from control level %d (%d dBm) to %d, %d dBm "
- "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm)\n",
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s MS power control level %d (%d dBm) => %d (%d dBm): "
+ "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm,"
+ " C/I[curr %d, avg %d, thresh %d..%d] dB\n",
(new_dbm > current_dbm) ? "Raising" : "Lowering",
- state->current, current_dbm, new_power_lvl, new_dbm,
- ms_power_lvl, state->max, ul_rssi_dbm, target_dbm);
+ state->current, current_dbm, new_power_lvl, new_dbm, ms_power_lvl,
+ state->max, ul_rssi_dbm, rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh),
+ ul_lqual_cb/10, ul_lqual_cb_avg/10, ci_meas->lower_thresh, ci_meas->upper_thresh);
/* store the resulting new MS power level in the lchan */
state->current = new_power_lvl;
@@ -234,129 +311,265 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
/*! compute the new Downlink attenuation value for the given logical channel.
* \param lchan logical channel for which to compute (and in which to store) new power value.
- * \param[in] gh pointer to the beginning of (presumably) a Measurement Report.
+ * \param[in] mr pointer to a *valid* Measurement Report.
*/
int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
- const struct gsm48_hdr *gh)
+ const struct gsm48_meas_res *mr)
{
struct lchan_power_ctrl_state *state = &lchan->bs_power_ctrl;
const struct gsm_power_ctrl_params *params = state->dpc_params;
- uint8_t rxqual_full, rxqual_sub;
- uint8_t rxlev_full, rxlev_sub;
- uint8_t rxqual, rxlev;
- int delta, new;
+ uint8_t rxqual, rxqual_avg, rxlev, rxlev_avg;
+ int new_att;
/* Check if dynamic BS Power Control is enabled */
if (params == NULL)
return 0;
- /* Check if this is a Measurement Report */
- if (gh->proto_discr != GSM48_PDISC_RR)
- return 0;
- if (gh->msg_type != GSM48_MT_RR_MEAS_REP)
- return 0;
-
- /* Check if the measurement results are valid */
- if ((gh->data[1] & 0x40) == 0x40) {
- LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG,
- "The measurement results are not valid\n");
- return 0;
- }
-
- /* See 3GPP TS 44.018, section 10.5.2.20 */
- rxqual_full = (gh->data[2] >> 4) & 0x7;
- rxqual_sub = (gh->data[2] >> 1) & 0x7;
-
- rxlev_full = gh->data[0] & 0x3f;
- rxlev_sub = gh->data[1] & 0x3f;
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Rx DL Measurement Report: "
"RXLEV-FULL(%02u), RXQUAL-FULL(%u), "
"RXLEV-SUB(%02u), RXQUAL-SUB(%u), "
"DTx is %s => using %s\n",
- rxlev_full, rxqual_full, rxlev_sub, rxqual_sub,
+ mr->rxlev_full, mr->rxqual_full,
+ mr->rxlev_sub, mr->rxqual_sub,
lchan->tch.dtx.dl_active ? "enabled" : "disabled",
lchan->tch.dtx.dl_active ? "SUB" : "FULL");
- /* Power control interval: how many blocks do we skip? */
- if (state->skip_block_num-- > 0)
+ /* Shall we skip current block based on configured interval? */
+ if (ctrl_interval_skip_block(params, state))
return 0;
- /* Reset the number of SACCH blocks to be skipped:
- * ctrl_interval=0 => 0 blocks to skip,
- * ctrl_interval=1 => 1 blocks to skip,
- * ctrl_interval=2 => 3 blocks to skip,
- * so basically ctrl_interval * 2 - 1. */
- state->skip_block_num = params->ctrl_interval * 2 - 1;
-
/* If DTx is active on Downlink, use the '-SUB' */
if (lchan->tch.dtx.dl_active) {
- rxqual = rxqual_sub;
- rxlev = rxlev_sub;
+ rxqual = mr->rxqual_sub;
+ rxlev = mr->rxlev_sub;
} else { /* ... otherwise use the '-FULL' */
- rxqual = rxqual_full;
- rxlev = rxlev_full;
+ rxqual = mr->rxqual_full;
+ rxlev = mr->rxlev_full;
}
+ rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, rxlev);
+ rxqual_avg = do_avg_algo(&params->rxqual_meas, &state->rxqual_meas_proc, rxqual);
/* If RxQual > L_RXQUAL_XX_P, try to increase Tx power */
- if (rxqual > params->rxqual_meas.lower_thresh) {
- uint8_t old = state->current;
-
- /* Tx power has reached the maximum, nothing to do */
- if (state->current == 0)
- return 0;
-
+ if (rxqual_avg > params->rxqual_meas.lower_thresh) {
/* Increase Tx power by reducing Tx attenuation */
- if (state->current >= params->inc_step_size_db)
- state->current -= params->inc_step_size_db;
- else
- state->current = 0;
-
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Reducing Downlink attenuation: "
- "%u -> %d dB due to RxQual %u worse than L_RXQUAL_XX_P %u\n",
- old, state->current, rxqual, params->rxqual_meas.lower_thresh);
- return 1;
+ new_att = state->current - params->inc_step_size_db;
+ } else if (rxqual_avg < params->rxqual_meas.upper_thresh) {
+ /* Increase Tx power by Increasing Tx attenuation */
+ new_att = state->current + params->red_step_size_db;
+ } else {
+ /* Basic signal transmission / reception formula:
+ *
+ * RxLev = TxPwr - (PathLoss + TxAtt)
+ *
+ * Here we want to change RxLev at the MS side, so:
+ *
+ * RxLev + Delta = TxPwr - (PathLoss + TxAtt) + Delta
+ *
+ * The only parameter we can change here is TxAtt, so:
+ *
+ * RxLev + Delta = TxPwr - PathLoss - TxAtt + Delta
+ * RxLev + Delta = TxPwr - PathLoss - (TxAtt - Delta)
+ */
+ new_att = state->current - calc_delta_rxlev(params, rxlev_avg);
}
- /* Calculate a 'delta' for the current attenuation level */
- delta = calc_delta(params, state, rxlev2dbm(rxlev));
-
- /* Basic signal transmission / reception formula:
- *
- * RxLev = TxPwr - (PathLoss + TxAtt)
- *
- * Here we want to change RxLev at the MS side, so:
- *
- * RxLev + Delta = TxPwr - (PathLoss + TxAtt) + Delta
- *
- * The only parameter we can change here is TxAtt, so:
- *
- * RxLev + Delta = TxPwr - PathLoss - TxAtt + Delta
- * RxLev + Delta = TxPwr - PathLoss - (TxAtt - Delta)
- */
- new = state->current - delta;
- if (new > state->max)
- new = state->max;
- if (new < 0)
- new = 0;
-
- if (state->current != new) {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Changing Downlink attenuation: "
- "%u -> %u dB (maximum %u dB, suggested delta %d dB, "
- "RxLev current %u (%d dBm), thresholds %u .. %u)\n",
- state->current, new, state->max,
- -delta, rxlev, rxlev2dbm(rxlev),
- params->rxlev_meas.lower_thresh,
- params->rxlev_meas.upper_thresh);
- state->current = new;
- return 1;
- } else {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping Downlink attenuation "
- "at %u dB (maximum %u dB, suggested delta %d dB, "
- "RxLev current %u (%d dBm), thresholds %u .. %u)\n",
- state->current, state->max,
- -delta, rxlev, rxlev2dbm(rxlev),
- params->rxlev_meas.lower_thresh,
- params->rxlev_meas.upper_thresh);
+ /* Make sure new TxAtt is never negative: */
+ if (new_att < 0)
+ new_att = 0;
+
+ /* Don't ask for higher TxAtt than permitted: */
+ if (new_att > state->max)
+ new_att = state->max;
+
+ if (state->current == new_att) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping DL attenuation at %u dB: "
+ "max %u dB, RSSI[curr %d, avg %d, thresh %d..%d] dBm, "
+ "RxQual[curr %d, avg %d, thresh %d..%d]\n",
+ state->current, state->max, rxlev2dbm(rxlev), rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh),
+ rxqual, rxqual_avg, params->rxqual_meas.lower_thresh, params->rxqual_meas.upper_thresh);
return 0;
}
+
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s DL attenuation %u dB => %u dB:"
+ "max %u dB, RSSI[curr %d, avg %d, thresh %d..%d] dBm, "
+ "RxQual[curr %d, avg %d, thresh %d..%d]\n",
+ (new_att > state->current) ? "Raising" : "Lowering",
+ state->current, new_att, state->max, rxlev2dbm(rxlev), rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh),
+ rxqual, rxqual_avg, params->rxqual_meas.lower_thresh, params->rxqual_meas.upper_thresh);
+ state->current = new_att;
+ return 1;
+}
+
+/* Default MS/BS Power Control parameters (see 3GPP TS 45.008, table A.1) */
+const struct gsm_power_ctrl_params power_ctrl_params_def = {
+
+ .ctrl_interval = 1, /* Trigger loop every second SACCH block. TS 45.008 sec 4.7.1 */
+
+ /* Power increasing/reducing step size (optimal defaults) */
+ .inc_step_size_db = 4, /* quickly increase MS/BS power */
+ .red_step_size_db = 2, /* slowly decrease MS/BS power */
+
+ /* RxLev measurement parameters */
+ .rxlev_meas = {
+ /* Thresholds for RxLev (see 3GPP TS 45.008, A.3.2.1) */
+ .lower_thresh = 32, /* L_RXLEV_XX_P (-78 dBm) */
+ .upper_thresh = 38, /* U_RXLEV_XX_P (-72 dBm) */
+
+ /* NOTE: only Osmocom specific EWMA is supported */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA,
+ .ewma.alpha = 50, /* Smoothing factor 50% */
+ },
+
+ /* RxQual measurement parameters */
+ .rxqual_meas = {
+ /* Thresholds for RxQual (see 3GPP TS 45.008, A.3.2.1) */
+ .lower_thresh = 3, /* L_RXQUAL_XX_P (0.8% <= BER < 1.6%) */
+ .upper_thresh = 0, /* U_RXQUAL_XX_P (BER < 0.2%) */
+
+ /* No averaging (filtering) by default.
+ * NOTE: only Osmocom specific EWMA is supported */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+ },
+
+ /* C/I measurement parameters.
+ * Target C/I retrieved from "GSM/EDGE: Evolution and Performance" Table 10.3.
+ * Set lower and upper so that (lower + upper) / 2 is equal or slightly
+ * above the target.
+ */
+ .ci_fr_meas = { /* FR: Target C/I = 15 dB, Soft blocking threshold = 10 dB */
+ .lower_thresh = 13,
+ .upper_thresh = 17,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_FR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_FR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_hr_meas = { /* HR: Target C/I = 18 dB, Soft blocking threshold = 13 dB */
+ .lower_thresh = 16,
+ .upper_thresh = 21,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_HR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_HR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_amr_fr_meas = { /* AMR-FR: Target C/I = 9 dB, Soft blocking threshold = 4 dB */
+ .lower_thresh = 7,
+ .upper_thresh = 11,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_AMR_FR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_AMR_FR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_amr_hr_meas = { /* AMR-HR: Target C/I = 15 dB, Soft blocking threshold = 10 dB */
+ .lower_thresh = 13,
+ .upper_thresh = 17,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_AMR_HR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_AMR_HR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_sdcch_meas = { /* SDCCH: Target C/I = 14 dB, Soft blocking threshold = 9 dB */
+ .lower_thresh = 12,
+ .upper_thresh = 16,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_SDCCH_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_SDCCH_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_gprs_meas = { /* GPRS: Target C/I = 20 dB, Soft blocking threshold = 15 dB */
+ .lower_thresh = 18,
+ .upper_thresh = 24,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_GPRS_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_GPRS_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+};
+
+void power_ctrl_params_def_reset(struct gsm_power_ctrl_params *params, bool is_bs_pwr)
+{
+ *params = power_ctrl_params_def;
+ if (!is_bs_pwr)
+ /* Trigger loop every fourth SACCH block (1.92s). TS 45.008 sec 4.7.1: */
+ params->ctrl_interval = 2;
}
diff --git a/src/common/probes.d b/src/common/probes.d
new file mode 100644
index 00000000..aaf9030e
--- /dev/null
+++ b/src/common/probes.d
@@ -0,0 +1,2 @@
+provider osmo_bts {
+};
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 18c03491..9b73869a 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -440,29 +440,37 @@ int rsl_tx_rf_res(struct gsm_bts_trx *trx)
uint8_t *len = msgb_tl_put(nmsg, RSL_IE_RESOURCE_INFO);
for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
- for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
- struct gsm_lchan *lchan = &ts->lchan[ln];
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (ts->mo.nm_state.availability != NM_AVSTATE_OK)
+ continue;
- /* We're not interested in active lchans */
- if (lchan->state == LCHAN_S_ACTIVE) {
- /* Avoid potential buffer overflow */
- lchan->meas.interf_meas_num = 0;
- continue;
- }
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ const struct gsm_lchan *lchan = &ts->lchan[ln];
- /* Only for GSM_LCHAN_{SDCCH,TCH_F,TCH_H} */
- if (!lchan_is_dcch(lchan))
+ /* No average interference value => no band */
+ if (lchan->meas.interf_meas_avg_dbm == 0)
continue;
- /* Average all collected samples */
- int band = gsm_lchan_interf_meas_calc_band(lchan);
- if (band < 0)
+ /* Only for GSM_LCHAN_{SDCCH,TCH_F,TCH_H,PDTCH} */
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ case GSM_LCHAN_TCH_F:
+ case GSM_LCHAN_TCH_H:
+ /* We're not interested in active CS lchans */
+ if (lchan->state == LCHAN_S_ACTIVE)
+ continue;
+ break;
+ case GSM_LCHAN_PDTCH:
+ break;
+ default:
continue;
+ }
- msgb_v_put(nmsg, gsm_lchan2chan_nr(lchan));
- msgb_v_put(nmsg, (band & 0x07) << 5);
+ msgb_v_put(nmsg, gsm_lchan2chan_nr_rsl(lchan));
+ msgb_v_put(nmsg, (lchan->meas.interf_band & 0x07) << 5);
}
}
@@ -909,7 +917,7 @@ static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params,
const uint8_t *data, size_t data_len)
{
const struct tlv_p_entry *ie;
- struct tlv_parsed tp[2];
+ struct tlv_parsed tp[3];
unsigned int i;
int rc;
@@ -938,6 +946,30 @@ static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params,
params->rxqual_meas.upper_thresh = thresh->u_rxqual;
}
+ /* Osmocom extension, C/I related thresholds: */
+ if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL, sizeof(struct osmo_preproc_pc_thresh))) {
+ const struct osmo_preproc_pc_thresh *osmo_thresh;
+ ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL);
+ osmo_thresh = (const struct osmo_preproc_pc_thresh *) ie->val;
+ params->ci_fr_meas.lower_thresh = osmo_thresh->l_ci_fr;
+ params->ci_fr_meas.upper_thresh = osmo_thresh->u_ci_fr;
+
+ params->ci_hr_meas.lower_thresh = osmo_thresh->l_ci_hr;
+ params->ci_hr_meas.upper_thresh = osmo_thresh->u_ci_hr;
+
+ params->ci_amr_fr_meas.lower_thresh = osmo_thresh->l_ci_amr_fr;
+ params->ci_amr_fr_meas.upper_thresh = osmo_thresh->u_ci_amr_fr;
+
+ params->ci_amr_hr_meas.lower_thresh = osmo_thresh->l_ci_amr_hr;
+ params->ci_amr_hr_meas.upper_thresh = osmo_thresh->u_ci_amr_hr;
+
+ params->ci_sdcch_meas.lower_thresh = osmo_thresh->l_ci_sdcch;
+ params->ci_sdcch_meas.upper_thresh = osmo_thresh->u_ci_sdcch;
+
+ params->ci_gprs_meas.lower_thresh = osmo_thresh->l_ci_gprs;
+ params->ci_gprs_meas.upper_thresh = osmo_thresh->u_ci_gprs;
+ }
+
/* (TV) PC Threshold Comparators */
if ((ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_PC_THRESH_COMP)) != NULL) {
const struct ipac_preproc_pc_comp *thresh_comp;
@@ -964,12 +996,31 @@ static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params,
params->red_step_size_db = thresh_comp->red_step_size;
}
+ /* Osmocom extension, C/I related thresholds: */
+ if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP, sizeof(struct osmo_preproc_pc_thresh))) {
+ const struct osmo_preproc_pc_comp *osmo_thresh_comp;
+ ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP);
+ osmo_thresh_comp = (const struct osmo_preproc_pc_comp *) ie->val;
+ #define SET_PREPROC_PC(PARAMS, FROM, TYPE) \
+ (PARAMS)->TYPE##_meas.lower_cmp_p = (FROM)->TYPE.lower_p; \
+ (PARAMS)->TYPE##_meas.lower_cmp_n = (FROM)->TYPE.lower_n; \
+ (PARAMS)->TYPE##_meas.upper_cmp_p = (FROM)->TYPE.upper_p; \
+ (PARAMS)->TYPE##_meas.upper_cmp_n = (FROM)->TYPE.upper_n
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_fr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_hr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_fr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_hr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_sdcch);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_gprs);
+ #undef SET_PREPROC_PC
+ }
+
/* (TLV) Measurement Averaging parameters for RxLev/RxQual */
for (i = 0; i < ARRAY_SIZE(tp); i++) {
const struct ipac_preproc_ave_cfg *ave_cfg;
struct gsm_power_ctrl_meas_params *mp;
- ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_MEAS_AVG_CFG);
+ ie = TLVP_GET(&tp[i], RSL_IPAC_EIE_MEAS_AVG_CFG);
if (ie == NULL)
break;
@@ -1008,6 +1059,42 @@ static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params,
}
}
+ /* (TLV) Measurement Averaging parameters for C/I (Osmocom extension)*/
+ if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG, sizeof(struct osmo_preproc_ave_cfg))) {
+ ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG);
+ const struct osmo_preproc_ave_cfg *cfg = (const struct osmo_preproc_ave_cfg *) ie->val;
+ unsigned params_offset = 0;
+ #define SET_AVE_CFG(PARAMS, FROM, TYPE, PARAM_OFFSET) do {\
+ if ((FROM)->TYPE.ave_enabled) { \
+ (PARAMS)->TYPE##_meas.h_reqave = (FROM)->TYPE.h_reqave; \
+ (PARAMS)->TYPE##_meas.h_reqt = (FROM)->TYPE.h_reqt; \
+ (PARAMS)->TYPE##_meas.algo = (FROM)->TYPE.ave_method + 1; \
+ switch ((FROM)->TYPE.ave_method) { \
+ case IPAC_OSMO_EWMA_AVE: \
+ if (ie->len > sizeof(*cfg) + (PARAM_OFFSET)) { \
+ (PARAMS)->TYPE##_meas.ewma.alpha = (FROM)->params[PARAM_OFFSET]; \
+ (PARAM_OFFSET)++; \
+ } \
+ break; \
+ /* FIXME: not implemented */ \
+ case IPAC_UNWEIGHTED_AVE: \
+ case IPAC_WEIGHTED_AVE: \
+ case IPAC_MEDIAN_AVE: \
+ break; \
+ } \
+ } else { \
+ (PARAMS)->TYPE##_meas.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE; \
+ } \
+ } while(0)
+ SET_AVE_CFG(params, cfg, ci_fr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_hr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_amr_fr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_amr_hr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_sdcch, params_offset);
+ SET_AVE_CFG(params, cfg, ci_gprs, params_offset);
+ #undef SET_AVE_CFG
+ }
+
return 0;
}
@@ -1032,7 +1119,8 @@ static int rsl_rx_meas_preproc_dft(struct gsm_bts_trx *trx, struct msgb *msg)
/* TLV (O) BS Power Parameters IE */
if ((ie = TLVP_GET(&tp, RSL_IE_BS_POWER_PARAM)) != NULL) {
/* Allocate a new chunk and initialize with default values */
- params = talloc_memdup(trx, &power_ctrl_params_def, sizeof(*params));
+ params = talloc(trx, struct gsm_power_ctrl_params);
+ power_ctrl_params_def_reset(params, true);
if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) == 0) {
/* Initially it points to the global defaults */
@@ -1049,7 +1137,8 @@ static int rsl_rx_meas_preproc_dft(struct gsm_bts_trx *trx, struct msgb *msg)
/* TLV (O) MS Power Parameters IE */
if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) {
/* Allocate a new chunk and initialize with default values */
- params = talloc_memdup(trx, &power_ctrl_params_def, sizeof(*params));
+ params = talloc(trx, struct gsm_power_ctrl_params);
+ power_ctrl_params_def_reset(params, false);
if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) == 0) {
/* Initially it points to the global defaults */
@@ -1085,6 +1174,30 @@ static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg)
msg->l2h = NULL;
msg->len = TLVP_LEN(&tp, RSL_IE_FULL_IMM_ASS_INFO);
+ /* Early Immediate Assignment: when there is a lot of latency on Abis, the Abis roundtrip of Chan Activ -> Chan
+ * Activ ACK -> Immediate Assignment may take so long that each MS sends a second RACH for Chan Rqd, reserving
+ * two SDCCH for each request but using only one. To help with that, the Early IA feature in osmo-bsc sends the
+ * Immediate Assignment without waiting for the Channel Activation ACK. This may then be too early, and the MS
+ * may not be able to establish a channel. So to help with Early IA, look up whether the target lchan is already
+ * active. If not, then hold back the RR Immediate Assignment message, and send it once L1 has confirmed that
+ * the channel is active. Hence we still wait for the activation, but don't need the Abis roundtrip of Activ ACK
+ * -> Immediate Assignment via the BSC.
+ * If anything is wrong with the sizes or the lchan lookup, behave normally, i.e. do not do the RR IA caching,
+ * but just send the RR message to the MS as-is. */
+ if (msg->len >= sizeof(struct gsm48_imm_ass)) {
+ struct gsm48_imm_ass *rr_ia = (void*)msg->data;
+ struct gsm_lchan *ia_target_lchan = lchan_lookup(trx, rr_ia->chan_desc.chan_nr, "Early IA check: ");
+ if (ia_target_lchan && ia_target_lchan->state != LCHAN_S_ACTIVE) {
+ /* Target lchan is not yet active. Cache the IA.
+ * If a previous IA is still lingering, free it. */
+ msgb_free(ia_target_lchan->early_rr_ia);
+ ia_target_lchan->early_rr_ia = msg;
+
+ /* return 1 means: don't msgb_free() the msg */
+ return 1;
+ }
+ }
+
/* put into the AGCH queue of the BTS */
if (bts_agch_enqueue(trx->bts, msg) < 0) {
/* if there is no space in the queue: send DELETE IND */
@@ -1122,7 +1235,7 @@ static int tx_rf_rel_ack(struct gsm_lchan *lchan, uint8_t chan_nr)
/* 8.4.19 sending RF CHANnel RELease ACKnowledge */
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
{
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
bool send_rel_ack;
switch (lchan->rel_act_kind) {
@@ -1188,21 +1301,6 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
gsm_ts_and_pchan_name(lchan->ts), lchan->nr,
gsm_lchant_name(lchan->type));
- /*
- * Free the LAPDm resources now that the BTS
- * has released all the resources.
- */
- lapdm_channel_exit(&lchan->lapdm_ch);
-
- /* Also ensure that there are no leftovers from repeated FACCH or
- * repeated SACCH that might cause memory leakage. */
- msgb_free(lchan->tch.rep_facch[0].msg);
- msgb_free(lchan->tch.rep_facch[1].msg);
- lchan->tch.rep_facch[0].msg = NULL;
- lchan->tch.rep_facch[1].msg = NULL;
- msgb_free(lchan->rep_sacch);
- lchan->rep_sacch = NULL;
-
return tx_rf_rel_ack(lchan, chan_nr);
}
@@ -1211,7 +1309,7 @@ static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
{
struct gsm_time *gtime = get_time(lchan->ts->trx->bts);
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
uint8_t ie[2];
LOGP(DRSL, LOGL_NOTICE, "%s (ss=%d) %s Tx CHAN ACT ACK\n",
@@ -1237,7 +1335,7 @@ static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending HANDOver DETect\n");
@@ -1279,7 +1377,7 @@ static int _rsl_tx_chan_act_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8
return abis_bts_rsl_sendmsg(msg);
}
static int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause) {
- return _rsl_tx_chan_act_nack(lchan->ts->trx, gsm_lchan2chan_nr(lchan), cause, lchan);
+ return _rsl_tx_chan_act_nack(lchan->ts->trx, gsm_lchan2chan_nr_rsl(lchan), cause, lchan);
}
/* Send an RSL Channel Activation Ack if cause is zero, a Nack otherwise. */
@@ -1300,7 +1398,7 @@ int rsl_tx_chan_act_acknack(struct gsm_lchan *lchan, uint8_t cause)
int rsl_tx_conn_fail(const struct gsm_lchan *lchan, uint8_t cause)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Sending Connection Failure: cause = 0x%02x\n", cause);
@@ -1409,7 +1507,7 @@ static void clear_lchan_for_pdch_activ(struct gsm_lchan *lchan)
memset(&lchan->ho, 0, sizeof(lchan->ho));
memset(&lchan->ms_power_ctrl, 0, sizeof(lchan->ms_power_ctrl));
memset(&lchan->bs_power_ctrl, 0, sizeof(lchan->bs_power_ctrl));
- lchan->rqd_ta = 0;
+ lchan->ta_ctrl.current = 0;
copy_sacch_si_to_lchan(lchan);
memset(&lchan->tch, 0, sizeof(lchan->tch));
}
@@ -1418,7 +1516,7 @@ static void clear_lchan_for_pdch_activ(struct gsm_lchan *lchan)
* Store the CHAN_ACTIV msg, connect the L1 timeslot in the proper type and
* then invoke rsl_rx_chan_activ() with msg.
*/
-static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg)
+static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts)
{
DEBUGP(DRSL, "%s dyn_ts_l1_reconnect\n", gsm_ts_and_pchan_name(ts));
@@ -1439,9 +1537,6 @@ static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg)
return -EINVAL;
}
- /* We will feed this back to rsl_rx_chan_activ() later */
- ts->dyn.pending_chan_activ = msg;
-
/* Disconnect, continue connecting from cb_ts_disconnected(). */
DEBUGP(DRSL, "%s Disconnect\n", gsm_ts_and_pchan_name(ts));
return bts_model_ts_disconnect(ts);
@@ -1476,7 +1571,7 @@ static enum gsm_phys_chan_config dyn_pchan_from_chan_nr(uint8_t chan_nr)
}
/* Parse RSL_IE_OSMO_REP_ACCH_CAP */
-static void parse_repeated_acch_capability(struct gsm_lchan *lchan, struct tlv_parsed *tp)
+static int parse_repeated_acch_capability(struct gsm_lchan *lchan, struct tlv_parsed *tp)
{
/* 3GPP TS 24.008, section 10.5.1.7 defines a Repeated ACCH Capability
* bit that indicates if REPEATED FACCH/SACCH is supported or not.
@@ -1484,15 +1579,42 @@ static void parse_repeated_acch_capability(struct gsm_lchan *lchan, struct tlv_p
* should be communicated in the RSL CHANNEL ACTIVATION. For osmo-bts
* we will use a propritary IE. */
- memset(&lchan->repeated_acch_capability, 0, sizeof(lchan->repeated_acch_capability));
+ memset(&lchan->rep_acch_cap, 0, sizeof(lchan->rep_acch_cap));
- if (!TLVP_PRESENT(tp, RSL_IE_OSMO_REP_ACCH_CAP))
- return;
- if (TLVP_LEN(tp, RSL_IE_OSMO_REP_ACCH_CAP) != sizeof(lchan->repeated_acch_capability))
- return;
+ if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_REP_ACCH_CAP, sizeof(lchan->rep_acch_cap)))
+ return 0;
+
+ if (!osmo_bts_has_feature(lchan->ts->trx->bts->features, BTS_FEAT_ACCH_REP))
+ return -RSL_ERR_OPT_IE_ERROR;
+
+ memcpy(&lchan->rep_acch_cap, TLVP_VAL(tp, RSL_IE_OSMO_REP_ACCH_CAP),
+ sizeof(lchan->rep_acch_cap));
- memcpy(&lchan->repeated_acch_capability, TLVP_VAL(tp, RSL_IE_OSMO_REP_ACCH_CAP),
- sizeof(lchan->repeated_acch_capability));
+ return 0;
+}
+
+/* Parse RSL_IE_OSMO_TOP_ACCH_CAP */
+static int parse_temporary_overpower_acch_capability(struct gsm_lchan *lchan,
+ const struct tlv_parsed *tp)
+{
+ memset(&lchan->top_acch_cap, 0, sizeof(lchan->top_acch_cap));
+
+ if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP, sizeof(lchan->top_acch_cap)))
+ return 0;
+
+ if (!osmo_bts_has_feature(lchan->ts->trx->bts->features, BTS_FEAT_ACCH_TEMP_OVP))
+ return -RSL_ERR_OPT_IE_ERROR;
+
+ memcpy(&lchan->top_acch_cap,
+ TLVP_VAL(tp, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP),
+ sizeof(lchan->top_acch_cap));
+
+ /* Simplify checking whether the overpower is enabled at all: allow
+ * testing just one parameter (overpower_db > 0) instead of all three. */
+ if (!lchan->top_acch_cap.sacch_enable && !lchan->top_acch_cap.facch_enable)
+ lchan->top_acch_cap.overpower_db = 0;
+
+ return 0;
}
/* 8.4.1 CHANnel ACTIVation is received */
@@ -1501,6 +1623,7 @@ static int rsl_rx_chan_activ(struct msgb *msg)
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
struct gsm_bts_trx_ts *ts = lchan->ts;
+ struct gsm_bts_trx_ts *primary_ts;
struct tlv_parsed tp;
const struct tlv_p_entry *ie;
uint8_t type, cause;
@@ -1512,6 +1635,15 @@ static int rsl_rx_chan_activ(struct msgb *msg)
return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
}
+ /* We need to pick the real TS here to check NM state: */
+ primary_ts = ts->vamos.is_shadow ? ts->vamos.peer : ts;
+ if (primary_ts->mo.nm_state.operational != NM_OPSTATE_ENABLED ||
+ primary_ts->mo.nm_state.availability != NM_AVSTATE_OK) {
+ LOGP(DRSL, LOGL_ERROR, "%s rx chan activ but TS not in nm_state oper=ENABLED avail=OK, nack!\n",
+ gsm_ts_and_pchan_name(ts));
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_RR_UNAVAIL);
+ }
+
if (ts->pchan == GSM_PCHAN_OSMO_DYN) {
ts->dyn.pchan_want = dyn_pchan_from_chan_nr(dch->chan_nr);
DEBUGP(DRSL, "%s rx chan activ\n", gsm_ts_and_pchan_name(ts));
@@ -1522,17 +1654,17 @@ static int rsl_rx_chan_activ(struct msgb *msg)
* mode than this activation needs it to be.
* Re-connect, then come back to rsl_rx_chan_activ().
*/
- rc = dyn_ts_l1_reconnect(ts, msg);
+ rc = dyn_ts_l1_reconnect(ts);
if (rc)
return rsl_tx_chan_act_nack(lchan, RSL_ERR_NORMAL_UNSPEC);
+ /* will be fed back to rsl_rx_chan_activ() later */
+ OSMO_ASSERT(lchan->pending_chan_activ == NULL);
+ lchan->pending_chan_activ = msg;
/* indicate that the msgb should not be freed. */
return 1;
}
}
- LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "rx Channel Activation in state: %s.\n",
- gsm_lchans_name(lchan->state));
-
/* Initialize MS Power Control defaults */
lchan->ms_power_ctrl = (struct lchan_power_ctrl_state) {
.max = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0),
@@ -1618,7 +1750,7 @@ static int rsl_rx_chan_activ(struct msgb *msg)
}
/* 9.3.24 Timing Advance */
if (TLVP_PRES_LEN(&tp, RSL_IE_TIMING_ADVANCE, 1))
- lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
+ lchan->ta_ctrl.current = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
/* 9.3.31 (TLV) MS Power Parameters IE (vendor specific) */
if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) {
@@ -1794,7 +1926,19 @@ static int rsl_rx_chan_activ(struct msgb *msg)
/* Remember to send an RSL ACK once the lchan is active */
lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
- parse_repeated_acch_capability(lchan, &tp);
+ rc = parse_repeated_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_chan_act_acknack(lchan, -rc);
+ rc = parse_temporary_overpower_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_chan_act_acknack(lchan, -rc);
+
+ /* Take the first ACCH overpower decision (if allowed): it can be
+ * enabled immediately if the RxQual threshold is disabled (0). */
+ if (lchan->top_acch_cap.overpower_db > 0)
+ lchan->top_acch_active = !lchan->top_acch_cap.rxqual;
+ else
+ lchan->top_acch_active = false;
/* actually activate the channel in the BTS */
rc = l1sap_chan_act(lchan->ts->trx, dch->chan_nr, &tp);
@@ -1804,37 +1948,9 @@ static int rsl_rx_chan_activ(struct msgb *msg)
return 0;
}
-static int dyn_ts_pdch_release(struct gsm_lchan *lchan)
-{
- struct gsm_bts_trx_ts *ts = lchan->ts;
-
- if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
- LOGP(DRSL, LOGL_ERROR, "%s: PDCH release requested but already"
- " in switchover\n", gsm_ts_and_pchan_name(ts));
- return -EINVAL;
- }
-
- /*
- * Indicate PDCH Disconnect in dyn_pdch.want, let pcu_tx_info_ind()
- * pick it up and wait for PCU to disable the channel.
- */
- ts->dyn.pchan_want = GSM_PCHAN_NONE;
-
- if (!pcu_connected()) {
- /* PCU not connected yet. Just record the new type and done,
- * the PCU will pick it up once connected. */
- ts->dyn.pchan_is = GSM_PCHAN_NONE;
- return 1;
- }
-
- return pcu_tx_info_ind();
-}
-
/* 8.4.14 RF CHANnel RELease is received */
static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr)
{
- int rc;
-
if (lchan->state == LCHAN_S_NONE) {
LOGP(DRSL, LOGL_ERROR,
"%s ss=%d state=%s Rx RSL RF Channel Release, but is already inactive;"
@@ -1845,38 +1961,7 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr)
* not necessarily reflecting the current lchan state. */
return tx_rf_rel_ack(lchan, chan_nr);
}
-
- if (lchan->abis_ip.rtp_socket) {
- rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
- osmo_rtp_socket_log_stats(lchan->abis_ip.rtp_socket, DRTP, LOGL_INFO,
- "Closing RTP socket on Channel Release ");
- osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- msgb_queue_flush(&lchan->dl_tch_queue);
- }
-
- /* release handover state */
- handover_reset(lchan);
-
- lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
-
- /* Dynamic channel in PDCH mode is released via PCU */
- if (lchan->ts->pchan == GSM_PCHAN_OSMO_DYN
- && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
- rc = dyn_ts_pdch_release(lchan);
- if (rc == 1) {
- /* If the PCU is not connected, continue to rel ack right away. */
- lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
- return rsl_tx_rf_rel_ack(lchan);
- }
- /* Waiting for PDCH release */
- return rc;
- }
-
- l1sap_chan_rel(lchan->ts->trx, chan_nr);
-
- lapdm_channel_exit(&lchan->lapdm_ch);
-
+ gsm_lchan_release(lchan, LCHAN_REL_ACT_RSL);
return 0;
}
@@ -1910,7 +1995,7 @@ static int tx_ciph_mod_compl_hack(struct gsm_lchan *lchan, uint8_t link_id,
}
}
- rsl_rll_push_l3(fake_msg, RSL_MT_DATA_IND, gsm_lchan2chan_nr(lchan),
+ rsl_rll_push_l3(fake_msg, RSL_MT_DATA_IND, gsm_lchan2chan_nr_rsl(lchan),
link_id, 1);
fake_msg->lchan = lchan;
@@ -2045,7 +2130,7 @@ static int _rsl_tx_mode_modif_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uin
}
static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
{
- return _rsl_tx_mode_modif_nack(lchan->ts->trx, gsm_lchan2chan_nr(lchan), cause, lchan);
+ return _rsl_tx_mode_modif_nack(lchan->ts->trx, gsm_lchan2chan_nr_rsl(lchan), cause, lchan);
}
@@ -2053,7 +2138,7 @@ static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Tx MODE MODIF ACK\n");
@@ -2118,7 +2203,19 @@ static int rsl_rx_mode_modif(struct msgb *msg)
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
- parse_repeated_acch_capability(lchan, &tp);
+ rc = parse_repeated_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_mode_modif_nack(lchan, -rc);
+ rc = parse_temporary_overpower_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_mode_modif_nack(lchan, -rc);
+
+ /* Immediately disable ACCH overpower if the value is 0 dB,
+ * or enable if the RxQual threshold becomes disabled (0). */
+ if (lchan->top_acch_cap.overpower_db == 0)
+ lchan->top_acch_active = false;
+ else if (lchan->top_acch_cap.rxqual == 0)
+ lchan->top_acch_active = true;
l1sap_chan_modify(lchan->ts->trx, dch->chan_nr);
@@ -2331,7 +2428,7 @@ int rsl_tx_cbch_load_indication(struct gsm_bts *bts, bool ext_cbch, bool overflo
return -ENOMEM;
/* 9.3.1 Channel Number */
- rsl_cch_push_hdr(msg, RSL_MT_CBCH_LOAD_IND, gsm_lchan2chan_nr(lchan));
+ rsl_cch_push_hdr(msg, RSL_MT_CBCH_LOAD_IND, gsm_lchan2chan_nr_rsl(lchan));
/* 9.3.43 CBCH Load Information */
load_info = ((overflow & 1) << 7) | (amount & 0x0F);
@@ -2393,7 +2490,7 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
msgb_tv16_put(nmsg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
rsl_add_rtp_stats(lchan, nmsg);
msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause);
- rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr(lchan));
+ rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr_rsl(lchan));
nmsg->trx = lchan->ts->trx;
@@ -2405,7 +2502,7 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
uint8_t orig_msgt)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
const char *name;
struct in_addr ia;
@@ -2453,7 +2550,7 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "RSL Tx IPAC_DLCX_ACK\n");
@@ -2476,7 +2573,7 @@ static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id,
uint8_t cause)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "RSL Tx IPAC_DLCX_NACK\n");
@@ -2502,7 +2599,7 @@ static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause,
int inc_ipport, uint8_t orig_msgtype)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
/* FIXME: allocate new msgb and copy old over */
LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "RSL Tx IPAC_BIND_NACK\n");
@@ -2817,7 +2914,7 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg)
static int rsl_tx_dyn_pdch_ack(struct gsm_lchan *lchan, bool pdch_act)
{
struct gsm_time *gtime = get_time(lchan->ts->trx->bts);
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
struct msgb *msg;
uint8_t ie[2];
@@ -2846,7 +2943,7 @@ static int rsl_tx_dyn_pdch_nack(struct gsm_lchan *lchan, bool pdch_act,
uint8_t cause)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Tx PDCH %s NACK (cause = 0x%02x)\n",
pdch_act ? "ACT" : "DEACT", cause);
@@ -3107,8 +3204,7 @@ static void ipacc_dyn_pdch_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
static void osmo_dyn_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
{
- struct msgb *msg = ts->dyn.pending_chan_activ;
- ts->dyn.pending_chan_activ = NULL;
+ unsigned int ln;
if (rc) {
LOGP(DRSL, LOGL_NOTICE, "%s PDCH ACT OSMO operation failed (%d) in bts model\n",
@@ -3117,20 +3213,23 @@ static void osmo_dyn_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
return;
}
- if (!msg) {
- LOGP(DRSL, LOGL_ERROR,
- "%s TS re-connected, but no chan activ msg pending\n",
- gsm_ts_and_pchan_name(ts));
- return;
- }
-
ts->dyn.pchan_is = ts->dyn.pchan_want;
DEBUGP(DRSL, "%s Connected\n", gsm_ts_and_pchan_name(ts));
- /* continue where we left off before re-connecting the TS. */
- rc = rsl_rx_chan_activ(msg);
- if (rc != 1)
- msgb_free(msg);
+ /* Handle postponed RSL CHANnel ACTIVation messages (if any) */
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &ts->lchan[ln];
+
+ if (lchan->pending_chan_activ == NULL)
+ continue;
+
+ struct msgb *msg = lchan->pending_chan_activ;
+ lchan->pending_chan_activ = NULL;
+
+ /* Continue where we left off before re-connecting the TS */
+ if (rsl_rx_chan_activ(msg) != 1)
+ msgb_free(msg);
+ }
}
void cb_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
@@ -3374,6 +3473,7 @@ static int handle_gprs_susp_req(struct msgb *msg)
if (!gh || msgb_l3len(msg) < sizeof(*gh)+sizeof(*gsr)) {
LOGP(DRSL, LOGL_NOTICE, "%s Short GPRS SUSPEND REQ received, ignoring\n", gsm_lchan_name(msg->lchan));
+ msgb_free(msg);
return -EINVAL;
}
@@ -3389,16 +3489,6 @@ static int handle_gprs_susp_req(struct msgb *msg)
return rc;
}
-static inline uint8_t ms_to2rsl(const struct gsm_lchan *lchan, const struct lapdm_entity *le)
-{
- return (lchan->ms_t_offs >= 0) ? lchan->ms_t_offs : (lchan->p_offs - le->ta);
-}
-
-static inline bool ms_to_valid(const struct gsm_lchan *lchan)
-{
- return (lchan->ms_t_offs >= 0) || (lchan->p_offs >= 0);
-}
-
struct osmo_bts_supp_meas_info {
int16_t toa256_mean;
int16_t toa256_min;
@@ -3406,12 +3496,12 @@ struct osmo_bts_supp_meas_info {
uint16_t toa256_std_dev;
} __attribute__((packed));
-/* Compose and send 8.4.8 MEASUREMENT RESult via RSL */
-int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const struct lapdm_entity *le)
+/* Compose and send 8.4.8 MEASUREMENT RESult via RSL. (timing_offset=-1 -> not present) */
+int rsl_tx_meas_res(struct gsm_lchan *lchan, const uint8_t *l3, int l3_len, int timing_offset)
{
struct msgb *msg;
uint8_t meas_res[16];
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
int res_valid = lchan->meas.flags & LC_UL_M_F_RES_VALID;
struct gsm_bts *bts = lchan->ts->trx->bts;
@@ -3433,13 +3523,12 @@ int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const stru
lchan->meas.ul_res.full.rx_qual,
lchan->meas.ul_res.sub.rx_qual,
lchan->meas.l1_info.ms_pwr,
- lchan->meas.l1_info.ta, l3_len, ms_to2rsl(lchan, le) - MEAS_MAX_TIMING_ADVANCE);
+ lchan->meas.l1_info.ta, l3_len, timing_offset - MEAS_MAX_TIMING_ADVANCE);
- msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
+ 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.dtx.dl_active,
meas_res);
- lchan->tch.dtx.dl_active = false;
if (ie_len >= 3) {
if (bts->supp_meas_toa256 && lchan->meas.flags & LC_UL_M_F_OSMO_EXT_VALID) {
struct osmo_bts_supp_meas_info *smi;
@@ -3457,24 +3546,18 @@ int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const stru
smi->toa256_min = htons(ta256 + lchan->meas.ext.toa256_min);
smi->toa256_max = htons(ta256 + lchan->meas.ext.toa256_max);
smi->toa256_std_dev = htons(lchan->meas.ext.toa256_std_dev);
- lchan->meas.flags &= ~LC_UL_M_F_OSMO_EXT_VALID;
}
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
- lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;
}
msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power_ctrl.current / 2);
if (lchan->meas.flags & LC_UL_M_F_L1_VALID) {
msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, sizeof(lchan->meas.l1_info), (uint8_t*)&lchan->meas.l1_info);
- lchan->meas.flags &= ~LC_UL_M_F_L1_VALID;
}
- if (l3 && l3_len > 0)
+ if (l3 && l3_len > 0) {
msgb_tl16v_put(msg, RSL_IE_L3_INFO, l3_len, l3);
- if (ms_to_valid(lchan)) {
- if (l3 && l3_len > 0)
- msgb_tv_put(msg, RSL_IE_MS_TIMING_OFFSET, ms_to2rsl(lchan, le));
- lchan->ms_t_offs = -1;
- lchan->p_offs = -1;
+ if (timing_offset != -1)
+ msgb_tv_put(msg, RSL_IE_MS_TIMING_OFFSET, timing_offset);
}
rsl_dch_push_hdr(msg, RSL_MT_MEAS_RES, chan_nr);
@@ -3502,17 +3585,19 @@ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx)
msg->trx = lchan->ts->trx;
msg->lchan = lchan;
- /* check if this is a measurement report from SACCH which needs special
- * processing before forwarding */
+ /* If this is a Measurement Report, then we simply ignore it,
+ * because it has already been processed in l1sap_ph_data_ind(). */
if (rslms_is_meas_rep(msg)) {
- int rc;
-
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Handing RLL msg %s from LAPDm to MEAS REP\n",
+ msgb_free(msg);
+ return 0;
+ } else if (rslms_is_gprs_susp_req(msg)) {
+ return handle_gprs_susp_req(msg);
+ } else {
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Fwd RLL msg %s from LAPDm to A-bis\n",
rsl_msg_name(rh->msg_type));
/* REL_IND handling */
- if (rh->msg_type == RSL_MT_REL_IND &&
- (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)) {
+ if (rh->msg_type == RSL_MT_REL_IND && lchan_is_tch(lchan)) {
LOGPLCHAN(lchan, DRSL, LOGL_INFO,
"Scheduling %s to L3 in next associated TCH-RTS.ind\n",
rsl_msg_name(rh->msg_type));
@@ -3527,16 +3612,6 @@ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx)
return 0;
}
- repeated_dl_facch_active_decision(lchan, msgb_l3(msg), msgb_l3len(msg));
- rc = rsl_tx_meas_res(lchan, msgb_l3(msg), msgb_l3len(msg), le);
- msgb_free(msg);
- return rc;
- } else if (rslms_is_gprs_susp_req(msg)) {
- return handle_gprs_susp_req(msg);
- } else {
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Fwd RLL msg %s from LAPDm to A-bis\n",
- rsl_msg_name(rh->msg_type));
-
return abis_bts_rsl_sendmsg(msg);
}
}
@@ -3767,14 +3842,6 @@ static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg)
return ret;
}
-int lchan_deactivate(struct gsm_lchan *lchan)
-{
- OSMO_ASSERT(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;
diff --git a/src/common/scheduler.c b/src/common/scheduler.c
index 6e1e4fb1..e854ce12 100644
--- a/src/common/scheduler.c
+++ b/src/common/scheduler.c
@@ -221,13 +221,14 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
/* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03,
* chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7):
*
- * - a traffic frame is interleaved over 6 consecutive bursts
+ * - a traffic frame is interleaved over 4 consecutive bursts
* using the even numbered bits of the first 2 bursts,
- * all bits of the middle two 2 bursts,
* and odd numbered bits of the last 2 bursts;
* - a FACCH/H frame 'steals' (replaces) two traffic frames,
- * interleaving is done over 4 consecutive bursts,
- * the same as given for a TCH/FS. */
+ * interleaving is done over 6 consecutive bursts,
+ * using the even numbered bits of the first 2 bursts,
+ * all bits of the middle two 2 bursts,
+ * and odd numbered bits of the last 2 bursts. */
.rts_fn = rts_tchh_fn,
.dl_fn = tx_tchh_fn,
.ul_fn = rx_tchh_fn,
@@ -653,35 +654,39 @@ void trx_sched_init(struct gsm_bts_trx *trx)
}
}
+static void trx_sched_clean_ts(struct gsm_bts_trx_ts *ts)
+{
+ struct l1sched_ts *l1ts = ts->priv;
+ unsigned int i;
+
+ msgb_queue_flush(&l1ts->dl_prims);
+ rate_ctr_group_free(l1ts->ctrs);
+ l1ts->ctrs = NULL;
+
+ /* clear lchan channel states */
+ for (i = 0; i < ARRAY_SIZE(ts->lchan); i++)
+ lchan_set_state(&ts->lchan[i], LCHAN_S_NONE);
+
+ talloc_free(l1ts);
+ ts->priv = NULL;
+}
+
void trx_sched_clean(struct gsm_bts_trx *trx)
{
- unsigned int tn, i;
+ unsigned int tn;
LOGPTRX(trx, DL1C, LOGL_DEBUG, "Clean scheduler structures\n");
for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
struct gsm_bts_trx_ts *ts = &trx->ts[tn];
- struct l1sched_ts *l1ts = ts->priv;
-
- msgb_queue_flush(&l1ts->dl_prims);
- rate_ctr_group_free(l1ts->ctrs);
- l1ts->ctrs = NULL;
- for (i = 0; i < _TRX_CHAN_MAX; i++) {
- struct l1sched_chan_state *chan_state;
- chan_state = &l1ts->chan_state[i];
- if (chan_state->dl_bursts) {
- talloc_free(chan_state->dl_bursts);
- chan_state->dl_bursts = NULL;
- }
- if (chan_state->ul_bursts) {
- talloc_free(chan_state->ul_bursts);
- chan_state->ul_bursts = NULL;
- }
- }
- /* clear lchan channel states */
- for (i = 0; i < ARRAY_SIZE(ts->lchan); i++)
- lchan_set_state(&ts->lchan[i], LCHAN_S_NONE);
+
+ /* Clean primary and shadow timeslots */
+ trx_sched_clean_ts(ts);
+ trx_sched_clean_ts(ts->vamos.peer);
}
+
+ /* Free previously allocated shadow timeslots */
+ gsm_bts_trx_free_shadow_ts(trx);
}
struct msgb *_sched_dequeue_prim(struct l1sched_ts *l1ts, const struct trx_dl_burst_req *br)
@@ -795,7 +800,7 @@ int _sched_compose_ph_data_ind(struct l1sched_ts *l1ts, uint32_t fn,
int _sched_compose_tch_ind(struct l1sched_ts *l1ts, uint32_t fn,
enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len,
int16_t ta_offs_256bits, uint16_t ber10k, float rssi,
- uint8_t is_sub)
+ int16_t link_qual_cb, uint8_t is_sub)
{
struct msgb *msg;
struct osmo_phsap_prim *l1sap;
@@ -816,6 +821,7 @@ int _sched_compose_tch_ind(struct l1sched_ts *l1ts, uint32_t fn,
l1sap->u.tch.rssi = (int8_t) (rssi);
l1sap->u.tch.ber10k = ber10k;
l1sap->u.tch.ta_offs_256bits = ta_offs_256bits;
+ l1sap->u.tch.lqual_cb = link_qual_cb;
l1sap->u.tch.is_sub = is_sub & 1;
msg->l2h = msgb_put(msg, tch_len);
@@ -852,7 +858,7 @@ int trx_sched_ph_data_req(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap
OSMO_ASSERT(l1sap->oph.msg);
/* ignore empty frame */
- if (!msgb_l2len(l1sap->oph.msg)) {
+ if (!l1sap->oph.msg->l2h || msgb_l2len(l1sap->oph.msg) == 0) {
msgb_free(l1sap->oph.msg);
return 0;
}
@@ -1068,6 +1074,12 @@ int trx_sched_set_lchan(struct gsm_lchan *lchan, uint8_t chan_nr, uint8_t link_i
bool found = false;
int i;
+ if (!l1ts) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "%s lchan with uninitialized scheduler structure\n",
+ (active) ? "Activating" : "Deactivating");
+ return -EINVAL;
+ }
+
/* VAMOS: convert Osmocom specific channel number to a generic one,
* otherwise we won't match anything in trx_chan_desc[]. */
if (lchan->ts->vamos.is_shadow)
@@ -1085,7 +1097,7 @@ int trx_sched_set_lchan(struct gsm_lchan *lchan, uint8_t chan_nr, uint8_t link_i
continue;
found = true;
- LOGPLCHAN(lchan, DL1C, LOGL_NOTICE, "%s %s\n",
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "%s %s\n",
(active) ? "Activating" : "Deactivating",
trx_chan_desc[i].name);
/* free burst memory, to cleanly start with burst 0 */
@@ -1097,6 +1109,10 @@ int trx_sched_set_lchan(struct gsm_lchan *lchan, uint8_t chan_nr, uint8_t link_i
talloc_free(chan_state->ul_bursts);
chan_state->ul_bursts = NULL;
}
+ if (chan_state->ul_bursts_prev) {
+ talloc_free(chan_state->ul_bursts_prev);
+ chan_state->ul_bursts_prev = NULL;
+ }
if (active) {
/* Clean up everything */
@@ -1278,6 +1294,26 @@ int _sched_rts(const struct l1sched_ts *l1ts, uint32_t fn)
return func(l1ts, &dbr);
}
+static void trx_sched_apply_att(const struct gsm_lchan *lchan,
+ struct trx_dl_burst_req *br)
+{
+ const struct trx_chan_desc *desc = &trx_chan_desc[br->chan];
+
+ /* Current BS power reduction value in dB */
+ br->att = lchan->bs_power_ctrl.current;
+
+ /* Temporary Overpower for SACCH/FACCH bursts */
+ if (!lchan->top_acch_active)
+ return;
+ if ((lchan->top_acch_cap.sacch_enable && desc->link_id == LID_SACCH) ||
+ (lchan->top_acch_cap.facch_enable && br->flags & TRX_BR_F_FACCH)) {
+ if (br->att > lchan->top_acch_cap.overpower_db)
+ br->att -= lchan->top_acch_cap.overpower_db;
+ else
+ br->att = 0;
+ }
+}
+
/* process downlink burst */
void _sched_dl_burst(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
{
@@ -1317,9 +1353,7 @@ void _sched_dl_burst(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* BS Power reduction (in dB) per logical channel */
if (l1cs->lchan != NULL)
- br->att = l1cs->lchan->bs_power_ctrl.current;
- else /* Ensure no attenuation in the absence of lchan (e.g. on PDCH) */
- br->att = 0;
+ trx_sched_apply_att(l1cs->lchan, br);
/* encrypt */
if (br->burst_len && l1cs->dl_encr_algo) {
diff --git a/src/common/sysinfo.c b/src/common/sysinfo.c
index 5c5af253..b0f1ebfd 100644
--- a/src/common/sysinfo.c
+++ b/src/common/sysinfo.c
@@ -164,22 +164,6 @@ uint8_t num_agch(const struct gsm_bts_trx *trx, const char * arg)
return 1;
}
-/* obtain the next to-be transmitted dowlink SACCH frame (L2 hdr + L3); returns pointer to lchan->si buffer */
-uint8_t *lchan_sacch_get(struct gsm_lchan *lchan)
-{
- uint32_t tmp, i;
-
- for (i = 0; i < _MAX_SYSINFO_TYPE; i++) {
- tmp = (lchan->si.last + 1 + i) % _MAX_SYSINFO_TYPE;
- if (!(lchan->si.valid & (1 << tmp)))
- continue;
- lchan->si.last = tmp;
- return GSM_LCHAN_SI(lchan, tmp);
- }
- LOGPLCHAN(lchan, DL1P, LOGL_NOTICE, "SACCH no SI available\n");
- return NULL;
-}
-
/* re-generate SI3 restoctets with GPRS indicator depending on the PCU socket connection state */
void regenerate_si3_restoctets(struct gsm_bts *bts)
{
diff --git a/src/common/ta_control.c b/src/common/ta_control.c
index ccb60e2b..025699ce 100644
--- a/src/common/ta_control.c
+++ b/src/common/ta_control.c
@@ -1,6 +1,7 @@
/* Loop control for Timing Advance */
/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -19,32 +20,83 @@
*
*/
+/* Related specs: 3GPP TS 45.010 sections 5.5, 5.6 */
+
#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/bts_trx.h>
#include <osmo-bts/logging.h>
-/* 90% of one bit duration in 1/256 symbols: 256*0.9 */
-#define TOA256_9OPERCENT 230
+/* 3GPP TS 45.010 sec 5.6.3 Delay assessment error:
+ * 75% of one bit duration in 1/256 symbols: 256*0.75 */
+#define TOA256_THRESH 192
/* rqd_ta value range */
#define TA_MIN 0
#define TA_MAX 63
-void lchan_ms_ta_ctrl(struct gsm_lchan *lchan)
+/* TODO: make configurable over osmo-bts VTY? Pass it BSC->BTS? */
+#define TA_MAX_INC_STEP 2
+#define TA_MAX_DEC_STEP 2
+
+
+/*! compute the new "Ordered Timing Advance" communicated to the MS and store it in lchan.
+ * \param lchan logical channel for which to compute (and in which to store) new power value.
+ * \param[in] ms_tx_ta The TA used by the MS and reported in L1SACCH, see struct gsm_sacch_l1_hdr field "ta".
+ * \param[in] toa256 Time of Arrival (in 1/256th bits) computed at Rx side
+ */
+void lchan_ms_ta_ctrl(struct gsm_lchan *lchan, uint8_t ms_tx_ta, int16_t toa256)
{
- int16_t toa256 = lchan->meas.ms_toa256;
-
- if (toa256 < -TOA256_9OPERCENT && lchan->rqd_ta > TA_MIN) {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO,
- "TOA is too early (%d), now lowering TA from %d to %d\n",
- toa256, lchan->rqd_ta, lchan->rqd_ta - 1);
- lchan->rqd_ta--;
- } else if (toa256 > TOA256_9OPERCENT && lchan->rqd_ta < TA_MAX) {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO,
- "TOA is too late (%d), now raising TA from %d to %d\n",
- toa256, lchan->rqd_ta, lchan->rqd_ta + 1);
- lchan->rqd_ta++;
- } else
+ int16_t new_ta;
+ /* Shall we skip current block based on configured interval? */
+
+ /* TA control interval: how many blocks do we skip? */
+ if (lchan->ta_ctrl.skip_block_num-- > 0)
+ return;
+
+ /* Reset the number of SACCH blocks to be skipped:
+ * ctrl_interval=0 => 0 blocks to skip,
+ * ctrl_interval=1 => 1 blocks to skip,
+ * ctrl_interval=2 => 3 blocks to skip,
+ * so basically ctrl_interval * 2 - 1. */
+ lchan->ta_ctrl.skip_block_num = lchan->ts->trx->ta_ctrl_interval * 2 - 1;
+
+ int16_t delta_ta = toa256/256;
+ if (toa256 >= 0) {
+ if ((toa256 - (256 * delta_ta)) > TOA256_THRESH)
+ delta_ta++;
+ if (delta_ta > TA_MAX_INC_STEP)
+ delta_ta = TA_MAX_INC_STEP;
+ } else {
+ if ((toa256 - (256 * delta_ta)) < -TOA256_THRESH)
+ delta_ta--;
+ if (delta_ta < -TA_MAX_DEC_STEP)
+ delta_ta = -TA_MAX_DEC_STEP;
+ }
+
+ new_ta = ms_tx_ta + delta_ta;
+
+ /* Make sure new_ta is never negative: */
+ if (new_ta < TA_MIN)
+ new_ta = TA_MIN;
+
+ /* Don't ask for out of range TA: */
+ if (new_ta > TA_MAX)
+ new_ta = TA_MAX;
+
+ if (lchan->ta_ctrl.current == (uint8_t)new_ta) {
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG,
- "TOA is correct (%d), keeping current TA of %d\n",
- toa256, lchan->rqd_ta);
+ "Keeping current TA at %u: TOA was %d\n",
+ lchan->ta_ctrl.current, toa256);
+ return;
+ }
+
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO,
+ "%s TA %u => %u: TOA was too %s (%d)\n",
+ (uint8_t)new_ta > lchan->ta_ctrl.current ? "Raising" : "Lowering",
+ lchan->ta_ctrl.current, (uint8_t)new_ta,
+ (uint8_t)new_ta > lchan->ta_ctrl.current ? "late" : "early",
+ toa256);
+
+ /* store the resulting new TA in the lchan */
+ lchan->ta_ctrl.current = (uint8_t)new_ta;
}
diff --git a/src/common/tx_power.c b/src/common/tx_power.c
index 348aba5c..03074221 100644
--- a/src/common/tx_power.c
+++ b/src/common/tx_power.c
@@ -259,7 +259,7 @@ int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass,
}
/* Cancel any pending request */
- osmo_timer_del(&tpp->ramp.step_timer);
+ power_ramp_abort(trx);
/* set the new target */
tpp->p_total_tgt_mdBm = p_total_tgt_mdBm;
@@ -297,6 +297,12 @@ int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass,
return 0;
}
+/* Cancel any pending request */
+void power_ramp_abort(struct gsm_bts_trx *trx)
+{
+ osmo_timer_del(&trx->power_params.ramp.step_timer);
+}
+
/* determine the initial transceiver output power at start-up time */
int power_ramp_initial_power_mdBm(const struct gsm_bts_trx *trx)
{
diff --git a/src/common/vty.c b/src/common/vty.c
index a7d1e845..97bd6589 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -37,11 +37,12 @@
#include <osmocom/vty/logging.h>
#include <osmocom/vty/misc.h>
#include <osmocom/vty/ports.h>
+#include <osmocom/vty/tdef_vty.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/trau/osmo_ortp.h>
-
+#include <osmocom/core/fsm.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
@@ -282,6 +283,7 @@ static void config_write_bts_single(struct vty *vty, const struct gsm_bts *bts)
const struct gsm_bts_trx *trx;
const char *sapi_buf;
int i;
+ struct bsc_oml_host *bsc_oml_host;
vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE);
if (bts->description)
@@ -291,7 +293,8 @@ static void config_write_bts_single(struct vty *vty, const struct gsm_bts *bts)
vty_out(vty, " auto-band%s", VTY_NEWLINE);
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", bts->bsc_oml_host, VTY_NEWLINE);
+ llist_for_each_entry(bsc_oml_host, &bts->bsc_oml_hosts, list)
+ vty_out(vty, " oml remote-ip %s%s", bsc_oml_host->addr, VTY_NEWLINE);
vty_out(vty, " rtp jitter-buffer %u", bts->rtp_jitter_buf_ms);
if (bts->rtp_jitter_adaptive)
vty_out(vty, " adaptive");
@@ -367,6 +370,8 @@ static void config_write_bts_single(struct vty *vty, const struct gsm_bts *bts)
vty_out(vty, " ms-power-control %s%s",
trx->ms_pwr_ctl_soft ? "osmo" : "dsp",
VTY_NEWLINE);
+ vty_out(vty, " ta-control interval %u%s",
+ trx->ta_ctrl_interval, VTY_NEWLINE);
vty_out(vty, " phy %u instance %u%s", pinst->phy_link->num,
pinst->num, VTY_NEWLINE);
@@ -382,6 +387,7 @@ static int config_write_bts(struct vty *vty)
llist_for_each_entry(bts, &net->bts_list, list)
config_write_bts_single(vty, bts);
+ osmo_tdef_vty_groups_write(vty, "");
return CMD_SUCCESS;
}
@@ -507,11 +513,51 @@ DEFUN(cfg_bts_oml_ip,
"OML Parameters\n" "OML IP Address\n" "OML IP Address\n")
{
struct gsm_bts *bts = vty->index;
+ struct bsc_oml_host *bsc_oml_host;
- if (bts->bsc_oml_host)
- talloc_free(bts->bsc_oml_host);
+ /* stop when the address is already in the list */
+ llist_for_each_entry(bsc_oml_host, &bts->bsc_oml_hosts, list) {
+ if (strcmp(argv[0], bsc_oml_host->addr) == 0) {
+ vty_out(vty, "%% duplicate BSC (A-Bis/OML) ip address configured ('%s')%s",
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ bsc_oml_host = talloc_zero(bts, struct bsc_oml_host);
+ OSMO_ASSERT(bsc_oml_host);
+ bsc_oml_host->addr = talloc_strdup(bsc_oml_host, argv[0]);
+ OSMO_ASSERT(bsc_oml_host->addr);
+ llist_add_tail(&bsc_oml_host->list, &bts->bsc_oml_hosts);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_no_oml_ip,
+ cfg_bts_no_oml_ip_cmd,
+ "no oml remote-ip A.B.C.D",
+ NO_STR "OML Parameters\n" "OML IP Address\n" "OML IP Address\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct bsc_oml_host *bsc_oml_host;
+ struct bsc_oml_host *bsc_oml_host_del = NULL;
+
+ llist_for_each_entry(bsc_oml_host, &bts->bsc_oml_hosts, list) {
+ if (strcmp(argv[0], bsc_oml_host->addr) == 0)
+ bsc_oml_host_del = bsc_oml_host;
+ }
- bts->bsc_oml_host = talloc_strdup(bts, argv[0]);
+ if (bsc_oml_host_del) {
+ if (bts->abis_link_fi) {
+ osmo_fsm_inst_dispatch(bts->abis_link_fi, ABIS_LINK_EV_VTY_RM_ADDR, bsc_oml_host_del);
+ llist_del(&bsc_oml_host_del->list);
+ talloc_free(bsc_oml_host_del);
+ }
+ } else {
+ vty_out(vty, "%% no such BSC (A-Bis/OML) ip address configured ('%s')%s",
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
return CMD_SUCCESS;
}
@@ -965,6 +1011,19 @@ DEFUN(cfg_trx_ms_power_control, cfg_trx_ms_power_control_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_ta_ctrl_interval, cfg_ta_ctrl_interval_cmd,
+ "ta-control interval <0-31>",
+ "Timing Advance Control Parameters\n"
+ "Set TA control loop interval\n"
+ "As in P_CON_INTERVAL, in units of 2 SACCH periods (0.96 seconds) (default=0, every SACCH block)\n")
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ trx->ta_ctrl_interval = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_trx_phy, cfg_trx_phy_cmd,
"phy <0-255> instance <0-255>",
"Configure PHY Link+Instance for this TRX\n"
@@ -1347,7 +1406,7 @@ static void dump_dpc_meas_params(struct vty *vty, const unsigned int indent,
}
static void dump_dpc_params(struct vty *vty, const unsigned int indent,
- const struct gsm_power_ctrl_params *cp)
+ const struct gsm_power_ctrl_params *cp, bool uplink)
{
cfg_out(vty, "Power control interval: %u ms (every %u SACCH block(s))%s",
cp->ctrl_interval ? cp->ctrl_interval * 2 * 480 : 480,
@@ -1364,6 +1423,26 @@ static void dump_dpc_params(struct vty *vty, const unsigned int indent,
cfg_out(vty, "RxQual measurement processing:%s", VTY_NEWLINE);
dump_dpc_meas_params(vty, indent + 2, &cp->rxqual_meas, "RXQUAL", 3);
+
+ if (uplink) {
+ cfg_out(vty, "C/I measurement processing (FR/EFR):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_fr_meas, "CI_FR", 0);
+
+ cfg_out(vty, "C/I measurement processing (HR):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_hr_meas, "CI_HR", 0);
+
+ cfg_out(vty, "C/I measurement processing (AMR-FR):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_amr_fr_meas, "CI_AMR_FR", 0);
+
+ cfg_out(vty, "C/I measurement processing (AMR-HR):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_amr_hr_meas, "CI_AMR_HR", 0);
+
+ cfg_out(vty, "C/I measurement processing (SDCCH):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_sdcch_meas, "CI_SDCCH", 0);
+
+ cfg_out(vty, "C/I measurement processing (GPRS):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_gprs_meas, "CI_GPRS", 0);
+ }
}
static void trx_dump_vty(struct vty *vty, const struct gsm_bts_trx *trx)
@@ -1381,13 +1460,13 @@ static void trx_dump_vty(struct vty *vty, const struct gsm_bts_trx *trx)
trx->bs_dpc_params == &trx->bts->bs_dpc_params ?
"fall-back" : "from BSC",
VTY_NEWLINE);
- dump_dpc_params(vty, 4, trx->bs_dpc_params);
+ dump_dpc_params(vty, 4, trx->bs_dpc_params, false);
vty_out(vty, " MS Power control parameters (%s):%s",
trx->ms_dpc_params == &trx->bts->ms_dpc_params ?
"fall-back" : "from BSC",
VTY_NEWLINE);
- dump_dpc_params(vty, 4, trx->ms_dpc_params);
+ dump_dpc_params(vty, 4, trx->ms_dpc_params, true);
vty_out(vty, " NM State: ");
net_dump_nmstate(vty, &trx->mo.nm_state);
@@ -1594,7 +1673,7 @@ static void lchan_bs_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
return;
cfg_out(vty, "Power Control parameters:%s", VTY_NEWLINE);
- dump_dpc_params(vty, indent + 2, st->dpc_params);
+ dump_dpc_params(vty, indent + 2, st->dpc_params, false);
}
static void lchan_ms_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
@@ -1620,7 +1699,7 @@ static void lchan_ms_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
return;
cfg_out(vty, "Power Control parameters:%s", VTY_NEWLINE);
- dump_dpc_params(vty, indent + 2, st->dpc_params);
+ dump_dpc_params(vty, indent + 2, st->dpc_params, true);
}
static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
@@ -1628,23 +1707,23 @@ static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
{
cfg_out(vty, "ACCH repetition:%s", VTY_NEWLINE);
indent += 2;
- if (lchan->repeated_acch_capability.rxqual)
+ if (lchan->rep_acch_cap.rxqual)
cfg_out(vty, "Enable RXQUAL threshold: %u%s",
- lchan->repeated_acch_capability.rxqual, VTY_NEWLINE);
+ lchan->rep_acch_cap.rxqual, VTY_NEWLINE);
else
cfg_out(vty, "Enable RXQUAL threshold: (none, alway on)%s",
VTY_NEWLINE);
cfg_out(vty, "DL-FACCH:%s", VTY_NEWLINE);
indent += 2;
- if (lchan->repeated_acch_capability.dl_facch_all)
+ if (lchan->rep_acch_cap.dl_facch_all)
cfg_out(vty, "retramsit all LAPDM block types%s", VTY_NEWLINE);
- else if (lchan->repeated_acch_capability.dl_facch_cmd)
+ else if (lchan->rep_acch_cap.dl_facch_cmd)
cfg_out(vty, "retramsit only LAPDM command blocks%s",
VTY_NEWLINE);
else
cfg_out(vty, "no retransmission (disabled)%s", VTY_NEWLINE);
- if (lchan->repeated_dl_facch_active)
+ if (lchan->rep_acch.dl_facch_active)
cfg_out(vty, "retransmission currently active%s", VTY_NEWLINE);
else
cfg_out(vty, "retransmission currently inactive%s",
@@ -1653,12 +1732,12 @@ static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
cfg_out(vty, "DL-SACCH:%s", VTY_NEWLINE);
indent += 2;
- if (lchan->repeated_acch_capability.ul_sacch)
+ if (lchan->rep_acch_cap.ul_sacch)
cfg_out(vty, "retramsit all SACCH blocks for SAPI=0%s",
VTY_NEWLINE);
else
cfg_out(vty, "no retransmission (disabled)%s", VTY_NEWLINE);
- if (lchan->repeated_dl_sacch_active)
+ if (lchan->rep_acch.dl_sacch_active)
cfg_out(vty, "retransmission currently active%s", VTY_NEWLINE);
else
cfg_out(vty, "retransmission currently inactive%s",
@@ -1667,12 +1746,12 @@ static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
cfg_out(vty, "UL-SACCH:%s", VTY_NEWLINE);
indent += 2;
- if (lchan->repeated_acch_capability.dl_sacch)
+ if (lchan->rep_acch_cap.dl_sacch)
cfg_out(vty, "retramsit all SACCH blocks for SAPI=0%s",
VTY_NEWLINE);
else
cfg_out(vty, "no retransmission (disabled)%s", VTY_NEWLINE);
- if (lchan->repeated_ul_sacch_active)
+ if (lchan->rep_acch.ul_sacch_active)
cfg_out(vty, "retransmission currently active%s", VTY_NEWLINE);
else
cfg_out(vty, "retransmission currently inactive%s",
@@ -1680,6 +1759,34 @@ static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
indent -= 2;
}
+static void lchan_acch_top_state_dump(struct vty *vty, unsigned int indent,
+ const struct gsm_lchan *lchan)
+{
+ if (lchan->top_acch_cap.overpower_db == 0)
+ return;
+
+ cfg_out(vty, "Temporary ACCH overpower:%s", VTY_NEWLINE);
+ indent += 2;
+
+ cfg_out(vty, "Overpower value: %u dB%s",
+ lchan->top_acch_cap.overpower_db, VTY_NEWLINE);
+
+ cfg_out(vty, "SACCH overpower: %sabled%s",
+ lchan->top_acch_cap.sacch_enable ? "en" : "dis",
+ VTY_NEWLINE);
+ cfg_out(vty, "FACCH overpower: %sabled%s",
+ lchan->top_acch_cap.facch_enable ? "en" : "dis",
+ VTY_NEWLINE);
+
+ if (lchan->top_acch_cap.rxqual == 0) {
+ cfg_out(vty, "RxQual threshold: disabled "
+ "(overpower is always on)%s", VTY_NEWLINE);
+ } else {
+ cfg_out(vty, "RxQual threshold: %u%s",
+ lchan->top_acch_cap.rxqual, VTY_NEWLINE);
+ }
+}
+
static void lchan_dump_full_vty(struct vty *vty, const struct gsm_lchan *lchan)
{
struct in_addr ia;
@@ -1751,10 +1858,21 @@ static void lchan_dump_full_vty(struct vty *vty, const struct gsm_lchan *lchan)
vty_out(vty, " RTP/PDCH Loopback Enabled%s", VTY_NEWLINE);
vty_out(vty, " Radio Link Failure Counter 'S': %d%s", lchan->s, VTY_NEWLINE);
+ /* Interference levels */
+ if (lchan->meas.interf_meas_avg_dbm != 0) {
+ vty_out(vty, " Interference: %d dBm (band %d)%s",
+ lchan->meas.interf_meas_avg_dbm,
+ lchan->meas.interf_band,
+ VTY_NEWLINE);
+ }
+
/* BS/MS Power Control state and parameters */
lchan_bs_power_ctrl_state_dump(vty, 2, lchan);
lchan_ms_power_ctrl_state_dump(vty, 2, lchan);
+
+ /* ACCH repetition / overpower state */
lchan_acch_rep_state_dump(vty, 2, lchan);
+ lchan_acch_top_state_dump(vty, 2, lchan);
}
static void lchan_dump_short_vty(struct vty *vty, const struct gsm_lchan *lchan)
@@ -2376,8 +2494,12 @@ int bts_vty_init(void *ctx)
install_node(&bts_node, config_write_bts);
install_element(CONFIG_NODE, &cfg_bts_cmd);
install_element(CONFIG_NODE, &cfg_vty_telnet_port_cmd);
+
+ osmo_tdef_vty_groups_init(CONFIG_NODE, bts_tdef_groups);
+
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
install_element(BTS_NODE, &cfg_bts_oml_ip_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_oml_ip_cmd);
install_element(BTS_NODE, &cfg_bts_rtp_bind_ip_cmd);
install_element(BTS_NODE, &cfg_bts_rtp_jitbuf_cmd);
install_element(BTS_NODE, &cfg_bts_rtp_port_range_cmd);
@@ -2419,6 +2541,7 @@ int bts_vty_init(void *ctx)
install_element(TRX_NODE, &cfg_trx_pr_step_size_cmd);
install_element(TRX_NODE, &cfg_trx_pr_step_interval_cmd);
install_element(TRX_NODE, &cfg_trx_ms_power_control_cmd);
+ install_element(TRX_NODE, &cfg_ta_ctrl_interval_cmd);
install_element(TRX_NODE, &cfg_trx_phy_cmd);
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
diff --git a/src/osmo-bts-lc15/l1_if.c b/src/osmo-bts-lc15/l1_if.c
index 02c8646e..ac165b88 100644
--- a/src/osmo-bts-lc15/l1_if.c
+++ b/src/osmo-bts-lc15/l1_if.c
@@ -553,7 +553,12 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
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);
+ if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg) < 0) {
+ LOGPFN(DL1P, LOGL_ERROR, u32Fn, "MQ_L1_WRITE queue full. Dropping msg.\n");
+ msgb_free(nmsg);
+ return -ENOBUFS;
+ }
+
if (dtx_is_first_p1(lchan))
dtx_dispatch(lchan, E_FIRST);
else
diff --git a/src/osmo-bts-lc15/lc15bts_vty.c b/src/osmo-bts-lc15/lc15bts_vty.c
index be6a4f8b..5efbfcc5 100644
--- a/src/osmo-bts-lc15/lc15bts_vty.c
+++ b/src/osmo-bts-lc15/lc15bts_vty.c
@@ -140,7 +140,7 @@ DEFUN(cfg_phy_no_dsp_trace_f, cfg_phy_no_dsp_trace_f_cmd,
/* runtime */
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
- "show trx <0-0> dsp-trace-flags",
+ "show dsp-trace-flags trx <0-0>",
SHOW_TRX_STR "Display the current setting of the DSP trace flags")
{
int trx_nr = atoi(argv[0]);
diff --git a/src/osmo-bts-lc15/oml.c b/src/osmo-bts-lc15/oml.c
index b4945be0..675e3fed 100644
--- a/src/osmo-bts-lc15/oml.c
+++ b/src/osmo-bts-lc15/oml.c
@@ -1197,10 +1197,6 @@ int lchan_activate(struct gsm_lchan *lchan)
enqueue_sapi_act_cmd(lchan, sapi, dir);
}
-
-#warning "FIXME: Should this be in sapi_activate_cb?"
- lchan_init_lapdm(lchan);
-
return 0;
}
@@ -1834,11 +1830,31 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
- if (kind == NM_OC_RADIO_CARRIER) {
- struct gsm_bts_trx *trx = obj;
- struct lc15l1_hdl *fl1h = trx_lc15l1_hdl(trx);
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+ struct gsm_bts_trx *trx;
+ struct lc15l1_hdl *fl1h;
+ uint8_t cell_size;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_RADIO_ATTR:
+ trx = obj;
+ fl1h = trx_lc15l1_hdl(trx);
/* convert max TA to max cell size in qbits */
- uint8_t cell_size = bts->max_ta << 2;
+ cell_size = bts->max_ta << 2;
#if LITECELL15_API_VERSION >= LITECELL15_API(2,1,7)
/* We do not need to check for L1 handle
@@ -1870,9 +1886,14 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
}
#endif
}
+ break;
}
- /* FIXME: we actually need to send a ACK or NACK for the OML message */
- return oml_fom_ack_nack(msg, 0);
+
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* callback from OML */
@@ -1906,13 +1927,13 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1, -1);
rc = oml_mo_opstart_ack(mo);
if (mo->obj_class == NM_OC_BTS) {
- oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK, -1);
}
break;
default:
diff --git a/src/osmo-bts-oc2g/l1_if.c b/src/osmo-bts-oc2g/l1_if.c
index 2cefc3b0..194f82a4 100644
--- a/src/osmo-bts-oc2g/l1_if.c
+++ b/src/osmo-bts-oc2g/l1_if.c
@@ -606,7 +606,11 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
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);
+ if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg) < 0) {
+ LOGPFN(DL1P, LOGL_ERROR, u32Fn, "MQ_L1_WRITE queue full. Dropping msg.\n");
+ msgb_free(nmsg);
+ return -ENOBUFS;
+ }
if (dtx_is_first_p1(lchan))
dtx_dispatch(lchan, E_FIRST);
else
diff --git a/src/osmo-bts-oc2g/oc2gbts_vty.c b/src/osmo-bts-oc2g/oc2gbts_vty.c
index d69225ac..ae7cd12d 100644
--- a/src/osmo-bts-oc2g/oc2gbts_vty.c
+++ b/src/osmo-bts-oc2g/oc2gbts_vty.c
@@ -133,7 +133,7 @@ DEFUN(cfg_phy_no_dsp_trace_f, cfg_phy_no_dsp_trace_f_cmd,
/* runtime */
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
- "show trx <0-0> dsp-trace-flags",
+ "show dsp-trace-flags trx <0-0>",
SHOW_TRX_STR "Display the current setting of the DSP trace flags")
{
int trx_nr = atoi(argv[0]);
diff --git a/src/osmo-bts-oc2g/oml.c b/src/osmo-bts-oc2g/oml.c
index 9791ab8b..d12571b7 100644
--- a/src/osmo-bts-oc2g/oml.c
+++ b/src/osmo-bts-oc2g/oml.c
@@ -1212,10 +1212,6 @@ int lchan_activate(struct gsm_lchan *lchan)
enqueue_sapi_act_cmd(lchan, sapi, dir);
}
-
-#warning "FIXME: Should this be in sapi_activate_cb?"
- lchan_init_lapdm(lchan);
-
return 0;
}
@@ -1843,11 +1839,31 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
- if (kind == NM_OC_RADIO_CARRIER) {
- struct gsm_bts_trx *trx = obj;
- struct oc2gl1_hdl *fl1h = trx_oc2gl1_hdl(trx);
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+ struct gsm_bts_trx *trx;
+ struct oc2gl1_hdl *fl1h;
+ uint8_t cell_size;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_RADIO_ATTR:
+ trx = obj;
+ fl1h = trx_oc2gl1_hdl(trx);
/* convert max TA to max cell size in qbits */
- uint8_t cell_size = bts->max_ta << 2;
+ cell_size = bts->max_ta << 2;
/* We do not need to check for L1 handle
* because the max cell size parameter can receive before MphInit */
@@ -1876,11 +1892,14 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
l1if_set_txpower_c0_idle_pwr_red(fl1h, fl1h->phy_inst->u.oc2g.tx_c0_idle_pwr_red);
}
}
-
+ break;
}
- /* FIXME: we actually need to send a ACK or NACK for the OML message */
- return oml_fom_ack_nack(msg, 0);
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* callback from OML */
@@ -1898,10 +1917,10 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
break;
case NM_OC_BTS:
rc = osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_OPSTART_ACK, NULL);
- oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK, -1);
break;
case NM_OC_RADIO_CARRIER:
trx = (struct gsm_bts_trx *) obj;
@@ -1918,7 +1937,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-octphy/l1_if.c b/src/osmo-bts-octphy/l1_if.c
index 294a65ec..ebd960e3 100644
--- a/src/osmo-bts-octphy/l1_if.c
+++ b/src/osmo-bts-octphy/l1_if.c
@@ -1302,7 +1302,11 @@ static int retransmit_wlc_upto(struct octphy_hdl *fl1h, uint32_t trans_id)
wlc->num_retrans++;
msg = msgb_copy(wlc->cmd_msg, "PHY CMD Retrans");
msg_set_retrans_flag(msg);
- osmo_wqueue_enqueue(&fl1h->phy_wq, msg);
+ if (osmo_wqueue_enqueue(&fl1h->phy_wq, msg) < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Queue full on wlc retransmit\n");
+ msgb_free(msg);
+ return 0;
+ }
osmo_timer_schedule(&wlc->timer, CMD_TIMEOUT, 0);
count++;
LOGP(DL1C, LOGL_INFO, "Re-transmitting %s "
diff --git a/src/osmo-bts-octphy/l1_oml.c b/src/osmo-bts-octphy/l1_oml.c
index 5b48b2e6..5894c210 100644
--- a/src/osmo-bts-octphy/l1_oml.c
+++ b/src/osmo-bts-octphy/l1_oml.c
@@ -459,7 +459,7 @@ static int mph_send_activate_req(struct gsm_lchan *lchan, struct sapi_cmd *cmd)
lac->LchId.bySAPI = cmd->sapi;
lac->LchId.byDirection = cmd->dir;
- lac->Config.byTimingAdvance = lchan->rqd_ta;
+ lac->Config.byTimingAdvance = lchan->ta_ctrl.current;
lac->Config.byBSIC = lchan->ts->trx->bts->bsic;
if ((rc = lchan2lch_par(lchan, &lac->Config)) != 0) {
talloc_free(msg);
@@ -1107,9 +1107,6 @@ int lchan_activate(struct gsm_lchan *lchan)
}
enqueue_sapi_act_cmd(lchan, sapi, dir);
}
-
- lchan_init_lapdm(lchan);
-
return 0;
}
@@ -1748,13 +1745,36 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
- if (kind == NM_OC_RADIO_CARRIER) {
- struct gsm_bts_trx *trx = obj;
- /*struct octphy_hdl *fl1h = trx_octphy_hdl(trx); */
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+ struct gsm_bts_trx *trx;
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_RADIO_ATTR:
+ trx = obj;
+ /*struct octphy_hdl *fl1h = trx_octphy_hdl(trx); */
power_ramp_start(trx, get_p_target_mdBm(trx, 0), 0, NULL);
+ break;
}
- return oml_fom_ack_nack(msg, 0);
+
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
@@ -1788,7 +1808,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj)
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-omldummy/bts_model.c b/src/osmo-bts-omldummy/bts_model.c
index 46558a1c..f5d59a30 100644
--- a/src/osmo-bts-omldummy/bts_model.c
+++ b/src/osmo-bts-omldummy/bts_model.c
@@ -97,20 +97,38 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
- int cause = 0;
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
switch (foh->msg_type) {
case NM_MT_SET_BTS_ATTR:
- cause = vbts_set_bts(obj);
+ ev_data.cause = vbts_set_bts(obj);
break;
case NM_MT_SET_RADIO_ATTR:
- cause = vbts_set_trx(obj);
+ ev_data.cause = vbts_set_trx(obj);
break;
case NM_MT_SET_CHAN_ATTR:
- cause = vbts_set_ts(obj);
+ ev_data.cause = vbts_set_ts(obj);
break;
}
- return oml_fom_ack_nack(msg, cause);
+
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* MO: TS 12.21 Managed Object */
@@ -143,7 +161,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj)
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-omldummy/main.c b/src/osmo-bts-omldummy/main.c
index a36e0db5..c74af745 100644
--- a/src/osmo-bts-omldummy/main.c
+++ b/src/osmo-bts-omldummy/main.c
@@ -107,7 +107,7 @@ int main(int argc, char **argv)
{
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
- struct e1inp_line *line;
+ struct bsc_oml_host *bsc_oml_host;
int i;
parse_cmdline(argc, argv);
@@ -144,9 +144,13 @@ int main(int argc, char **argv)
//btsb = bts_role_bts(bts);
abis_init(bts);
- line = abis_open(bts, cmdline.dst_host, "OMLdummy");
- if (!line)
- exit(2);
+ bsc_oml_host = talloc_zero(bts, struct bsc_oml_host);
+ OSMO_ASSERT(bsc_oml_host);
+ bsc_oml_host->addr = talloc_strdup(bsc_oml_host, cmdline.dst_host);
+ OSMO_ASSERT(bsc_oml_host->addr);
+ llist_add_tail(&bsc_oml_host->list, &bts->bsc_oml_hosts);
+ if (abis_open(bts, "OMLdummy") != 0)
+ exit(1);
while (1) {
osmo_select_main(0);
diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c
index 93ecf902..ba60b76e 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -1204,10 +1204,6 @@ int lchan_activate(struct gsm_lchan *lchan)
continue;
enqueue_sapi_act_cmd(lchan, sapi, dir);
}
-
-#warning "FIXME: Should this be in sapi_activate_cb?"
- lchan_init_lapdm(lchan);
-
return 0;
}
@@ -1748,17 +1744,40 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
- if (kind == NM_OC_RADIO_CARRIER) {
- struct gsm_bts_trx *trx = obj;
- struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+ struct gsm_bts_trx *trx;
+ struct femtol1_hdl *fl1h;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_RADIO_ATTR:
+ trx = obj;
+ fl1h = trx_femtol1_hdl(trx);
/* Did we go through MphInit yet? If yes fire and forget */
if (fl1h->hLayer1)
power_ramp_start(trx, get_p_target_mdBm(trx, 0), 0, NULL);
+ break;
}
- /* FIXME: we actually need to send a ACK or NACK for the OML message */
- return oml_fom_ack_nack(msg, 0);
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* callback from OML */
@@ -1776,10 +1795,10 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
break;
case NM_OC_BTS:
rc = osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_OPSTART_ACK, NULL);
- oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK, -1);
break;
case NM_OC_RADIO_CARRIER:
trx = (struct gsm_bts_trx *) obj;
@@ -1796,7 +1815,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-sysmo/sysmobts_vty.c b/src/osmo-bts-sysmo/sysmobts_vty.c
index a8e7401f..2e853356 100644
--- a/src/osmo-bts-sysmo/sysmobts_vty.c
+++ b/src/osmo-bts-sysmo/sysmobts_vty.c
@@ -226,7 +226,7 @@ DEFUN(show_phy_clksrc, show_trx_clksrc_cmd,
}
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
- "show trx <0-0> dsp-trace-flags",
+ "show dsp-trace-flags trx <0-0>",
SHOW_TRX_STR "Display the current setting of the DSP trace flags")
{
int trx_nr = atoi(argv[0]);
diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am
index 54d1af9e..afa54141 100644
--- a/src/osmo-bts-trx/Makefile.am
+++ b/src/osmo-bts-trx/Makefile.am
@@ -1,6 +1,7 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
$(NULL)
AM_CFLAGS = \
@@ -51,6 +52,7 @@ osmo_bts_trx_SOURCES = \
trx_provision_fsm.c \
trx_vty.c \
loops.c \
+ probes.d \
$(NULL)
osmo_bts_trx_LDADD = \
@@ -58,3 +60,14 @@ osmo_bts_trx_LDADD = \
$(top_builddir)/src/common/libbts.a \
$(LDADD) \
$(NULL)
+
+if ENABLE_SYSTEMTAP
+probes.h: probes.d
+ $(DTRACE) -C -h -s $< -o $@
+
+probes.lo: probes.d
+ $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -C -G -s $< -o $@
+
+BUILT_SOURCES = probes.h probes.lo
+osmo_bts_trx_LDADD += probes.lo
+endif
diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c
index 754e9d7a..5ac3e314 100644
--- a/src/osmo-bts-trx/l1_if.c
+++ b/src/osmo-bts-trx/l1_if.c
@@ -87,26 +87,6 @@ struct trx_l1h *trx_l1h_alloc(void *tall_ctx, struct phy_instance *pinst)
return l1h;
}
-static void check_transceiver_availability_trx(struct trx_l1h *l1h, int avail)
-{
- struct phy_instance *pinst = l1h->phy_inst;
- struct gsm_bts_trx *trx = pinst->trx;
-
- /* HACK, we should change state when we receive first clock from
- * transceiver */
- if (avail) {
- /* signal availability */
- if (!pinst->u.osmotrx.sw_act_reported) {
- osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_SW_ACT, NULL);
- osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_SW_ACT, NULL);
- pinst->u.osmotrx.sw_act_reported = true;
- }
- } else {
- osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_DISABLE, NULL);
- osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_DISABLE, NULL);
- }
-}
-
int bts_model_lchan_deactivate(struct gsm_lchan *lchan)
{
if (lchan->rel_act_kind == LCHAN_REL_ACT_REACT) {
@@ -185,10 +165,6 @@ static int trx_init(struct gsm_bts_trx *trx)
if (rc != 0)
return osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_OPSTART_NACK,
(void*)(intptr_t)NM_NACK_CANT_PERFORM);
-
- if (trx == trx->bts->c0)
- lchan_init_lapdm(&trx->ts[0].lchan[CCCH_LCHAN]);
-
/* Send OPSTART ack */
return osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_OPSTART_ACK, NULL);
}
@@ -211,13 +187,15 @@ void bts_model_trx_close(struct gsm_bts_trx *trx)
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CLOSE, NULL);
/* Set to Operational State: Disabled */
- check_transceiver_availability_trx(l1h, 0);
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_DISABLE, NULL);
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_DISABLE, NULL);
}
-/* on RSL failure, deactivate transceiver */
void bts_model_abis_close(struct gsm_bts *bts)
{
- bts_shutdown(bts, "Abis close");
+ /* Go into shutdown state deactivating transceivers until Abis link
+ * becomes up again */
+ bts_shutdown_ext(bts, "Abis close", false);
}
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
@@ -230,15 +208,19 @@ int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
/* set bts attributes */
static uint8_t trx_set_bts(struct gsm_bts *bts, struct tlv_parsed *new_attr)
{
- struct gsm_bts_trx *trx;
+ struct phy_instance *pinst = trx_phy_instance(bts->c0);
+ struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
uint8_t bsic = bts->bsic;
+ struct gsm_bts_trx *trx;
+
+ /* ARFCN for C0 is assigned during Set BTS Attr, see oml.c */
+ osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_ARFCN, (void *)(intptr_t)pinst->trx->arfcn);
llist_for_each_entry(trx, &bts->trx_list, list) {
- struct phy_instance *pinst = trx_phy_instance(trx);
- struct phy_link *plink = pinst->phy_link;
- struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
+ pinst = trx_phy_instance(trx);
+ l1h = pinst->u.osmotrx.hdl;
+
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_BSIC, (void*)(intptr_t)bsic);
- check_transceiver_availability_trx(l1h, phy_link_state_get(plink) != PHY_LINK_SHUTDOWN);
}
return 0;
@@ -252,7 +234,9 @@ static uint8_t trx_set_trx(struct gsm_bts_trx *trx)
struct phy_link *plink = pinst->phy_link;
uint16_t arfcn = trx->arfcn;
- osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_ARFCN, (void*)(intptr_t)arfcn);
+ /* ARFCN for C0 is assigned during Set BTS Attr, see oml.c */
+ if (trx != trx->bts->c0)
+ osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_ARFCN, (void *)(intptr_t)arfcn);
/* Begin to ramp up the power if power reduction is set by OML and TRX
is already running. Otherwise skip, power ramping will be started
@@ -297,7 +281,7 @@ static uint8_t trx_set_ts_as_pchan(struct gsm_bts_trx_ts *ts,
struct trx_prov_ev_cfg_ts_data data = { .tn = tn, .slottype = slottype };
- if (ts->tsc_set != 0 || ts->tsc != BTS_TSC(ts->trx->bts)) {
+ if (ts->tsc_set != 0) {
/* On TRXC we use 3GPP compliant numbering, so +1 */
data.tsc_set = ts->tsc_set + 1;
data.tsc_val = ts->tsc;
@@ -436,19 +420,6 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
/* attempt to allocate an Error Concealment Unit instance, if available */
lchan->ecu_state = osmo_ecu_init(trx, lchan2ecu_codec(lchan));
- /* trx_chan_desc[] in scheduler.c uses the RSL_CHAN_OSMO_PDCH cbits
- * (0xc0) to indicate the need for PDTCH and PTCCH SAPI activation.
- * However, 0xc0 is a cbits pattern exclusively used for Osmocom style
- * dyn TS (a non-standard RSL Chan Activ mod); hence, for IPA style dyn
- * TS, the chan_nr will never reflect 0xc0 and we would omit the
- * PDTCH,PTTCH SAPIs. To properly de-/activate the PDTCH SAPIs in
- * scheduler.c, make sure the 0xc0 cbits are set for de-/activating PDTCH
- * lchans, i.e. both Osmocom and IPA style dyn TS. (For Osmocom style dyn
- * TS, the chan_nr typically already reflects 0xc0, while it doesn't for
- * IPA style.) */
- if (lchan->type == GSM_LCHAN_PDTCH)
- chan_nr = RSL_CHAN_OSMO_PDCH | (chan_nr & ~RSL_CHAN_NR_MASK);
-
/* activate dedicated channel */
trx_sched_set_lchan(lchan, chan_nr, LID_DEDIC, true);
/* activate associated channel */
@@ -463,8 +434,6 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
lchan->tch.amr_mr.bts_mode[3].mode,
amr_get_initial_mode(lchan),
(lchan->ho.active == 1));
- /* init lapdm */
- lchan_init_lapdm(lchan);
/* set lchan active */
lchan_set_state(lchan, LCHAN_S_ACTIVE);
/* set initial ciphering */
@@ -555,21 +524,38 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
- int cause = 0;
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
switch (foh->msg_type) {
case NM_MT_SET_BTS_ATTR:
- cause = trx_set_bts(obj, new_attr);
+ ev_data.cause = trx_set_bts(obj, new_attr);
break;
case NM_MT_SET_RADIO_ATTR:
- cause = trx_set_trx(obj);
+ ev_data.cause = trx_set_trx(obj);
break;
case NM_MT_SET_CHAN_ATTR:
- cause = trx_set_ts(obj);
+ ev_data.cause = trx_set_ts(obj);
break;
}
- return oml_fom_ack_nack(msg, cause);
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* callback from OML */
@@ -604,7 +590,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c
index 9720b035..5d680f05 100644
--- a/src/osmo-bts-trx/main.c
+++ b/src/osmo-bts-trx/main.c
@@ -148,8 +148,10 @@ int bts_model_init(struct gsm_bts *bts)
osmo_bts_set_feature(bts->features, BTS_FEAT_MULTI_TSC);
osmo_bts_set_feature(bts->features, BTS_FEAT_VAMOS);
osmo_bts_set_feature(bts->features, BTS_FEAT_BCCH_POWER_RED);
+ osmo_bts_set_feature(bts->features, BTS_FEAT_ACCH_TEMP_OVP);
bts_internal_flag_set(bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB);
+ bts_internal_flag_set(bts, BTS_INTERNAL_FLAG_INTERF_MEAS);
return 0;
}
diff --git a/src/osmo-bts-trx/probes.d b/src/osmo-bts-trx/probes.d
new file mode 100644
index 00000000..2f905bd1
--- /dev/null
+++ b/src/osmo-bts-trx/probes.d
@@ -0,0 +1,7 @@
+provider osmo_bts_trx {
+ probe ul_data_start(int, int, int); /* trx_nr, ts_nr, fn */
+ probe ul_data_done(int, int, int); /* trx_nr, ts_nr, fn */
+
+ probe dl_rts_start(int, int, int); /* trx_nr, ts_nr, fn */
+ probe dl_rts_done(int, int, int); /* trx_nr, ts_nr, fn */
+};
diff --git a/src/osmo-bts-trx/sched_lchan_pdtch.c b/src/osmo-bts-trx/sched_lchan_pdtch.c
index 335ba6fe..6a2ad0da 100644
--- a/src/osmo-bts-trx/sched_lchan_pdtch.c
+++ b/src/osmo-bts-trx/sched_lchan_pdtch.c
@@ -58,8 +58,7 @@ int rx_pdtch_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
/* allocate burst memory, if not already */
if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx,
- GSM0503_EGPRS_BURSTS_NBITS);
+ *bursts_p = talloc_zero_size(l1ts, GSM0503_EGPRS_BURSTS_NBITS);
if (!*bursts_p)
return -ENOMEM;
}
@@ -155,32 +154,26 @@ int tx_pdtch_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* send burst, if we already got a frame */
if (br->bid > 0) {
if (!*bursts_p)
- return 0;
+ return -ENODEV;
goto send_burst;
}
/* get mac block from queue */
msg = _sched_dequeue_prim(l1ts, br);
- if (msg)
- goto got_msg;
-
- LOGL1SB(DL1P, LOGL_INFO, l1ts, br, "No prim for transmit.\n");
-
-no_msg:
- /* free burst memory */
- if (*bursts_p) {
- talloc_free(*bursts_p);
- *bursts_p = NULL;
+ if (!msg) {
+ /* It's totally fine to get no block here, since PCU may submit
+ * empty blocks when there's no MS listening. The scheduler will
+ * take care of filling C0 with dummy bursts to keep expected
+ * power transmit levels
+ */
+ goto no_msg;
}
- return -ENODEV;
-got_msg:
/* BURST BYPASS */
/* allocate burst memory, if not already */
if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx,
- GSM0503_EGPRS_BURSTS_NBITS);
+ *bursts_p = talloc_zero_size(l1ts, GSM0503_EGPRS_BURSTS_NBITS);
if (!*bursts_p)
return -ENOMEM;
}
@@ -229,4 +222,12 @@ send_burst:
LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "Transmitting burst=%u.\n", br->bid);
return 0;
+
+no_msg:
+ /* free burst memory */
+ if (*bursts_p) {
+ talloc_free(*bursts_p);
+ *bursts_p = NULL;
+ }
+ return -ENODEV;
}
diff --git a/src/osmo-bts-trx/sched_lchan_tchf.c b/src/osmo-bts-trx/sched_lchan_tchf.c
index 689925fb..08a58bb0 100644
--- a/src/osmo-bts-trx/sched_lchan_tchf.c
+++ b/src/osmo-bts-trx/sched_lchan_tchf.c
@@ -65,6 +65,7 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
uint16_t ber10k;
uint8_t is_sub = 0;
uint8_t ft;
+ bool amr_is_cmr;
/* If handover RACH detection is turned on, treat this burst as an Access Burst.
* Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
@@ -75,7 +76,7 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
/* allocate burst memory, if not already */
if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 928);
+ *bursts_p = talloc_zero_size(l1ts, 928);
if (!*bursts_p)
return -ENOMEM;
}
@@ -129,6 +130,8 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
* the first FN 4,13,21 defines that CMR is included in frame.
* NOTE: A frame ends 7 FN after start.
*/
+ fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_F);
+ amr_is_cmr = !ul_amr_fn_is_cmi(fn_begin);
/* The AFS_ONSET frame itself does not result into an RTP frame
* since it only contains a recognition pattern that marks the
@@ -144,8 +147,7 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
* know this before we actually decode the frame) */
amr = 2;
rc = gsm0503_tch_afs_decode_dtx(tch_data + amr, *bursts_p,
- (((bi->fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec,
- chan_state->codecs, &chan_state->ul_ft,
+ amr_is_cmr, chan_state->codec, chan_state->codecs, &chan_state->ul_ft,
&chan_state->ul_cmr, &n_errors, &n_bits_total, &chan_state->amr_last_dtx);
/* Tag all frames that are not regular AMR voice frames as
@@ -186,7 +188,7 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
/* only good speech frames get rtp header */
if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
if (chan_state->amr_last_dtx == AMR_OTHER) {
- ft = chan_state->codec[chan_state->ul_cmr];
+ ft = chan_state->codec[chan_state->ul_ft];
} else {
/* SID frames will always get Frame Type Index 8 (AMR_SID) */
ft = AMR_SID;
@@ -274,8 +276,8 @@ bfi:
break;
case GSM48_CMODE_SPEECH_AMR: /* AMR */
rc = osmo_amr_rtp_enc(tch_data,
- chan_state->codec[chan_state->dl_cmr],
- chan_state->codec[chan_state->dl_ft],
+ chan_state->codec[chan_state->ul_cmr],
+ chan_state->codec[chan_state->ul_ft],
AMR_BAD);
if (rc < 2) {
LOGL1SB(DL1P, LOGL_ERROR, l1ts, bi,
@@ -302,7 +304,9 @@ compose_l1sap:
return _sched_compose_tch_ind(l1ts, fn_begin, bi->chan, tch_data, rc,
/* FIXME: what should we use for BFI here? */
bfi_flag ? bi->toa256 : meas_avg.toa256, ber10k,
- bfi_flag ? bi->rssi : meas_avg.rssi, is_sub);
+ bfi_flag ? bi->rssi : meas_avg.rssi,
+ bfi_flag ? bi->ci_cb : meas_avg.ci_cb,
+ is_sub);
}
/* common section for generation of TCH bursts (TCH/H and TCH/F).
@@ -364,7 +368,7 @@ inval_mode1:
/* Note: RSSI/ToA256 is set to 0 to indicate to the higher
* layers that this is a faked tch_ind */
_sched_compose_tch_ind(l1ts, br->fn, br->chan,
- tch_data, len, 0, 10000, 0, 0);
+ tch_data, len, 0, 10000, 0, 0, 0);
}
}
@@ -394,12 +398,6 @@ inval_mode1:
msg_tch = msg2;
}
}
- } else if (msg2) {
- l1sap = msgb_l1sap_prim(msg2);
- if (l1sap->oph.primitive == PRIM_TCH)
- msg_tch = msg2;
- else
- msg_facch = msg2;
}
/* check validity of message */
@@ -419,6 +417,7 @@ inval_mode1:
enum osmo_amr_type ft_codec;
enum osmo_amr_quality bfi;
int8_t sti, cmi;
+ bool amr_is_cmr = !dl_amr_fn_is_cmi(br->fn);
if (rsl_cmode != RSL_CMOD_SPD_SPEECH) {
LOGL1SB(DL1P, LOGL_NOTICE, l1ts, br, "Dropping speech frame, "
@@ -463,7 +462,7 @@ inval_mode1:
"Codec (FT = %d) of RTP frame not in list\n", ft_codec);
goto free_bad_msg;
}
- if (fn_is_codec_mode_request(br->fn) && chan_state->dl_ft != ft) {
+ if (amr_is_cmr && chan_state->dl_ft != ft) {
LOGL1SB(DL1P, LOGL_NOTICE, l1ts, br, "Codec (FT = %d) "
" of RTP cannot be changed now, but in next frame\n", ft_codec);
goto free_bad_msg;
@@ -511,7 +510,7 @@ int tx_tchf_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* send burst, if we already got a frame */
if (br->bid > 0) {
if (!*bursts_p)
- return 0;
+ return -ENODEV;
goto send_burst;
}
@@ -522,7 +521,7 @@ int tx_tchf_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* allocate burst memory, if not already,
* otherwise shift buffer by 4 bursts for interleaving */
if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 928);
+ *bursts_p = talloc_zero_size(l1ts, 928);
if (!*bursts_p)
return -ENOMEM;
} else {
@@ -530,22 +529,30 @@ int tx_tchf_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
memset(*bursts_p + 464, 0, 464);
}
- /* no message at all */
+ /* no message at all, send a dummy L2 frame on FACCH */
if (!msg_tch && !msg_facch) {
- LOGL1SB(DL1P, LOGL_INFO, l1ts, br, "No TCH or FACCH prim for transmit.\n");
+ static const uint8_t dummy[GSM_MACBLOCK_LEN] = {
+ 0x03, 0x03, 0x01, /* TODO: use randomized padding */
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ };
+
+ LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "No TCH or FACCH prim for transmit.\n");
+ gsm0503_tch_fr_encode(*bursts_p, dummy, sizeof(dummy), 1);
goto send_burst;
}
/* encode bursts (prioritize FACCH) */
- if (msg_facch)
+ if (msg_facch) {
gsm0503_tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch),
1);
- else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
+ chan_state->dl_facch_bursts = 8;
+ } else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
/* the first FN 4,13,21 defines that CMI is included in frame,
* the first FN 0,8,17 defines that CMR is included in frame.
*/
gsm0503_tch_afs_encode(*bursts_p, msg_tch->l2h + 2,
- msgb_l2len(msg_tch) - 2, fn_is_codec_mode_request(br->fn),
+ msgb_l2len(msg_tch) - 2, !dl_amr_fn_is_cmi(br->fn),
chan_state->codec, chan_state->codecs,
chan_state->dl_ft,
chan_state->dl_cmr);
@@ -567,6 +574,11 @@ send_burst:
br->burst_len = GSM_BURST_LEN;
+ if (chan_state->dl_facch_bursts > 0) {
+ chan_state->dl_facch_bursts--;
+ br->flags |= TRX_BR_F_FACCH;
+ }
+
LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "Transmitting burst=%u.\n", br->bid);
return 0;
diff --git a/src/osmo-bts-trx/sched_lchan_tchh.c b/src/osmo-bts-trx/sched_lchan_tchh.c
index 4f03bd12..51a0b90d 100644
--- a/src/osmo-bts-trx/sched_lchan_tchh.c
+++ b/src/osmo-bts-trx/sched_lchan_tchh.c
@@ -72,6 +72,7 @@ int rx_tchh_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
uint8_t is_sub = 0;
uint8_t ft;
bool mask_stolen_tch_block = false;
+ bool fn_is_cmi;
/* If handover RACH detection is turned on, treat this burst as an Access Burst.
* Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
@@ -82,7 +83,7 @@ int rx_tchh_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
/* allocate burst memory, if not already */
if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 696);
+ *bursts_p = talloc_zero_size(l1ts, 696);
if (!*bursts_p)
return -ENOMEM;
}
@@ -164,10 +165,21 @@ int rx_tchh_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
break;
}
+ /* Calculate the frame number where the block begins */
+ if (bi->fn % 13 < 4)
+ fn_tch_end = GSM_TDMA_FN_SUB(bi->fn, 5);
+ else
+ fn_tch_end = GSM_TDMA_FN_SUB(bi->fn, 4);
+ if (lchan->nr == 0)
+ fn_begin = gsm0502_fn_remap(fn_tch_end, FN_REMAP_TCH_H0);
+ else
+ fn_begin = gsm0502_fn_remap(fn_tch_end, FN_REMAP_TCH_H1);
+ fn_is_cmi = ul_amr_fn_is_cmi(fn_begin);
+
/* See comment in function rx_tchf_fn() */
amr = 2;
rc = gsm0503_tch_ahs_decode_dtx(tch_data + amr, *bursts_p,
- fn_is_odd, fn_is_odd, chan_state->codec,
+ fn_is_odd, !fn_is_cmi, chan_state->codec,
chan_state->codecs, &chan_state->ul_ft,
&chan_state->ul_cmr, &n_errors, &n_bits_total, &chan_state->amr_last_dtx);
@@ -210,7 +222,7 @@ int rx_tchh_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
/* only good speech frames get rtp header */
if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
if (chan_state->amr_last_dtx == AMR_OTHER) {
- ft = chan_state->codec[chan_state->ul_cmr];
+ ft = chan_state->codec[chan_state->ul_ft];
} else {
/* SID frames will always get Frame Type Index 8 (AMR_SID) */
ft = AMR_SID;
@@ -305,8 +317,8 @@ bfi:
break;
case GSM48_CMODE_SPEECH_AMR: /* AMR */
rc = osmo_amr_rtp_enc(tch_data,
- chan_state->codec[chan_state->dl_cmr],
- chan_state->codec[chan_state->dl_ft],
+ chan_state->codec[chan_state->ul_cmr],
+ chan_state->codec[chan_state->ul_ft],
AMR_BAD);
if (rc < 2) {
LOGL1SB(DL1P, LOGL_ERROR, l1ts, bi,
@@ -343,7 +355,6 @@ compose_l1sap:
fn_tch_end = GSM_TDMA_FN_SUB(bi->fn, 5);
else
fn_tch_end = GSM_TDMA_FN_SUB(bi->fn, 4);
-
if (lchan->nr == 0)
fn_begin = gsm0502_fn_remap(fn_tch_end, FN_REMAP_TCH_H0);
else
@@ -364,7 +375,9 @@ compose_l1sap:
return _sched_compose_tch_ind(l1ts, fn_begin, bi->chan, tch_data, rc,
/* FIXME: what should we use for BFI here? */
bfi_flag ? bi->toa256 : meas_avg.toa256, ber10k,
- bfi_flag ? bi->rssi : meas_avg.rssi, is_sub);
+ bfi_flag ? bi->rssi : meas_avg.rssi,
+ bfi_flag ? bi->ci_cb : meas_avg.ci_cb,
+ is_sub);
}
/* common section for generation of TCH bursts (TCH/H and TCH/F).
@@ -384,7 +397,7 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* send burst, if we already got a frame */
if (br->bid > 0) {
if (!*bursts_p)
- return 0;
+ return -ENODEV;
goto send_burst;
}
@@ -404,7 +417,7 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* allocate burst memory, if not already,
* otherwise shift buffer by 2 bursts for interleaving */
if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 696);
+ *bursts_p = talloc_zero_size(l1ts, 696);
if (!*bursts_p)
return -ENOMEM;
} else {
@@ -417,9 +430,16 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
}
}
- /* no message at all */
+ /* no message at all, send a dummy L2 frame on FACCH */
if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) {
+ static const uint8_t dummy[GSM_MACBLOCK_LEN] = {
+ 0x03, 0x03, 0x01, /* TODO: use randomized padding */
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ };
+
LOGL1SB(DL1P, LOGL_INFO, l1ts, br, "No TCH or FACCH prim for transmit.\n");
+ gsm0503_tch_hr_encode(*bursts_p, dummy, sizeof(dummy));
goto send_burst;
}
@@ -427,6 +447,7 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
if (msg_facch) {
gsm0503_tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch));
chan_state->dl_ongoing_facch = 1; /* first of two TCH frames */
+ chan_state->dl_facch_bursts = 6;
} else if (chan_state->dl_ongoing_facch) /* second of two TCH frames */
chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */
else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
@@ -434,7 +455,7 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
* in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is
* included in frame. */
gsm0503_tch_ahs_encode(*bursts_p, msg_tch->l2h + 2,
- msgb_l2len(msg_tch) - 2, fn_is_codec_mode_request(br->fn),
+ msgb_l2len(msg_tch) - 2, !dl_amr_fn_is_cmi(br->fn),
chan_state->codec, chan_state->codecs,
chan_state->dl_ft,
chan_state->dl_cmr);
@@ -456,6 +477,11 @@ send_burst:
br->burst_len = GSM_BURST_LEN;
+ if (chan_state->dl_facch_bursts > 0) {
+ chan_state->dl_facch_bursts--;
+ br->flags |= TRX_BR_F_FACCH;
+ }
+
LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "Transmitting burst=%u.\n", br->bid);
return 0;
diff --git a/src/osmo-bts-trx/sched_lchan_xcch.c b/src/osmo-bts-trx/sched_lchan_xcch.c
index 4bfc101a..6a655741 100644
--- a/src/osmo-bts-trx/sched_lchan_xcch.c
+++ b/src/osmo-bts-trx/sched_lchan_xcch.c
@@ -60,7 +60,7 @@ int rx_data_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
uint16_t ber10k;
int rc;
struct gsm_lchan *lchan = chan_state->lchan;
- bool rep_sacch = L1SAP_IS_LINK_SACCH(trx_chan_desc[bi->chan].link_id) && lchan->repeated_ul_sacch_active;
+ bool rep_sacch = L1SAP_IS_LINK_SACCH(trx_chan_desc[bi->chan].link_id) && lchan->rep_acch.ul_sacch_active;
/* If handover RACH detection is turned on, treat this burst as an Access Burst.
* Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
@@ -71,7 +71,7 @@ int rx_data_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
/* allocate burst memory, if not already */
if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
+ *bursts_p = talloc_zero_size(l1ts, 464);
if (!*bursts_p)
return -ENOMEM;
}
@@ -79,7 +79,7 @@ int rx_data_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
/* UL-SACCH requires additional memory to keep a copy of each previous
* burst set. */
if (L1SAP_IS_LINK_SACCH(trx_chan_desc[bi->chan].link_id) && !chan_state->ul_bursts_prev) {
- chan_state->ul_bursts_prev = talloc_zero_size(tall_bts_ctx, 464);
+ chan_state->ul_bursts_prev = talloc_zero_size(l1ts, 464);
if (!chan_state->ul_bursts_prev)
return -ENOMEM;
}
@@ -176,7 +176,7 @@ int tx_data_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* send burst, if we already got a frame */
if (br->bid > 0) {
if (!*bursts_p)
- return 0;
+ return -ENODEV;
goto send_burst;
}
@@ -224,7 +224,7 @@ got_msg:
/* allocate burst memory, if not already */
if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
+ *bursts_p = talloc_zero_size(l1ts, 464);
if (!*bursts_p)
return -ENOMEM;
}
diff --git a/src/osmo-bts-trx/sched_utils.h b/src/osmo-bts-trx/sched_utils.h
index 4a1aaf5f..f76e49bb 100644
--- a/src/osmo-bts-trx/sched_utils.h
+++ b/src/osmo-bts-trx/sched_utils.h
@@ -23,6 +23,8 @@
#include <stdint.h>
#include <errno.h>
+#include <stdbool.h>
+#include <osmo-bts/scheduler.h>
extern void *tall_bts_ctx;
@@ -35,8 +37,76 @@ static inline uint16_t compute_ber10k(int n_bits_total, int n_errors)
return 10000 * n_errors / n_bits_total;
}
-/* determine if the FN is transmitting a CMR (1) or not (0) */
-static inline int fn_is_codec_mode_request(uint32_t fn)
+/*! determine whether an uplink AMR block is CMI according to 3GPP TS 45.009.
+ * \param[in] fn_begin frame number of the beginning of the block.
+ * \returns true in case of CMI; false otherwise. */
+static inline bool ul_amr_fn_is_cmi(uint32_t fn_begin)
{
- return (((fn + 4) % 26) >> 2) & 1;
+ switch (fn_begin % 26) {
+ /*! See also: 3GPP TS 45.009, section 3.2.1.3 Transmitter/Receiver Synchronisation */
+ /* valid for AHS subslot 0 and AFS: */
+ case 0:
+ case 8:
+ case 17:
+ /* valid for AHS subslot 1: */
+ case 1:
+ case 9:
+ case 18:
+ return true;
+ break;
+ /* Complementary values for sanity check */
+ /* valid for AHS subslot 0 and AFS: */
+ case 4:
+ case 13:
+ case 21:
+ /* valid for AHS subslot 1: */
+ case 5:
+ case 14:
+ case 22:
+ return false;
+ break;
+ default:
+ LOGP(DL1P, LOGL_DEBUG,
+ "uplink frame number fn_begin=%u does not mark the beginning of a voice block!\n", fn_begin);
+ OSMO_ASSERT(false);
+ return false;
+ break;
+ }
+}
+
+/*! determine the whether a downlink AMR block is CMI according to 3GPP TS 45.009.
+ * \param[in] fn_begin frame number of the beginning of the block.
+ * \returns true in case of CMI; false otherwise. */
+static inline bool dl_amr_fn_is_cmi(uint32_t fn_begin)
+{
+ switch (fn_begin % 26) {
+ /*! See also: 3GPP TS 45.009, section 3.2.1.3 Transmitter/Receiver Synchronisation */
+ /* valid for AHS subslot 0 and AFS: */
+ case 4:
+ case 13:
+ case 21:
+ /* valid for AHS subslot 1: */
+ case 5:
+ case 14:
+ case 22:
+ return true;
+ break;
+ /* Complementary values for sanity check */
+ /* valid for AHS subslot 0 and AFS: */
+ case 0:
+ case 8:
+ case 17:
+ /* valid for AHS subslot 1: */
+ case 1:
+ case 9:
+ case 18:
+ return false;
+ break;
+ default:
+ LOGP(DL1P, LOGL_DEBUG,
+ "downlink frame number fn_begin=%u does not mark the beginning of a voice block!\n", fn_begin);
+ OSMO_ASSERT(false);
+ return false;
+ break;
+ }
}
diff --git a/src/osmo-bts-trx/scheduler_trx.c b/src/osmo-bts-trx/scheduler_trx.c
index 0a907fba..1159e594 100644
--- a/src/osmo-bts-trx/scheduler_trx.c
+++ b/src/osmo-bts-trx/scheduler_trx.c
@@ -50,88 +50,86 @@
#include "l1_if.h"
#include "trx_if.h"
+#include "btsconfig.h"
+
+#ifdef HAVE_SYSTEMTAP
+/* include the generated probes header and put markers in code */
+#include "probes.h"
+#define TRACE(probe) probe
+#define TRACE_ENABLED(probe) probe ## _ENABLED()
+#else
+/* Wrap the probe to allow it to be removed when no systemtap available */
+#define TRACE(probe)
+#define TRACE_ENABLED(probe) (0)
+#endif /* HAVE_SYSTEMTAP */
+
#define SCHED_FH_PARAMS_FMT "hsn=%u, maio=%u, ma_len=%u"
#define SCHED_FH_PARAMS_VALS(ts) \
(ts)->hopping.hsn, (ts)->hopping.maio, (ts)->hopping.arfcn_num
-static void ts_report_interf_meas(const struct gsm_bts_trx_ts *ts)
+static void lchan_report_interf_meas(const struct gsm_lchan *lchan)
{
+ const struct gsm_bts_trx_ts *ts = lchan->ts;
const struct l1sched_ts *l1ts = ts->priv;
- unsigned int ln;
-
- for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
- const struct gsm_lchan *lchan = &ts->lchan[ln];
- enum trx_chan_type dcch, acch;
- int interf_avg;
+ enum trx_chan_type dcch, acch;
+ int interf_avg;
- /* We're not interested in active channels */
- if (lchan->state == LCHAN_S_ACTIVE)
- continue;
+ /* We're not interested in active CS channels */
+ if (lchan->state == LCHAN_S_ACTIVE) {
+ if (lchan->type != GSM_LCHAN_PDTCH)
+ return;
+ }
- switch (lchan->type) {
- case GSM_LCHAN_SDCCH:
- if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4 ||
- ts->pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH) {
- dcch = TRXC_SDCCH4_0 + ln;
- acch = TRXC_SACCH4_0 + ln;
- } else { /* SDCCH/8 otherwise */
- dcch = TRXC_SDCCH8_0 + ln;
- acch = TRXC_SACCH8_0 + ln;
- }
- break;
- case GSM_LCHAN_TCH_F:
- dcch = TRXC_TCHF;
- acch = TRXC_SACCHTF;
- break;
- case GSM_LCHAN_TCH_H:
- dcch = TRXC_TCHH_0 + ln;
- acch = TRXC_SACCHTH_0 + ln;
- break;
- default:
- /* Skip other lchan types */
- continue;
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4 ||
+ ts->pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH) {
+ dcch = TRXC_SDCCH4_0 + lchan->nr;
+ acch = TRXC_SACCH4_0 + lchan->nr;
+ } else { /* SDCCH/8 otherwise */
+ dcch = TRXC_SDCCH8_0 + lchan->nr;
+ acch = TRXC_SACCH8_0 + lchan->nr;
}
+ break;
+ case GSM_LCHAN_TCH_F:
+ dcch = TRXC_TCHF;
+ acch = TRXC_SACCHTF;
+ break;
+ case GSM_LCHAN_TCH_H:
+ dcch = TRXC_TCHH_0 + lchan->nr;
+ acch = TRXC_SACCHTH_0 + lchan->nr;
+ break;
+ case GSM_LCHAN_PDTCH:
+ /* We use idle TDMA frames on PDCH */
+ dcch = TRXC_IDLE;
+ acch = TRXC_IDLE;
+ break;
+ default:
+ /* Skip other lchan types */
+ return;
+ }
- OSMO_ASSERT(dcch < ARRAY_SIZE(l1ts->chan_state));
- OSMO_ASSERT(acch < ARRAY_SIZE(l1ts->chan_state));
+ OSMO_ASSERT(dcch < ARRAY_SIZE(l1ts->chan_state));
+ OSMO_ASSERT(acch < ARRAY_SIZE(l1ts->chan_state));
- interf_avg = (l1ts->chan_state[dcch].meas.interf_avg +
- l1ts->chan_state[acch].meas.interf_avg) / 2;
+ interf_avg = (l1ts->chan_state[dcch].meas.interf_avg +
+ l1ts->chan_state[acch].meas.interf_avg) / 2;
- gsm_lchan_interf_meas_push((struct gsm_lchan *) lchan, interf_avg);
- }
+ gsm_lchan_interf_meas_push((struct gsm_lchan *) lchan, interf_avg);
}
static void bts_report_interf_meas(const struct gsm_bts *bts,
const uint32_t fn)
{
const struct gsm_bts_trx *trx;
+ unsigned int tn, ln;
llist_for_each_entry(trx, &bts->trx_list, list) {
- uint8_t pdch_interf[8] = { 0 };
- unsigned int tn, pdch_num = 0;
-
for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
- const struct l1sched_ts *l1ts = ts->priv;
- const struct l1sched_chan_state *l1cs;
-
- /* PS interference reports for the PCU */
- if (ts_pchan(ts) == GSM_PCHAN_PDCH) {
- l1cs = &l1ts->chan_state[TRXC_IDLE];
- /* Interference value is encoded as -x dBm */
- pdch_interf[tn] = -1 * l1cs->meas.interf_avg;
- pdch_num++;
- continue;
- }
-
- /* CS interference reports for the BSC */
- ts_report_interf_meas(ts);
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++)
+ lchan_report_interf_meas(&ts->lchan[ln]);
}
-
- /* Report interference levels on PDCH to the PCU */
- if (pdch_num > 0)
- pcu_tx_interf_ind(bts->nr, trx->nr, fn, pdch_interf);
}
}
@@ -295,8 +293,10 @@ static void bts_sched_fn(struct gsm_bts *bts, const uint32_t fn)
struct trx_dl_burst_req *br;
/* ready-to-send */
+ TRACE(OSMO_BTS_TRX_DL_RTS_START(trx->nr, tn, fn));
_sched_rts(l1ts, GSM_TDMA_FN_SUM(fn, plink->u.osmotrx.clock_advance
+ plink->u.osmotrx.rts_advance));
+ TRACE(OSMO_BTS_TRX_DL_RTS_DONE(trx->nr, tn, fn));
/* pre-initialized buffer for the Downlink burst */
br = &pinst->u.osmotrx.br[tn];
diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c
index 968c335e..9232e64f 100644
--- a/src/osmo-bts-trx/trx_if.c
+++ b/src/osmo-bts-trx/trx_if.c
@@ -51,6 +51,19 @@
#include "trx_if.h"
#include "trx_provision_fsm.h"
+#include "btsconfig.h"
+
+#ifdef HAVE_SYSTEMTAP
+/* include the generated probes header and put markers in code */
+#include "probes.h"
+#define TRACE(probe) probe
+#define TRACE_ENABLED(probe) probe ## _ENABLED()
+#else
+/* Wrap the probe to allow it to be removed when no systemtap available */
+#define TRACE(probe)
+#define TRACE_ENABLED(probe) (0)
+#endif /* HAVE_SYSTEMTAP */
+
/*
* socket helper functions
*/
@@ -332,8 +345,10 @@ int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn,
trx_if_cmd_setslot_cb *cb)
{
const struct trx_config *cfg = &l1h->config;
+ const struct phy_instance *pinst = l1h->phy_inst;
- if (cfg->setslot[tn].tsc_valid) { /* PHY is instructed to use a custom TSC */
+ if (cfg->setslot[tn].tsc_valid && cfg->setslot[tn].tsc_val != BTS_TSC(pinst->trx->bts)) {
+ /* PHY is instructed to use a custom TSC */
return trx_ctrl_cmd_cb(l1h, 1, cb, "SETSLOT", "%u %u C%u/S%u",
tn, cfg->setslot[tn].slottype,
cfg->setslot[tn].tsc_val,
@@ -707,6 +722,13 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
rsp.cb = tcm->cb;
+ /* Remove command from list, save it to last_acked and remove previous
+ * last_acked. Do it before calling callback to avoid user freeing tcm
+ * pointer if flushing/closing the iface. */
+ llist_del(&tcm->list);
+ talloc_free(l1h->last_acked);
+ l1h->last_acked = tcm;
+
/* check for response code */
rc = trx_ctrl_rx_rsp(l1h, &rsp, tcm);
if (rc == -EINVAL)
@@ -714,15 +736,11 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
/* re-schedule last cmd in rc seconds time */
if (rc > 0) {
- osmo_timer_schedule(&l1h->trx_ctrl_timer, rc, 0);
+ if (!llist_empty(&l1h->trx_ctrl_list))
+ osmo_timer_schedule(&l1h->trx_ctrl_timer, rc, 0);
return 0;
}
- /* remove command from list, save it to last_acked and removed previous last_acked */
- llist_del(&tcm->list);
- talloc_free(l1h->last_acked);
- l1h->last_acked = tcm;
-
trx_ctrl_send(l1h);
return 0;
@@ -1078,7 +1096,9 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what)
bi._num_pdus++;
/* feed received burst into scheduler code */
+ TRACE(OSMO_BTS_TRX_UL_DATA_START(l1h->phy_inst->trx->nr, bi.tn, bi.fn));
trx_sched_route_burst_ind(l1h->phy_inst->trx, &bi);
+ TRACE(OSMO_BTS_TRX_UL_DATA_DONE(l1h->phy_inst->trx->nr, bi.tn, bi.fn));
} while (bi.flags & TRX_BI_F_BATCH_IND);
return 0;
@@ -1199,6 +1219,11 @@ void trx_if_flush(struct trx_l1h *l1h)
talloc_free(tcm);
}
talloc_free(l1h->last_acked);
+ l1h->last_acked = NULL;
+
+ /* Tx queue is now empty, so there's no point in keeping the retrans timer armed: */
+ if (osmo_timer_pending(&l1h->trx_ctrl_timer))
+ osmo_timer_del(&l1h->trx_ctrl_timer);
}
/*! close the TRX for given handle (data + control socket) */
@@ -1262,7 +1287,8 @@ static void trx_phy_inst_close(struct phy_instance *pinst)
struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
trx_if_close(l1h);
- trx_sched_clean(pinst->trx);
+ if (pinst->trx)
+ trx_sched_clean(pinst->trx);
}
/*! open the control + burst data sockets for one phy_instance */
@@ -1331,6 +1357,23 @@ cleanup:
return -1;
}
+/*! close the PHY link using TRX protocol */
+int bts_model_phy_link_close(struct phy_link *plink)
+{
+ bool clock_stopped = false;
+ struct phy_instance *pinst;
+ llist_for_each_entry(pinst, &plink->instances, list) {
+ if (!clock_stopped) {
+ clock_stopped = true;
+ trx_sched_clock_stopped(pinst->trx->bts);
+ }
+ trx_phy_inst_close(pinst);
+ }
+ trx_udp_close(&plink->u.osmotrx.trx_ofd_clk);
+ phy_link_state_set(plink, PHY_LINK_SHUTDOWN);
+ return 0;
+}
+
/*! determine if the TRX for given handle is powered up */
int trx_if_powered(struct trx_l1h *l1h)
{
diff --git a/src/osmo-bts-trx/trx_provision_fsm.c b/src/osmo-bts-trx/trx_provision_fsm.c
index 5beca2ac..5deecd78 100644
--- a/src/osmo-bts-trx/trx_provision_fsm.c
+++ b/src/osmo-bts-trx/trx_provision_fsm.c
@@ -33,6 +33,8 @@
#include <osmo-bts/bts_model.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
+#include <osmo-bts/nm_common_fsm.h>
+#include <osmo-bts/signal.h>
#include "l1_if.h"
#include "trx_provision_fsm.h"
@@ -48,12 +50,14 @@ static void l1if_poweronoff_cb(struct trx_l1h *l1h, bool poweronoff, int rc)
struct phy_link *plink = pinst->phy_link;
plink->u.osmotrx.powered = poweronoff;
- plink->u.osmotrx.poweronoff_sent = false;
- if (poweronoff)
+ if (poweronoff) {
+ plink->u.osmotrx.poweron_sent = false;
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_POWERON_CNF, (void*)(intptr_t)rc);
- else
+ } else {
+ plink->u.osmotrx.poweroff_sent = false;
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_POWEROFF_CNF, (void*)(intptr_t)rc);
+ }
}
@@ -92,6 +96,53 @@ void l1if_setformat_cb(struct trx_l1h *l1h, int rc)
/*
* transceiver provisioning
*/
+
+static void trx_provision_reset(struct trx_l1h *l1h)
+{
+ struct phy_instance *pinst = l1h->phy_inst;
+ uint8_t tn;
+
+ l1h->config.trxd_pdu_ver_req = pinst->phy_link->u.osmotrx.trxd_pdu_ver_max;
+ l1h->config.trxd_pdu_ver_use = 0;
+ l1h->config.setformat_sent = false;
+ l1h->config.setformat_acked = false;
+
+ l1h->config.enabled = false;
+ l1h->config.arfcn_valid = false;
+ l1h->config.arfcn = 0;
+ l1h->config.rxtune_sent = false;
+ l1h->config.rxtune_acked = false;
+ l1h->config.txtune_sent = false;
+ l1h->config.txtune_acked = false;
+
+ l1h->config.tsc_valid = false;
+ l1h->config.tsc = 0;
+ l1h->config.tsc_sent = false;
+ l1h->config.tsc_acked = false;
+
+ l1h->config.bsic_valid = false;
+ l1h->config.bsic = 0;
+ l1h->config.bsic_sent = false;
+ l1h->config.bsic_acked = false;
+
+ l1h->config.rxgain_sent = false;
+
+ l1h->config.nomtxpower_sent = false;
+ l1h->config.nomtxpower_acked = false;
+
+ l1h->config.maxdly_sent = false;
+
+ l1h->config.maxdlynb_sent = false;
+
+ for (tn = 0; tn < TRX_NR_TS; tn++) {
+ l1h->config.setslot_valid[tn] = false;
+ l1h->config.setslot_sent[tn] = false;
+ l1h->config.setslot[tn].slottype = 0;
+ l1h->config.setslot[tn].tsc_set = 0;
+ l1h->config.setslot[tn].tsc_val = 0;
+ l1h->config.setslot[tn].tsc_valid = 0;
+ }
+}
int l1if_provision_transceiver_trx(struct trx_l1h *l1h)
{
struct phy_instance *pinst = l1h->phy_inst;
@@ -248,6 +299,48 @@ static bool trx_other_trx0_ready(struct trx_l1h *l1h)
return true;
}
+/* Closes a phy_link and all its associated TRX */
+static void trx_prov_fsm_apply_close(struct phy_link *plink, int rc)
+{
+ struct trx_l1h *l1h;
+ struct phy_instance *pinst;
+
+ if (plink->state == PHY_LINK_SHUTDOWN)
+ return;
+
+ bts_model_phy_link_close(plink);
+ /* Notify TRX close on all TRX associated with this phy */
+ llist_for_each_entry(pinst, &plink->instances, list) {
+ l1h = pinst->u.osmotrx.hdl;
+ trx_prov_fsm_state_chg(l1h->provision_fi, TRX_PROV_ST_CLOSED);
+ bts_model_trx_close_cb(pinst->trx, rc);
+ }
+}
+
+static int trx_prov_fsm_signal_cb(unsigned int subsys, unsigned int signal,
+ void *hdlr_data, void *signal_data)
+{
+ struct nm_statechg_signal_data *nsd;
+ struct gsm_bts_trx *trx;
+
+ if (subsys != SS_GLOBAL)
+ return -EINVAL;
+
+ if (signal != S_NEW_OP_STATE)
+ return 0;
+
+ nsd = (struct nm_statechg_signal_data *)signal_data;
+
+ if (nsd->mo->obj_class != NM_OC_RADIO_CARRIER)
+ return 0;
+
+ if (nsd->old_state != NM_OPSTATE_ENABLED && nsd->new_state == NM_OPSTATE_ENABLED) {
+ trx = gsm_objclass2obj(nsd->mo->bts, nsd->mo->obj_class, &nsd->mo->obj_inst);
+ l1if_trx_start_power_ramp(trx, NULL);
+ }
+ return 0;
+}
+
//////////////////////////
// FSM STATE ACTIONS
//////////////////////////
@@ -274,13 +367,18 @@ static void st_open_poweroff_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_st
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
struct phy_instance *pinst = l1h->phy_inst;
- l1h->config.trxd_pdu_ver_req = pinst->phy_link->u.osmotrx.trxd_pdu_ver_max;
+ trx_provision_reset(l1h);
- /* Apply initial RFMUTE state */
- if (pinst->trx != NULL)
- trx_if_cmd_rfmute(l1h, pinst->trx->mo.nm_state.administrative != NM_STATE_UNLOCKED);
- else
+ if (pinst->trx == NULL) {
trx_if_cmd_rfmute(l1h, true);
+ return;
+ }
+
+ /* Apply initial RFMUTE state */
+ trx_if_cmd_rfmute(l1h, pinst->trx->mo.nm_state.administrative != NM_STATE_UNLOCKED);
+
+ osmo_fsm_inst_dispatch(pinst->trx->mo.fi, NM_EV_SW_ACT, NULL);
+ osmo_fsm_inst_dispatch(pinst->trx->bb_transc.mo.fi, NM_EV_SW_ACT, NULL);
}
static void st_open_poweroff(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -294,6 +392,13 @@ static void st_open_poweroff(struct osmo_fsm_inst *fi, uint32_t event, void *dat
bool others_ready;
switch (event) {
+ case TRX_PROV_EV_CLOSE:
+ /* In this state, we didn't for sure send a POWERON yet, hence we
+ are save directly applying the close as if we received a
+ POWEROFF RSP: */
+ if (pinst->num == 0)
+ trx_prov_fsm_apply_close(pinst->phy_link, 0);
+ return;
case TRX_PROV_EV_CFG_ENABLE:
l1h->config.enabled =(bool)data;
break;
@@ -419,7 +524,7 @@ static void st_open_wait_power_cnf_on_enter(struct osmo_fsm_inst *fi, uint32_t p
struct phy_instance *pinst = l1h->phy_inst;
trx_if_cmd_poweron(l1h, l1if_poweronoff_cb);
- pinst->phy_link->u.osmotrx.poweronoff_sent = true;
+ pinst->phy_link->u.osmotrx.poweron_sent = true;
}
static void st_open_wait_power_cnf(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -435,13 +540,6 @@ static void st_open_wait_power_cnf(struct osmo_fsm_inst *fi, uint32_t event, voi
if (rc == 0 && plink->state != PHY_LINK_CONNECTED) {
trx_sched_clock_started(pinst->trx->bts);
phy_link_state_set(plink, PHY_LINK_CONNECTED);
-
- /* Begin to ramp up the power on all TRX associated with this phy */
- llist_for_each_entry(pinst, &plink->instances, list) {
- if (pinst->trx->mo.nm_state.administrative == NM_STATE_UNLOCKED)
- l1if_trx_start_power_ramp(pinst->trx, NULL);
- }
-
trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_POWERON);
} else if (rc != 0 && plink->state != PHY_LINK_SHUTDOWN) {
trx_sched_clock_stopped(pinst->trx->bts);
@@ -451,6 +549,14 @@ static void st_open_wait_power_cnf(struct osmo_fsm_inst *fi, uint32_t event, voi
case TRX_PROV_EV_CFG_TS:
update_ts_data(l1h, (struct trx_prov_ev_cfg_ts_data*)data);
break;
+ case TRX_PROV_EV_CLOSE:
+ /* power off transceiver, if not already */
+ if (pinst->num == 0 && !plink->u.osmotrx.poweroff_sent) {
+ trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb);
+ plink->u.osmotrx.poweroff_sent = true;
+ }
+ trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF);
+ break;
default:
OSMO_ASSERT(0);
}
@@ -490,29 +596,15 @@ static void st_open_poweron(struct osmo_fsm_inst *fi, uint32_t event, void *data
struct phy_instance *pinst = l1h->phy_inst;
struct phy_link *plink = pinst->phy_link;
struct trx_prov_ev_cfg_ts_data* ts_data;
- uint8_t tn;
switch (event) {
case TRX_PROV_EV_CLOSE:
/* power off transceiver, if not already */
- if (l1h->config.enabled) {
- if (pinst->num == 0 && plink->u.osmotrx.powered && !plink->u.osmotrx.poweronoff_sent) {
- trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb);
- plink->u.osmotrx.poweronoff_sent = true;
- }
- l1h->config.rxgain_sent = false;
- l1h->config.maxdly_sent = false;
- l1h->config.maxdlynb_sent = false;
- for (tn = 0; tn < TRX_NR_TS; tn++)
- l1h->config.setslot_sent[tn] = false;
- } else if (!pinst->phy_link->u.osmotrx.poweronoff_sent) {
- bts_model_trx_close_cb(pinst->trx, 0);
- } /* else: poweroff in progress, cb will be called upon TRXC RSP */
-
- if (pinst->num == 0)
- trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF);
- else
- trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_POWEROFF);
+ if (pinst->num == 0 && plink->u.osmotrx.powered && !plink->u.osmotrx.poweroff_sent) {
+ trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb);
+ plink->u.osmotrx.poweroff_sent = true;
+ }
+ trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF);
break;
case TRX_PROV_EV_CFG_TS:
ts_data = (struct trx_prov_ev_cfg_ts_data*)data;
@@ -536,15 +628,7 @@ static void st_open_wait_poweroff_cnf(struct osmo_fsm_inst *fi, uint32_t event,
switch (event) {
case TRX_PROV_EV_POWEROFF_CNF:
rc = (uint16_t)(intptr_t)data;
- if (plink->state != PHY_LINK_SHUTDOWN) {
- trx_sched_clock_stopped(pinst->trx->bts);
- phy_link_state_set(plink, PHY_LINK_SHUTDOWN);
-
- /* Notify TRX close on all TRX associated with this phy */
- llist_for_each_entry(pinst, &plink->instances, list) {
- bts_model_trx_close_cb(pinst->trx, rc);
- }
- }
+ trx_prov_fsm_apply_close(plink, rc);
break;
default:
OSMO_ASSERT(0);
@@ -562,6 +646,7 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
},
[TRX_PROV_ST_OPEN_POWEROFF] = {
.in_event_mask =
+ X(TRX_PROV_EV_CLOSE) |
X(TRX_PROV_EV_OTHER_TRX_READY) |
X(TRX_PROV_EV_CFG_ENABLE) |
X(TRX_PROV_EV_CFG_BSIC) |
@@ -574,6 +659,7 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
X(TRX_PROV_EV_SETTSC_CNF) |
X(TRX_PROV_EV_SETFORMAT_CNF),
.out_state_mask =
+ X(TRX_PROV_ST_CLOSED) |
X(TRX_PROV_ST_OPEN_WAIT_POWERON_CNF) |
X(TRX_PROV_ST_OPEN_POWERON),
.name = "OPEN_POWEROFF",
@@ -582,10 +668,12 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
},
[TRX_PROV_ST_OPEN_WAIT_POWERON_CNF] = {
.in_event_mask =
+ X(TRX_PROV_EV_CLOSE) |
X(TRX_PROV_EV_POWERON_CNF) |
X(TRX_PROV_EV_CFG_TS),
.out_state_mask =
- X(TRX_PROV_ST_OPEN_POWERON),
+ X(TRX_PROV_ST_OPEN_POWERON) |
+ X(TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF),
.name = "OPEN_WAIT_POWERON_CNF",
.onenter = st_open_wait_power_cnf_on_enter,
.action = st_open_wait_power_cnf,
@@ -595,8 +683,7 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
X(TRX_PROV_EV_CLOSE) |
X(TRX_PROV_EV_CFG_TS),
.out_state_mask =
- X(TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF) |
- X(TRX_PROV_ST_OPEN_POWEROFF),
+ X(TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF),
.name = "OPEN_POWERON",
.onenter = st_open_poweron_on_enter,
.action = st_open_poweron,
@@ -605,7 +692,7 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
.in_event_mask =
X(TRX_PROV_EV_POWEROFF_CNF),
.out_state_mask =
- X(TRX_PROV_ST_OPEN_POWEROFF),
+ X(TRX_PROV_ST_CLOSED),
.name = "OPEN_WAIT_POWEROFF_CNF",
.action = st_open_wait_poweroff_cnf,
},
@@ -643,4 +730,5 @@ struct osmo_fsm trx_prov_fsm = {
static __attribute__((constructor)) void trx_prov_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&trx_prov_fsm) == 0);
+ OSMO_ASSERT(osmo_signal_register_handler(SS_GLOBAL, trx_prov_fsm_signal_cb, NULL) == 0);
}
diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c
index bb9d1584..2b8bc24b 100644
--- a/src/osmo-bts-trx/trx_vty.c
+++ b/src/osmo-bts-trx/trx_vty.c
@@ -265,18 +265,12 @@ DEFUN_DEPRECATED(cfg_phy_no_timing_advance_loop, cfg_phy_no_timing_advance_loop_
return CMD_SUCCESS;
}
-DEFUN_ATTR(cfg_phyinst_maxdly, cfg_phyinst_maxdly_cmd,
- "osmotrx maxdly <0-31>",
- OSMOTRX_STR
- "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",
- CMD_ATTR_IMMEDIATE)
+DEFUN_USRATTR(cfg_phyinst_maxdly, cfg_phyinst_maxdly_cmd,
+ X(BTS_VTY_TRX_POWERCYCLE),
+ "osmotrx maxdly <0-63>",
+ OSMOTRX_STR
+ "Set the maximum acceptable delay of an Access Burst\n"
+ "Delay in GSMK symbol periods (approx. 550m per symbol)\n")
{
struct phy_instance *pinst = vty->index;
struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
@@ -284,25 +278,17 @@ DEFUN_ATTR(cfg_phyinst_maxdly, cfg_phyinst_maxdly_cmd,
l1h->config.maxdly = atoi(argv[0]);
l1h->config.maxdly_valid = 1;
l1h->config.maxdly_sent = false;
- l1if_provision_transceiver_trx(l1h);
return CMD_SUCCESS;
}
-DEFUN_ATTR(cfg_phyinst_maxdlynb, cfg_phyinst_maxdlynb_cmd,
- "osmotrx maxdlynb <0-31>",
- OSMOTRX_STR
- "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 controlled 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",
- CMD_ATTR_IMMEDIATE)
+DEFUN_ATTR_USRATTR(cfg_phyinst_maxdlynb, cfg_phyinst_maxdlynb_cmd,
+ CMD_ATTR_HIDDEN, /* expert mode command */
+ X(BTS_VTY_TRX_POWERCYCLE),
+ "osmotrx maxdlynb <0-63>",
+ OSMOTRX_STR
+ "Set the maximum acceptable delay of a Normal Burst\n"
+ "Delay in GMSK symbol periods (approx. 550m per symbol)\n")
{
struct phy_instance *pinst = vty->index;
struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
@@ -310,7 +296,6 @@ DEFUN_ATTR(cfg_phyinst_maxdlynb, cfg_phyinst_maxdlynb_cmd,
l1h->config.maxdlynb = atoi(argv[0]);
l1h->config.maxdlynb_valid = 1;
l1h->config.maxdlynb_sent = false;
- l1if_provision_transceiver_trx(l1h);
return CMD_SUCCESS;
}
diff --git a/src/osmo-bts-virtual/bts_model.c b/src/osmo-bts-virtual/bts_model.c
index af8a6f2a..87040566 100644
--- a/src/osmo-bts-virtual/bts_model.c
+++ b/src/osmo-bts-virtual/bts_model.c
@@ -110,20 +110,38 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
- int cause = 0;
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
switch (foh->msg_type) {
case NM_MT_SET_BTS_ATTR:
- cause = vbts_set_bts(obj);
+ ev_data.cause = vbts_set_bts(obj);
break;
case NM_MT_SET_RADIO_ATTR:
- cause = vbts_set_trx(obj);
+ ev_data.cause = vbts_set_trx(obj);
break;
case NM_MT_SET_CHAN_ATTR:
- cause = vbts_set_ts(obj);
+ ev_data.cause = vbts_set_ts(obj);
break;
}
- return oml_fom_ack_nack(msg, cause);
+
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* MO: TS 12.21 Managed Object */
@@ -156,7 +174,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj)
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-virtual/l1_if.c b/src/osmo-bts-virtual/l1_if.c
index 444d6ad2..0d36db18 100644
--- a/src/osmo-bts-virtual/l1_if.c
+++ b/src/osmo-bts-virtual/l1_if.c
@@ -224,8 +224,6 @@ int bts_model_phy_link_open(struct phy_link *plink)
* / PRIM_INFO_ACTIVATE */
if (pinst->trx == pinst->trx->bts->c0) {
vbts_sched_start(pinst->trx->bts);
- /* init lapdm layer 3 callback for the trx on timeslot 0 == BCCH */
- lchan_init_lapdm(&pinst->trx->ts[0].lchan[CCCH_LCHAN]);
/* FIXME: This is probably the wrong location to set the CCCH to active... the OML link def. needs to be reworked and fixed. */
pinst->trx->ts[0].lchan[CCCH_LCHAN].rel_act_kind = LCHAN_REL_ACT_OML;
lchan_set_state(&pinst->trx->ts[0].lchan[CCCH_LCHAN], LCHAN_S_ACTIVE);
@@ -320,11 +318,11 @@ static int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t f
float ber = n_bits_total==0 ? 1.0 : (float)n_errors / (float)n_bits_total;
DEBUGPFN(DMEAS, fn, "RX L1 frame %s chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS "
- "ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n",
+ "ber=%.2f%% (%d/%d bits) L1_ta=%d ta_ctrl.current=%d toa=%.2f\n",
gsm_lchan_name(lchan), chan_nr, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power_ctrl.max),
- rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info.ta, lchan->rqd_ta, toa);
+ rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info.ta, lchan->ta_ctrl.current, toa);
- l1if_fill_meas_res(&l1sap, chan_nr, lchan->rqd_ta + toa, ber, rssi, fn);
+ l1if_fill_meas_res(&l1sap, chan_nr, lchan->ta_ctrl.current + toa, ber, rssi, fn);
return l1sap_up(trx, &l1sap);
}
@@ -392,8 +390,6 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
lchan->tch.amr_mr.bts_mode[3].mode,
amr_get_initial_mode(lchan),
(lchan->ho.active == 1));
- /* init lapdm */
- lchan_init_lapdm(lchan);
/* set lchan active */
lchan_set_state(lchan, LCHAN_S_ACTIVE);
/* set initial ciphering */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 57687eef..a1d04a77 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = paging cipher agch misc handover tx_power power meas ta_control
+SUBDIRS = paging cipher agch misc handover tx_power power meas ta_control amr
if ENABLE_SYSMOBTS
SUBDIRS += sysmobts
@@ -22,12 +22,38 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
echo ' [$(PACKAGE_URL)])'; \
} >'$(srcdir)/package.m4'
-EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)
+EXTRA_DIST = \
+ testsuite.at \
+ $(srcdir)/package.m4 \
+ $(TESTSUITE) \
+ osmo-bts.vty \
+ $(NULL)
TESTSUITE = $(srcdir)/testsuite
DISTCLEANFILES = atconfig
+if ENABLE_EXT_TESTS
+python-tests: $(BUILT_SOURCES)
+ $(MAKE) vty-test
+else
+python-tests: $(BUILT_SOURCES)
+ echo "Not running python-based tests (determined at configure-time)"
+endif
+
+# Run a specific test with: 'make vty-test VTY_TEST=foo.vty'
+VTY_TEST ?= *.vty
+
+# To update the VTY script from current application behavior,
+# pass -u to vty_script_runner.py by doing:
+# make vty-test U=-u
+vty-test:
+ osmo_verify_transcript_vty.py -v \
+ -n OsmoBTS -p 4241 \
+ -r "$(top_builddir)/src/osmo-bts-virtual/osmo-bts-virtual --vty-test -c $(top_srcdir)/doc/examples/virtual/osmo-bts-virtual.cfg" \
+ $(U) $(srcdir)/$(VTY_TEST)
+
check-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
+ $(MAKE) $(AM_MAKEFLAGS) python-tests
installcheck-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
diff --git a/tests/amr/Makefile.am b/tests/amr/Makefile.am
new file mode 100644
index 00000000..dc0f1b81
--- /dev/null
+++ b/tests/amr/Makefile.am
@@ -0,0 +1,11 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
+LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) \
+ $(LIBOSMOABIS_LIBS) $(LIBOSMOTRAU_LIBS)
+noinst_PROGRAMS = amr_test
+EXTRA_DIST = amr_test.ok
+
+misc_test_SOURCES = amr_test.c
+misc_test_LDADD = $(top_builddir)/src/common/libbts.a \
+ $(LDADD)
diff --git a/tests/amr/amr_test.c b/tests/amr/amr_test.c
new file mode 100644
index 00000000..4efbf402
--- /dev/null
+++ b/tests/amr/amr_test.c
@@ -0,0 +1,151 @@
+/* (C) 2021 by sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * 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/logging.h>
+#include <osmocom/core/utils.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../../src/osmo-bts-trx/sched_utils.h"
+
+struct amr_cmi_test_data {
+ /* Frame number that marks the beginning of the voice block */
+ uint32_t gsm_fn;
+ /* In uplink: True, when the voice block is a CMI block, false otherwise. */
+ /* In downlink: False, when the voice block is a CMI block, true otherwise. */
+ bool is_cmi;
+};
+
+/* The behavior of AHS in subslot 0 and AFS is the same */
+static const struct amr_cmi_test_data testvec_ahs_h0_and_afs[] = {
+ { 0, true },
+ { 4, false },
+ { 8, true },
+ { 13, false },
+ { 17, true },
+ { 21, false },
+ { 26, true },
+ { 30, false },
+ { 34, true },
+ { 39, false },
+ { 43, true },
+ { 47, false },
+ { 52, true },
+ { 56, false },
+ { 60, true },
+ { 65, false },
+ { 69, true },
+ { 73, false },
+ { 78, true },
+ { 82, false },
+ { 86, true },
+ { 91, false },
+ { 95, true },
+ { 99, false },
+};
+
+static const struct amr_cmi_test_data testvec_ahs_h1[] = {
+ { 1, true },
+ { 5, false },
+ { 9, true },
+ { 14, false },
+ { 18, true },
+ { 22, false },
+ { 27, true },
+ { 31, false },
+ { 35, true },
+ { 40, false },
+ { 44, true },
+ { 48, false },
+ { 53, true },
+ { 57, false },
+ { 61, true },
+ { 66, false },
+ { 70, true },
+ { 74, false },
+ { 79, true },
+ { 83, false },
+ { 87, true },
+ { 92, false },
+ { 96, true },
+ { 100, false },
+};
+
+static void test_amr_cmi_sched(void)
+{
+ unsigned int i;
+ bool res;
+
+ printf("AMR transmission phase (CMI) in relation to GSM FN:\n");
+
+ for (i = 0; i < ARRAY_SIZE(testvec_ahs_h0_and_afs); i++) {
+ res = ul_amr_fn_is_cmi(testvec_ahs_h0_and_afs[i].gsm_fn);
+ printf("Uplink, AMR AHS on HR subslot 0: fn_begin=%u, CMI=%u\n", testvec_ahs_h0_and_afs[i].gsm_fn, res);
+ OSMO_ASSERT(res == testvec_ahs_h0_and_afs[i].is_cmi);
+ }
+
+ printf("\n");
+
+ for (i = 0; i < ARRAY_SIZE(testvec_ahs_h0_and_afs); i++) {
+ res = dl_amr_fn_is_cmi(testvec_ahs_h0_and_afs[i].gsm_fn);
+ printf("Downlink, AMR AHS on HR subslot 0: fn_begin=%u, CMI=%u\n", testvec_ahs_h0_and_afs[i].gsm_fn, res);
+ OSMO_ASSERT(res == !testvec_ahs_h0_and_afs[i].is_cmi);
+ }
+
+ printf("\n");
+ printf("\n");
+
+ for (i = 0; i < ARRAY_SIZE(testvec_ahs_h1); i++) {
+ res = ul_amr_fn_is_cmi(testvec_ahs_h1[i].gsm_fn);
+ printf("Uplink, AMR AHS on HR subslot 1: fn_begin=%u, CMI=%u\n", testvec_ahs_h1[i].gsm_fn, res);
+ OSMO_ASSERT(res == testvec_ahs_h1[i].is_cmi);
+ }
+
+ printf("\n");
+
+ for (i = 0; i < ARRAY_SIZE(testvec_ahs_h1); i++) {
+ res = dl_amr_fn_is_cmi(testvec_ahs_h1[i].gsm_fn);
+ printf("Downlink, AMR AHS on HR subslot 1: fn_begin=%u, CMI=%u\n", testvec_ahs_h1[i].gsm_fn, res);
+ OSMO_ASSERT(res == !testvec_ahs_h1[i].is_cmi);
+ }
+
+ printf("\n");
+ printf("\n");
+
+ for (i = 0; i < ARRAY_SIZE(testvec_ahs_h0_and_afs); i++) {
+ res = ul_amr_fn_is_cmi(testvec_ahs_h0_and_afs[i].gsm_fn);
+ printf("Uplink, AMR AFS: fn_begin=%u, CMI=%u\n", testvec_ahs_h0_and_afs[i].gsm_fn, res);
+ OSMO_ASSERT(res == testvec_ahs_h0_and_afs[i].is_cmi);
+ }
+
+ printf("\n");
+
+ for (i = 0; i < ARRAY_SIZE(testvec_ahs_h0_and_afs); i++) {
+ res = dl_amr_fn_is_cmi(testvec_ahs_h0_and_afs[i].gsm_fn);
+ printf("Downlink, AMR AFS: fn_begin=%u, CMI=%u\n", testvec_ahs_h0_and_afs[i].gsm_fn, res);
+ OSMO_ASSERT(res == !testvec_ahs_h0_and_afs[i].is_cmi);
+ }
+}
+
+int main(int argc, char **argv)
+{
+
+ test_amr_cmi_sched();
+ return EXIT_SUCCESS;
+}
diff --git a/tests/amr/amr_test.ok b/tests/amr/amr_test.ok
new file mode 100644
index 00000000..ec1d1a08
--- /dev/null
+++ b/tests/amr/amr_test.ok
@@ -0,0 +1,152 @@
+AMR transmission phase (CMI) in relation to GSM FN:
+Uplink, AMR AHS on HR subslot 0: fn_begin=0, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=4, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=8, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=13, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=17, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=21, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=26, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=30, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=34, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=39, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=43, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=47, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=52, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=56, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=60, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=65, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=69, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=73, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=78, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=82, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=86, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=91, CMI=0
+Uplink, AMR AHS on HR subslot 0: fn_begin=95, CMI=1
+Uplink, AMR AHS on HR subslot 0: fn_begin=99, CMI=0
+
+Downlink, AMR AHS on HR subslot 0: fn_begin=0, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=4, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=8, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=13, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=17, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=21, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=26, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=30, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=34, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=39, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=43, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=47, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=52, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=56, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=60, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=65, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=69, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=73, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=78, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=82, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=86, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=91, CMI=1
+Downlink, AMR AHS on HR subslot 0: fn_begin=95, CMI=0
+Downlink, AMR AHS on HR subslot 0: fn_begin=99, CMI=1
+
+
+Uplink, AMR AHS on HR subslot 1: fn_begin=1, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=5, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=9, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=14, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=18, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=22, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=27, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=31, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=35, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=40, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=44, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=48, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=53, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=57, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=61, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=66, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=70, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=74, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=79, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=83, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=87, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=92, CMI=0
+Uplink, AMR AHS on HR subslot 1: fn_begin=96, CMI=1
+Uplink, AMR AHS on HR subslot 1: fn_begin=100, CMI=0
+
+Downlink, AMR AHS on HR subslot 1: fn_begin=1, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=5, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=9, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=14, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=18, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=22, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=27, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=31, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=35, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=40, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=44, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=48, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=53, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=57, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=61, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=66, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=70, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=74, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=79, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=83, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=87, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=92, CMI=1
+Downlink, AMR AHS on HR subslot 1: fn_begin=96, CMI=0
+Downlink, AMR AHS on HR subslot 1: fn_begin=100, CMI=1
+
+
+Uplink, AMR AFS: fn_begin=0, CMI=1
+Uplink, AMR AFS: fn_begin=4, CMI=0
+Uplink, AMR AFS: fn_begin=8, CMI=1
+Uplink, AMR AFS: fn_begin=13, CMI=0
+Uplink, AMR AFS: fn_begin=17, CMI=1
+Uplink, AMR AFS: fn_begin=21, CMI=0
+Uplink, AMR AFS: fn_begin=26, CMI=1
+Uplink, AMR AFS: fn_begin=30, CMI=0
+Uplink, AMR AFS: fn_begin=34, CMI=1
+Uplink, AMR AFS: fn_begin=39, CMI=0
+Uplink, AMR AFS: fn_begin=43, CMI=1
+Uplink, AMR AFS: fn_begin=47, CMI=0
+Uplink, AMR AFS: fn_begin=52, CMI=1
+Uplink, AMR AFS: fn_begin=56, CMI=0
+Uplink, AMR AFS: fn_begin=60, CMI=1
+Uplink, AMR AFS: fn_begin=65, CMI=0
+Uplink, AMR AFS: fn_begin=69, CMI=1
+Uplink, AMR AFS: fn_begin=73, CMI=0
+Uplink, AMR AFS: fn_begin=78, CMI=1
+Uplink, AMR AFS: fn_begin=82, CMI=0
+Uplink, AMR AFS: fn_begin=86, CMI=1
+Uplink, AMR AFS: fn_begin=91, CMI=0
+Uplink, AMR AFS: fn_begin=95, CMI=1
+Uplink, AMR AFS: fn_begin=99, CMI=0
+
+Downlink, AMR AFS: fn_begin=0, CMI=0
+Downlink, AMR AFS: fn_begin=4, CMI=1
+Downlink, AMR AFS: fn_begin=8, CMI=0
+Downlink, AMR AFS: fn_begin=13, CMI=1
+Downlink, AMR AFS: fn_begin=17, CMI=0
+Downlink, AMR AFS: fn_begin=21, CMI=1
+Downlink, AMR AFS: fn_begin=26, CMI=0
+Downlink, AMR AFS: fn_begin=30, CMI=1
+Downlink, AMR AFS: fn_begin=34, CMI=0
+Downlink, AMR AFS: fn_begin=39, CMI=1
+Downlink, AMR AFS: fn_begin=43, CMI=0
+Downlink, AMR AFS: fn_begin=47, CMI=1
+Downlink, AMR AFS: fn_begin=52, CMI=0
+Downlink, AMR AFS: fn_begin=56, CMI=1
+Downlink, AMR AFS: fn_begin=60, CMI=0
+Downlink, AMR AFS: fn_begin=65, CMI=1
+Downlink, AMR AFS: fn_begin=69, CMI=0
+Downlink, AMR AFS: fn_begin=73, CMI=1
+Downlink, AMR AFS: fn_begin=78, CMI=0
+Downlink, AMR AFS: fn_begin=82, CMI=1
+Downlink, AMR AFS: fn_begin=86, CMI=0
+Downlink, AMR AFS: fn_begin=91, CMI=1
+Downlink, AMR AFS: fn_begin=95, CMI=0
+Downlink, AMR AFS: fn_begin=99, CMI=1
diff --git a/tests/meas/meas_testcases.h b/tests/meas/meas_testcases.h
index d7eee5c3..90f0f850 100644
--- a/tests/meas/meas_testcases.h
+++ b/tests/meas/meas_testcases.h
@@ -1,5 +1,5 @@
#define ULM(ber, ta, sub, neg_rssi) \
- { .ber10k = (ber), .ta_offs_256bits = (ta), .c_i = 1.0, .is_sub = sub, .inv_rssi = (neg_rssi) }
+ { .ber10k = (ber), .ta_offs_256bits = (ta), .c_i = 10, .is_sub = sub, .inv_rssi = (neg_rssi) }
struct meas_testcase {
const char *name;
diff --git a/tests/osmo-bts.vty b/tests/osmo-bts.vty
new file mode 100644
index 00000000..fb74747c
--- /dev/null
+++ b/tests/osmo-bts.vty
@@ -0,0 +1,297 @@
+OsmoBTS> list
+...
+ show bts [<0-255>]
+ show trx [<0-255>] [<0-255>]
+ show timeslot [<0-255>] [<0-255>] [<0-7>]
+ show lchan [<0-255>] [<0-255>] [<0-7>] [<0-7>]
+ show lchan summary [<0-255>] [<0-255>] [<0-7>] [<0-7>]
+ show bts <0-255> gprs
+...
+ show timer [(bts|abis)] [TNNNN]
+ show e1_driver
+ show e1_line [<0-255>] [stats]
+ show e1_timeslot [<0-255>] [<0-31>]
+...
+OsmoBTS> ?
+...
+ show Show running system information
+...
+
+OsmoBTS> show ?
+...
+ bts Display information about a BTS
+ trx Display information about a TRX
+ timeslot Display information about a TS
+ lchan Display information about a logical channel
+ timer Show timers
+ e1_driver Display information about available E1 drivers
+ e1_line Display information about a E1 line
+ e1_timeslot Display information about a E1 timeslot
+...
+OsmoBTS> show bts ?
+ [<0-255>] BTS Number
+ <0-255> BTS Number
+OsmoBTS> show bts 0 ?
+ gprs GPRS/EGPRS configuration
+ <cr>
+OsmoBTS> show trx ?
+ [<0-255>] BTS Number
+OsmoBTS> show trx 0 ?
+ [<0-255>] TRX Number
+OsmoBTS> show timeslot ?
+ [<0-255>] BTS Number
+OsmoBTS> show timeslot 0 ?
+ [<0-255>] TRX Number
+OsmoBTS> show timeslot 0 0 ?
+ [<0-7>] Timeslot Number
+OsmoBTS> show lchan ?
+ [<0-255>] BTS Number
+ summary Short summary
+OsmoBTS> show lchan 0 ?
+ [<0-255>] TRX Number
+OsmoBTS> show lchan 0 0 ?
+ [<0-7>] Timeslot Number
+OsmoBTS> show lchan 0 0 0 ?
+ [<0-7>] Logical Channel Number
+OsmoBTS> show lchan summary ?
+ [<0-255>] BTS Number
+OsmoBTS> show lchan summary 0 ?
+ [<0-255>] TRX Number
+OsmoBTS> show lchan summary 0 0 ?
+ [<0-7>] Timeslot Number
+OsmoBTS> show lchan summary 0 0 0 ?
+ [<0-7>] Logical Channel Number
+
+OsmoBTS> show timer ?
+ [bts] BTS process timers
+ [abis] Abis (RSL) related timers
+OsmoBTS> show timer
+bts: X1 = 300 s Time after which osmo-bts exits if regular ramp down during shut down process does not finish (s) (default: 300 s)
+bts: X2 = 3 s Time after which osmo-bts exits if requesting transceivers to stop during shut down process does not finish (s) (default: 3 s)
+abis: X15 = 0 ms Time to wait between Channel Activation and dispatching a cached early Immediate Assignment (default: 0 ms)
+OsmoBTS> show timer bts ?
+ [TNNNN] T- or X-timer-number -- 3GPP compliant timer number of the format '1234' or 'T1234' or 't1234'; Osmocom-specific timer number of the format: 'X1234' or 'x1234'.
+OsmoBTS> show timer bts
+bts: X1 = 300 s Time after which osmo-bts exits if regular ramp down during shut down process does not finish (s) (default: 300 s)
+bts: X2 = 3 s Time after which osmo-bts exits if requesting transceivers to stop during shut down process does not finish (s) (default: 3 s)
+OsmoBTS> show timer bts X1
+bts: X1 = 300 s Time after which osmo-bts exits if regular ramp down during shut down process does not finish (s) (default: 300 s)
+OsmoBTS> show timer bts X2
+bts: X2 = 3 s Time after which osmo-bts exits if requesting transceivers to stop during shut down process does not finish (s) (default: 3 s)
+OsmoBTS> show timer abis ?
+ [TNNNN] T- or X-timer-number -- 3GPP compliant timer number of the format '1234' or 'T1234' or 't1234'; Osmocom-specific timer number of the format: 'X1234' or 'x1234'.
+OsmoBTS> show timer abis X15
+abis: X15 = 0 ms Time to wait between Channel Activation and dispatching a cached early Immediate Assignment (default: 0 ms)
+
+OsmoBTS> show e1_driver ?
+ <cr>
+OsmoBTS> show e1_line ?
+ [<0-255>] E1 Line Number
+OsmoBTS> show e1_line 0 ?
+ [stats] Include statistics
+OsmoBTS> show e1_timeslot ?
+ [<0-255>] E1 Line Number
+OsmoBTS> show e1_timeslot 0 ?
+ [<0-31>] E1 Timeslot Number
+
+OsmoBTS> enable
+OsmoBTS# list
+...
+ show bts [<0-255>]
+ show trx [<0-255>] [<0-255>]
+ show timeslot [<0-255>] [<0-255>] [<0-7>]
+ show lchan [<0-255>] [<0-255>] [<0-7>] [<0-7>]
+ show lchan summary [<0-255>] [<0-255>] [<0-7>] [<0-7>]
+ show bts <0-255> gprs
+...
+ show timer [(bts|abis)] [TNNNN]
+ bts <0-0> trx <0-255> ts <0-7> (lchan|shadow-lchan) <0-7> rtp jitter-buffer <0-10000>
+ test send-failure-event-report <0-255>
+ bts <0-255> c0-power-red <0-6>
+ show e1_driver
+ show e1_line [<0-255>] [stats]
+ show e1_timeslot [<0-255>] [<0-31>]
+...
+
+OsmoBTS# ?
+...
+ show Show running system information
+...
+
+OsmoBTS# show ?
+...
+ bts Display information about a BTS
+ trx Display information about a TRX
+ timeslot Display information about a TS
+ lchan Display information about a logical channel
+ timer Show timers
+ e1_driver Display information about available E1 drivers
+ e1_line Display information about a E1 line
+ e1_timeslot Display information about a E1 timeslot
+...
+OsmoBTS# show bts ?
+ [<0-255>] BTS Number
+ <0-255> BTS Number
+OsmoBTS# show bts 0 ?
+ gprs GPRS/EGPRS configuration
+ <cr>
+OsmoBTS# show trx ?
+ [<0-255>] BTS Number
+OsmoBTS# show trx 0 ?
+ [<0-255>] TRX Number
+OsmoBTS# show timeslot ?
+ [<0-255>] BTS Number
+OsmoBTS# show timeslot 0 ?
+ [<0-255>] TRX Number
+OsmoBTS# show timeslot 0 0 ?
+ [<0-7>] Timeslot Number
+OsmoBTS# show lchan ?
+ [<0-255>] BTS Number
+ summary Short summary
+OsmoBTS# show lchan 0 ?
+ [<0-255>] TRX Number
+OsmoBTS# show lchan 0 0 ?
+ [<0-7>] Timeslot Number
+OsmoBTS# show lchan 0 0 0 ?
+ [<0-7>] Logical Channel Number
+OsmoBTS# show lchan summary ?
+ [<0-255>] BTS Number
+OsmoBTS# show lchan summary 0 ?
+ [<0-255>] TRX Number
+OsmoBTS# show lchan summary 0 0 ?
+ [<0-7>] Timeslot Number
+OsmoBTS# show lchan summary 0 0 0 ?
+ [<0-7>] Logical Channel Number
+OsmoBTS# show e1_driver ?
+ <cr>
+OsmoBTS# show e1_line ?
+ [<0-255>] E1 Line Number
+OsmoBTS# show e1_line 0 ?
+ [stats] Include statistics
+OsmoBTS# show e1_timeslot ?
+ [<0-255>] E1 Line Number
+OsmoBTS# show e1_timeslot 0 ?
+ [<0-31>] E1 Timeslot Number
+
+OsmoBTS# configure terminal
+OsmoBTS(config)# list
+...
+ bts BTS_NR
+...
+ timer [(bts|abis)] [TNNNN] [(<0-2147483647>|default)]
+ phy <0-255>
+ e1_input
+...
+OsmoBTS(config)# ?
+...
+ bts Select a BTS to configure
+...
+ timer Configure or show timers
+ phy Select a PHY to configure
+ e1_input Configure E1/T1/J1 TDM input
+...
+OsmoBTS(config)# bts ?
+ BTS_NR BTS Number
+OsmoBTS(config)# phy ?
+ <0-255> PHY number
+
+OsmoBTS(config)# timer ?
+ [bts] BTS process timers
+ [abis] Abis (RSL) related timers
+OsmoBTS(config)# timer bts ?
+ [TNNNN] T- or X-timer-number -- 3GPP compliant timer number of the format '1234' or 'T1234' or 't1234'; Osmocom-specific timer number of the format: 'X1234' or 'x1234'.
+OsmoBTS(config)# timer bts X1
+bts: X1 = 300 s Time after which osmo-bts exits if regular ramp down during shut down process does not finish (s) (default: 300 s)
+OsmoBTS(config)# timer bts X2
+bts: X2 = 3 s Time after which osmo-bts exits if requesting transceivers to stop during shut down process does not finish (s) (default: 3 s)
+OsmoBTS(config)# timer bts X1 ?
+ [<0-2147483647>] New timer value
+ [default] Set to default timer value
+OsmoBTS(config)# timer bts X1 123
+OsmoBTS(config)# timer bts X1
+bts: X1 = 123 s Time after which osmo-bts exits if regular ramp down during shut down process does not finish (s) (default: 300 s)
+OsmoBTS(config)# timer bts X1 default
+OsmoBTS(config)# timer bts X1
+bts: X1 = 300 s Time after which osmo-bts exits if regular ramp down during shut down process does not finish (s) (default: 300 s)
+
+OsmoBTS(config)# timer abis X15
+abis: X15 = 0 ms Time to wait between Channel Activation and dispatching a cached early Immediate Assignment (default: 0 ms)
+OsmoBTS(config)# timer abis X15 123
+OsmoBTS(config)# timer abis X15
+abis: X15 = 123 ms Time to wait between Channel Activation and dispatching a cached early Immediate Assignment (default: 0 ms)
+OsmoBTS(config)# timer abis X15 default
+OsmoBTS(config)# timer abis X15
+abis: X15 = 0 ms Time to wait between Channel Activation and dispatching a cached early Immediate Assignment (default: 0 ms)
+
+OsmoBTS(config)# bts 0
+OsmoBTS(bts)# list
+...
+ ipa unit-id <0-65534> <0-255>
+ oml remote-ip A.B.C.D
+ no oml remote-ip A.B.C.D
+ rtp jitter-buffer <0-10000> [adaptive]
+ rtp port-range <1-65534> <1-65534>
+ rtp ip-dscp <0-63>
+ rtp socket-priority <0-255>
+ band (450|GSM450|480|GSM480|750|GSM750|810|GSM810|850|GSM850|900|GSM900|1800|DCS1800|1900|PCS1900)
+ description .TEXT
+ no description
+ paging queue-size <1-1024>
+ paging lifetime <0-60>
+ agch-queue-mgmt default
+ agch-queue-mgmt threshold <0-100> low <0-100> high <0-100000>
+ min-qual-rach <-100-100>
+ min-qual-norm <-100-100>
+ max-ber10k-rach <0-10000>
+ pcu-socket PATH
+ supp-meas-info toa256
+ no supp-meas-info toa256
+ smscb queue-max-length <1-60>
+ smscb queue-target-length <1-30>
+ smscb queue-hysteresis <0-30>
+ gsmtap-remote-host [HOSTNAME]
+ no gsmtap-remote-host
+ gsmtap-sapi (enable-all|disable-all)
+ gsmtap-sapi (bcch|ccch|rach|agch|pch|sdcch|tch/f|tch/h|pacch|pdtch|ptcch|cbch|sacch)
+ no gsmtap-sapi (bcch|ccch|rach|agch|pch|sdcch|tch/f|tch/h|pacch|pdtch|ptcch|cbch|sacch)
+ trx <0-254>
+...
+OsmoBTS(bts)# ?
+...
+ ipa ip.access RSL commands
+ oml OML Parameters
+ no Negate a command or set its defaults
+ rtp RTP parameters
+ band Set the frequency band of this BTS
+ description Save human-readable description of the object
+ paging Paging related parameters
+ agch-queue-mgmt AGCH queue mgmt
+ min-qual-rach Set the minimum link quality level of Access Bursts to be accepted
+ min-qual-norm Set the minimum link quality level of Normal Bursts to be accepted
+ max-ber10k-rach Set the maximum BER for valid RACH requests
+ pcu-socket Configure the PCU socket file/path name
+ supp-meas-info Configure the RSL Supplementary Measurement Info
+ smscb SMSCB (SMS Cell Broadcast) / CBCH configuration
+ gsmtap-remote-host Enable GSMTAP Um logging (see also 'gsmtap-sapi')
+ gsmtap-sapi Enable/disable sending of UL/DL messages over GSMTAP
+ trx Select a TRX to configure
+...
+OsmoBTS(bts)# trx 0
+OsmoBTS(trx)# list
+...
+ user-gain <-100000-100000> (dB|mdB)
+ power-ramp max-initial <-10000-100000> (dBm|mdBm)
+ power-ramp step-size <1-100000> (dB|mdB)
+ power-ramp step-interval <1-100>
+ ms-power-control (dsp|osmo)
+ ta-control interval <0-31>
+ phy <0-255> instance <0-255>
+...
+OsmoBTS(trx)# ?
+...
+ user-gain Inform BTS about additional, user-provided gain or attenuation at TRX output
+ power-ramp Power-Ramp settings
+ ms-power-control Mobile Station Power Level Control
+ ta-control Timing Advance Control Parameters
+ phy Configure PHY Link+Instance for this TRX
+...
diff --git a/tests/power/bs_power_loop_test.c b/tests/power/bs_power_loop_test.c
index 53fdbba6..06fe3ed4 100644
--- a/tests/power/bs_power_loop_test.c
+++ b/tests/power/bs_power_loop_test.c
@@ -47,14 +47,8 @@
{ DL_MEAS_FULL(rxqual, rxlev), \
DL_MEAS_SUB(rxqual, rxlev) }
-#define DL_MEAS_FULL_SUB_INV(rxqual, rxlev) \
- { DL_MEAS_FULL(rxqual, rxlev), \
- DL_MEAS_SUB(rxqual, rxlev), \
- .invalid = true }
-
enum power_test_step_type {
PWR_TEST_ST_IND_MEAS = 0,
- PWR_TEST_ST_IND_DUMMY,
PWR_TEST_ST_SET_STATE,
PWR_TEST_ST_SET_CTRL_INTERVAL,
PWR_TEST_ST_SET_STEP_SIZE,
@@ -78,7 +72,6 @@ struct power_test_step {
uint8_t rxqual_sub;
uint8_t rxlev_full;
uint8_t rxlev_sub;
- bool invalid;
} meas;
/* Increase / reduce step size */
struct {
@@ -112,31 +105,32 @@ static void init_test(const char *name)
g_bts->band = GSM_BAND_900;
g_bts->c0 = g_trx;
+ /* Init defaultBS power control parameters, enable dynamic power control */
+ struct gsm_power_ctrl_params *params = &g_trx->ts[0].lchan[0].bs_dpc_params;
+ g_trx->ts[0].lchan[0].bs_power_ctrl.dpc_params = params;
+ *params = power_ctrl_params_def;
+
+ /* Disable loop SACCH block skip by default: */
+ params->ctrl_interval = 0;
+
printf("\nStarting test case '%s'\n", name);
}
-static void enc_meas_rep(struct gsm48_hdr *gh,
+static void enc_meas_rep(struct gsm48_meas_res *mr,
const unsigned int n,
const struct power_test_step *step)
{
- struct gsm48_meas_res *mr = (struct gsm48_meas_res *) gh->data;
-
- gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_MEAS_REP;
-
*mr = (struct gsm48_meas_res) {
.rxlev_full = step->meas.rxlev_full,
.rxlev_sub = step->meas.rxlev_sub,
.rxqual_full = step->meas.rxqual_full,
.rxqual_sub = step->meas.rxqual_sub,
- /* NOTE: inversed logic (1 means invalid) */
- .meas_valid = step->meas.invalid,
};
- printf("#%02u %s() -> Measurement Results (%svalid): "
+ printf("#%02u %s() -> Measurement Results (valid): "
"RXLEV-FULL(%02u), RXQUAL-FULL(%u), "
"RXLEV-SUB(%02u), RXQUAL-SUB(%u)\n",
- n, __func__, step->meas.invalid ? "in" : "",
+ n, __func__,
mr->rxlev_full, mr->rxqual_full,
mr->rxlev_sub, mr->rxqual_sub);
}
@@ -145,11 +139,8 @@ static int exec_power_step(struct gsm_lchan *lchan,
const unsigned int n,
const struct power_test_step *step)
{
- struct gsm48_hdr *gh;
+ struct gsm48_meas_res mr;
uint8_t old, new;
- uint8_t buf[18];
-
- gh = (struct gsm48_hdr *) buf;
switch (step->type) {
case PWR_TEST_ST_SET_STATE:
@@ -184,20 +175,16 @@ static int exec_power_step(struct gsm_lchan *lchan,
printf("#%02u %s() <- Enable DTXd\n", n, __func__);
lchan->tch.dtx.dl_active = true;
return 0; /* we're done */
- case PWR_TEST_ST_IND_DUMMY:
- printf("#%02u %s() <- Dummy block\n", n, __func__);
- memset(buf, 0x2b, sizeof(buf));
- break;
case PWR_TEST_ST_IND_MEAS:
- enc_meas_rep(gh, n, step);
+ enc_meas_rep(&mr, n, step);
break;
}
- printf("#%02u lchan_bs_pwr_ctrl() <- UL SACCH: %s\n",
- n, osmo_hexdump(buf, sizeof(buf)));
+ printf("#%02u lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 %s\n",
+ n, osmo_hexdump((void *)&mr, sizeof(mr)));
old = lchan->bs_power_ctrl.current;
- lchan_bs_pwr_ctrl(lchan, gh);
+ lchan_bs_pwr_ctrl(lchan, &mr);
new = lchan->bs_power_ctrl.current;
printf("#%02u lchan_bs_pwr_ctrl() -> BS power reduction: "
@@ -219,9 +206,6 @@ static void exec_power_test(const struct power_test_step *steps,
struct gsm_lchan *lchan = &g_trx->ts[0].lchan[0];
struct gsm_power_ctrl_params *params = &lchan->bs_dpc_params;
- /* Default BS power control parameters */
- memcpy(params, &power_ctrl_params_def, sizeof(*params));
-
/* No RxLev hysteresis: lower == upper */
params->rxlev_meas.lower_thresh = PWR_TEST_RXLEV_TARGET;
params->rxlev_meas.upper_thresh = PWR_TEST_RXLEV_TARGET;
@@ -401,24 +385,6 @@ static const struct power_test_step TC_rxqual_ber[] = {
{ .meas = DL_MEAS_FULL_SUB(7, PWR_TEST_RXLEV_TARGET) }, /* max */
};
-/* Verify that invalid and dummy SACCH blocks are ignored. */
-static const struct power_test_step TC_inval_dummy[] = {
- /* Initial state: 16 dB, up to 20 dB */
- { .type = PWR_TEST_ST_SET_STATE,
- .state = { .current = 16, .max = 2 * 10 } },
-
- /* MS sends invalid measurement results which must be ignored */
- { .meas = DL_MEAS_FULL_SUB_INV(7, 63), .exp_txred = 16 },
- { .meas = DL_MEAS_FULL_SUB_INV(0, 0), .exp_txred = 16 },
-
- /* Let's say SMS (SAPI=3) blocks substitute some of the reports */
- { .meas = DL_MEAS_FULL_SUB(0, PWR_TEST_RXLEV_TARGET), .exp_txred = 16 },
- { .type = PWR_TEST_ST_IND_DUMMY, /* not a report */ .exp_txred = 16 },
- { .meas = DL_MEAS_FULL_SUB(0, PWR_TEST_RXLEV_TARGET), .exp_txred = 16 },
- { .type = PWR_TEST_ST_IND_DUMMY, /* not a report */ .exp_txred = 16 },
- { .meas = DL_MEAS_FULL_SUB(0, PWR_TEST_RXLEV_TARGET), .exp_txred = 16 },
-};
-
/* Verify handling of optional power control interval (P_Con_INTERVAL). */
static const struct power_test_step TC_ctrl_interval[] = {
/* Initial state: 0 dB, up to 20 dB */
@@ -540,7 +506,6 @@ int main(int argc, char **argv)
exec_test(TC_dtxd_mode);
exec_test(TC_rxqual_ber);
- exec_test(TC_inval_dummy);
exec_test(TC_ctrl_interval);
exec_test(TC_rxlev_hyst);
diff --git a/tests/power/bs_power_loop_test.err b/tests/power/bs_power_loop_test.err
index c4726b1b..24107ba9 100644
--- a/tests/power/bs_power_loop_test.err
+++ b/tests/power/bs_power_loop_test.err
@@ -1,196 +1,191 @@
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 0 -> 2 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 0 dB => 2 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 2 -> 4 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 2 dB => 4 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 4 -> 6 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 4 dB => 6 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 6 -> 8 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 6 dB => 8 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 8 -> 10 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 8 dB => 10 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 10 -> 12 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 10 dB => 12 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 12 -> 14 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 12 dB => 14 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 14 -> 16 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 14 dB => 16 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 16 -> 18 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 16 dB => 18 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 18 -> 20 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 18 dB => 20 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 20 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 20 dB: max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 20 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 20 dB: max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 20 -> 16 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 20 dB => 16 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 16 -> 12 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 16 dB => 12 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 12 -> 8 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 12 dB => 8 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 8 -> 4 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 8 dB => 4 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 4 -> 0 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 4 dB => 0 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 0 -> 4 dB (maximum 20 dB, suggested delta 4 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 0 dB => 4 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 4 -> 8 dB (maximum 20 dB, suggested delta 4 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 4 dB => 8 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 8 -> 12 dB (maximum 20 dB, suggested delta 4 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 8 dB => 12 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 12 -> 16 dB (maximum 20 dB, suggested delta 4 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 12 dB => 16 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 16 -> 20 dB (maximum 20 dB, suggested delta 4 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 16 dB => 20 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 20 dB (maximum 20 dB, suggested delta 4 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 20 dB: max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 20 -> 14 dB (maximum 20 dB, suggested delta -6 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 20 dB => 14 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 14 -> 8 dB (maximum 20 dB, suggested delta -6 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 14 dB => 8 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 8 -> 2 dB (maximum 20 dB, suggested delta -6 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 8 dB => 2 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 2 -> 0 dB (maximum 20 dB, suggested delta -6 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 2 dB => 0 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta -6 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(31), RXQUAL-FULL(0), RXLEV-SUB(31), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 10 -> 11 dB (maximum 20 dB, suggested delta 1 dB, RxLev current 31 (-79 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 10 dB => 11 dB:max 20 dB, RSSI[curr -79, avg -79, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 11 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 11 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(32), RXQUAL-FULL(0), RXLEV-SUB(32), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 11 -> 13 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 32 (-78 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 11 dB => 13 dB:max 20 dB, RSSI[curr -78, avg -78, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 13 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 13 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(33), RXQUAL-FULL(0), RXLEV-SUB(33), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 13 -> 16 dB (maximum 20 dB, suggested delta 3 dB, RxLev current 33 (-77 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 13 dB => 16 dB:max 20 dB, RSSI[curr -77, avg -77, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 16 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(29), RXQUAL-FULL(0), RXLEV-SUB(29), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 10 -> 9 dB (maximum 20 dB, suggested delta -1 dB, RxLev current 29 (-81 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 10 dB => 9 dB:max 20 dB, RSSI[curr -81, avg -81, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 9 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 9 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(27), RXQUAL-FULL(0), RXLEV-SUB(27), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 9 -> 6 dB (maximum 20 dB, suggested delta -3 dB, RxLev current 27 (-83 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 9 dB => 6 dB:max 20 dB, RSSI[curr -83, avg -83, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 6 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 6 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(25), RXQUAL-FULL(0), RXLEV-SUB(25), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 6 -> 1 dB (maximum 20 dB, suggested delta -5 dB, RxLev current 25 (-85 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 6 dB => 1 dB:max 20 dB, RSSI[curr -85, avg -85, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 1 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 1 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(00), RXQUAL-FULL(7), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is enabled => using SUB
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(3), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is enabled => using SUB
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(63), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is enabled => using SUB
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 0 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 16 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 16 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(1), RXLEV-SUB(30), RXQUAL-SUB(1), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 16 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 1, avg 1, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(2), RXLEV-SUB(30), RXQUAL-SUB(2), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 16 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 2, avg 2, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(3), RXLEV-SUB(30), RXQUAL-SUB(3), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 16 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 3, avg 3, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(4), RXLEV-SUB(30), RXQUAL-SUB(4), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Reducing Downlink attenuation: 16 -> 12 dB due to RxQual 4 worse than L_RXQUAL_XX_P 3
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 16 dB => 12 dB:max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 4, avg 4, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(34), RXQUAL-FULL(5), RXLEV-SUB(34), RXQUAL-SUB(5), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Reducing Downlink attenuation: 12 -> 8 dB due to RxQual 5 worse than L_RXQUAL_XX_P 3
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 12 dB => 8 dB:max 20 dB, RSSI[curr -76, avg -76, thresh -80..-80] dBm, RxQual[curr 5, avg 5, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(38), RXQUAL-FULL(6), RXLEV-SUB(38), RXQUAL-SUB(6), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Reducing Downlink attenuation: 8 -> 4 dB due to RxQual 6 worse than L_RXQUAL_XX_P 3
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 8 dB => 4 dB:max 20 dB, RSSI[curr -72, avg -72, thresh -80..-80] dBm, RxQual[curr 6, avg 6, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(42), RXQUAL-FULL(7), RXLEV-SUB(42), RXQUAL-SUB(7), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Reducing Downlink attenuation: 4 -> 0 dB due to RxQual 7 worse than L_RXQUAL_XX_P 3
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 4 dB => 0 dB:max 20 dB, RSSI[curr -68, avg -68, thresh -80..-80] dBm, RxQual[curr 7, avg 7, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(46), RXQUAL-FULL(7), RXLEV-SUB(46), RXQUAL-SUB(7), DTx is disabled => using FULL
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -64, avg -64, thresh -80..-80] dBm, RxQual[curr 7, avg 7, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(46), RXQUAL-FULL(0), RXLEV-SUB(46), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 0 -> 2 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 46 (-64 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 0 dB => 2 dB:max 20 dB, RSSI[curr -64, avg -64, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(44), RXQUAL-FULL(0), RXLEV-SUB(44), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 2 -> 4 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 44 (-66 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 2 dB => 4 dB:max 20 dB, RSSI[curr -66, avg -66, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(42), RXQUAL-FULL(0), RXLEV-SUB(42), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 4 -> 6 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 42 (-68 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 4 dB => 6 dB:max 20 dB, RSSI[curr -68, avg -68, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(7), RXLEV-SUB(30), RXQUAL-SUB(7), DTx is disabled => using FULL
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 7, avg 7, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(7), RXLEV-SUB(30), RXQUAL-SUB(7), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) The measurement results are not valid
-(bts=0,trx=0,ts=0,ss=0) The measurement results are not valid
-(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
-(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
-(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 20 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 0 dB: max 20 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 7, avg 7, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 0 -> 2 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 0 dB => 2 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 2 -> 4 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 2 dB => 4 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 4 -> 6 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 4 dB => 6 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 6 -> 8 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 6 dB => 8 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 8 -> 4 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 8 dB => 4 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 4 -> 0 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 4 dB => 0 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 0 -> 2 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 0 dB => 2 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 2 -> 4 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 2 dB => 4 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 4 -> 6 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 4 dB => 6 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 6 -> 8 dB (maximum 20 dB, suggested delta 2 dB, RxLev current 60 (-50 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 6 dB => 8 dB:max 20 dB, RSSI[curr -50, avg -50, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(60), RXQUAL-FULL(0), RXLEV-SUB(60), RXQUAL-SUB(0), DTx is disabled => using FULL
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 8 -> 4 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 8 dB => 4 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(10), RXQUAL-FULL(0), RXLEV-SUB(10), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 4 -> 0 dB (maximum 20 dB, suggested delta -4 dB, RxLev current 10 (-100 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 4 dB => 0 dB:max 20 dB, RSSI[curr -100, avg -100, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(31), RXQUAL-FULL(0), RXLEV-SUB(31), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 12 -> 13 dB (maximum 16 dB, suggested delta 1 dB, RxLev current 31 (-79 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 12 dB => 13 dB:max 16 dB, RSSI[curr -79, avg -79, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(28), RXQUAL-FULL(0), RXLEV-SUB(28), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 13 -> 11 dB (maximum 16 dB, suggested delta -2 dB, RxLev current 28 (-82 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 13 dB => 11 dB:max 16 dB, RSSI[curr -82, avg -82, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(33), RXQUAL-FULL(0), RXLEV-SUB(33), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 11 -> 13 dB (maximum 16 dB, suggested delta 2 dB, RxLev current 33 (-77 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 11 dB => 13 dB:max 16 dB, RSSI[curr -77, avg -77, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(28), RXQUAL-FULL(0), RXLEV-SUB(28), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 13 -> 11 dB (maximum 16 dB, suggested delta -2 dB, RxLev current 28 (-82 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 13 dB => 11 dB:max 16 dB, RSSI[curr -82, avg -82, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(31), RXQUAL-FULL(0), RXLEV-SUB(31), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 11 dB (maximum 16 dB, suggested delta 0 dB, RxLev current 31 (-79 dBm), thresholds 27 .. 33)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 11 dB: max 16 dB, RSSI[curr -79, avg -79, thresh -83..-77] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(28), RXQUAL-FULL(0), RXLEV-SUB(28), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 11 dB (maximum 16 dB, suggested delta 0 dB, RxLev current 28 (-82 dBm), thresholds 27 .. 33)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 11 dB: max 16 dB, RSSI[curr -82, avg -82, thresh -83..-77] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(33), RXQUAL-FULL(0), RXLEV-SUB(33), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 11 dB (maximum 16 dB, suggested delta 0 dB, RxLev current 33 (-77 dBm), thresholds 27 .. 33)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 11 dB: max 16 dB, RSSI[curr -77, avg -77, thresh -83..-77] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(28), RXQUAL-FULL(0), RXLEV-SUB(28), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 11 dB (maximum 16 dB, suggested delta 0 dB, RxLev current 28 (-82 dBm), thresholds 27 .. 33)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 11 dB: max 16 dB, RSSI[curr -82, avg -82, thresh -83..-77] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 30 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 16 dB: max 30 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Keeping Downlink attenuation at 16 dB (maximum 30 dB, suggested delta 0 dB, RxLev current 30 (-80 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Keeping DL attenuation at 16 dB: max 30 dB, RSSI[curr -80, avg -80, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(26), RXQUAL-FULL(0), RXLEV-SUB(26), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 16 -> 14 dB (maximum 30 dB, suggested delta -2 dB, RxLev current 26 (-84 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 16 dB => 14 dB:max 30 dB, RSSI[curr -84, avg -82, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(26), RXQUAL-FULL(0), RXLEV-SUB(26), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 14 -> 11 dB (maximum 30 dB, suggested delta -3 dB, RxLev current 26 (-84 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Lowering DL attenuation 14 dB => 11 dB:max 30 dB, RSSI[curr -84, avg -83, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(35), RXQUAL-FULL(0), RXLEV-SUB(35), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 11 -> 12 dB (maximum 30 dB, suggested delta 1 dB, RxLev current 35 (-75 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 11 dB => 12 dB:max 30 dB, RSSI[curr -75, avg -79, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
(bts=0,trx=0,ts=0,ss=0) Rx DL Measurement Report: RXLEV-FULL(35), RXQUAL-FULL(0), RXLEV-SUB(35), RXQUAL-SUB(0), DTx is disabled => using FULL
-(bts=0,trx=0,ts=0,ss=0) Changing Downlink attenuation: 12 -> 14 dB (maximum 30 dB, suggested delta 2 dB, RxLev current 35 (-75 dBm), thresholds 30 .. 30)
+(bts=0,trx=0,ts=0,ss=0) Raising DL attenuation 12 dB => 14 dB:max 30 dB, RSSI[curr -75, avg -77, thresh -80..-80] dBm, RxQual[curr 0, avg 0, thresh 3..0]
diff --git a/tests/power/bs_power_loop_test.ok b/tests/power/bs_power_loop_test.ok
index 2c123928..18a94ba7 100644
--- a/tests/power/bs_power_loop_test.ok
+++ b/tests/power/bs_power_loop_test.ok
@@ -243,31 +243,6 @@ Starting test case 'TC_rxqual_ber'
#16 lchan_bs_pwr_ctrl() -> BS power reduction: 0 -> 0 (expected 0)
Test case verdict: SUCCESS
-Starting test case 'TC_inval_dummy'
-#00 exec_power_step() <- State (re)set (current 16 dB, max 20 dB)
-#01 enc_meas_rep() -> Measurement Results (invalid): RXLEV-FULL(63), RXQUAL-FULL(7), RXLEV-SUB(63), RXQUAL-SUB(7)
-#01 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 3f 7f 7e 00 00 00 00 00 00 00 00 00 00 00 00 00
-#01 lchan_bs_pwr_ctrl() -> BS power reduction: 16 -> 16 (expected 16)
-#02 enc_meas_rep() -> Measurement Results (invalid): RXLEV-FULL(00), RXQUAL-FULL(0), RXLEV-SUB(00), RXQUAL-SUB(0)
-#02 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-#02 lchan_bs_pwr_ctrl() -> BS power reduction: 16 -> 16 (expected 16)
-#03 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0)
-#03 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1e 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-#03 lchan_bs_pwr_ctrl() -> BS power reduction: 16 -> 16 (expected 16)
-#04 exec_power_step() <- Dummy block
-#04 lchan_bs_pwr_ctrl() <- UL SACCH: 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
-#04 lchan_bs_pwr_ctrl() -> BS power reduction: 16 -> 16 (expected 16)
-#05 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0)
-#05 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1e 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-#05 lchan_bs_pwr_ctrl() -> BS power reduction: 16 -> 16 (expected 16)
-#06 exec_power_step() <- Dummy block
-#06 lchan_bs_pwr_ctrl() <- UL SACCH: 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
-#06 lchan_bs_pwr_ctrl() -> BS power reduction: 16 -> 16 (expected 16)
-#07 enc_meas_rep() -> Measurement Results (valid): RXLEV-FULL(30), RXQUAL-FULL(0), RXLEV-SUB(30), RXQUAL-SUB(0)
-#07 lchan_bs_pwr_ctrl() <- UL SACCH: 06 15 1e 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-#07 lchan_bs_pwr_ctrl() -> BS power reduction: 16 -> 16 (expected 16)
-Test case verdict: SUCCESS
-
Starting test case 'TC_ctrl_interval'
#00 exec_power_step() <- State (re)set (current 0 dB, max 20 dB)
#01 exec_power_step() <- (Re)set power control interval: 0 -> 0
diff --git a/tests/power/ms_power_loop_test.c b/tests/power/ms_power_loop_test.c
index 0d863108..e54d75ff 100644
--- a/tests/power/ms_power_loop_test.c
+++ b/tests/power/ms_power_loop_test.c
@@ -59,6 +59,9 @@ static void init_test(const char *name)
g_trx->ts[0].lchan[0].ms_power_ctrl.dpc_params = params;
*params = power_ctrl_params_def;
+ /* Disable loop SACCH block skip by default: */
+ params->ctrl_interval = 0;
+
/* Disable RxLev pre-processing and hysteresis by default */
struct gsm_power_ctrl_meas_params *mp = &params->rxlev_meas;
mp->lower_thresh = mp->upper_thresh = PWR_TEST_RXLEV_TARGET;
@@ -67,13 +70,11 @@ static void init_test(const char *name)
printf("\nStarting test case '%s'\n", name);
}
-static inline void apply_power_test(struct gsm_lchan *lchan, int rxlev, int exp_ret, uint8_t exp_current)
+static void apply_power_test_ext(struct gsm_lchan *lchan, uint8_t ms_pwr, int rxlev, int lqual_cb, int exp_ret, uint8_t exp_current)
{
- uint8_t old;
int ret;
- old = lchan->ms_power_ctrl.current;
- ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, rxlev);
+ ret = lchan_ms_pwr_ctrl(lchan, ms_pwr, rxlev, lqual_cb);
/* Keep the measurement counter updated */
lchan->meas.res_nr++;
@@ -81,15 +82,25 @@ static inline void apply_power_test(struct gsm_lchan *lchan, int rxlev, int exp_
printf("lchan_ms_pwr_ctrl(RxLvl=%d dBm) returns %d (expected %d)\n",
rxlev, ret, exp_ret);
printf("\tMS current power %u -> %u (expected %u)\n",
- old, lchan->ms_power_ctrl.current, exp_current);
+ ms_pwr, lchan->ms_power_ctrl.current, exp_current);
+}
+
+static inline void apply_power_test(struct gsm_lchan *lchan, int rxlev, int lqual_cb, int exp_ret, uint8_t exp_current)
+{
+ apply_power_test_ext(lchan, lchan->ms_power_ctrl.current, rxlev, lqual_cb, exp_ret, exp_current);
}
static void test_power_loop(void)
{
struct gsm_lchan *lchan;
+ const struct gsm_power_ctrl_params *params;
+ int16_t good_lqual;
init_test(__func__);
lchan = &g_trx->ts[0].lchan[0];
+ params = lchan->ms_power_ctrl.dpc_params;
+ lchan->type = GSM_LCHAN_SDCCH;
+ good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
@@ -97,73 +108,78 @@ static void test_power_loop(void)
OSMO_ASSERT(lchan->ms_power_ctrl.max == 2);
/* Simply clamping */
- apply_power_test(lchan, -60, 0, 15);
+ apply_power_test(lchan, -60, good_lqual, 0, 15);
/*
* Now 15 dB too little and we should power it up. Could be a
* power level of 7 or 8 for 15 dBm. However, since we limit peace at
* which we change values, expect several steps of MS_RAISE_MAX_DB/2 levels:
*/
- apply_power_test(lchan, -90, 1, 13);
- apply_power_test(lchan, -90, 1, 11);
- apply_power_test(lchan, -90, 1, 9);
- apply_power_test(lchan, -90, 1, 7);
- apply_power_test(lchan, -90, 1, 5);
+ apply_power_test(lchan, -90, good_lqual, 1, 13);
+ apply_power_test(lchan, -90, good_lqual, 1, 11);
+ apply_power_test(lchan, -90, good_lqual, 1, 9);
+ apply_power_test(lchan, -90, good_lqual, 1, 7);
+ apply_power_test(lchan, -90, good_lqual, 1, 5);
/* Check good RSSI value keeps it at same power level: */
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 5);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, good_lqual, 0, 5);
- apply_power_test(lchan, -90, 1, 3);
- apply_power_test(lchan, -90, 1, 2); /* .max is pwr lvl 2 */
- apply_power_test(lchan, -90, 0, 2); /* .max is pwr lvl 2 */
+ apply_power_test(lchan, -90, good_lqual, 1, 3);
+ apply_power_test(lchan, -90, good_lqual, 1, 2); /* .max is pwr lvl 2 */
+ apply_power_test(lchan, -90, good_lqual, 0, 2); /* .max is pwr lvl 2 */
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 30);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 0);
- apply_power_test(lchan, -90, 1, 0); /* .max is pwr lvl 0 */
- apply_power_test(lchan, -90, 0, 0); /* .max is pwr lvl 0 */
+ apply_power_test(lchan, -90, good_lqual, 1, 0); /* .max is pwr lvl 0 */
+ apply_power_test(lchan, -90, good_lqual, 0, 0); /* .max is pwr lvl 0 */
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 36);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 29);
- apply_power_test(lchan, -90, 1, 30);
- apply_power_test(lchan, -90, 1, 29);
- apply_power_test(lchan, -90, 0, 29);
+ apply_power_test(lchan, -90, good_lqual, 1, 30);
+ apply_power_test(lchan, -90, good_lqual, 1, 29);
+ apply_power_test(lchan, -90, good_lqual, 0, 29);
/* Check good RSSI value keeps it at same power level: */
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 29);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, good_lqual, 0, 29);
/* Now go down, steps are double size in this direction: */
- apply_power_test(lchan, -45, 1, 1);
- apply_power_test(lchan, -45, 1, 5);
- apply_power_test(lchan, -45, 1, 9);
+ apply_power_test(lchan, -45, good_lqual, 1, 1);
+ apply_power_test(lchan, -45, good_lqual, 1, 5);
+ apply_power_test(lchan, -45, good_lqual, 1, 9);
/* Go down only one level down and up: */
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 2, 1, 10);
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 2, 1, 9);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 2, good_lqual, 1, 10);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 2, good_lqual, 1, 9);
/* Check if BSC requesting a low max power is applied after loop calculation: */
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 2);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 14);
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 2, 1, 14);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 2, good_lqual, 1, 14);
/* Set back a more normal max: */
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 30);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 0);
/* Disable dynamic power control and jump down */
lchan->ms_power_ctrl.dpc_params = NULL;
- apply_power_test(lchan, -60, 0, 14);
+ apply_power_test(lchan, -60, good_lqual, 0, 14);
/* Enable and leave it again */
lchan->ms_power_ctrl.dpc_params = &lchan->ms_dpc_params;
- apply_power_test(lchan, -40, 1, 15);
+ apply_power_test(lchan, -40, good_lqual, 1, 15);
}
static void test_pf_algo_ewma(void)
{
struct gsm_lchan *lchan;
+ const struct gsm_power_ctrl_params *params;
+ int16_t good_lqual;
const int *avg100;
init_test(__func__);
lchan = &g_trx->ts[0].lchan[0];
+ lchan->type = GSM_LCHAN_SDCCH;
+ params = lchan->ms_power_ctrl.dpc_params;
+ good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
avg100 = &lchan->ms_power_ctrl.rxlev_meas_proc.ewma.Avg100;
struct gsm_power_ctrl_meas_params *mp = &lchan->ms_dpc_params.rxlev_meas;
@@ -175,32 +191,30 @@ static void test_pf_algo_ewma(void)
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 26);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 2);
-#define CHECK_UL_RSSI_AVG100(exp) \
- printf("\tAvg[t] is %2.2f dBm (expected %2.2f dBm)\n", \
+#define CHECK_RXLEV_AVG100(exp) \
+ printf("\tAvg[t] is RxLev %2.2f (expected %2.2f)\n", \
((float) *avg100) / 100, exp);
/* UL RSSI remains constant => no UL power change */
- apply_power_test(lchan, -75, 0, 15);
- CHECK_UL_RSSI_AVG100(-75.00);
+ apply_power_test(lchan, -75, good_lqual, 0, 15);
+ CHECK_RXLEV_AVG100((float)dbm2rxlev(-75)); /* RXLEV 35 */
- /* Avg[t] = (0.2 * -90) + (0.8 * -75) = -78.0 dBm */
- apply_power_test(lchan, -90, 1, 13);
- CHECK_UL_RSSI_AVG100(-78.00);
+ /* Avg[t] = (0.2 * 20) + (0.8 * 35) = RXLEV 32, (-78 dBm) */
+ apply_power_test(lchan, -90, good_lqual, 1, 13); /* -90 dBm = RXLEV 20 */
+ CHECK_RXLEV_AVG100(32.00);
- /* Avg[t] = (0.2 * -90) + (0.8 * -78) = -80.4 dBm */
- apply_power_test(lchan, -90, 1, 11);
- CHECK_UL_RSSI_AVG100(-80.40);
+ /* Avg[t] = (0.2 * 20) + (0.8 * 32) = RXLEV 29.6 (-80.4 dBm) */
+ apply_power_test(lchan, -90, good_lqual, 1, 11); /* -90 dBm = RXLEV 20 */
+ CHECK_RXLEV_AVG100(29.60);
- /* Avg[t] = (0.2 * -70) + (0.8 * -80.4) = -78.32 dBm,
+ /* Avg[t] = (0.2 * 40) + (0.8 * 29.60) = RXLEV 31.68 (-78.32 dBm),
* but due to up-/down-scaling artefacts we get the following:
* Avg100[t] = Avg100[t - 1] + A * (Pwr - Avg[t] / 100)
- * Avg100[t] = -8040 + 20 * (-70 - (-8040 / 100))
- * Avg100[t] = -8040 + 20 * (-70 - (-8040 / 100))
- * Avg100[t] = -8040 + 20 * (-70 + 80)
- * Avg100[t] = -8040 + 200 = -7840
- * Avg[t] = -7840 / 100 = -78.4 */
- apply_power_test(lchan, -70, 1, 9);
- CHECK_UL_RSSI_AVG100(-78.40);
+ * Avg100[t] = 2960 + 20 * (40 - ((2960+50) / 100)) <- HERE we lose 0.1: (2960+50) / 100) = 30.1
+ * Avg100[t] = 2960 + 20 * (40 - 30) <- HERE we lose 20*0.1 = 2.0! (upscaled, hence we lose finally 2.0/100=0.2)
+ * Avg[t] = (3160) / 100 = 31.60*/
+ apply_power_test(lchan, -70, good_lqual, 1, 9); /* RXLEV 40 */
+ CHECK_RXLEV_AVG100(31.60);
mp->ewma.alpha = 70; /* 30% smoothing */
lchan->ms_power_ctrl.current = 15;
@@ -208,25 +222,30 @@ static void test_pf_algo_ewma(void)
(struct gsm_power_ctrl_meas_proc_state) { 0 };
/* This is the first sample, the filter outputs it as-is */
- apply_power_test(lchan, -50, 0, 15);
- CHECK_UL_RSSI_AVG100(-50.00);
+ apply_power_test(lchan, -50, good_lqual, 0, 15); /* RXLEV 60 */
+ CHECK_RXLEV_AVG100((float)dbm2rxlev(-50));
- /* Avg[t] = (0.7 * -50) + (0.3 * -50) = -50.0 dBm */
- apply_power_test(lchan, -50, 0, 15);
- CHECK_UL_RSSI_AVG100(-50.0);
+ /* Avg[t] = (0.7 * 60) + (0.3 * 60) = RXLEV 60 (-50.0 dBm) */
+ apply_power_test(lchan, -50, good_lqual, 0, 15);
+ CHECK_RXLEV_AVG100((float)dbm2rxlev(-50));
/* Simulate SACCH block loss (-110 dBm):
- * Avg[t] = (0.7 * -110) + (0.3 * -50) = -92.0 dBm */
- apply_power_test(lchan, -110, 1, 13);
- CHECK_UL_RSSI_AVG100(-92.0);
+ * Avg[t] = (0.7 * 0) + (0.3 * 60) = RXLEV 18.0 (-92.0 dBm) */
+ apply_power_test(lchan, -110, good_lqual, 1, 13); /* RXLEV 0 */
+ CHECK_RXLEV_AVG100(18.0);
}
static void test_power_hysteresis(void)
{
struct gsm_lchan *lchan;
+ const struct gsm_power_ctrl_params *params;
+ int16_t good_lqual;
init_test(__func__);
lchan = &g_trx->ts[0].lchan[0];
+ lchan->type = GSM_LCHAN_SDCCH;
+ params = lchan->ms_power_ctrl.dpc_params;
+ good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
/* Tolerate power deviations in range -80 .. -70 */
lchan->ms_dpc_params.rxlev_meas.lower_thresh = 30;
@@ -237,61 +256,66 @@ static void test_power_hysteresis(void)
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 26);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 2);
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 15);
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 3, 0, 15);
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 3, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, good_lqual, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 3, good_lqual, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 3, good_lqual, 0, 15);
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, 0, 15);
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 5, 0, 15);
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 5, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM, good_lqual, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM + 5, good_lqual, 0, 15);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 5, good_lqual, 0, 15);
- apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 10, 1, 13);
+ apply_power_test(lchan, PWR_TEST_RXLEV_TARGET_DBM - 10, good_lqual, 1, 13);
}
static void test_power_ctrl_interval(void)
{
struct gsm_lchan *lchan;
+ const struct gsm_power_ctrl_params *params;
+ int16_t good_lqual;
unsigned int i, j;
init_test(__func__);
lchan = &g_trx->ts[0].lchan[0];
+ lchan->type = GSM_LCHAN_SDCCH;
+ params = lchan->ms_power_ctrl.dpc_params;
+ good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 26);
OSMO_ASSERT(lchan->ms_power_ctrl.max == 2);
- static const int script[][8][3] = {
+ const int script[][8][4] = {
{ /* P_Con_INTERVAL=0 (480 ms) */
/* { UL RxLev, expected rc, expected Tx power level } */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 13 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 11 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 9 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 7 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 5 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 3 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 2 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 2 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 13 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 11 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 9 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 7 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 5 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 3 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 2 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 2 },
},
{ /* P_Con_INTERVAL=1 (960 ms) */
/* { UL RxLev, expected rc, expected Tx power level } */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 13 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 13 }, /* skipped */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 11 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 11 }, /* skipped */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 9 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 9 }, /* skipped */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 7 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 7 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 13 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 13 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 11 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 11 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 9 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 9 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 7 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 7 }, /* skipped */
},
{ /* P_Con_INTERVAL=2 (1920 ms) */
/* { UL RxLev, expected rc, expected Tx power level } */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 13 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 13 }, /* skipped */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 13 }, /* skipped */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 13 }, /* skipped */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 1, 11 },
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 11 }, /* skipped */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 11 }, /* skipped */
- { PWR_TEST_RXLEV_TARGET_DBM - 15, 0, 11 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 13 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 13 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 13 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 13 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 1, 11 },
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 11 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 11 }, /* skipped */
+ { PWR_TEST_RXLEV_TARGET_DBM - 15, good_lqual, 0, 11 }, /* skipped */
},
};
@@ -305,14 +329,82 @@ static void test_power_ctrl_interval(void)
for (j = 0; j < ARRAY_SIZE(script[i]); j++) {
apply_power_test(lchan, script[i][j][0], /* UL RxLev */
- script[i][j][1], /* expected rc */
- script[i][j][2]); /* expected Tx power level */
+ script[i][j][1], /* UL C/I */
+ script[i][j][2], /* expected rc */
+ script[i][j][3]); /* expected Tx power level */
}
printf("\n");
}
}
+static void test_power_loop_ci(void)
+{
+ struct gsm_lchan *lchan;
+ const struct gsm_power_ctrl_params *params;
+ int16_t good_lqual, too_low_lqual, too_high_lqual;
+
+ init_test(__func__);
+ lchan = &g_trx->ts[0].lchan[0];
+ params = lchan->ms_power_ctrl.dpc_params;
+ lchan->type = GSM_LCHAN_SDCCH;
+ good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
+ too_low_lqual = (params->ci_sdcch_meas.lower_thresh - 1) * 10;
+ too_high_lqual = (params->ci_sdcch_meas.upper_thresh + 1) * 10;
+
+ lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);
+ OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
+ lchan->ms_power_ctrl.max = ms_pwr_ctl_lvl(GSM_BAND_1800, 26);
+ OSMO_ASSERT(lchan->ms_power_ctrl.max == 2);
+
+ /* Simply clamping */
+ apply_power_test(lchan, -60, good_lqual, 0, 15);
+
+ /* Now UL C/I is too bad as well as RSSI: */
+ apply_power_test(lchan, -100, too_low_lqual, 1, 13);
+ apply_power_test(lchan, -100, too_low_lqual, 1, 11);
+
+ /* Now UL C/I is good again while RSSI is good: */
+ apply_power_test(lchan, -60, good_lqual, 1, 12);
+ apply_power_test(lchan, -60, too_high_lqual, 1, 13);
+
+ /* Now UL C/I is good while RSSI is bad, C/I mandates: */
+ apply_power_test(lchan, -100, good_lqual, 1, 11);
+ apply_power_test(lchan, -100, too_high_lqual, 1, 12);
+
+ /* Now UL C/I is bad again while RSSI is good, C/I mandates: */
+ apply_power_test(lchan, -60, good_lqual, 1, 13);
+ apply_power_test(lchan, -60, too_high_lqual, 1, 14);
+}
+
+/* Test whether ping pong between requested MS Power Level and announced MS
+ * Power level occurs, oscillating between considered good levels all the time:
+ * FIXME: Current code shows there's an issue with oscillating values. */
+static void test_good_threshold_convergence(void)
+{
+ struct gsm_lchan *lchan;
+ const struct gsm_power_ctrl_params *params;
+ int16_t good_lqual, good_rxlev;
+
+ init_test(__func__);
+ lchan = &g_trx->ts[0].lchan[0];
+ params = lchan->ms_power_ctrl.dpc_params;
+ lchan->ms_dpc_params.rxlev_meas.upper_thresh = 37;
+ lchan->ms_dpc_params.rxlev_meas.lower_thresh = 30;
+ lchan->type = GSM_LCHAN_SDCCH;
+ good_lqual = (params->ci_sdcch_meas.lower_thresh + 2) * 10;
+ good_rxlev = rxlev2dbm(params->rxlev_meas.lower_thresh + 2);
+
+ lchan->ms_power_ctrl.current = 10;
+ lchan->ms_power_ctrl.max = 2;
+
+ apply_power_test_ext(lchan, 9, good_rxlev, good_lqual, 0, 10);
+ apply_power_test_ext(lchan, 10, good_rxlev, good_lqual, 0, 10);
+ apply_power_test_ext(lchan, 9, good_rxlev, good_lqual, 0, 10);
+ apply_power_test_ext(lchan, 10, good_rxlev, good_lqual, 0, 10);
+ apply_power_test_ext(lchan, 9, good_rxlev, good_lqual, 0, 10);
+}
+
int main(int argc, char **argv)
{
printf("Testing power loop...\n");
@@ -332,6 +424,8 @@ int main(int argc, char **argv)
test_pf_algo_ewma();
test_power_hysteresis();
test_power_ctrl_interval();
+ test_power_loop_ci();
+ test_good_threshold_convergence();
printf("Power loop test OK\n");
diff --git a/tests/power/ms_power_loop_test.err b/tests/power/ms_power_loop_test.err
index ae8ad033..07d90691 100644
--- a/tests/power/ms_power_loop_test.err
+++ b/tests/power/ms_power_loop_test.err
@@ -1,51 +1,65 @@
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -60 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 9 (12 dBm) to 7, 16 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 7 (16 dBm) to 5, 20 dBm (rx-ms-pwr-lvl 7, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 5, 20 dBm (rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 5 (20 dBm) to 3, 24 dBm (rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 3 (24 dBm) to 2, 26 dBm (rx-ms-pwr-lvl 3, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 2, 26 dBm (rx-ms-pwr-lvl 2, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 2 (26 dBm) to 0, 30 dBm (rx-ms-pwr-lvl 2, max-ms-pwr-lvl 0, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 0, 30 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 0, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 0 (30 dBm) to 30, 34 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 29, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 30 (34 dBm) to 29, 36 dBm (rx-ms-pwr-lvl 30, max-ms-pwr-lvl 29, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 29, 36 dBm (rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 29, 36 dBm (rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 29 (36 dBm) to 30, 34 dBm (rx-ms-pwr-lvl 29, max-ms-pwr-lvl 29, rx-current -45 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 30 (34 dBm) to 31, 32 dBm (rx-ms-pwr-lvl 30, max-ms-pwr-lvl 29, rx-current -45 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 31 (32 dBm) to 0, 30 dBm (rx-ms-pwr-lvl 31, max-ms-pwr-lvl 29, rx-current -45 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 0 (30 dBm) to 1, 28 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 29, rx-current -73 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 1 (28 dBm) to 0, 30 dBm (rx-ms-pwr-lvl 1, max-ms-pwr-lvl 29, rx-current -77 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 0 (30 dBm) to 14, 2 dBm (rx-ms-pwr-lvl 0, max-ms-pwr-lvl 14, rx-current -73 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Lowering MS power from control level 14 (2 dBm) to 15, 0 dBm (rx-ms-pwr-lvl 14, max-ms-pwr-lvl 0, rx-current -40 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 3 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 11 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -70 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -50 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -50 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -110 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -72 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -78 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -75 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -70 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15, 0 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -80 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -85 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 9 (12 dBm) to 7, 16 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 7 (16 dBm) to 5, 20 dBm (rx-ms-pwr-lvl 7, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 5 (20 dBm) to 3, 24 dBm (rx-ms-pwr-lvl 5, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 3 (24 dBm) to 2, 26 dBm (rx-ms-pwr-lvl 3, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 2, 26 dBm (rx-ms-pwr-lvl 2, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 11 (8 dBm) to 9, 12 dBm (rx-ms-pwr-lvl 11, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 9 (12 dBm) to 7, 16 dBm (rx-ms-pwr-lvl 9, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 15 (0 dBm) to 13, 4 dBm (rx-ms-pwr-lvl 15, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
-(bts=0,trx=0,ts=0,ss=0) Raising MS power from control level 13 (4 dBm) to 11, 8 dBm (rx-ms-pwr-lvl 13, max-ms-pwr-lvl 2, rx-current -90 dBm, rx-target -75 dBm)
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -60, avg -60, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): ms-pwr-lvl[curr 13, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 11 (8 dBm) => 9 (12 dBm): ms-pwr-lvl[curr 11, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 9 (12 dBm) => 7 (16 dBm): ms-pwr-lvl[curr 9, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 7 (16 dBm) => 5 (20 dBm): ms-pwr-lvl[curr 7, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 5 (20 dBm): ms-pwr-lvl[curr 5, max 2], RSSI[curr -75, avg -75, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 5 (20 dBm) => 3 (24 dBm): ms-pwr-lvl[curr 5, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 3 (24 dBm) => 2 (26 dBm): ms-pwr-lvl[curr 3, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 2 (26 dBm): ms-pwr-lvl[curr 2, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 2 (26 dBm) => 0 (30 dBm): ms-pwr-lvl[curr 2, max 0], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 0 (30 dBm): ms-pwr-lvl[curr 0, max 0], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 0 (30 dBm) => 30 (34 dBm): ms-pwr-lvl[curr 0, max 29], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 30 (34 dBm) => 29 (36 dBm): ms-pwr-lvl[curr 30, max 29], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 29 (36 dBm): ms-pwr-lvl[curr 29, max 29], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 29 (36 dBm): ms-pwr-lvl[curr 29, max 29], RSSI[curr -75, avg -75, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 29 (36 dBm) => 30 (34 dBm): ms-pwr-lvl[curr 29, max 29], RSSI[curr -45, avg -47, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 30 (34 dBm) => 31 (32 dBm): ms-pwr-lvl[curr 30, max 29], RSSI[curr -45, avg -47, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 31 (32 dBm) => 0 (30 dBm): ms-pwr-lvl[curr 31, max 29], RSSI[curr -45, avg -47, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 0 (30 dBm) => 1 (28 dBm): ms-pwr-lvl[curr 0, max 29], RSSI[curr -73, avg -73, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 1 (28 dBm) => 0 (30 dBm): ms-pwr-lvl[curr 1, max 29], RSSI[curr -77, avg -77, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 0 (30 dBm) => 14 (2 dBm): ms-pwr-lvl[curr 0, max 14], RSSI[curr -73, avg -73, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 14 (2 dBm) => 15 (0 dBm): ms-pwr-lvl[curr 14, max 0], RSSI[curr -40, avg -47, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -75, avg -75, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (3 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -90, avg -78, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): ms-pwr-lvl[curr 13, max 2], RSSI[curr -90, avg -80, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 11 (8 dBm) => 9 (11 dBm): ms-pwr-lvl[curr 11, max 2], RSSI[curr -70, avg -78, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -50, avg -50, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -50, avg -50, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -110, avg -92, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -75, avg -75, thresh -80..-70] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -72, avg -72, thresh -80..-70] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -78, avg -78, thresh -80..-70] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -75, avg -75, thresh -80..-70] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -70, avg -70, thresh -80..-70] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -80, avg -80, thresh -80..-70] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -85, avg -85, thresh -80..-70] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): ms-pwr-lvl[curr 13, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 11 (8 dBm) => 9 (12 dBm): ms-pwr-lvl[curr 11, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 9 (12 dBm) => 7 (16 dBm): ms-pwr-lvl[curr 9, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 7 (16 dBm) => 5 (20 dBm): ms-pwr-lvl[curr 7, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 5 (20 dBm) => 3 (24 dBm): ms-pwr-lvl[curr 5, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 3 (24 dBm) => 2 (26 dBm): ms-pwr-lvl[curr 3, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 2 (26 dBm): ms-pwr-lvl[curr 2, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): ms-pwr-lvl[curr 13, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 11 (8 dBm) => 9 (12 dBm): ms-pwr-lvl[curr 11, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 9 (12 dBm) => 7 (16 dBm): ms-pwr-lvl[curr 9, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): ms-pwr-lvl[curr 13, max 2], RSSI[curr -90, avg -90, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 15 (0 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -60, avg -60, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 15 (0 dBm) => 13 (4 dBm): ms-pwr-lvl[curr 15, max 2], RSSI[curr -100, avg -100, thresh -75..-75] dBm, C/I[curr 11, avg 11, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): ms-pwr-lvl[curr 13, max 2], RSSI[curr -100, avg -100, thresh -75..-75] dBm, C/I[curr 11, avg 11, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 11 (8 dBm) => 12 (6 dBm): ms-pwr-lvl[curr 11, max 2], RSSI[curr -60, avg -60, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 12 (6 dBm) => 13 (4 dBm): ms-pwr-lvl[curr 12, max 2], RSSI[curr -60, avg -60, thresh -75..-75] dBm, C/I[curr 17, avg 17, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Raising MS power control level 13 (4 dBm) => 11 (8 dBm): ms-pwr-lvl[curr 13, max 2], RSSI[curr -100, avg -100, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 11 (8 dBm) => 12 (6 dBm): ms-pwr-lvl[curr 11, max 2], RSSI[curr -100, avg -100, thresh -75..-75] dBm, C/I[curr 17, avg 17, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 12 (6 dBm) => 13 (4 dBm): ms-pwr-lvl[curr 12, max 2], RSSI[curr -60, avg -60, thresh -75..-75] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Lowering MS power control level 13 (4 dBm) => 14 (2 dBm): ms-pwr-lvl[curr 13, max 2], RSSI[curr -60, avg -60, thresh -75..-75] dBm, C/I[curr 17, avg 17, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 9 (12 dBm): ms-pwr-lvl[curr 9, max 2], RSSI[curr -78, avg -78, thresh -80..-73] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 10 (10 dBm): ms-pwr-lvl[curr 10, max 2], RSSI[curr -78, avg -78, thresh -80..-73] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 9 (12 dBm): ms-pwr-lvl[curr 9, max 2], RSSI[curr -78, avg -78, thresh -80..-73] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 10 (10 dBm): ms-pwr-lvl[curr 10, max 2], RSSI[curr -78, avg -78, thresh -80..-73] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
+(bts=0,trx=0,ts=0,ss=0) Keeping MS power at control level 9 (12 dBm): ms-pwr-lvl[curr 9, max 2], RSSI[curr -78, avg -78, thresh -80..-73] dBm, C/I[curr 14, avg 14, thresh 12..16] dB
diff --git a/tests/power/ms_power_loop_test.ok b/tests/power/ms_power_loop_test.ok
index 5fea4747..09e19ad4 100644
--- a/tests/power/ms_power_loop_test.ok
+++ b/tests/power/ms_power_loop_test.ok
@@ -53,25 +53,25 @@ lchan_ms_pwr_ctrl(RxLvl=-40 dBm) returns 1 (expected 1)
Starting test case 'test_pf_algo_ewma'
lchan_ms_pwr_ctrl(RxLvl=-75 dBm) returns 0 (expected 0)
MS current power 15 -> 15 (expected 15)
- Avg[t] is -75.00 dBm (expected -75.00 dBm)
+ Avg[t] is RxLev 35.00 (expected 35.00)
lchan_ms_pwr_ctrl(RxLvl=-90 dBm) returns 1 (expected 1)
MS current power 15 -> 13 (expected 13)
- Avg[t] is -78.00 dBm (expected -78.00 dBm)
+ Avg[t] is RxLev 32.00 (expected 32.00)
lchan_ms_pwr_ctrl(RxLvl=-90 dBm) returns 1 (expected 1)
MS current power 13 -> 11 (expected 11)
- Avg[t] is -80.40 dBm (expected -80.40 dBm)
+ Avg[t] is RxLev 29.60 (expected 29.60)
lchan_ms_pwr_ctrl(RxLvl=-70 dBm) returns 1 (expected 1)
MS current power 11 -> 9 (expected 9)
- Avg[t] is -78.40 dBm (expected -78.40 dBm)
+ Avg[t] is RxLev 31.60 (expected 31.60)
lchan_ms_pwr_ctrl(RxLvl=-50 dBm) returns 0 (expected 0)
MS current power 15 -> 15 (expected 15)
- Avg[t] is -50.00 dBm (expected -50.00 dBm)
+ Avg[t] is RxLev 60.00 (expected 60.00)
lchan_ms_pwr_ctrl(RxLvl=-50 dBm) returns 0 (expected 0)
MS current power 15 -> 15 (expected 15)
- Avg[t] is -50.00 dBm (expected -50.00 dBm)
+ Avg[t] is RxLev 60.00 (expected 60.00)
lchan_ms_pwr_ctrl(RxLvl=-110 dBm) returns 1 (expected 1)
MS current power 15 -> 13 (expected 13)
- Avg[t] is -92.00 dBm (expected -92.00 dBm)
+ Avg[t] is RxLev 18.00 (expected 18.00)
Starting test case 'test_power_hysteresis'
lchan_ms_pwr_ctrl(RxLvl=-75 dBm) returns 0 (expected 0)
@@ -144,4 +144,36 @@ lchan_ms_pwr_ctrl(RxLvl=-90 dBm) returns 0 (expected 0)
lchan_ms_pwr_ctrl(RxLvl=-90 dBm) returns 0 (expected 0)
MS current power 11 -> 11 (expected 11)
+
+Starting test case 'test_power_loop_ci'
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 0 (expected 0)
+ MS current power 15 -> 15 (expected 15)
+lchan_ms_pwr_ctrl(RxLvl=-100 dBm) returns 1 (expected 1)
+ MS current power 15 -> 13 (expected 13)
+lchan_ms_pwr_ctrl(RxLvl=-100 dBm) returns 1 (expected 1)
+ MS current power 13 -> 11 (expected 11)
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 1 (expected 1)
+ MS current power 11 -> 12 (expected 12)
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 1 (expected 1)
+ MS current power 12 -> 13 (expected 13)
+lchan_ms_pwr_ctrl(RxLvl=-100 dBm) returns 1 (expected 1)
+ MS current power 13 -> 11 (expected 11)
+lchan_ms_pwr_ctrl(RxLvl=-100 dBm) returns 1 (expected 1)
+ MS current power 11 -> 12 (expected 12)
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 1 (expected 1)
+ MS current power 12 -> 13 (expected 13)
+lchan_ms_pwr_ctrl(RxLvl=-60 dBm) returns 1 (expected 1)
+ MS current power 13 -> 14 (expected 14)
+
+Starting test case 'test_good_threshold_convergence'
+lchan_ms_pwr_ctrl(RxLvl=-78 dBm) returns 0 (expected 0)
+ MS current power 9 -> 10 (expected 10)
+lchan_ms_pwr_ctrl(RxLvl=-78 dBm) returns 0 (expected 0)
+ MS current power 10 -> 10 (expected 10)
+lchan_ms_pwr_ctrl(RxLvl=-78 dBm) returns 0 (expected 0)
+ MS current power 9 -> 10 (expected 10)
+lchan_ms_pwr_ctrl(RxLvl=-78 dBm) returns 0 (expected 0)
+ MS current power 10 -> 10 (expected 10)
+lchan_ms_pwr_ctrl(RxLvl=-78 dBm) returns 0 (expected 0)
+ MS current power 9 -> 10 (expected 10)
Power loop test OK
diff --git a/tests/ta_control/ta_control_test.c b/tests/ta_control/ta_control_test.c
index 2e981b38..253491a8 100644
--- a/tests/ta_control/ta_control_test.c
+++ b/tests/ta_control/ta_control_test.c
@@ -26,36 +26,35 @@
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/ta_control.h>
+#include <osmo-bts/bts_trx.h>
void lchan_ms_ta_ctrl_test(int16_t toa256_start, unsigned int steps)
{
- struct gsm_lchan lchan = { };
+ struct gsm_bts_trx trx = { };
+ struct gsm_bts_trx_ts ts = { .trx = &trx };
+ struct gsm_lchan lchan = { .ts = &ts };
unsigned int i;
uint8_t rqd_ta_after;
uint8_t rqd_ta_before;
int16_t toa256 = toa256_start;
- /* Arbitrary value, high enough so that a computation can happen. */
- lchan.meas.num_ul_meas = 10;
-
printf("toa256_start = %u / 256 = %u, steps = %u\n", toa256_start,
toa256_start / 256, steps);
for (i = 0; i < steps; i++) {
printf("Step #%u\n", i);
- printf(" lchan.rqd_ta (before) = %u\n", lchan.rqd_ta);
+ printf(" lchan.ta_ctrl.current (before) = %u\n", lchan.ta_ctrl.current);
printf(" toa256 (before) = %u / 256 = %u\n", toa256,
toa256 / 256);
- rqd_ta_before = lchan.rqd_ta;
+ rqd_ta_before = lchan.ta_ctrl.current;
- lchan.meas.ms_toa256 = toa256;
- lchan_ms_ta_ctrl(&lchan);
+ lchan_ms_ta_ctrl(&lchan, rqd_ta_before, toa256);
- rqd_ta_after = lchan.rqd_ta;
+ rqd_ta_after = lchan.ta_ctrl.current;
toa256 -= (rqd_ta_after - rqd_ta_before) * 256;
- printf(" lchan.rqd_ta (after) = %u\n", lchan.rqd_ta);
+ printf(" lchan.ta_ctrl.current (after) = %u\n", lchan.ta_ctrl.current);
printf(" toa256 (after) = %u / 256 = %u\n", toa256,
toa256 / 256);
}
diff --git a/tests/ta_control/ta_control_test.ok b/tests/ta_control/ta_control_test.ok
index 8ebe5d54..a1586759 100644
--- a/tests/ta_control/ta_control_test.ok
+++ b/tests/ta_control/ta_control_test.ok
@@ -1,609 +1,609 @@
toa256_start = 4096 / 256 = 16, steps = 20
Step #0
- lchan.rqd_ta (before) = 0
+ lchan.ta_ctrl.current (before) = 0
toa256 (before) = 4096 / 256 = 16
- lchan.rqd_ta (after) = 1
- toa256 (after) = 3840 / 256 = 15
-Step #1
- lchan.rqd_ta (before) = 1
- toa256 (before) = 3840 / 256 = 15
- lchan.rqd_ta (after) = 2
+ lchan.ta_ctrl.current (after) = 2
toa256 (after) = 3584 / 256 = 14
-Step #2
- lchan.rqd_ta (before) = 2
+Step #1
+ lchan.ta_ctrl.current (before) = 2
toa256 (before) = 3584 / 256 = 14
- lchan.rqd_ta (after) = 3
- toa256 (after) = 3328 / 256 = 13
-Step #3
- lchan.rqd_ta (before) = 3
- toa256 (before) = 3328 / 256 = 13
- lchan.rqd_ta (after) = 4
+ lchan.ta_ctrl.current (after) = 4
toa256 (after) = 3072 / 256 = 12
-Step #4
- lchan.rqd_ta (before) = 4
+Step #2
+ lchan.ta_ctrl.current (before) = 4
toa256 (before) = 3072 / 256 = 12
- lchan.rqd_ta (after) = 5
- toa256 (after) = 2816 / 256 = 11
-Step #5
- lchan.rqd_ta (before) = 5
- toa256 (before) = 2816 / 256 = 11
- lchan.rqd_ta (after) = 6
+ lchan.ta_ctrl.current (after) = 6
toa256 (after) = 2560 / 256 = 10
-Step #6
- lchan.rqd_ta (before) = 6
+Step #3
+ lchan.ta_ctrl.current (before) = 6
toa256 (before) = 2560 / 256 = 10
- lchan.rqd_ta (after) = 7
- toa256 (after) = 2304 / 256 = 9
-Step #7
- lchan.rqd_ta (before) = 7
- toa256 (before) = 2304 / 256 = 9
- lchan.rqd_ta (after) = 8
+ lchan.ta_ctrl.current (after) = 8
toa256 (after) = 2048 / 256 = 8
-Step #8
- lchan.rqd_ta (before) = 8
+Step #4
+ lchan.ta_ctrl.current (before) = 8
toa256 (before) = 2048 / 256 = 8
- lchan.rqd_ta (after) = 9
- toa256 (after) = 1792 / 256 = 7
-Step #9
- lchan.rqd_ta (before) = 9
- toa256 (before) = 1792 / 256 = 7
- lchan.rqd_ta (after) = 10
+ lchan.ta_ctrl.current (after) = 10
toa256 (after) = 1536 / 256 = 6
-Step #10
- lchan.rqd_ta (before) = 10
+Step #5
+ lchan.ta_ctrl.current (before) = 10
toa256 (before) = 1536 / 256 = 6
- lchan.rqd_ta (after) = 11
- toa256 (after) = 1280 / 256 = 5
-Step #11
- lchan.rqd_ta (before) = 11
- toa256 (before) = 1280 / 256 = 5
- lchan.rqd_ta (after) = 12
+ lchan.ta_ctrl.current (after) = 12
toa256 (after) = 1024 / 256 = 4
-Step #12
- lchan.rqd_ta (before) = 12
+Step #6
+ lchan.ta_ctrl.current (before) = 12
toa256 (before) = 1024 / 256 = 4
- lchan.rqd_ta (after) = 13
- toa256 (after) = 768 / 256 = 3
-Step #13
- lchan.rqd_ta (before) = 13
- toa256 (before) = 768 / 256 = 3
- lchan.rqd_ta (after) = 14
+ lchan.ta_ctrl.current (after) = 14
toa256 (after) = 512 / 256 = 2
-Step #14
- lchan.rqd_ta (before) = 14
+Step #7
+ lchan.ta_ctrl.current (before) = 14
toa256 (before) = 512 / 256 = 2
- lchan.rqd_ta (after) = 15
- toa256 (after) = 256 / 256 = 1
+ lchan.ta_ctrl.current (after) = 16
+ toa256 (after) = 0 / 256 = 0
+Step #8
+ lchan.ta_ctrl.current (before) = 16
+ toa256 (before) = 0 / 256 = 0
+ lchan.ta_ctrl.current (after) = 16
+ toa256 (after) = 0 / 256 = 0
+Step #9
+ lchan.ta_ctrl.current (before) = 16
+ toa256 (before) = 0 / 256 = 0
+ lchan.ta_ctrl.current (after) = 16
+ toa256 (after) = 0 / 256 = 0
+Step #10
+ lchan.ta_ctrl.current (before) = 16
+ toa256 (before) = 0 / 256 = 0
+ lchan.ta_ctrl.current (after) = 16
+ toa256 (after) = 0 / 256 = 0
+Step #11
+ lchan.ta_ctrl.current (before) = 16
+ toa256 (before) = 0 / 256 = 0
+ lchan.ta_ctrl.current (after) = 16
+ toa256 (after) = 0 / 256 = 0
+Step #12
+ lchan.ta_ctrl.current (before) = 16
+ toa256 (before) = 0 / 256 = 0
+ lchan.ta_ctrl.current (after) = 16
+ toa256 (after) = 0 / 256 = 0
+Step #13
+ lchan.ta_ctrl.current (before) = 16
+ toa256 (before) = 0 / 256 = 0
+ lchan.ta_ctrl.current (after) = 16
+ toa256 (after) = 0 / 256 = 0
+Step #14
+ lchan.ta_ctrl.current (before) = 16
+ toa256 (before) = 0 / 256 = 0
+ lchan.ta_ctrl.current (after) = 16
+ toa256 (after) = 0 / 256 = 0
Step #15
- lchan.rqd_ta (before) = 15
- toa256 (before) = 256 / 256 = 1
- lchan.rqd_ta (after) = 16
+ lchan.ta_ctrl.current (before) = 16
+ toa256 (before) = 0 / 256 = 0
+ lchan.ta_ctrl.current (after) = 16
toa256 (after) = 0 / 256 = 0
Step #16
- lchan.rqd_ta (before) = 16
+ lchan.ta_ctrl.current (before) = 16
toa256 (before) = 0 / 256 = 0
- lchan.rqd_ta (after) = 16
+ lchan.ta_ctrl.current (after) = 16
toa256 (after) = 0 / 256 = 0
Step #17
- lchan.rqd_ta (before) = 16
+ lchan.ta_ctrl.current (before) = 16
toa256 (before) = 0 / 256 = 0
- lchan.rqd_ta (after) = 16
+ lchan.ta_ctrl.current (after) = 16
toa256 (after) = 0 / 256 = 0
Step #18
- lchan.rqd_ta (before) = 16
+ lchan.ta_ctrl.current (before) = 16
toa256 (before) = 0 / 256 = 0
- lchan.rqd_ta (after) = 16
+ lchan.ta_ctrl.current (after) = 16
toa256 (after) = 0 / 256 = 0
Step #19
- lchan.rqd_ta (before) = 16
+ lchan.ta_ctrl.current (before) = 16
toa256 (before) = 0 / 256 = 0
- lchan.rqd_ta (after) = 16
+ lchan.ta_ctrl.current (after) = 16
toa256 (after) = 0 / 256 = 0
Done.
toa256_start = 4000 / 256 = 15, steps = 50
Step #0
- lchan.rqd_ta (before) = 0
+ lchan.ta_ctrl.current (before) = 0
toa256 (before) = 4000 / 256 = 15
- lchan.rqd_ta (after) = 1
- toa256 (after) = 3744 / 256 = 14
-Step #1
- lchan.rqd_ta (before) = 1
- toa256 (before) = 3744 / 256 = 14
- lchan.rqd_ta (after) = 2
+ lchan.ta_ctrl.current (after) = 2
toa256 (after) = 3488 / 256 = 13
-Step #2
- lchan.rqd_ta (before) = 2
+Step #1
+ lchan.ta_ctrl.current (before) = 2
toa256 (before) = 3488 / 256 = 13
- lchan.rqd_ta (after) = 3
- toa256 (after) = 3232 / 256 = 12
-Step #3
- lchan.rqd_ta (before) = 3
- toa256 (before) = 3232 / 256 = 12
- lchan.rqd_ta (after) = 4
+ lchan.ta_ctrl.current (after) = 4
toa256 (after) = 2976 / 256 = 11
-Step #4
- lchan.rqd_ta (before) = 4
+Step #2
+ lchan.ta_ctrl.current (before) = 4
toa256 (before) = 2976 / 256 = 11
- lchan.rqd_ta (after) = 5
- toa256 (after) = 2720 / 256 = 10
-Step #5
- lchan.rqd_ta (before) = 5
- toa256 (before) = 2720 / 256 = 10
- lchan.rqd_ta (after) = 6
+ lchan.ta_ctrl.current (after) = 6
toa256 (after) = 2464 / 256 = 9
-Step #6
- lchan.rqd_ta (before) = 6
+Step #3
+ lchan.ta_ctrl.current (before) = 6
toa256 (before) = 2464 / 256 = 9
- lchan.rqd_ta (after) = 7
- toa256 (after) = 2208 / 256 = 8
-Step #7
- lchan.rqd_ta (before) = 7
- toa256 (before) = 2208 / 256 = 8
- lchan.rqd_ta (after) = 8
+ lchan.ta_ctrl.current (after) = 8
toa256 (after) = 1952 / 256 = 7
-Step #8
- lchan.rqd_ta (before) = 8
+Step #4
+ lchan.ta_ctrl.current (before) = 8
toa256 (before) = 1952 / 256 = 7
- lchan.rqd_ta (after) = 9
- toa256 (after) = 1696 / 256 = 6
-Step #9
- lchan.rqd_ta (before) = 9
- toa256 (before) = 1696 / 256 = 6
- lchan.rqd_ta (after) = 10
+ lchan.ta_ctrl.current (after) = 10
toa256 (after) = 1440 / 256 = 5
-Step #10
- lchan.rqd_ta (before) = 10
+Step #5
+ lchan.ta_ctrl.current (before) = 10
toa256 (before) = 1440 / 256 = 5
- lchan.rqd_ta (after) = 11
- toa256 (after) = 1184 / 256 = 4
-Step #11
- lchan.rqd_ta (before) = 11
- toa256 (before) = 1184 / 256 = 4
- lchan.rqd_ta (after) = 12
+ lchan.ta_ctrl.current (after) = 12
toa256 (after) = 928 / 256 = 3
-Step #12
- lchan.rqd_ta (before) = 12
+Step #6
+ lchan.ta_ctrl.current (before) = 12
toa256 (before) = 928 / 256 = 3
- lchan.rqd_ta (after) = 13
- toa256 (after) = 672 / 256 = 2
-Step #13
- lchan.rqd_ta (before) = 13
- toa256 (before) = 672 / 256 = 2
- lchan.rqd_ta (after) = 14
+ lchan.ta_ctrl.current (after) = 14
toa256 (after) = 416 / 256 = 1
-Step #14
- lchan.rqd_ta (before) = 14
+Step #7
+ lchan.ta_ctrl.current (before) = 14
toa256 (before) = 416 / 256 = 1
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
+ toa256 (after) = 160 / 256 = 0
+Step #8
+ lchan.ta_ctrl.current (before) = 15
+ toa256 (before) = 160 / 256 = 0
+ lchan.ta_ctrl.current (after) = 15
+ toa256 (after) = 160 / 256 = 0
+Step #9
+ lchan.ta_ctrl.current (before) = 15
+ toa256 (before) = 160 / 256 = 0
+ lchan.ta_ctrl.current (after) = 15
+ toa256 (after) = 160 / 256 = 0
+Step #10
+ lchan.ta_ctrl.current (before) = 15
+ toa256 (before) = 160 / 256 = 0
+ lchan.ta_ctrl.current (after) = 15
+ toa256 (after) = 160 / 256 = 0
+Step #11
+ lchan.ta_ctrl.current (before) = 15
+ toa256 (before) = 160 / 256 = 0
+ lchan.ta_ctrl.current (after) = 15
+ toa256 (after) = 160 / 256 = 0
+Step #12
+ lchan.ta_ctrl.current (before) = 15
+ toa256 (before) = 160 / 256 = 0
+ lchan.ta_ctrl.current (after) = 15
+ toa256 (after) = 160 / 256 = 0
+Step #13
+ lchan.ta_ctrl.current (before) = 15
+ toa256 (before) = 160 / 256 = 0
+ lchan.ta_ctrl.current (after) = 15
+ toa256 (after) = 160 / 256 = 0
+Step #14
+ lchan.ta_ctrl.current (before) = 15
+ toa256 (before) = 160 / 256 = 0
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #15
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #16
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #17
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #18
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #19
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #20
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #21
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #22
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #23
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #24
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #25
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #26
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #27
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #28
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #29
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #30
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #31
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #32
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #33
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #34
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #35
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #36
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #37
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #38
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #39
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #40
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #41
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #42
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #43
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #44
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #45
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #46
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #47
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #48
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Step #49
- lchan.rqd_ta (before) = 15
+ lchan.ta_ctrl.current (before) = 15
toa256 (before) = 160 / 256 = 0
- lchan.rqd_ta (after) = 15
+ lchan.ta_ctrl.current (after) = 15
toa256 (after) = 160 / 256 = 0
Done.
toa256_start = 12345 / 256 = 48, steps = 50
Step #0
- lchan.rqd_ta (before) = 0
+ lchan.ta_ctrl.current (before) = 0
toa256 (before) = 12345 / 256 = 48
- lchan.rqd_ta (after) = 1
- toa256 (after) = 12089 / 256 = 47
-Step #1
- lchan.rqd_ta (before) = 1
- toa256 (before) = 12089 / 256 = 47
- lchan.rqd_ta (after) = 2
+ lchan.ta_ctrl.current (after) = 2
toa256 (after) = 11833 / 256 = 46
-Step #2
- lchan.rqd_ta (before) = 2
+Step #1
+ lchan.ta_ctrl.current (before) = 2
toa256 (before) = 11833 / 256 = 46
- lchan.rqd_ta (after) = 3
- toa256 (after) = 11577 / 256 = 45
-Step #3
- lchan.rqd_ta (before) = 3
- toa256 (before) = 11577 / 256 = 45
- lchan.rqd_ta (after) = 4
+ lchan.ta_ctrl.current (after) = 4
toa256 (after) = 11321 / 256 = 44
-Step #4
- lchan.rqd_ta (before) = 4
+Step #2
+ lchan.ta_ctrl.current (before) = 4
toa256 (before) = 11321 / 256 = 44
- lchan.rqd_ta (after) = 5
- toa256 (after) = 11065 / 256 = 43
-Step #5
- lchan.rqd_ta (before) = 5
- toa256 (before) = 11065 / 256 = 43
- lchan.rqd_ta (after) = 6
+ lchan.ta_ctrl.current (after) = 6
toa256 (after) = 10809 / 256 = 42
-Step #6
- lchan.rqd_ta (before) = 6
+Step #3
+ lchan.ta_ctrl.current (before) = 6
toa256 (before) = 10809 / 256 = 42
- lchan.rqd_ta (after) = 7
- toa256 (after) = 10553 / 256 = 41
-Step #7
- lchan.rqd_ta (before) = 7
- toa256 (before) = 10553 / 256 = 41
- lchan.rqd_ta (after) = 8
+ lchan.ta_ctrl.current (after) = 8
toa256 (after) = 10297 / 256 = 40
-Step #8
- lchan.rqd_ta (before) = 8
+Step #4
+ lchan.ta_ctrl.current (before) = 8
toa256 (before) = 10297 / 256 = 40
- lchan.rqd_ta (after) = 9
- toa256 (after) = 10041 / 256 = 39
-Step #9
- lchan.rqd_ta (before) = 9
- toa256 (before) = 10041 / 256 = 39
- lchan.rqd_ta (after) = 10
+ lchan.ta_ctrl.current (after) = 10
toa256 (after) = 9785 / 256 = 38
-Step #10
- lchan.rqd_ta (before) = 10
+Step #5
+ lchan.ta_ctrl.current (before) = 10
toa256 (before) = 9785 / 256 = 38
- lchan.rqd_ta (after) = 11
- toa256 (after) = 9529 / 256 = 37
-Step #11
- lchan.rqd_ta (before) = 11
- toa256 (before) = 9529 / 256 = 37
- lchan.rqd_ta (after) = 12
+ lchan.ta_ctrl.current (after) = 12
toa256 (after) = 9273 / 256 = 36
-Step #12
- lchan.rqd_ta (before) = 12
+Step #6
+ lchan.ta_ctrl.current (before) = 12
toa256 (before) = 9273 / 256 = 36
- lchan.rqd_ta (after) = 13
- toa256 (after) = 9017 / 256 = 35
-Step #13
- lchan.rqd_ta (before) = 13
- toa256 (before) = 9017 / 256 = 35
- lchan.rqd_ta (after) = 14
+ lchan.ta_ctrl.current (after) = 14
toa256 (after) = 8761 / 256 = 34
-Step #14
- lchan.rqd_ta (before) = 14
+Step #7
+ lchan.ta_ctrl.current (before) = 14
toa256 (before) = 8761 / 256 = 34
- lchan.rqd_ta (after) = 15
- toa256 (after) = 8505 / 256 = 33
-Step #15
- lchan.rqd_ta (before) = 15
- toa256 (before) = 8505 / 256 = 33
- lchan.rqd_ta (after) = 16
+ lchan.ta_ctrl.current (after) = 16
toa256 (after) = 8249 / 256 = 32
-Step #16
- lchan.rqd_ta (before) = 16
+Step #8
+ lchan.ta_ctrl.current (before) = 16
toa256 (before) = 8249 / 256 = 32
- lchan.rqd_ta (after) = 17
- toa256 (after) = 7993 / 256 = 31
-Step #17
- lchan.rqd_ta (before) = 17
- toa256 (before) = 7993 / 256 = 31
- lchan.rqd_ta (after) = 18
+ lchan.ta_ctrl.current (after) = 18
toa256 (after) = 7737 / 256 = 30
-Step #18
- lchan.rqd_ta (before) = 18
+Step #9
+ lchan.ta_ctrl.current (before) = 18
toa256 (before) = 7737 / 256 = 30
- lchan.rqd_ta (after) = 19
- toa256 (after) = 7481 / 256 = 29
-Step #19
- lchan.rqd_ta (before) = 19
- toa256 (before) = 7481 / 256 = 29
- lchan.rqd_ta (after) = 20
+ lchan.ta_ctrl.current (after) = 20
toa256 (after) = 7225 / 256 = 28
-Step #20
- lchan.rqd_ta (before) = 20
+Step #10
+ lchan.ta_ctrl.current (before) = 20
toa256 (before) = 7225 / 256 = 28
- lchan.rqd_ta (after) = 21
- toa256 (after) = 6969 / 256 = 27
-Step #21
- lchan.rqd_ta (before) = 21
- toa256 (before) = 6969 / 256 = 27
- lchan.rqd_ta (after) = 22
+ lchan.ta_ctrl.current (after) = 22
toa256 (after) = 6713 / 256 = 26
-Step #22
- lchan.rqd_ta (before) = 22
+Step #11
+ lchan.ta_ctrl.current (before) = 22
toa256 (before) = 6713 / 256 = 26
- lchan.rqd_ta (after) = 23
- toa256 (after) = 6457 / 256 = 25
-Step #23
- lchan.rqd_ta (before) = 23
- toa256 (before) = 6457 / 256 = 25
- lchan.rqd_ta (after) = 24
+ lchan.ta_ctrl.current (after) = 24
toa256 (after) = 6201 / 256 = 24
-Step #24
- lchan.rqd_ta (before) = 24
+Step #12
+ lchan.ta_ctrl.current (before) = 24
toa256 (before) = 6201 / 256 = 24
- lchan.rqd_ta (after) = 25
- toa256 (after) = 5945 / 256 = 23
-Step #25
- lchan.rqd_ta (before) = 25
- toa256 (before) = 5945 / 256 = 23
- lchan.rqd_ta (after) = 26
+ lchan.ta_ctrl.current (after) = 26
toa256 (after) = 5689 / 256 = 22
-Step #26
- lchan.rqd_ta (before) = 26
+Step #13
+ lchan.ta_ctrl.current (before) = 26
toa256 (before) = 5689 / 256 = 22
- lchan.rqd_ta (after) = 27
- toa256 (after) = 5433 / 256 = 21
-Step #27
- lchan.rqd_ta (before) = 27
- toa256 (before) = 5433 / 256 = 21
- lchan.rqd_ta (after) = 28
+ lchan.ta_ctrl.current (after) = 28
toa256 (after) = 5177 / 256 = 20
-Step #28
- lchan.rqd_ta (before) = 28
+Step #14
+ lchan.ta_ctrl.current (before) = 28
toa256 (before) = 5177 / 256 = 20
- lchan.rqd_ta (after) = 29
- toa256 (after) = 4921 / 256 = 19
-Step #29
- lchan.rqd_ta (before) = 29
- toa256 (before) = 4921 / 256 = 19
- lchan.rqd_ta (after) = 30
+ lchan.ta_ctrl.current (after) = 30
toa256 (after) = 4665 / 256 = 18
-Step #30
- lchan.rqd_ta (before) = 30
+Step #15
+ lchan.ta_ctrl.current (before) = 30
toa256 (before) = 4665 / 256 = 18
- lchan.rqd_ta (after) = 31
- toa256 (after) = 4409 / 256 = 17
-Step #31
- lchan.rqd_ta (before) = 31
- toa256 (before) = 4409 / 256 = 17
- lchan.rqd_ta (after) = 32
+ lchan.ta_ctrl.current (after) = 32
toa256 (after) = 4153 / 256 = 16
-Step #32
- lchan.rqd_ta (before) = 32
+Step #16
+ lchan.ta_ctrl.current (before) = 32
toa256 (before) = 4153 / 256 = 16
- lchan.rqd_ta (after) = 33
- toa256 (after) = 3897 / 256 = 15
-Step #33
- lchan.rqd_ta (before) = 33
- toa256 (before) = 3897 / 256 = 15
- lchan.rqd_ta (after) = 34
+ lchan.ta_ctrl.current (after) = 34
toa256 (after) = 3641 / 256 = 14
-Step #34
- lchan.rqd_ta (before) = 34
+Step #17
+ lchan.ta_ctrl.current (before) = 34
toa256 (before) = 3641 / 256 = 14
- lchan.rqd_ta (after) = 35
- toa256 (after) = 3385 / 256 = 13
-Step #35
- lchan.rqd_ta (before) = 35
- toa256 (before) = 3385 / 256 = 13
- lchan.rqd_ta (after) = 36
+ lchan.ta_ctrl.current (after) = 36
toa256 (after) = 3129 / 256 = 12
-Step #36
- lchan.rqd_ta (before) = 36
+Step #18
+ lchan.ta_ctrl.current (before) = 36
toa256 (before) = 3129 / 256 = 12
- lchan.rqd_ta (after) = 37
- toa256 (after) = 2873 / 256 = 11
-Step #37
- lchan.rqd_ta (before) = 37
- toa256 (before) = 2873 / 256 = 11
- lchan.rqd_ta (after) = 38
+ lchan.ta_ctrl.current (after) = 38
toa256 (after) = 2617 / 256 = 10
-Step #38
- lchan.rqd_ta (before) = 38
+Step #19
+ lchan.ta_ctrl.current (before) = 38
toa256 (before) = 2617 / 256 = 10
- lchan.rqd_ta (after) = 39
- toa256 (after) = 2361 / 256 = 9
-Step #39
- lchan.rqd_ta (before) = 39
- toa256 (before) = 2361 / 256 = 9
- lchan.rqd_ta (after) = 40
+ lchan.ta_ctrl.current (after) = 40
toa256 (after) = 2105 / 256 = 8
-Step #40
- lchan.rqd_ta (before) = 40
+Step #20
+ lchan.ta_ctrl.current (before) = 40
toa256 (before) = 2105 / 256 = 8
- lchan.rqd_ta (after) = 41
- toa256 (after) = 1849 / 256 = 7
-Step #41
- lchan.rqd_ta (before) = 41
- toa256 (before) = 1849 / 256 = 7
- lchan.rqd_ta (after) = 42
+ lchan.ta_ctrl.current (after) = 42
toa256 (after) = 1593 / 256 = 6
-Step #42
- lchan.rqd_ta (before) = 42
+Step #21
+ lchan.ta_ctrl.current (before) = 42
toa256 (before) = 1593 / 256 = 6
- lchan.rqd_ta (after) = 43
- toa256 (after) = 1337 / 256 = 5
-Step #43
- lchan.rqd_ta (before) = 43
- toa256 (before) = 1337 / 256 = 5
- lchan.rqd_ta (after) = 44
+ lchan.ta_ctrl.current (after) = 44
toa256 (after) = 1081 / 256 = 4
-Step #44
- lchan.rqd_ta (before) = 44
+Step #22
+ lchan.ta_ctrl.current (before) = 44
toa256 (before) = 1081 / 256 = 4
- lchan.rqd_ta (after) = 45
- toa256 (after) = 825 / 256 = 3
-Step #45
- lchan.rqd_ta (before) = 45
- toa256 (before) = 825 / 256 = 3
- lchan.rqd_ta (after) = 46
+ lchan.ta_ctrl.current (after) = 46
toa256 (after) = 569 / 256 = 2
-Step #46
- lchan.rqd_ta (before) = 46
+Step #23
+ lchan.ta_ctrl.current (before) = 46
toa256 (before) = 569 / 256 = 2
- lchan.rqd_ta (after) = 47
- toa256 (after) = 313 / 256 = 1
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #24
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #25
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #26
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #27
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #28
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #29
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #30
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #31
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #32
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #33
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #34
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #35
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #36
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #37
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #38
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #39
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #40
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #41
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #42
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #43
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #44
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #45
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
+Step #46
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
+ toa256 (after) = 57 / 256 = 0
Step #47
- lchan.rqd_ta (before) = 47
- toa256 (before) = 313 / 256 = 1
- lchan.rqd_ta (after) = 48
+ lchan.ta_ctrl.current (before) = 48
+ toa256 (before) = 57 / 256 = 0
+ lchan.ta_ctrl.current (after) = 48
toa256 (after) = 57 / 256 = 0
Step #48
- lchan.rqd_ta (before) = 48
+ lchan.ta_ctrl.current (before) = 48
toa256 (before) = 57 / 256 = 0
- lchan.rqd_ta (after) = 48
+ lchan.ta_ctrl.current (after) = 48
toa256 (after) = 57 / 256 = 0
Step #49
- lchan.rqd_ta (before) = 48
+ lchan.ta_ctrl.current (before) = 48
toa256 (before) = 57 / 256 = 0
- lchan.rqd_ta (after) = 48
+ lchan.ta_ctrl.current (after) = 48
toa256 (after) = 57 / 256 = 0
Done.
diff --git a/tests/testsuite.at b/tests/testsuite.at
index ba5a409b..f2d17fbf 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -63,3 +63,9 @@ AT_KEYWORDS([ta_control])
cat $abs_srcdir/ta_control/ta_control_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/ta_control/ta_control_test], [], [expout], [ignore])
AT_CLEANUP
+
+AT_SETUP([amr])
+AT_KEYWORDS([amr])
+cat $abs_srcdir/amr/amr_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/amr/amr_test], [], [expout], [ignore])
+AT_CLEANUP