aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--TODO-RELEASE4
-rw-r--r--configure.ac12
-rwxr-xr-xcontrib/jenkins.sh2
-rw-r--r--doc/manuals/chapters/bts.adoc34
-rw-r--r--doc/manuals/chapters/control.adoc32
-rw-r--r--doc/manuals/chapters/handover.adoc34
-rw-r--r--doc/manuals/chapters/handover_inter_bsc.dot2
-rw-r--r--doc/manuals/chapters/interf_meas.adoc72
-rw-r--r--doc/manuals/chapters/mgwpool.adoc252
-rw-r--r--doc/manuals/chapters/power_control.adoc240
-rw-r--r--doc/manuals/chapters/running.adoc3
-rw-r--r--doc/manuals/osmobsc-usermanual.adoc4
-rw-r--r--include/osmocom/bsc/Makefile.am3
-rw-r--r--include/osmocom/bsc/assignment_fsm.h2
-rw-r--r--include/osmocom/bsc/bsc_stats.h112
-rw-r--r--include/osmocom/bsc/bssmap_reset.h1
-rw-r--r--include/osmocom/bsc/bts.h68
-rw-r--r--include/osmocom/bsc/bts_trx.h1
-rw-r--r--include/osmocom/bsc/chan_counts.h76
-rw-r--r--include/osmocom/bsc/debug.h1
-rw-r--r--include/osmocom/bsc/gsm_04_08_rr.h3
-rw-r--r--include/osmocom/bsc/gsm_data.h284
-rw-r--r--include/osmocom/bsc/handover.h2
-rw-r--r--include/osmocom/bsc/lchan_fsm.h6
-rw-r--r--include/osmocom/bsc/lchan_rtp_fsm.h2
-rw-r--r--include/osmocom/bsc/lcs_loc_req.h2
-rw-r--r--include/osmocom/bsc/lcs_ta_req.h2
-rw-r--r--include/osmocom/bsc/neighbor_ident.h8
-rw-r--r--include/osmocom/bsc/osmo_bsc_rf.h5
-rw-r--r--include/osmocom/bsc/pcuif_proto.h28
-rw-r--r--include/osmocom/bsc/power_control.h7
-rw-r--r--include/osmocom/bsc/signal.h4
-rw-r--r--include/osmocom/bsc/timeslot_fsm.h5
-rw-r--r--include/osmocom/bsc/vty.h1
-rw-r--r--src/ipaccess/ipaccess-config.c22
-rw-r--r--src/ipaccess/ipaccess-proxy.c8
-rw-r--r--src/ipaccess/stubs.c14
-rw-r--r--src/osmo-bsc/Makefile.am4
-rw-r--r--src/osmo-bsc/a_reset.c4
-rw-r--r--src/osmo-bsc/abis_osmo.c69
-rw-r--r--src/osmo-bsc/abis_rsl.c267
-rw-r--r--src/osmo-bsc/assignment_fsm.c45
-rw-r--r--src/osmo-bsc/bsc_ctrl_commands.c192
-rw-r--r--src/osmo-bsc/bsc_init.c58
-rw-r--r--src/osmo-bsc/bsc_rf_ctrl.c90
-rw-r--r--src/osmo-bsc/bsc_stats.c260
-rw-r--r--src/osmo-bsc/bsc_subscr_conn_fsm.c161
-rw-r--r--src/osmo-bsc/bsc_vty.c144
-rw-r--r--src/osmo-bsc/bssmap_reset.c6
-rw-r--r--src/osmo-bsc/bts.c221
-rw-r--r--src/osmo-bsc/bts_ipaccess_nanobts.c9
-rw-r--r--src/osmo-bsc/bts_osmobts.c115
-rw-r--r--src/osmo-bsc/bts_trx.c69
-rw-r--r--src/osmo-bsc/bts_vty.c579
-rw-r--r--src/osmo-bsc/chan_counts.c142
-rw-r--r--src/osmo-bsc/gsm_04_08_rr.c3
-rw-r--r--src/osmo-bsc/gsm_08_08.c18
-rw-r--r--src/osmo-bsc/gsm_data.c218
-rw-r--r--src/osmo-bsc/handover_decision_2.c21
-rw-r--r--src/osmo-bsc/handover_fsm.c42
-rw-r--r--src/osmo-bsc/lchan_fsm.c150
-rw-r--r--src/osmo-bsc/lchan_rtp_fsm.c2
-rw-r--r--src/osmo-bsc/lcs_loc_req.c4
-rw-r--r--src/osmo-bsc/lcs_ta_req.c2
-rw-r--r--src/osmo-bsc/neighbor_ident.c98
-rw-r--r--src/osmo-bsc/neighbor_ident_ctrl.c713
-rw-r--r--src/osmo-bsc/neighbor_ident_vty.c73
-rw-r--r--src/osmo-bsc/net_init.c14
-rw-r--r--src/osmo-bsc/nm_channel_fsm.c12
-rw-r--r--src/osmo-bsc/nm_rcarrier_fsm.c2
-rw-r--r--src/osmo-bsc/osmo_bsc_bssap.c5
-rw-r--r--src/osmo-bsc/osmo_bsc_ctrl.c21
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c228
-rw-r--r--src/osmo-bsc/osmo_bsc_msc.c8
-rw-r--r--src/osmo-bsc/osmo_bsc_sigtran.c3
-rw-r--r--src/osmo-bsc/paging.c1
-rw-r--r--src/osmo-bsc/pcu_sock.c3
-rw-r--r--src/osmo-bsc/power_control.c261
-rw-r--r--src/osmo-bsc/system_information.c6
-rw-r--r--src/osmo-bsc/timeslot_fsm.c26
-rw-r--r--src/utils/bs11_config.c21
-rw-r--r--src/utils/meas_json.c9
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/abis/abis_test.c13
-rw-r--r--tests/acc/acc_test.c24
-rw-r--r--tests/acch_overpower.vty88
-rw-r--r--tests/bsc/bsc_test.c28
-rwxr-xr-xtests/ctrl_test_runner.py212
-rw-r--r--tests/gsm0408/gsm0408_test.c60
-rw-r--r--tests/gsm0408/gsm0408_test.ok2
-rw-r--r--tests/handover/handover_test.c29
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.c95
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.ok4
-rw-r--r--tests/osmo-bsc.vty28
-rw-r--r--tests/power_ctrl.vty97
-rw-r--r--tests/timer.vty6
97 files changed, 5260 insertions, 1189 deletions
diff --git a/.gitignore b/.gitignore
index c4d8984ff..0bbe4782b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@ debian/*.log
*.o
*.lo
*.a
+*.la
.deps
Makefile
Makefile.in
@@ -47,7 +48,6 @@ hlr.sqlite3
src/utils/bs11_config
src/ipaccess/ipaccess-config
src/ipaccess/abisip-find
-src/ipaccess/ipaccess-firmware
src/ipaccess/ipaccess-proxy
src/utils/isdnsync
src/osmo-bsc_nat/osmo-bsc_nat
@@ -84,7 +84,7 @@ doc/manuals/*.pdf
doc/manuals/*__*.png
doc/manuals/*.check
doc/manuals/generated/
-doc/manuals/osmomsc-usermanual.xml
+doc/manuals/osmobsc-usermanual.xml
doc/manuals/common
doc/manuals/build
diff --git a/TODO-RELEASE b/TODO-RELEASE
index a9bf06c4d..cfb9cf9ab 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -13,3 +13,7 @@ libosmogsm >1.5.1 introduced struct needed gsm0808_old_bss_to_new_
libosmo-mgcp-client >1.8.0 need osmo_mgcpc_ep_ci_get_remote_rtp_info()
libosmovty >1.5.1 needs vty_read_config_filep()
libosmosgsm >1.5.1 needs GSM_PCHAN_OSMO_DYN
+libosmocore >1.5.1 RSL_IPAC_EIE_OSMO*, struct osmo_preproc_*
+libosmocore >1.5.1 needs osmo_str_to_int()
+libosmocore >1.5.1 needs new osmo_stat_item implementation (omits FIFO size for stat item)
+libosmocore >=1.6 need osmo_time_cc
diff --git a/configure.ac b/configure.ac
index 195acfe80..bcf91bacd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,11 +36,6 @@ if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
fi
PKG_PROG_PKG_CONFIG([0.20])
-dnl check for AX_CHECK_COMPILE_FLAG
-m4_ifdef([AX_CHECK_COMPILE_FLAG], [], [
- AC_MSG_ERROR([Please install autoconf-archive; re-run 'autoreconf -fi' for it to take effect.])
- ])
-
dnl checks for libraries
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
@@ -120,13 +115,6 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
CFLAGS="$saved_CFLAGS"
AC_SUBST(SYMBOL_VISIBILITY)
-AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"])
-AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"])
-AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"])
-AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
-AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"])
-AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"])
-
# Coverage build taken from WebKit's configure.in
AC_MSG_CHECKING([whether to enable code coverage support])
AC_ARG_ENABLE(coverage,
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index 0b978dca2..189eb2ca0 100755
--- a/contrib/jenkins.sh
+++ b/contrib/jenkins.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-# jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org
+# jenkins build helper script for osmo-bsc. This is how we build on jenkins.osmocom.org
#
# environment variables:
# * WITH_MANUALS: build manual PDFs if set to "1"
diff --git a/doc/manuals/chapters/bts.adoc b/doc/manuals/chapters/bts.adoc
index 1a693f9d0..4662c1a0c 100644
--- a/doc/manuals/chapters/bts.adoc
+++ b/doc/manuals/chapters/bts.adoc
@@ -470,23 +470,35 @@ network
<2> Rotate the generated permanent list subsets every 20 seconds in a fair fashion
Furthermore, cells with large number of subscribers and limited overlapping
-coverage may become overwhelmed with traffic after the cell starts brodacasting.
+coverage may become overwhelmed with traffic after the cell starts broadcasting.
This is especially true in areas with little to no reception from other
networks. To manage the load, OsmoBSC has an option to further restrict the
rotating ACC subset during startup and slowly increment it over time and taking
-current load into account.
+current channel load into account.
+The channel load will always be checked, even after the start up procedure, at
+an interval specified by the `access-control-class-ramping-step-interval` VTY
+command. It will either keep, increase or decrease the size of the current
+rotating ACC subset based on certain thresholds configured by
+the `access-control-class-ramping-chan-load` VTY command.
+As a result, if ACC ramping is enabled (`access-control-class-ramping`), the
+number of concurrent allowed ACCs will start with *1* and will fluctuate over
+time based on channel load in the interval *[1, `access-control-rotate`]*. This
+means at any time there will be at least *1* allowed ACC which, together with
+ACC rotation, will prevent from subscriber being unable to use the network.
.Example: Ramp up access to the BTS after startup
----
network
bts 0
access-control-class-ramping <1>
- access-control-class-ramping-step-interval 30 <2>
- access-control-class-ramping-step-size 1 <3>
+ access-control-class-ramping-step-size 1 <2>
+ access-control-class-ramping-step-interval 30 <3>
+ access-control-class-ramping-chan-load 71 89 <4>
----
<1> Turn on access-control-class ramping
-<2> Enable more ACCs every 30 seconds
-<3> At each step enable one more ACC
+<2> At each step enable one more ACC
+<3> Check whether to allow more/less ACCs every 30 seconds
+<4> The rotate subset size is increased if channel load is < 71%, and decreased if channel load is > 89%.
Here a full example of all the mechanisms combined can be found:
@@ -503,14 +515,16 @@ bts 0
access-control-class-rotate-quantum 20 <3>
access-control-class-ramping <4>
access-control-class-ramping-step-size 1 <5>
- access-control-class-ramping-step-interval dynamic <6>
+ access-control-class-ramping-step-interval 30 <6>
+ access-control-class-ramping-chan-load 71 89 <7>
----
<1> ACCs 5-9 are administratively barred, ie they will never be used until somebody manually enables them in VTY config
<2> Allow access through temporary subsets of len=3 from ACC set 0-4: (0,1,2) -> (1,2,3) -> (2,3,4) -> (3,4,0), etc.
<3> Each subset iteration will happen every 20 seconds
-<4> During startup since ramping is enabled, it will further restrict the rotate subset size parameter (len=3)
-<5> The rotate subset size parameter will be increased one ACC slot at a time: len=0 -> len=1 -> len=2 -> len=3
-<6> The time until the subset size is further increased will be calculated based on current channel load
+<4> Ramping is enabled: during startup it will further restrict the rotate subset size parameter (start at len=1, end at len=3)
+<5> The rotate subset size parameter will be increased or decreased one ACC slot at a time: len=1 -> len=2 -> len=3
+<6> Check to further increase or decrease the rotate subset size based on current channel load is triggered every 30 seconds
+<7> The rotate subset size is increased if channel load is < 71%, and decreased if channel load is > 89%.
=== Configuring FACCH/SACCH repetition
diff --git a/doc/manuals/chapters/control.adoc b/doc/manuals/chapters/control.adoc
index 258512636..09e828e9e 100644
--- a/doc/manuals/chapters/control.adoc
+++ b/doc/manuals/chapters/control.adoc
@@ -32,7 +32,7 @@ TRX-specific commands are additionally prefixed with TRX number e. g.
|bts.N.location-area-code|RW|No|"<lac>"|Set/Get LAC (value between (0, 65535)).
|bts.N.cell-identity|RW|No|"<id>"|Set/Get Cell Identity (value between (0, 65535)).
|bts.N.apply-configuration|WO|No|Ignored|Restart BTS via OML.
-|bts.N.send-new-system-informations|WO|No|Ignored|Regenerate System Information messages for given BTS.
+|bts.N.send-new-system-informations|WO|No|Ignored|Regenerate and resend System Information messages for given BTS.
|bts.N.channel-load|RO|No|"<name>,<used>,<total>"|See <<chanlo>> for details.
|bts.N.oml-connection-state|RO|No|"connected", "disconnected", "degraded"|Indicate the status of OML connection of BTS.
|bts.N.oml-uptime|RO|No|<uptime>|Return OML link uptime in seconds.
@@ -68,6 +68,9 @@ TRX-specific commands are additionally prefixed with TRX number e. g.
|[bts.N.]handover2.penalty-time.failed-assignment|RW|No|<0-99999>,"default"|Time to suspend handover for a subscriber after a failed re-assignment within this cell.
|[bts.N.]handover2.retries|RW|No|<0-9>,"default"|Number of times to immediately retry a failed handover/assignment, before a penalty time is applied.
|handover2.congestion-check|RW|No|"disabled",<1-999>,"now"|Congestion check interval in seconds, "now" triggers immediate congestion check.
+|bts.N.neighbor-list.mode|WO|No|"automatic","manual","manual-si5"|Mode of Neighbor List generation.
+|bts.N.neighbor-list.add|WO|No|<0-1023>|Add to manual neighbor list.
+|bts.N.neighbor-list.del|WO|No|<0-1023>|Delete from manual neighbor list.
|===
[[notif]]
@@ -120,4 +123,29 @@ RF Ctrl is enabled in the BSC Configuration.
Set/Get the value of maximum power reduction. Even values between 0 and 22 are
accepted.
-FIXME: add variables defined in src/ctrl/control_if.c?
+=== add/del neighbor cell
+
+The control interface allows for editing the neighbor cell configuration. Neighbor
+cells can be added or removed during runtime. It is also possible to clear the
+entire neighbor list if necessary.
+
+.Variables available over control interface
+[options="header",width="100%",cols="20%,5%,5%,50%,20%"]
+|===
+|Name|Access|Trap|Value|Comment
+|bts.N.neighbor-bts.add|WO|No|"<num>"|Add neighbor cell by local BTS number.
+|bts.N.neighbor-bts.del|WO|No|"<num>"|Delete neighbor cell by local BTS number.
+|bts.N.neighbor-lac.add|WO|No|"<lac>[-<arfcn>-<bsic>]"|Add neighbor cell by LAC.
+|bts.N.neighbor-lac.del|WO|No|"<lac>[-<arfcn>-<bsic>]"|Delete neighbor cell by LAC.
+|bts.N.neighbor-lac-ci.add|WO|No|"<lac>-<ci>[-<arfcn>-<bsic>]"|Add neighbor cell by LAC and CI.
+|bts.N.neighbor-lac-ci.del|WO|No|"<lac>-<ci>[-<arfcn>-<bsic>]"|Delete neighbor cell by LAC and CI.
+|bts.N.neighbor-cgi.add|WO|No|"<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"|Add neighbor cell by cgi.
+|bts.N.neighbor-cgi.del|WO|No|"<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"|Delete neighbor cell by cgi.
+|bts.N.neighbor-cgi-ps.add|WO|No|"<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"|Add neighbor cell by cgi (Packet Switched, with RAC)
+|bts.N.neighbor-cgi-ps.del|WO|No|"<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"|Delete neighbor cell by cgi (Packet Switched, with RAC).
+|bts.N.neighbor-clear|WO|No|Ignored|Delete all neighbor cells.
+|===
+
+NOTE: The bsic-number (<bsic>) can also be set to "any" if no explcit bsic shall be given
+
+//FIXME: add variables defined in src/ctrl/control_if.c?
diff --git a/doc/manuals/chapters/handover.adoc b/doc/manuals/chapters/handover.adoc
index ab0b1fa58..f56725977 100644
--- a/doc/manuals/chapters/handover.adoc
+++ b/doc/manuals/chapters/handover.adoc
@@ -747,19 +747,41 @@ network
This PS information is solely used by the Neighbor Resolution Service, aimed at
nodes other than BSC itself, and described below.
-=== Neighbor Resolution Service CTRL interface
+=== Neighbor Address Resolution Service
-This service is provided in order to provide the PCU a way to translate
+This service is provided in order to provide the PCU with a way to translate
ARCFCN+BSIC into CGI-PS in order to use RIM services and accomplish PS
Handovers.
This interface is Osmocom specific, since the standard doesn't provide any
specifications on how to provide this kind of information to the PCU.
-Since the PCU can be either BSC-colocated or BTS-colocated (hence on a different
-host than BSC), this must be a network-based interface. Since the service is
-Osmocom-specific, it was decided to re-use the CTRL interface available in most
-Osmocom processes (see <<control>>).
+Since the PCU can be either BSC co-located or BTS co-located (hence on a
+different host than BSC), messages may need to be sent over the network at some
+point.
+
+In consequence, it was decided implement a new _Container_ type message in
+PCUIF which the PCU can use to directly communicate with the BSC. If the PCU is
+BSC co-located, then the BSC can directly communicate over the unix socket. If
+the PCU is BTS co-located, then PCU sends PCUIF messages over the unix socket to
+the BTS, and the BTS forwards the PCUIF messages transparently over the the IPA
+multiplex of the OML connection towards the BSC and back.
+
+Support for this interface is available by default nowadays in OsmoPCU, OsmoBTS
+and OsmoBSC, and requires no specific configuration, it will just work out of
+the box.
+
+==== Neighbor Address Resolution Service CTRL interface (deprecated)
+
+Before the existence of the PCUIF forwarded over IPA multiplex interface (see
+above), the Neighbor Address Resolution Service was implemented by means of a
+CTRL interface, similar to the one used in most Osmocom processes (see
+<<control>>).
+
+CAUTION: This interface is nowadays considered deprecated and should not be used
+anymore. Any related VTY options should be dropped from configuration files, to
+let OsmoPCU use the new interface instead. This section is kept here for a while
+as a reference for old deployments using old versions of the programs.
Due to security concerns, the set of CTRL commands available in this
service is configured in a different IP address and port, since the service
diff --git a/doc/manuals/chapters/handover_inter_bsc.dot b/doc/manuals/chapters/handover_inter_bsc.dot
index 42decef32..3ff8093d0 100644
--- a/doc/manuals/chapters/handover_inter_bsc.dot
+++ b/doc/manuals/chapters/handover_inter_bsc.dot
@@ -27,7 +27,7 @@ BTS_a1 -> BTS_b0 [label="(5) BSC decides to do\ninter-BSC Handover",style=dashed
{BSC_a,BSC_b} -> MSC [arrowhead=none,label=A]
-BSC_a -> MSC [label="(6) --> Handover Required\nto LAC=42 CI=6\n(10) <-- Handover Command",style=dashed,constraint=false,arrowhead=none]
+BSC_a -> MSC [label="(6) --> Handover Required\nto LAC=42 CI=3\n(10) <-- Handover Command",style=dashed,constraint=false,arrowhead=none]
MSC -> BSC_b [label="(7) <-- Handover Request\n(9) --> Handover Request ACK",style=dashed,constraint=false,arrowhead=none]
BSC_b -> BTS_b0 [label="(8) activate new lchan",style=dashed,constraint=false]
diff --git a/doc/manuals/chapters/interf_meas.adoc b/doc/manuals/chapters/interf_meas.adoc
new file mode 100644
index 000000000..c82ff4907
--- /dev/null
+++ b/doc/manuals/chapters/interf_meas.adoc
@@ -0,0 +1,72 @@
+== Interference reporting
+
+According to 3GPP 48.058, section 6.1, the BTS shall periodically report the
+interference levels on *idle* channels using the "Radio resource indication"
+procedure. This is done by sending the `RF RESource INDication` message,
+which is specified in sections 8.6.1 and 9.3.21.
+
+// TODO: BSC -> MSC reporting (3GPP TS 48.008, section 3.1.3)
+
+=== Interference reporting parameters
+
+The interference band is calculated by the BTS based on the `Interference level
+Boundaries` and the `Averaging period`. These parameters are sent by the BSC
+over the A-bis/OML, and can be configured via the VTY interface.
+
+Below are the default values for them:
+
+----
+network
+ bts 0
+ interference-meas avg-period 6 <1>
+ interference-meas level-bounds -115 <2> -109 -103 -97 -91 -85 <3>
+----
+<1> Averaging period (`Intave`) in SACCH multiframe periods (480ms).
+<2> Interference level boundary `0` (in dBm).
+<3> Interference level boundary `X5` (in dBm).
+
+The `Intave` parameter defines the averaging and reporting period. With the
+default value of 6 SACCH multiframe periods the BTS is instructed to report
+averaged interference levels approximately every 3 seconds.
+
+According to 3GPP TS 48.008, there exist five interference bands and six
+`Interference level Boundaries` (`0`, `X1`, ... `X5`). The BTS shall map the
+averaged interference levels (initially in dBm) into these 5 bands.
+
+----
+-115 dBm -109 dBm -103 dBm -97 dBm -91 dBm -85 dBm
+ | <1> | <2> | <3> | <4> | <5> | <6>
+ +----------+----------+----------+----------+----------+
+ | band 1 | band 2 | band 3 | band 4 | band 5 |
+ +----------+----------+----------+----------+----------+
+----
+<1> Interference level boundary `0` (outer).
+<2> Interference level boundary `X1`.
+<3> Interference level boundary `X2`.
+<4> Interference level boundary `X3`.
+<5> Interference level boundary `X4`.
+<6> Interference level boundary `X5` (outer).
+
+Unfortunately, it's not clearly defined by 3GPP how the BTS is supposed to map
+dBm values outside of the outer boundaries (`0` and `X5`) to band values. The
+ip.access nanoBTS, for example, would map values -120 dBm and -75 dBm to bands
+1 and 5, respectively. osmo-bts replicates this behavior.
+
+=== PDCH and dynamic timeslot handling
+
+The BTS may optionally report interference levels for PDCH timeslots. This
+may be useful for the BSC to determine whether dynamic PDCH timeslots might
+be better used for new circuit switched connections, or whether alternative
+PDCH resources should be allocated for interference reasons.
+
+NOTE: Currently osmo-bsc makes no use of PDCH interference reports, neither
+they get forwarded to the BSC co-located PCU over the PCUIF.
+
+For dynamic timeslots (`TCH/F_TCH/H_SDCCH/8_PDCH` and `TCH/F_PDCH`), the
+following expectations apply:
+
+* when in TCH/F mode: no interference reports, because the only sub-channel is active;
+* when in TCH/H mode: interference reports for *inactive* sub-channels only;
+* when in SDCCH mode: interference reports for *inactive* sub-channels only;
+* when in PDCH mode: optional interference reports;
+** measurements can be performed during IDLE TDMA frames.
diff --git a/doc/manuals/chapters/mgwpool.adoc b/doc/manuals/chapters/mgwpool.adoc
new file mode 100644
index 000000000..2221925bd
--- /dev/null
+++ b/doc/manuals/chapters/mgwpool.adoc
@@ -0,0 +1,252 @@
+[[mgw_pooling]]
+== MGW Pooling
+
+OsmoBSC is able to use a pool of media gateway (MGW) instances since mid 2021.
+The aim of MGW pooling is to evenly distribute the RTP voice stream load across
+multiple MGW instances. This can help to scale out over multiple VMs or physical
+machines. Until osmo-mgw includes multithreading support, it may also be used to
+scale-out to multiple cores on a single host.
+
+The load distribution is managed in such a way that when a new call is placed,
+the pool will automatically assign the call to the MGW with the lowest load.
+
+MGW pooling is recommended for larger RAN installations. For small networks and
+lab installations the classic method with one MGW per BSC offers sufficient
+performance.
+
+=== VTY configuration
+
+In OsmoBSC the MGW is controlled via an MGCP-Client. The VTY commands to
+configure the MGCP-Client are part of the 'msc' node due to historical reasons.
+Unfortunately this concept does not allow to configure multiple MGCP-Client
+instances as required by MGW pooling. In order to support MGW pooling a new
+'mgw' node has been added under the 'network' node.
+
+=== Existing configuration files
+
+Existing OsmoBSC configuration files will continue to work, but as soon as an
+MGW pool is configured the 'mgw' settings under the 'msc' node will be ignored.
+
+Example configuration with only one MGCP-Client under the 'msc' node:
+----
+msc 0
+ mgw remote-ip 127.0.0.1
+ mgw remote-port 2428
+----
+
+=== MGW pool configuration
+
+To setup an MGW pool, the user must first install multiple OsmoMGW instances, so
+that they won’t interfere with each other. This can be done using different
+local host IP addresses or different ports. When OsmoMGW is installed from
+packages, the systemd configuration may also require adjustment.
+
+The VTY settings under the 'mgw' node works the same way as the VTY settings for
+the MGW under the 'msc' node.
+
+Example configuration with two MGCP-Client instances in a pool:
+----
+ mgw 0
+ description media-gw-0 <2>
+ mgw remote-ip 127.0.0.1
+ mgw remote-port 2432
+ mgw local-ip 127.0.0.1
+ mgw local-port 2431
+ mgw endpoint-domain mgw0 <1>
+ mgw 1
+ description media-gw-1 <2>
+ mgw remote-ip 127.0.0.1
+ mgw remote-port 2430
+ mgw local-ip 127.0.0.1
+ mgw local-port 2429
+ mgw endpoint-domain mgw1 <1>
+----
+
+<1> When working with multiple MGW / MGCP-Client instances, the domain name for
+each MGW should be different. Otherwise it won't be possible to distinguish the
+endpoint names in the log. It should also be noted that the domain name must
+match the configuration of the related OsmoMGW instance.
+
+<2> It is also possible to assign a descriptive name to each MGW instance. The
+MGCP client specific log lines will then use this name as logging context. If
+no description is set, the domain name will be used.
+
+=== MGW pool management
+
+While it was not possible to change the MGCP-Client configuration under the
+“msc” node at runtime, the pool is fully runtime-manageable. The only limitation
+is that an MGCP-Client can not be restarted or removed as long as it is serving
+calls (see also: <<mgw_pooling_blocking>>).
+
+==== MGW pool status
+
+The VTY implements a 'show mgw-pool' command that lists the currently configured
+MGW pool members, their status and call utilization.
+
+----
+OsmoBSC> show mgw-pool
+% MGW-Pool:
+% MGW 0:media-gw-0
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 1
+% MGW 1:media-gw-1
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 0
+----
+
+==== Adding an MGW / MGCP-Client to the MGW pool
+
+To add a new MGCP-Client to the pool, the 'mgw' node is used. Like with the
+'bts' or the 'msc' node a reference number is used that usually starts at 0.
+However it is still possible to assign any number from 0-255. The enumeration
+also may contain gaps.
+
+----
+OsmoBSC> enable
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# mgw 2
+OsmoBSC(config-mgw)# mgw
+ local-ip local bind to connect to MGW from
+ local-port local port to connect to MGW from
+ remote-ip remote IP address to reach the MGW at
+ remote-port remote port to reach the MGW at
+ endpoint-domain Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.
+ reset-endpoint Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,e.g. 'rtpbridge/*'
+----
+
+The newly added MGW will immediately appear in the mgw-pool list but it won't
+be used until its configuration finished by reconnecting it.
+
+----
+% MGW-Pool:
+% MGW 0:media-gw-0
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 2
+% MGW 1:media-gw-1
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 3
+% MGW 2:mgw <1>
+% mgcp-client: disconnected
+% service: unblocked
+% ongoing calls: 0
+----
+
+<1> In this example a description is not set yet, so the domain name ("mgw")
+is displayed.
+
+==== Reconnecting an MGW / MGCP-Client
+
+It may become necessary to reconnect an MGCP-Client. This is the case when the
+VTY configuration was changed at runtime. In order to make the changes effective
+the MGW configuration must be reloaded by reconnecting the MGW connection. Also
+newly created MGW instances require a reconnect once their configuration is
+done.
+
+To reconnect an MGCP-Client use the 'reconnect' VTY command:
+----
+OsmoBSC# mgw 2 reconnect
+----
+
+The mgcp-client status should immediately change to 'connected'. The MGW is now
+ready to be used for new calls.
+
+----
+OsmoBSC# show mgw-pool
+% MGW-Pool:
+% MGW 0:media-gw-0
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 2
+% MGW 1:media-gw-1
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 3
+% MGW 2:mgw
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 0
+----
+
+It should be noted that MGCP a protocol is used via UDP, the connect only
+happens locally to forward the UDP datagrams properly. Also (unless a reset
+endpoint is configured like in the example config above) there will be no
+immediate interaction with the MGW. However, the log should at least confirm
+the the connect worked and the MGCP client has been created successfully.
+
+----
+Mon Aug 2 17:15:00 2021 DLMGCP mgcp_client.c:788 MGCP client: using endpoint domain '@mgw'
+Mon Aug 2 17:15:00 2021 DLMGCP mgcp_client.c:908 MGCP GW connection: r=127.0.0.1:2427<->l=127.0.0.1:2727
+----
+
+It is strongly advised to check the activity on the related MGW and to follow
+the log in order to see that the communication between OsmoBSC and the MGW is
+working correctly.
+
+[[mgw_pooling_blocking]]
+==== Blocking an MGW / MGCP-Client
+
+If it becomes apparent that an MGCP-Client must be restarted or removed from
+the config (maintenance) the operator can put that MGCP-Client into a blocked
+mode. A blocked MGCP-Client will still serve the ongoing calls but it will not
+be picked for the assignment of new calls.
+
+To block an MGCP-Client use the 'block' VTY command:
+----
+OsmoBSC# mgw 2 block
+OsmoBSC# show mgw-pool
+% MGW-Pool:
+% MGW 0:media-gw-0
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 11
+% MGW 1:media-gw-1
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 12
+% MGW 2:mgw
+% mgcp-client: connected
+% service: blocked
+% ongoing calls: 10
+----
+
+When the number of ongoing calls has tapered off, the MGW / MGCP-Client can be
+restarted or removed if necessary.
+
+----
+OsmoBSC# show mgw-pool
+% MGW-Pool:
+% MGW 0:media-gw-0
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 15
+% MGW 1:media-gw-1
+% mgcp-client: connected
+% service: unblocked
+% ongoing calls: 14
+% MGW 2:mgw
+% mgcp-client: connected
+% service: blocked
+% ongoing calls: 0
+----
+
+If the blockade should be reverted, the 'unblock' VTY command can be used in
+the same way to remove the blockade. (Reconnecting will not remove the
+blockade.)
+
+==== Removing an MGW / MGCP-Client
+
+An MGCP-Client is removed from the pool using the 'no mgw' command from the
+configure terminal. The MGCP-Client instance will automatically be terminated
+and the related resources are freed. The only requirement is that there are no
+ongoing calls on the selected instance.
+
+----
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# no mgw 2
+----
diff --git a/doc/manuals/chapters/power_control.adoc b/doc/manuals/chapters/power_control.adoc
index d456484ea..f5f94f6a4 100644
--- a/doc/manuals/chapters/power_control.adoc
+++ b/doc/manuals/chapters/power_control.adoc
@@ -29,8 +29,11 @@ interface, this is further described in the next sections.
So far only the ip.access specific format is implemented, so it should be possible
to enable power control for nanoBTS. OsmoBTS also accepts this format, but may
-ignore some of the received parameters due to incomplete implementation.
+ignore some of the received parameters due to incomplete implementation. On the
+other hand, OsmoBTS may support some extra parameters coming in Osmocom specific
+IEs not supported by nanoBTS, such as those configuring C/I measurement thresholds.
+[[apply_pwr_ctrl]]
==== When the parameters come into effect?
It depends on how the power control parameters are signaled to the BTS. If a given
@@ -92,12 +95,18 @@ OsmoBSC(config-ms-power-ctrl)# list with-flags
. lv step-size inc <2-6> red <2-4>
. lv rxlev-thresh lower <0-63> upper <0-63>
. lv rxqual-thresh lower <0-7> upper <0-7>
+ . lv ci-thresh (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-30> upper <0-30>
. lv rxlev-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
. lv rxqual-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
+ . lv ci-thresh-comp (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-31> <0-31> upper <0-31> <0-31>
. lv no (rxlev-avg|rxqual-avg)
. lv (rxlev-avg|rxqual-avg) params hreqave <1-31> hreqt <1-31>
. lv (rxlev-avg|rxqual-avg) algo (unweighted|weighted|mod-median)
. lv (rxlev-avg|rxqual-avg) algo osmo-ewma beta <1-99>
+ . lv no ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) params hreqave <1-31> hreqt <1-31>
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo (unweighted|weighted|mod-median)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo osmo-ewma beta <1-99>
----
NOTE: Flag `v` indicates that a given parameter is vendor specific, so different
@@ -139,29 +148,99 @@ attenuation (0 dB) is applied by default (full power).
==== Power control interval
Having requested a transmit power level, the MS/BS power control loop may optionally
-by suspended for a certain number of SACCH multiframes defined by VTY parameter
+be suspended for a certain number of SACCH multiframes defined by VTY parameter
`ctrl-interval`. Given that SACCH is relatively slow and transmission of a data block
takes 480 ms, suspension allows an observation of the effect of one power control
-decision before initiating the next one.
+decision before initiating the next one. This is mostly important due to the
+fact that MS must change the transmit power at nominal steps of 2dB every 60ms
+(TS 45.008 sec 4.7.1). As a result, if the network requests the MS to change its
+transmit power by several MS Power Levels at a time, the MS will do so gradually
+over the next measurement period. Hence, upon next received L1 SACCH block, the
+_MS_PWR_ value announced by the MS will match only the one used to transmit the
+last block, and so the related measured RxLevel/RxQual values will be
+inaccurate. By skipping one or several SACCH blocks, the algorithm will always
+use values which match correctly the announced _MS_PWR_ and the measured
+RxLevel/RxQual (because the _MS_PWR_ will already have changed and hence will be
+kept stable over that measurement period).
----
OsmoBSC(config-bs-power-ctrl)# ctrl-interval ?
<0-31> P_CON_INTERVAL, in units of 2 SACCH periods (0.96 seconds)
----
-By default, the suspension interval is set to 0 for both MS/BS power control loops,
-therefore the power control decision is taken every 480 ms (one SACCH block period).
-Setting `ctrl-interval` to 1 increases the interval to 960 ms, so basically every
-second Uplink SACCH block is skipped; value 2 corresponds to the interval of
-1920 ms, so 3/4 received SACCH blocks are skipped.
+3GPP TS 45.008 briefly mentions this parameter in table A.1 (`P_Con_INTERVAL`).
-3GPP TS 45.008 briefly mentiones this parameter in table A.1 (P_Con_INTERVAL).
+A small time graph is depicted below for better understanding of the meaning of
+values for this parameter, since it is not obvious at all.
+
+.Example: Suspension interval accomplished by several values of P_CON_INTERVAL
+----
+|<-->| - one SACCH multi-frame period
+| |
+|----|----|----|----|----|----|----|----|----> SACCH multi-frames
+a) * * * * * * * * * P_CON_INTERVAL=0 (0.48 s)
+b) * * * * * P_CON_INTERVAL=1 (0.96 s)
+c) * * * P_CON_INTERVAL=2 (1.92 s, default)
+d) * * P_CON_INTERVAL=3 (2.88 s)
+e) * * P_CON_INTERVAL=4 (3.84 s)
+----
+
+The value to use for this parameter is closely related to that of VTY option
+`step-size inc <2-6> red <2-4>`, which configures the maximum step (in dB) at
+which the MS Power can be requested to changed when the MS Power Control Loop is
+triggered. The higher the `step-size`, the more time it will need the MS to do
+the necessary ramping of 2 dB steps, and hence the amount of time required for
+the MS to settle on the requested MS Power Level for an entire SACCH block. Or
+equally, the time the network can start using the full measurement period to
+trigger the MS Power Control Loop again with reliable measurements
+(`P_CON_INTERVAL`).
+
+By default, increment `step-size` is set to 4 dB and the decrement `step-size`
+is set to 2 dB, hence the MS requiring `4 * 60 = 240` milliseconds. That's less
+than 1 measurement period (480 ms), hence only the first measurement period
+needs to be skipped. Therefore, a suspension interval of 1 for both
+MS/BS power control loops can be used, and so the power control decision is
+taken every 960 ms (every second SACCH block period).
+
+However, OsmoBSC currently uses a default value of `ctrl-interval 2`
+(`P_CON_INTERVAL=2`, 1.92s, 3/4 received SACCH blocks are skipped), because
+that's the minimum amount of frames required for one loop step to run
+completely, that is: BTS fetching measurements and transmitting the new MS Power
+Level, then the MS retrieving the MS Power Level, transmitting with that exact
+MS Power level during the entire period and then finally submitting the
+Measurement Result containing that same MS Power Level. Using a value of
+`P_CON_INTERVAL=1` also provides good results, but in that case the loop tends
+to produce more temporary power oscillations due to the loop acting on periods
+where an older (earlier requested) MS Power level is still in use (and
+announced) by the MS.
+
+.Example: Timeline showing propagation of a new MS Power Level (P_CON_INTERVAL=2)
+----
+|<------------->| - one SACCH multi-frame period
+| 1 | 2 | 3 | 4 | ---> SACCH multi-frames
+|SA0|SA1|SA2|SA3|SA0|SA1|SA2|SA3|SA0|SA1|SA2|SA3|SA0|SA1|SA2|SA3| ---> SACCH bursts
+|<1>|...|...|<2>|<3>|...|...|<4>|<5>|...|...|<6>|<7>|...|...|<8>|
+----
+<1> BTS sends new requested MS Power Level in header of SACCH
+<2> MS receives SACCH block (new MS Power Level)
+<3> MS starts ramping towards new MS Power Level (hence potentially variable power used over the period)
+<4> MS should already be in desired MS Power Level (for step increments of less-or-equal than 8 dB)
+<5> MS starts transmitting at desired MS Power level constantly (ramping is over)
+<6> MS builds Measurement Results to be sent on next SACCH period
+<7> MS sends the Measurement Results of the previous multiframe to the BTS
+<8> BTS receives the Measurement Results from MS on SACCH, starts next loop iteration
+
+
+Setting `ctrl-interval` to 0 increases the interval to 480 ms, so basically no
+SACCH block is skipped and MS Power Control loop is triggered upon receival of
+every UL SACCH block.
==== Power change step size
In order to slow down the reactivity of the power control loop and thus make it more
-robust against sporadic fluctuations of the input values (RxLev and RxQual), the
-transmit power on both Uplink and Downlink is changed gradually, step by step.
+robust against sporadic fluctuations of the input values (RxLev and either
+RxQual or C/I), the transmit power on both Uplink and Downlink is changed
+gradually, step by step.
OsmoBSC allows to configure the step sizes for both increasing and reducing directions
separately. The corresponding power control loop would apply different delta values
@@ -245,16 +324,66 @@ network
bts 0
bs-power-control
mode dyn-bts <1>
- rxlev-thresh lower 32 upper 38
- rxlev-thresh-comp lower 10 12 <2> upper 19 20 <3>
- rxqual-thresh lower 3 upper 0
- rxqual-thresh-comp lower 5 7 <4> upper 15 18 <5>
+ rxlev-thresh lower 32 upper 38 <2>
+ rxlev-thresh-comp lower 10 12 <3> upper 19 20 <4>
+ rxqual-thresh lower 3 upper 0 <5>
+ rxqual-thresh-comp lower 5 7 <6> upper 15 18 <7>
----
<1> BS power control is to be performed by the BTS autonomously.
-<2> P1=10 out of N1=12 averages < L_RXLEV_XX_P => increase power.
-<3> P2=19 out of N2=20 averages > U_RXLEV_XX_P => decrease power.
-<4> P3=5 out of N3=7 averages > L_RXQUAL_XX_P => increase power.
-<5> P4=15 out of N4=18 averages < U_RXQUAL_XX_P => decrease power.
+<2> L_RXLEV_XX_P=32, U_RXLEV_XX_P=38.
+<3> P1=10 out of N1=12 averages < L_RXLEV_XX_P => increase power.
+<4> P2=19 out of N2=20 averages > U_RXLEV_XX_P => decrease power.
+<5> L_RXQUAL_XX_P=3, U_RXQAUL_XX_P=0.
+<6> P3=5 out of N3=7 averages > L_RXQUAL_XX_P => increase power.
+<7> P4=15 out of N4=18 averages < U_RXQUAL_XX_P => decrease power.
+
+==== Carrier-to-Interference (C/I) thresholds
+
+Carrier-to-Interference (C/I) provides a similar description of link quality to
+that provided by RxQual. However, C/I provides higher granularity than RxQual
+levels (0-7), hence providing the operator with a more refined way to set up
+targets levels and thresholds. C/I measurements can only be used for MS Power
+Control, since values are only available on the Uplink when computed by the BTS,
+and the MS doesn't provide figure for the Downlink to the BTS/BSC during
+measurement Reports.
+
+Usual C/I value range for MS uplink channels are between 0dB to 30dB, with 9dB
+being a usual average target around cell edges. Furthermore, publicly available
+studies conclude that different channel types with different codecs used have
+different target C/I where signal is considered good enough. This means MS using
+a given channel type with better codec capabilities can be instructed to
+transmit at lower levels, hence reducing noise or channel interference among MS.
+
+OsmoBTS MS Power Control Loop algorithm supports using C/I computed
+measurements. Related parameters can be configured similar to those of RxLev or
+RxQual (see previous section), with the main difference being that instead of
+having a global set of parameters, there's one set per channel type, hence
+allowing different parametrization based on the channel type in use by the MS.
+
+.Example: C/I threshold comparators for AMR-FR
+----
+network
+ bts 0
+ ms-power-control
+ mode dyn-bts <1>
+ ci-thresh amr-fr enable <2>
+ ci-thresh amr-fr lower 7 upper 11 <3>
+ ci-thresh-comp amr-fr lower 2 10 <4> upper 3 4 <5>
+----
+<1> MS power control is to be performed by the BTS autonomously.
+<2> MS power control loop should take C/I into account.
+<3> L_CI_AMR_FR_XX_P=7, U_CI_AMR_FR_XX_P=11.
+<4> P0=2 out of N1=10 averages < L_CI_AMR_FR_XX_P => increase power.
+<5> P1=3 out of N2=4 averages > U_CI_AMR_FR_XX_P => decrease power.
+
+NOTE: The BSC can instruct a BTS to disable C/I related logic in its
+autonomous MS Power Control Loop for a given channel type (hence not taking C/I
+measurements into account) by means of setting both related LOWER_CMP_N and
+UPPER_CMP_N parameters to zero (see _ci-thresh-comp_ VTY command). For the sake
+of easing configuration, a placeholder VTY command to disable C/I for all
+channel types is available under VTY node _ms-power-control_ as *_ci-thresh
+all disable_*. Afterwards, the new configuration must be deployed to the target
+BTS (see <<apply_pwr_ctrl>>).
==== Measurement averaging process
@@ -268,7 +397,7 @@ corresponding power control loops in any of the following ways:
(outliers) removed before the median calculation.
The pre-processing is expected to be performed by both MS and BS power control
-loops independently, for every input parameter (i.e. RxLev and RxQual).
+loops independently, for every input parameter (i.e. RxLev, RxQual and C/I).
----
OsmoBSC(config-bs-power-ctrl)# rxlev-avg algo ?
@@ -317,12 +446,16 @@ network
rxlev-avg params hreqave 4 hreqt 6 <3>
rxqual-avg algo osmo-ewma beta 50 <4>
rxqual-avg params hreqave 2 hreqt 3 <5>
+ ci-avg amr-fr algo osmo-ewma beta 50 <6>
+ ci-avg amr-fr params hreqave 2 hreqt 3 <7>
----
<1> Both MS and BS power control is to be performed by the BTS autonomously.
<2> Unweighted average is applied to RxLev values.
<3> RxLev: Hreqave and Hreqt values: 4 out of 6 SACCH blocks produce an averaged measurement.
<4> Osmocom specific EWMA is applied to RxQual values with smoothing factor = 50% (beta=0.5).
<5> RxQual: Hreqave and Hreqt values: 2 out of 3 SACCH blocks produce an averaged measurement.
+<6> Osmocom specific EWMA is applied to C/I values on AMR-FR channels with smoothing factor = 50% (beta=0.5).
+<7> C/I AMR-FR: Hreqave and Hreqt values: 2 out of 3 SACCH blocks produce an averaged measurement.
// TODO: Document other power control parameters:
// OsmoBSC(config-net-bts)# ms max power <0-40>
@@ -432,3 +565,70 @@ Got message: b'GET_REPLY 3652121201381481804 bts.0.c0-power-reduction 4 <2>'
----
<1> Remote address of the host running osmo-bsc (localhost in this example)
<2> Maximum BCCH carrier power reduction currently applied
+
+=== Temporary ACCH overpower
+
+Temporary overpower (TOP) is a power control technique that allows to improve
+SACCH/FACCH performance in case of bad C/I. The key idea of TOP is to
+increment the BS transmit power by 2..4 dB only for FACCH/SACCH bursts, while
+keeping all voice bursts at the lower (normal) level as determined by the
+downlink power control loop. This allows to reduce call drop rate and
+increase capacity in deployments with tight frequency reuse.
+
+NOTE: It's not possible to increase the current BS power beyond the maximum
+transmit power level supported by the PHY. Thus if the BTS is already
+transmitting at full power, the overpower logic cannot increase it even
+further. This is also why TOP must be employed *together with BS power
+control*, either static or dynamic.
+
+The main area of use for TOP is traffic channels employing the AMR (Adaptive
+Multi Rate) codec, which is more robust to interference than the associated
+signalling channels. While AMR provides sufficient speech quality even at
+very low C/I levels, the associated signalling channels may be suffering from
+channel coding errors. This imbalance can be compensated by employing TOP,
+which can be efficiently combined with the ACCH repetition technique.
+
+This feature requires no support on the mobile station side and can be used
+with UEs implementing the most recent 3GPP relese features, as well as legacy
+UEs. However, it needs to be implemented in the BTS. Given that TOP itself
+is not specified in 3GPP specifications, osmo-bsc uses Osmocom specific
+A-bis/RSL IEs in order to activate it. Therefore, only the recent osmo-bts
+versions may be instructed to activate this feature. Make sure that feature
+#023 "FACCH/SACCH Temporary overpower" is present in the feature vector.
+This can be checked by issuing `show bts` command in OsmoBSC's VTY interface.
+
+TOP is disabled by default. Below is a configuration example enabling it:
+
+----
+network
+ bts 0
+ overpower dl-acch 2 <1>
+ overpower rxqual 4 <2>
+ overpower chan-mode speech-amr <3>
+----
+<1> Overpower of maximum 2 dB for both SACCH and FACCH.
+<2> Enable TOP only if RxQual is worse than 4 (BER >= 1.6%).
+<3> Permit TOP only for speech channels using AMR codec.
+
+For advanced use cases, OsmoBSC can be configured to:
+
+* enable TOP only for FACCH or SACCH selectively, and/or
+* keep TOP enabled permanently regardless of the reported RxQual, and/or
+* permit TOP for any kind of dedicated channels.
+
+----
+OsmoBSC(config-net-bts)# overpower ?
+ dl-acch Enable overpower for both SACCH and FACCH
+ dl-sacch Enable overpower for SACCH only
+ dl-facch Enable overpower for FACCH only
+
+OsmoBSC(config-net-bts)# overpower rxqual 0?
+ 0 BER >= 0% (always on)
+
+OsmoBSC(config-net-bts)# overpower chan-mode ?
+ speech-amr Speech channels using AMR codec (default)
+ any Any kind of channel mode
+----
+
+These parameters are indicated to the BTS during a logical channel activation
+or modifications procedures, so they can be changed at run-time.
diff --git a/doc/manuals/chapters/running.adoc b/doc/manuals/chapters/running.adoc
index 327e0bd7f..8e1c8e94a 100644
--- a/doc/manuals/chapters/running.adoc
+++ b/doc/manuals/chapters/running.adoc
@@ -143,6 +143,9 @@ msc 0
DLCX to the media gateway. This helps to clear lingering calls from the
media gateway when the OsmoBSC is restarted.
+NOTE: OsmoBSC is also able to handle a pool of media gateways for load
+distribution. See also <<mgw_pooling>>.
+
==== Configure Lb to connect to an SMLC
diff --git a/doc/manuals/osmobsc-usermanual.adoc b/doc/manuals/osmobsc-usermanual.adoc
index c7589cc35..0afa0158c 100644
--- a/doc/manuals/osmobsc-usermanual.adoc
+++ b/doc/manuals/osmobsc-usermanual.adoc
@@ -26,12 +26,16 @@ include::{srcdir}/chapters/bsc.adoc[]
include::{srcdir}/chapters/power_control.adoc[]
+include::{srcdir}/chapters/interf_meas.adoc[]
+
include::{srcdir}/chapters/handover.adoc[]
include::{srcdir}/chapters/smscb.adoc[]
include::{srcdir}/chapters/mscpool.adoc[]
+include::{srcdir}/chapters/mgwpool.adoc[]
+
include::{srcdir}/chapters/smlc.adoc[]
include::./common/chapters/qos-dscp-pcp.adoc[]
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index dde08a138..3ddad45db 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -7,6 +7,7 @@ noinst_HEADERS = \
acc.h \
assignment_fsm.h \
bsc_rll.h \
+ bsc_stats.h \
bsc_subscriber.h \
bsc_subscr_conn_fsm.h \
bss.h \
@@ -15,6 +16,7 @@ noinst_HEADERS = \
bts_trx.h \
bts_ipaccess_nanobts_omlattr.h \
chan_alloc.h \
+ chan_counts.h \
codec_pref.h \
ctrl.h \
debug.h \
@@ -61,4 +63,5 @@ noinst_HEADERS = \
penalty_timers.h \
osmo_bsc_lcls.h \
smscb.h \
+ power_control.h \
$(NULL)
diff --git a/include/osmocom/bsc/assignment_fsm.h b/include/osmocom/bsc/assignment_fsm.h
index 3a64f7dd1..6c429e418 100644
--- a/include/osmocom/bsc/assignment_fsm.h
+++ b/include/osmocom/bsc/assignment_fsm.h
@@ -17,7 +17,7 @@
conn->assignment.new_lchan ? " of " : "", \
conn->assignment.new_lchan ? gsm_lchan_name(conn->assignment.new_lchan) : "", \
## args); \
- } while(0)
+ } while (0)
enum assignment_fsm_state {
ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE,
diff --git a/include/osmocom/bsc/bsc_stats.h b/include/osmocom/bsc/bsc_stats.h
new file mode 100644
index 000000000..9140f852e
--- /dev/null
+++ b/include/osmocom/bsc/bsc_stats.h
@@ -0,0 +1,112 @@
+/* osmo-bsc statistics */
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <osmocom/core/rate_ctr.h>
+
+struct osmo_stat_item_group_desc;
+struct gsm_network;
+
+/* OsmoBSC rate_ctr indexes */
+enum {
+ BSC_CTR_ASSIGNMENT_ATTEMPTED,
+ BSC_CTR_ASSIGNMENT_COMPLETED,
+ BSC_CTR_ASSIGNMENT_STOPPED,
+ BSC_CTR_ASSIGNMENT_NO_CHANNEL,
+ BSC_CTR_ASSIGNMENT_TIMEOUT,
+ BSC_CTR_ASSIGNMENT_FAILED,
+ BSC_CTR_ASSIGNMENT_ERROR,
+ BSC_CTR_HANDOVER_ATTEMPTED,
+ BSC_CTR_HANDOVER_COMPLETED,
+ BSC_CTR_HANDOVER_STOPPED,
+ BSC_CTR_HANDOVER_NO_CHANNEL,
+ BSC_CTR_HANDOVER_TIMEOUT,
+ BSC_CTR_HANDOVER_FAILED,
+ BSC_CTR_HANDOVER_ERROR,
+ BSC_CTR_INTRA_CELL_HO_ATTEMPTED,
+ BSC_CTR_INTRA_CELL_HO_COMPLETED,
+ BSC_CTR_INTRA_CELL_HO_STOPPED,
+ BSC_CTR_INTRA_CELL_HO_NO_CHANNEL,
+ BSC_CTR_INTRA_CELL_HO_TIMEOUT,
+ BSC_CTR_INTRA_CELL_HO_FAILED,
+ BSC_CTR_INTRA_CELL_HO_ERROR,
+ BSC_CTR_INTRA_BSC_HO_ATTEMPTED,
+ BSC_CTR_INTRA_BSC_HO_COMPLETED,
+ BSC_CTR_INTRA_BSC_HO_STOPPED,
+ BSC_CTR_INTRA_BSC_HO_NO_CHANNEL,
+ BSC_CTR_INTRA_BSC_HO_TIMEOUT,
+ BSC_CTR_INTRA_BSC_HO_FAILED,
+ BSC_CTR_INTRA_BSC_HO_ERROR,
+ BSC_CTR_INTER_BSC_HO_OUT_ATTEMPTED,
+ BSC_CTR_INTER_BSC_HO_OUT_COMPLETED,
+ BSC_CTR_INTER_BSC_HO_OUT_STOPPED,
+ BSC_CTR_INTER_BSC_HO_OUT_TIMEOUT,
+ BSC_CTR_INTER_BSC_HO_OUT_FAILED,
+ BSC_CTR_INTER_BSC_HO_OUT_ERROR,
+ BSC_CTR_INTER_BSC_HO_IN_ATTEMPTED,
+ BSC_CTR_INTER_BSC_HO_IN_COMPLETED,
+ BSC_CTR_INTER_BSC_HO_IN_STOPPED,
+ BSC_CTR_INTER_BSC_HO_IN_NO_CHANNEL,
+ BSC_CTR_INTER_BSC_HO_IN_FAILED,
+ BSC_CTR_INTER_BSC_HO_IN_TIMEOUT,
+ BSC_CTR_INTER_BSC_HO_IN_ERROR,
+ BSC_CTR_SRVCC_ATTEMPTED,
+ BSC_CTR_SRVCC_COMPLETED,
+ BSC_CTR_SRVCC_STOPPED,
+ BSC_CTR_SRVCC_NO_CHANNEL,
+ BSC_CTR_SRVCC_TIMEOUT,
+ BSC_CTR_SRVCC_FAILED,
+ BSC_CTR_SRVCC_ERROR,
+ BSC_CTR_PAGING_ATTEMPTED,
+ BSC_CTR_PAGING_DETACHED,
+ BSC_CTR_PAGING_RESPONDED,
+ BSC_CTR_PAGING_NO_ACTIVE_PAGING,
+ BSC_CTR_UNKNOWN_UNIT_ID,
+ BSC_CTR_MSCPOOL_SUBSCR_NO_MSC,
+ BSC_CTR_MSCPOOL_EMERG_FORWARDED,
+ BSC_CTR_MSCPOOL_EMERG_LOST,
+ BSC_CTR_ALL_ALLOCATED_SDCCH,
+ BSC_CTR_ALL_ALLOCATED_STATIC_SDCCH,
+ BSC_CTR_ALL_ALLOCATED_TCH,
+ BSC_CTR_ALL_ALLOCATED_STATIC_TCH,
+};
+
+extern const struct rate_ctr_desc bsc_ctr_description[];
+extern const struct rate_ctr_group_desc bsc_ctrg_desc;
+
+/* OsmoBSC stat_item indexes */
+enum {
+ BSC_STAT_NUM_BTS_OML_CONNECTED,
+ BSC_STAT_NUM_BTS_ALL_TRX_RSL_CONNECTED,
+ BSC_STAT_NUM_BTS_TOTAL,
+ BSC_STAT_NUM_TRX_RSL_CONNECTED,
+ BSC_STAT_NUM_TRX_TOTAL,
+ BSC_STAT_NUM_MSC_CONNECTED,
+ BSC_STAT_NUM_MSC_TOTAL,
+};
+
+/* BTS counter index if a BTS could not be found
+ * Currently we are limited to bts 0 - 255 in the VTY, but that might change in
+ * the future so use 2**16 */
+#define BTS_STAT_IDX_UNKNOWN (UINT16_MAX + 1)
+
+extern const struct osmo_stat_item_group_desc bsc_statg_desc;
+
+void bsc_update_connection_stats(struct gsm_network *net);
+void bsc_update_time_cc_all_allocated(struct gsm_network *net);
diff --git a/include/osmocom/bsc/bssmap_reset.h b/include/osmocom/bsc/bssmap_reset.h
index f90f5ec56..fcd850b8b 100644
--- a/include/osmocom/bsc/bssmap_reset.h
+++ b/include/osmocom/bsc/bssmap_reset.h
@@ -27,4 +27,5 @@ struct bssmap_reset {
struct bssmap_reset *bssmap_reset_alloc(void *ctx, const char *label, const struct bssmap_reset_cfg *cfg);
bool bssmap_reset_is_conn_ready(const struct bssmap_reset *bssmap_reset);
+void bssmap_reset_resend_reset(struct bssmap_reset *bssmap_reset);
void bssmap_reset_term_and_free(struct bssmap_reset *bssmap_reset);
diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h
index 1d566f5d1..922753c9e 100644
--- a/include/osmocom/bsc/bts.h
+++ b/include/osmocom/bsc/bts.h
@@ -27,6 +27,13 @@ enum bts_counter_id {
BTS_CTR_CHREQ_ATTEMPTED_OTHER,
BTS_CTR_CHREQ_ATTEMPTED_UNKNOWN,
BTS_CTR_CHREQ_SUCCESSFUL,
+ BTS_CTR_CHREQ_SUCCESSFUL_EMERG,
+ BTS_CTR_CHREQ_SUCCESSFUL_CALL,
+ BTS_CTR_CHREQ_SUCCESSFUL_LOCATION_UPD,
+ BTS_CTR_CHREQ_SUCCESSFUL_PAG,
+ BTS_CTR_CHREQ_SUCCESSFUL_PDCH,
+ BTS_CTR_CHREQ_SUCCESSFUL_OTHER,
+ BTS_CTR_CHREQ_SUCCESSFUL_UNKNOWN,
BTS_CTR_CHREQ_NO_CHANNEL,
BTS_CTR_CHREQ_MAX_DELAY_EXCEEDED,
BTS_CTR_CHAN_RF_FAIL,
@@ -124,6 +131,13 @@ enum bts_counter_id {
BTS_CTR_INTRA_BSC_HO_TIMEOUT,
BTS_CTR_INTRA_BSC_HO_FAILED,
BTS_CTR_INTRA_BSC_HO_ERROR,
+ BTS_CTR_INCOMING_INTRA_BSC_HO_ATTEMPTED,
+ BTS_CTR_INCOMING_INTRA_BSC_HO_COMPLETED,
+ BTS_CTR_INCOMING_INTRA_BSC_HO_STOPPED,
+ BTS_CTR_INCOMING_INTRA_BSC_HO_NO_CHANNEL,
+ BTS_CTR_INCOMING_INTRA_BSC_HO_TIMEOUT,
+ BTS_CTR_INCOMING_INTRA_BSC_HO_FAILED,
+ BTS_CTR_INCOMING_INTRA_BSC_HO_ERROR,
BTS_CTR_INTER_BSC_HO_OUT_ATTEMPTED,
BTS_CTR_INTER_BSC_HO_OUT_COMPLETED,
BTS_CTR_INTER_BSC_HO_OUT_STOPPED,
@@ -144,6 +158,35 @@ enum bts_counter_id {
BTS_CTR_SRVCC_TIMEOUT,
BTS_CTR_SRVCC_FAILED,
BTS_CTR_SRVCC_ERROR,
+ BTS_CTR_ALL_ALLOCATED_SDCCH,
+ BTS_CTR_ALL_ALLOCATED_STATIC_SDCCH,
+ BTS_CTR_ALL_ALLOCATED_TCH,
+ BTS_CTR_ALL_ALLOCATED_STATIC_TCH,
+ BTS_CTR_CM_SERV_REJ,
+ BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_HLR,
+ BTS_CTR_CM_SERV_REJ_ILLEGAL_MS,
+ BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_VLR,
+ BTS_CTR_CM_SERV_REJ_IMEI_NOT_ACCEPTED,
+ BTS_CTR_CM_SERV_REJ_ILLEGAL_ME,
+ BTS_CTR_CM_SERV_REJ_PLMN_NOT_ALLOWED,
+ BTS_CTR_CM_SERV_REJ_LOC_NOT_ALLOWED,
+ BTS_CTR_CM_SERV_REJ_ROAMING_NOT_ALLOWED,
+ BTS_CTR_CM_SERV_REJ_NETWORK_FAILURE,
+ BTS_CTR_CM_SERV_REJ_SYNCH_FAILURE,
+ BTS_CTR_CM_SERV_REJ_CONGESTION,
+ BTS_CTR_CM_SERV_REJ_SRV_OPT_NOT_SUPPORTED,
+ BTS_CTR_CM_SERV_REJ_RQD_SRV_OPT_NOT_SUPPORTED,
+ BTS_CTR_CM_SERV_REJ_SRV_OPT_TMP_OUT_OF_ORDER,
+ BTS_CTR_CM_SERV_REJ_CALL_CAN_NOT_BE_IDENTIFIED,
+ BTS_CTR_CM_SERV_REJ_INCORRECT_MESSAGE,
+ BTS_CTR_CM_SERV_REJ_INVALID_MANDANTORY_INF,
+ BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_IMPLEMENTED,
+ BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_COMPATIBLE,
+ BTS_CTR_CM_SERV_REJ_INF_ELEME_NOT_IMPLEMENTED,
+ BTS_CTR_CM_SERV_REJ_CONDTIONAL_IE_ERROR,
+ BTS_CTR_CM_SERV_REJ_MSG_NOT_COMPATIBLE,
+ BTS_CTR_CM_SERV_REJ_PROTOCOL_ERROR,
+ BTS_CTR_CM_SERV_REJ_RETRY_IN_NEW_CELL,
};
extern const struct rate_ctr_desc bts_ctr_description[];
@@ -175,6 +218,8 @@ enum {
BTS_STAT_RSL_CONNECTED,
BTS_STAT_LCHAN_BORKEN,
BTS_STAT_TS_BORKEN,
+ BTS_STAT_NUM_TRX_RSL_CONNECTED,
+ BTS_STAT_NUM_TRX_TOTAL,
};
extern const struct osmo_stat_item_desc bts_stat_desc[];
@@ -545,9 +590,16 @@ struct gsm_bts {
struct llist_head oml_fail_rep;
struct llist_head chan_rqd_queue;
- /* osmocom specific FACCH/SACCH repetition mode flags set by VTY to
- * enable/disable certain ACCH repeation features individually */
- struct abis_rsl_osmo_rep_acch_cap repeated_acch_policy;
+ /* ACCH Repetition capabilities */
+ struct abis_rsl_osmo_rep_acch_cap rep_acch_cap;
+
+ /* ACCH Temporary overpower capabilities */
+ struct abis_rsl_osmo_temp_ovp_acch_cap top_acch_cap;
+ /* Channel mode(s) for which to allow TOP */
+ enum {
+ TOP_ACCH_CHAN_MODE_ANY = 0, /* Any kind of channel mode */
+ TOP_ACCH_CHAN_MODE_SPEECH_V3, /* Speech channels using AMR codec */
+ } top_acch_chan_mode;
/* MS/BS Power Control parameters */
struct gsm_power_ctrl_params ms_power_ctrl;
@@ -566,6 +618,14 @@ struct gsm_bts {
/* Is Fast return to LTE allowed during Chan Release in this BTS? */
bool srvcc_fast_return_allowed;
+
+ /* At what point in the channel allocation sequence to dispatch the Immediate Assignment (Abis optimization) */
+ enum imm_ass_time imm_ass_time;
+
+ struct osmo_time_cc all_allocated_sdcch;
+ struct osmo_time_cc all_allocated_static_sdcch;
+ struct osmo_time_cc all_allocated_tch;
+ struct osmo_time_cc all_allocated_static_tch;
};
#define GSM_BTS_SI2Q(bts, i) (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i])
@@ -702,8 +762,6 @@ void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value);
void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data);
-int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan);
-
int gsm_bts_set_system_infos(struct gsm_bts *bts);
int gsm_bts_set_c0_power_red(struct gsm_bts *bts, const uint8_t red);
diff --git a/include/osmocom/bsc/bts_trx.h b/include/osmocom/bsc/bts_trx.h
index 4d705d0d9..c8df9d9c4 100644
--- a/include/osmocom/bsc/bts_trx.h
+++ b/include/osmocom/bsc/bts_trx.h
@@ -95,7 +95,6 @@ void gsm_trx_lock_rf(struct gsm_bts_trx *trx, bool locked, const char *reason);
bool trx_is_usable(const struct gsm_bts_trx *trx);
void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data);
-int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan);
bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx);
int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx);
diff --git a/include/osmocom/bsc/chan_counts.h b/include/osmocom/bsc/chan_counts.h
new file mode 100644
index 000000000..9f73bc418
--- /dev/null
+++ b/include/osmocom/bsc/chan_counts.h
@@ -0,0 +1,76 @@
+/* API to count total, allocated and free channels of all types */
+#pragma once
+
+struct gsm_bts;
+struct gsm_bts_trx;
+
+/* First array index to typedef chan_counts_arr. */
+enum chan_counts_dim1 {
+ CHAN_COUNTS1_ALL = 0,
+ CHAN_COUNTS1_STATIC = 1,
+ CHAN_COUNTS1_DYNAMIC = 2,
+ _CHAN_COUNTS1_NUM
+};
+
+/* Second array index to typedef chan_counts_arr. */
+enum chan_counts_dim2 {
+ /* The maximum possible nr of lchans of this type. Counts all dynamic timeslots as if they are fully available
+ * for this type, regardless of the current pchan mode. (For CHAN_COUNTS1_STATIC, of course no dyn TS are counted
+ * at all.) */
+ CHAN_COUNTS2_MAX_TOTAL = 0,
+ /* Like MAX_TOTAL, but as soon as dynamic timeslots are switched to a specific pchan kind, current_total shrinks
+ * to count only currently present lchans (used and unused). */
+ CHAN_COUNTS2_CURRENT_TOTAL = 1,
+ /* Currently used lchans of this type. To get currently free lchans, calculate CURRENT_TOTAL - ALLOCATED. */
+ CHAN_COUNTS2_ALLOCATED = 2,
+ /* Currently assignable lchans of this type, same as CURRENT_TOTAL - ALLOCATED. */
+ CHAN_COUNTS2_FREE = 3,
+ _CHAN_COUNTS2_NUM
+};
+
+struct chan_counts {
+ unsigned int val[_CHAN_COUNTS1_NUM][_CHAN_COUNTS2_NUM][_GSM_LCHAN_MAX];
+};
+
+void chan_counts_for_bts(struct chan_counts *bts_counts, const struct gsm_bts *bts);
+void chan_counts_for_trx(struct chan_counts *trx_counts, const struct gsm_bts_trx *trx);
+
+static inline void chan_counts_zero(struct chan_counts *counts)
+{
+ *counts = (struct chan_counts){0};
+}
+
+static inline void chan_counts_dim3_add(struct chan_counts *dst,
+ enum chan_counts_dim1 dst_dim1, enum chan_counts_dim2 dst_dim2,
+ const struct chan_counts *add,
+ enum chan_counts_dim1 add_dim1, enum chan_counts_dim2 add_dim2)
+{
+ int i;
+ for (i = 0; i < _GSM_LCHAN_MAX; i++)
+ dst->val[dst_dim1][dst_dim2][i] += add->val[add_dim1][add_dim2][i];
+}
+
+static inline void chan_counts_dim3_sub(struct chan_counts *dst,
+ enum chan_counts_dim1 dst_dim1, enum chan_counts_dim2 dst_dim2,
+ const struct chan_counts *sub,
+ enum chan_counts_dim1 sub_dim1, enum chan_counts_dim2 sub_dim2)
+{
+ int i;
+ for (i = 0; i < _GSM_LCHAN_MAX; i++)
+ dst->val[dst_dim1][dst_dim2][i] -= sub->val[sub_dim1][sub_dim2][i];
+}
+
+static inline void chan_counts_dim2_add(struct chan_counts *dst, enum chan_counts_dim1 dst_dim1,
+ const struct chan_counts *add, enum chan_counts_dim1 add_dim1)
+{
+ int i;
+ for (i = 0; i < _CHAN_COUNTS2_NUM; i++)
+ chan_counts_dim3_add(dst, dst_dim1, i, add, add_dim1, i);
+}
+
+static inline void chan_counts_add(struct chan_counts *dst, const struct chan_counts *add)
+{
+ int i;
+ for (i = 0; i < _CHAN_COUNTS1_NUM; i++)
+ chan_counts_dim2_add(dst, i, add, i);
+}
diff --git a/include/osmocom/bsc/debug.h b/include/osmocom/bsc/debug.h
index 4ad61b42e..a3cad68b7 100644
--- a/include/osmocom/bsc/debug.h
+++ b/include/osmocom/bsc/debug.h
@@ -29,6 +29,7 @@ enum {
DCBS,
DLCS,
DRESET,
+ DLOOP,
Debug_LastEntry,
};
diff --git a/include/osmocom/bsc/gsm_04_08_rr.h b/include/osmocom/bsc/gsm_04_08_rr.h
index 5ddee7fc3..1f50ef982 100644
--- a/include/osmocom/bsc/gsm_04_08_rr.h
+++ b/include/osmocom/bsc/gsm_04_08_rr.h
@@ -33,9 +33,6 @@ int gsm48_send_rr_app_info(struct gsm_lchan *lchan, uint8_t apdu_id, uint8_t apd
int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode);
int gsm48_rx_rr_modif_ack(struct msgb *msg);
int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg);
-int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn);
-int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
- enum gsm48_reject_value value);
struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value);
struct msgb *gsm48_create_loc_upd_rej(uint8_t cause);
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 159aa7a53..4e26ec152 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -18,6 +18,7 @@
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/tdef.h>
+#include <osmocom/core/time_cc.h>
#include <osmocom/crypt/auth.h>
@@ -138,6 +139,14 @@ extern const struct value_string assign_for_names[];
static inline const char *assign_for_name(enum assign_for assign_for)
{ return get_value_string(assign_for_names, assign_for); }
+/* If .present is false, use the default value defined elsewhere. If true, use .val below.
+ * (A practical benefit of this is that the default initialization sets .present to false, so that even if a .val == 0
+ * is a valid value, a struct containing this as member does not need to explicitly set .val = INVALID_VAL_CONSTANT.) */
+struct optional_val {
+ bool present;
+ int val;
+};
+
/* Information retrieved during an Assignment Request from the MSC. This is storage of the Assignment instructions
* parsed from the Assignment Request message, to pass on until the gscon and assignment FSMs have decided whether an
* Assignment is actually going to be carried out. Should remain unchanged after initial decoding. */
@@ -165,12 +174,12 @@ struct assignment_request {
* target_lchan to it. */
struct gsm_lchan *target_lchan;
- /* TSC Set to use, or -1 for automatically determining the TSC Set to use. Valid range is 1 to 4, as described
- * in 3GPP TS 45.002. */
- int tsc_set;
- /* TSC to use, or -1 for automatically determining the TSC to use. Valid range is 0 to 7, as described in 3GPP
- * TS 45.002. */
- int tsc;
+ /* The TSC Set to use if 'use' is true, otherwise automatically determine the TSC Set value to use. Valid range
+ * is 1 to 4, as described in 3GPP TS 45.002. */
+ struct optional_val tsc_set;
+ /* The TSC to use if 'use' is true, otherwise automatically determine the TSC value to use. Valid range is 0 to
+ * 7, as described in 3GPP TS 45.002. */
+ struct optional_val tsc;
};
/* State of an ongoing Assignment, while the assignment_fsm is still busy. This serves as state separation to keep the
@@ -603,8 +612,16 @@ extern const struct value_string lchan_activate_mode_names[];
static inline const char *lchan_activate_mode_name(enum lchan_activate_for activ_for)
{ return get_value_string(lchan_activate_mode_names, activ_for); }
+enum imm_ass_time {
+ IMM_ASS_TIME_POST_CHAN_ACK = 0,
+ IMM_ASS_TIME_PRE_CHAN_ACK,
+ IMM_ASS_TIME_PRE_TS_ACK,
+};
+
struct lchan_activate_info {
enum lchan_activate_for activ_for;
+ /* If activ_for == ACTIVATE_FOR_MS_CHANNEL_REQUEST, the original CHREQ reason. */
+ enum gsm_chreq_reason_t chreq_reason;
struct gsm_subscriber_connection *for_conn;
struct channel_mode_and_rate ch_mode_rate;
struct gsm_encr encr;
@@ -617,14 +634,19 @@ struct lchan_activate_info {
bool ta_known;
uint8_t ta;
- /* TSC Set to use, or -1 for automatically determining the TSC Set to use. Valid range is 1 to 4, as described
- * in 3GPP TS 45.002. */
- int tsc_set;
- /* TSC to use, or -1 for automatically determining the TSC to use. Valid range is 0 to 7, as described in 3GPP
- * TS 45.002. */
- int tsc;
+ /* The TSC Set to use if 'use' is true, otherwise automatically determine the TSC Set value to use. Valid range
+ * is 1 to 4, as described in 3GPP TS 45.002. */
+ struct optional_val tsc_set;
+ /* The TSC to use if 'use' is true, otherwise automatically determine the TSC value to use. Valid range is 0 to
+ * 7, as described in 3GPP TS 45.002. */
+ struct optional_val tsc;
bool vamos;
+
+ /* A copy of bts->imm_ass_time at the time where Channel Activation was requested. A change in the VTY
+ * configuration has immediate effect on the value, so make sure we don't get mixed up when it gets changed
+ * while a channel activation is in progress. */
+ enum imm_ass_time imm_ass_time;
};
enum lchan_modify_for {
@@ -643,12 +665,12 @@ struct lchan_modify_info {
bool requires_voice_stream;
uint16_t msc_assigned_cic;
- /* TSC Set to use, or -1 for automatically determining the TSC Set to use. Valid range is 1 to 4, as described
- * in 3GPP TS 45.002. */
- int tsc_set;
- /* TSC to use, or -1 for automatically determining the TSC to use. Valid range is 0 to 7, as described in 3GPP
- * TS 45.002. */
- int tsc;
+ /* The TSC Set to use if 'use' is true, otherwise automatically determine the TSC Set value to use. Valid range
+ * is 1 to 4, as described in 3GPP TS 45.002. */
+ struct optional_val tsc_set;
+ /* The TSC to use if 'use' is true, otherwise automatically determine the TSC value to use. Valid range is 0 to
+ * 7, as described in 3GPP TS 45.002. */
+ struct optional_val tsc;
bool vamos;
};
@@ -656,6 +678,27 @@ struct lchan_modify_info {
#define INTERF_DBM_UNKNOWN 0
#define INTERF_BAND_UNKNOWN 0xff
+/* 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 {
+ /* 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;
+ /* Number of SACCH blocks to skip (for dynamic mode) */
+ int skip_block_num;
+};
+
struct gsm_lchan {
/* The TS that we're part of */
struct gsm_bts_trx_ts *ts;
@@ -686,7 +729,9 @@ struct gsm_lchan {
* occur later, e.g. during release, that we don't send a NACK out of context. */
bool concluded;
enum gsm0808_cause gsm0808_error_cause;
+ /* Actually used TSC Set. */
int tsc_set;
+ /* Actually used TSC. */
uint8_t tsc;
} activate;
@@ -700,7 +745,9 @@ struct gsm_lchan {
struct channel_mode_and_rate ch_mode_rate;
struct gsm48_multi_rate_conf mr_conf_filtered;
+ /* Actually used TSC Set. */
int tsc_set;
+ /* Actually used TSC. */
uint8_t tsc;
bool concluded;
} modify;
@@ -799,6 +846,8 @@ struct gsm_lchan {
/* Actual reported interference band index, or INTERF_BAND_UNKNOWN if this lchan was not included in the most
* recent Resource Indication. */
uint8_t interf_band;
+ /* MS power control state */
+ struct lchan_power_ctrl_state ms_power_ctrl;
};
/* One Timeslot in a TRX */
@@ -1077,12 +1126,15 @@ gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class,
int gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
uint8_t ts_nr, uint8_t lchan_nr, bool vamos_is_secondary);
int gsm_lchan2chan_nr(const struct gsm_lchan *lchan, bool allow_osmo_cbits);
+int gsm_lchan_and_pchan2chan_nr(const struct gsm_lchan *lchan, enum gsm_phys_chan_config pchan, bool allow_osmo_cbits);
int gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
const struct gsm_lchan *lchan,
uint8_t tsc, bool allow_osmo_cbits);
-int gsm48_lchan2chan_desc_as_configured(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan,
- uint8_t tsc);
+int gsm48_lchan_and_pchan2chan_desc(struct gsm48_chan_desc *cd,
+ const struct gsm_lchan *lchan,
+ enum gsm_phys_chan_config pchan,
+ uint8_t tsc, bool allow_osmo_cbits);
uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts);
@@ -1098,163 +1150,6 @@ struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn);
void conn_update_ms_power_class(struct gsm_subscriber_connection *conn, uint8_t power_class);
void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm);
-enum {
- BSC_CTR_ASSIGNMENT_ATTEMPTED,
- BSC_CTR_ASSIGNMENT_COMPLETED,
- BSC_CTR_ASSIGNMENT_STOPPED,
- BSC_CTR_ASSIGNMENT_NO_CHANNEL,
- BSC_CTR_ASSIGNMENT_TIMEOUT,
- BSC_CTR_ASSIGNMENT_FAILED,
- BSC_CTR_ASSIGNMENT_ERROR,
- BSC_CTR_HANDOVER_ATTEMPTED,
- BSC_CTR_HANDOVER_COMPLETED,
- BSC_CTR_HANDOVER_STOPPED,
- BSC_CTR_HANDOVER_NO_CHANNEL,
- BSC_CTR_HANDOVER_TIMEOUT,
- BSC_CTR_HANDOVER_FAILED,
- BSC_CTR_HANDOVER_ERROR,
- BSC_CTR_INTRA_CELL_HO_ATTEMPTED,
- BSC_CTR_INTRA_CELL_HO_COMPLETED,
- BSC_CTR_INTRA_CELL_HO_STOPPED,
- BSC_CTR_INTRA_CELL_HO_NO_CHANNEL,
- BSC_CTR_INTRA_CELL_HO_TIMEOUT,
- BSC_CTR_INTRA_CELL_HO_FAILED,
- BSC_CTR_INTRA_CELL_HO_ERROR,
- BSC_CTR_INTRA_BSC_HO_ATTEMPTED,
- BSC_CTR_INTRA_BSC_HO_COMPLETED,
- BSC_CTR_INTRA_BSC_HO_STOPPED,
- BSC_CTR_INTRA_BSC_HO_NO_CHANNEL,
- BSC_CTR_INTRA_BSC_HO_TIMEOUT,
- BSC_CTR_INTRA_BSC_HO_FAILED,
- BSC_CTR_INTRA_BSC_HO_ERROR,
- BSC_CTR_INTER_BSC_HO_OUT_ATTEMPTED,
- BSC_CTR_INTER_BSC_HO_OUT_COMPLETED,
- BSC_CTR_INTER_BSC_HO_OUT_STOPPED,
- BSC_CTR_INTER_BSC_HO_OUT_TIMEOUT,
- BSC_CTR_INTER_BSC_HO_OUT_FAILED,
- BSC_CTR_INTER_BSC_HO_OUT_ERROR,
- BSC_CTR_INTER_BSC_HO_IN_ATTEMPTED,
- BSC_CTR_INTER_BSC_HO_IN_COMPLETED,
- BSC_CTR_INTER_BSC_HO_IN_STOPPED,
- BSC_CTR_INTER_BSC_HO_IN_NO_CHANNEL,
- BSC_CTR_INTER_BSC_HO_IN_FAILED,
- BSC_CTR_INTER_BSC_HO_IN_TIMEOUT,
- BSC_CTR_INTER_BSC_HO_IN_ERROR,
- BSC_CTR_SRVCC_ATTEMPTED,
- BSC_CTR_SRVCC_COMPLETED,
- BSC_CTR_SRVCC_STOPPED,
- BSC_CTR_SRVCC_NO_CHANNEL,
- BSC_CTR_SRVCC_TIMEOUT,
- BSC_CTR_SRVCC_FAILED,
- BSC_CTR_SRVCC_ERROR,
- BSC_CTR_PAGING_ATTEMPTED,
- BSC_CTR_PAGING_DETACHED,
- BSC_CTR_PAGING_RESPONDED,
- BSC_CTR_PAGING_NO_ACTIVE_PAGING,
- BSC_CTR_UNKNOWN_UNIT_ID,
- BSC_CTR_MSCPOOL_SUBSCR_NO_MSC,
- BSC_CTR_MSCPOOL_EMERG_FORWARDED,
- BSC_CTR_MSCPOOL_EMERG_LOST,
-};
-
-static const struct rate_ctr_desc bsc_ctr_description[] = {
- [BSC_CTR_ASSIGNMENT_ATTEMPTED] = {"assignment:attempted", "Assignment attempts"},
- [BSC_CTR_ASSIGNMENT_COMPLETED] = {"assignment:completed", "Assignment completed"},
- [BSC_CTR_ASSIGNMENT_STOPPED] = {"assignment:stopped", "Connection ended during Assignment"},
- [BSC_CTR_ASSIGNMENT_NO_CHANNEL] = {"assignment:no_channel", "Failure to allocate lchan for Assignment"},
- [BSC_CTR_ASSIGNMENT_TIMEOUT] = {"assignment:timeout", "Assignment timed out"},
- [BSC_CTR_ASSIGNMENT_FAILED] = {"assignment:failed", "Received Assignment Failure message"},
- [BSC_CTR_ASSIGNMENT_ERROR] = {"assignment:error", "Assignment failed for other reason"},
-
- [BSC_CTR_HANDOVER_ATTEMPTED] = {"handover:attempted", "Intra-BSC handover attempts"},
- [BSC_CTR_HANDOVER_COMPLETED] = {"handover:completed", "Intra-BSC handover completed"},
- [BSC_CTR_HANDOVER_STOPPED] = {"handover:stopped", "Connection ended during HO"},
- [BSC_CTR_HANDOVER_NO_CHANNEL] = {"handover:no_channel", "Failure to allocate lchan for HO"},
- [BSC_CTR_HANDOVER_TIMEOUT] = {"handover:timeout", "Handover timed out"},
- [BSC_CTR_HANDOVER_FAILED] = {"handover:failed", "Received Handover Fail messages"},
- [BSC_CTR_HANDOVER_ERROR] = {"handover:error", "Re-assignment failed for other reason"},
-
- [BSC_CTR_INTRA_CELL_HO_ATTEMPTED] = {"intra_cell_ho:attempted", "Intra-Cell handover attempts"},
- [BSC_CTR_INTRA_CELL_HO_COMPLETED] = {"intra_cell_ho:completed", "Intra-Cell handover completed"},
- [BSC_CTR_INTRA_CELL_HO_STOPPED] = {"intra_cell_ho:stopped", "Connection ended during HO"},
- [BSC_CTR_INTRA_CELL_HO_NO_CHANNEL] = {"intra_cell_ho:no_channel", "Failure to allocate lchan for HO"},
- [BSC_CTR_INTRA_CELL_HO_TIMEOUT] = {"intra_cell_ho:timeout", "Handover timed out"},
- [BSC_CTR_INTRA_CELL_HO_FAILED] = {"intra_cell_ho:failed", "Received Handover Fail messages"},
- [BSC_CTR_INTRA_CELL_HO_ERROR] = {"intra_cell_ho:error", "Re-assignment failed for other reason"},
-
- [BSC_CTR_INTRA_BSC_HO_ATTEMPTED] = {"intra_bsc_ho:attempted", "Intra-BSC handover attempts"},
- [BSC_CTR_INTRA_BSC_HO_COMPLETED] = {"intra_bsc_ho:completed", "Intra-BSC handover completed"},
- [BSC_CTR_INTRA_BSC_HO_STOPPED] = {"intra_bsc_ho:stopped", "Connection ended during HO"},
- [BSC_CTR_INTRA_BSC_HO_NO_CHANNEL] = {"intra_bsc_ho:no_channel", "Failure to allocate lchan for HO"},
- [BSC_CTR_INTRA_BSC_HO_TIMEOUT] = {"intra_bsc_ho:timeout", "Handover timed out"},
- [BSC_CTR_INTRA_BSC_HO_FAILED] = {"intra_bsc_ho:failed", "Received Handover Fail messages"},
- [BSC_CTR_INTRA_BSC_HO_ERROR] = {"intra_bsc_ho:error", "Re-assignment failed for other reason"},
-
- [BSC_CTR_INTER_BSC_HO_OUT_ATTEMPTED] = {"interbsc_ho_out:attempted",
- "Attempts to handover to remote BSS"},
- [BSC_CTR_INTER_BSC_HO_OUT_COMPLETED] = {"interbsc_ho_out:completed",
- "Handover to remote BSS completed"},
- [BSC_CTR_INTER_BSC_HO_OUT_STOPPED] = {"interbsc_ho_out:stopped", "Connection ended during HO"},
- [BSC_CTR_INTER_BSC_HO_OUT_TIMEOUT] = {"interbsc_ho_out:timeout", "Handover timed out"},
- [BSC_CTR_INTER_BSC_HO_OUT_FAILED] = {"interbsc_ho_out:failed", "Received Handover Fail message"},
- [BSC_CTR_INTER_BSC_HO_OUT_ERROR] = {"interbsc_ho_out:error",
- "Handover to remote BSS failed for other reason"},
-
- [BSC_CTR_INTER_BSC_HO_IN_ATTEMPTED] = {"interbsc_ho_in:attempted",
- "Attempts to handover from remote BSS"},
- [BSC_CTR_INTER_BSC_HO_IN_COMPLETED] = {"interbsc_ho_in:completed",
- "Handover from remote BSS completed"},
- [BSC_CTR_INTER_BSC_HO_IN_STOPPED] = {"interbsc_ho_in:stopped", "Connection ended during HO"},
- [BSC_CTR_INTER_BSC_HO_IN_NO_CHANNEL] = {"interbsc_ho_in:no_channel",
- "Failure to allocate lchan for HO"},
- [BSC_CTR_INTER_BSC_HO_IN_TIMEOUT] = {"interbsc_ho_in:timeout", "Handover from remote BSS timed out"},
- [BSC_CTR_INTER_BSC_HO_IN_FAILED] = {"interbsc_ho_in:failed", "Received Handover Fail message"},
- [BSC_CTR_INTER_BSC_HO_IN_ERROR] = {"interbsc_ho_in:error",
- "Handover from remote BSS failed for other reason"},
-
- [BSC_CTR_SRVCC_ATTEMPTED] = {"srvcc:attempted", "Intra-BSC SRVCC attempts"},
- [BSC_CTR_SRVCC_COMPLETED] = {"srvcc:completed", "Intra-BSC SRVCC completed"},
- [BSC_CTR_SRVCC_STOPPED] = {"srvcc:stopped", "Connection ended during HO"},
- [BSC_CTR_SRVCC_NO_CHANNEL] = {"srvcc:no_channel", "Failure to allocate lchan for HO"},
- [BSC_CTR_SRVCC_TIMEOUT] = {"srvcc:timeout", "SRVCC timed out"},
- [BSC_CTR_SRVCC_FAILED] = {"srvcc:failed", "Received SRVCC Fail messages"},
- [BSC_CTR_SRVCC_ERROR] = {"srvcc:error", "Re-assignment failed for other reason"},
-
- [BSC_CTR_PAGING_ATTEMPTED] = {"paging:attempted", "Paging attempts for a subscriber"},
- [BSC_CTR_PAGING_DETACHED] = {"paging:detached", "Paging request send failures because no responsible BTS was found"},
- [BSC_CTR_PAGING_RESPONDED] = {"paging:responded", "Paging attempts with successful response"},
- [BSC_CTR_PAGING_NO_ACTIVE_PAGING] = {"paging:no_active_paging", "Paging response without an active paging request (arrived after paging expiration?)"},
-
- [BSC_CTR_UNKNOWN_UNIT_ID] = {"abis:unknown_unit_id", "Connection attempts from unknown IPA CCM Unit ID"},
-
- [BSC_CTR_MSCPOOL_SUBSCR_NO_MSC] = {"mscpool:subscr:no_msc",
- "Complete Layer 3 requests lost because no connected MSC is found available"},
- [BSC_CTR_MSCPOOL_EMERG_FORWARDED] = {"mscpool:emerg:forwarded",
- "Emergency call requests forwarded to an MSC (see also per-MSC counters"},
- [BSC_CTR_MSCPOOL_EMERG_LOST] = {"mscpool:emerg:lost",
- "Emergency call requests lost because no MSC was found available"},
-};
-
-
-
-static const struct rate_ctr_group_desc bsc_ctrg_desc = {
- "bsc",
- "base station controller",
- OSMO_STATS_CLASS_GLOBAL,
- ARRAY_SIZE(bsc_ctr_description),
- bsc_ctr_description,
-};
-
-/* Constants for the BSC stats */
-enum {
- BSC_STAT_NUM_BTS_TOTAL,
-};
-
-/* BTS counter index if a BTS could not be found
- * Currently we are limited to bts 0 - 255 in the VTY, but that might change in
- * the future so use 2**16 */
-#define BTS_STAT_IDX_UNKNOWN (UINT16_MAX + 1)
-
struct gsm_tz {
int override; /* if 0, use system's time zone instead. */
int hr; /* hour */
@@ -1263,12 +1158,6 @@ struct gsm_tz {
};
struct gsm_network {
- /* TODO MSCSPLIT the gsm_network struct is basically a kitchen sink for
- * global settings and variables, "madly" mixing BSC and MSC stuff. Split
- * this in e.g. struct osmo_bsc and struct osmo_msc, with the things
- * these have in common, like country and network code, put in yet
- * separate structs and placed as members in osmo_bsc and osmo_msc. */
-
struct osmo_plmn_id plmn;
/* bit-mask of permitted encryption algorithms. LSB=A5/0, MSB=A5/7 */
@@ -1348,8 +1237,15 @@ struct gsm_network {
struct osmo_timer_list bts_store_uptime_timer;
struct {
+ /* Single MGCP client configuration under msc node (also required for
+ * MGCP proxy when sccp-lite is used) */
struct mgcp_client_conf *conf;
- struct mgcp_client *client;
+
+ /* MGW pool, also includes the single MGCP client as fallback if no
+ * pool is configured. */
+ struct mgcp_client_pool *mgw_pool;
+
+ /* Timer definitions, the same for all MGW pool members */
struct osmo_tdef *tdefs;
} mgw;
@@ -1367,6 +1263,11 @@ struct gsm_network {
struct osmo_nri_ranges *null_nri_ranges;
struct smlc_config *smlc;
+
+ struct osmo_time_cc all_allocated_sdcch;
+ struct osmo_time_cc all_allocated_static_sdcch;
+ struct osmo_time_cc all_allocated_tch;
+ struct osmo_time_cc all_allocated_static_tch;
};
struct gsm_audio_support {
@@ -1427,6 +1328,9 @@ enum gsm_power_ctrl_meas_avg_algo {
/* MS/BS Power related measurement parameters */
struct gsm_power_ctrl_meas_params {
+ /* Are these measurement paremeters to be taken into account by loop? */
+ bool enabled;
+
/* 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 */
@@ -1465,6 +1369,9 @@ enum gsm_power_ctrl_mode {
GSM_PWR_CTRL_MODE_STATIC,
/* Send MS/BS Power [Parameters] IEs (dynamic mode) */
GSM_PWR_CTRL_MODE_DYN_BTS,
+ /* Do not send MS/BS Power IEs and use BSC Power Loop */
+ GSM_PWR_CTRL_MODE_DYN_BSC,
+
};
/* MS/BS Power Control Parameters */
@@ -1488,9 +1395,18 @@ struct gsm_power_ctrl_params {
/* 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: */
+ 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;
};
extern const struct gsm_power_ctrl_params power_ctrl_params_def;
+void power_ctrl_params_def_reset(struct gsm_power_ctrl_params *params,
+ enum gsm_power_ctrl_dir dir);
/* Interference Measurement Parameters */
struct gsm_interf_meas_params {
diff --git a/include/osmocom/bsc/handover.h b/include/osmocom/bsc/handover.h
index f67149142..58fea353e 100644
--- a/include/osmocom/bsc/handover.h
+++ b/include/osmocom/bsc/handover.h
@@ -17,7 +17,7 @@
else \
LOGP(DHODEC, level, "%s: " fmt, \
handover_status(conn), ## args); \
- } while(0)
+ } while (0)
struct gsm_network;
struct gsm_lchan;
diff --git a/include/osmocom/bsc/lchan_fsm.h b/include/osmocom/bsc/lchan_fsm.h
index e7a06d56a..eb8312e03 100644
--- a/include/osmocom/bsc/lchan_fsm.h
+++ b/include/osmocom/bsc/lchan_fsm.h
@@ -10,14 +10,14 @@
LOGPFSML((lchan)->fi, level, "(type=%s) " fmt, gsm_lchant_name((lchan)->type), ## args); \
else \
LOGP(DRSL, level, "%s (not initialized) " fmt, gsm_lchan_name(lchan), ## args); \
- } while(0)
+ } while (0)
#define LCHAN_SET_LAST_ERROR(LCHAN, fmt, args...) do { \
if ((LCHAN)->last_error) \
talloc_free((LCHAN)->last_error); \
(LCHAN)->last_error = talloc_asprintf((LCHAN)->ts->trx, fmt, ##args); \
LOG_LCHAN(LCHAN, LOGL_ERROR, "%s\n", (LCHAN)->last_error); \
- } while(0)
+ } while (0)
enum lchan_fsm_state {
LCHAN_ST_UNUSED,
@@ -72,7 +72,7 @@ static inline const char *lchan_state_name(struct gsm_lchan *lchan)
return lchan->fi ? osmo_fsm_inst_state_name(lchan->fi) : "NULL";
}
-static inline bool lchan_state_is(struct gsm_lchan *lchan, uint32_t state)
+static inline bool lchan_state_is(const struct gsm_lchan *lchan, uint32_t state)
{
return (!lchan->fi && state == LCHAN_ST_UNUSED)
|| (lchan->fi && lchan->fi->state == state);
diff --git a/include/osmocom/bsc/lchan_rtp_fsm.h b/include/osmocom/bsc/lchan_rtp_fsm.h
index 6ff8fe362..18ab348f2 100644
--- a/include/osmocom/bsc/lchan_rtp_fsm.h
+++ b/include/osmocom/bsc/lchan_rtp_fsm.h
@@ -7,7 +7,7 @@
else \
LOGP(DLMGCP, level, "%s (not initialized) " fmt, gsm_lchan_name(lchan), \
## args); \
- } while(0)
+ } while (0)
struct gsm_lchan;
struct mgcp_conn_peer;
diff --git a/include/osmocom/bsc/lcs_loc_req.h b/include/osmocom/bsc/lcs_loc_req.h
index ba677e867..86540f278 100644
--- a/include/osmocom/bsc/lcs_loc_req.h
+++ b/include/osmocom/bsc/lcs_loc_req.h
@@ -8,7 +8,7 @@
LOGPFSML((LOC_REQ)->fi, level, fmt, ## args); \
else \
LOGP(DLCS, level, "LCS Perf Loc Req: " fmt, ## args); \
- } while(0)
+ } while (0)
struct lcs_ta_req;
diff --git a/include/osmocom/bsc/lcs_ta_req.h b/include/osmocom/bsc/lcs_ta_req.h
index b9b7a4e58..bdfc14f60 100644
--- a/include/osmocom/bsc/lcs_ta_req.h
+++ b/include/osmocom/bsc/lcs_ta_req.h
@@ -11,7 +11,7 @@
LOGPFSML((TA_REQ)->fi, level, fmt, ## args); \
else \
LOGP(DLCS, level, "LCS TA Req: " fmt, ## args); \
- } while(0)
+ } while (0)
enum lcs_ta_req_fsm_event {
LCS_TA_REQ_EV_GOT_TA,
diff --git a/include/osmocom/bsc/neighbor_ident.h b/include/osmocom/bsc/neighbor_ident.h
index 0565d528c..c6a2c4237 100644
--- a/include/osmocom/bsc/neighbor_ident.h
+++ b/include/osmocom/bsc/neighbor_ident.h
@@ -77,6 +77,10 @@ void neighbor_ident_vty_init();
void neighbor_ident_vty_write_bts(struct vty *vty, const char *indent, struct gsm_bts *bts);
void neighbor_ident_vty_write_network(struct vty *vty, const char *indent);
+int neighbor_ident_add_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n);
+int neighbor_ident_del_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n);
+int neighbor_ident_ctrl_init(void);
+
int neighbors_check_cfg();
#define CELL_AB_VTY_PARAMS "arfcn <0-1023> bsic (<0-63>|any)"
@@ -86,6 +90,10 @@ int neighbors_check_cfg();
"for all BSICs / use any BSIC in this ARFCN\n"
void neighbor_ident_vty_parse_arfcn_bsic(struct cell_ab *ab, const char **argv);
+int neighbor_address_resolution(const struct gsm_network *net, const struct cell_ab *ab,
+ uint16_t lac, uint16_t cell_id,
+ struct osmo_cell_global_id_ps *res_cgi_ps);
+
struct ctrl_handle *neighbor_controlif_setup(struct gsm_network *net);
int neighbor_ctrl_cmds_install(struct gsm_network *net);
diff --git a/include/osmocom/bsc/osmo_bsc_rf.h b/include/osmocom/bsc/osmo_bsc_rf.h
index 56ac980ca..f88ccbf6a 100644
--- a/include/osmocom/bsc/osmo_bsc_rf.h
+++ b/include/osmocom/bsc/osmo_bsc_rf.h
@@ -60,7 +60,12 @@ const char *osmo_bsc_rf_get_policy_name(enum osmo_bsc_rf_policy policy);
enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts);
enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts);
enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts);
+enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_trx(struct gsm_bts_trx *trx);
+enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_trx(struct gsm_bts_trx *trx);
struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net);
void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd);
+char *bsc_rf_states_of_bts_c(void *ctx, struct gsm_bts *bts);
+char *bsc_rf_states_c(void *ctx);
+
#endif
diff --git a/include/osmocom/bsc/pcuif_proto.h b/include/osmocom/bsc/pcuif_proto.h
index 3e6f6510e..4921fc38d 100644
--- a/include/osmocom/bsc/pcuif_proto.h
+++ b/include/osmocom/bsc/pcuif_proto.h
@@ -26,6 +26,10 @@
#define PCU_IF_MSG_TXT_IND 0x70 /* Text indication for BTS */
#define PCU_IF_MSG_CONTAINER 0x80 /* Transparent container message */
+/* msg_type coming from BSC (inside PCU_IF_MSG_CONTAINER) */
+#define PCU_IF_MSG_NEIGH_ADDR_REQ 0x81 /* Neighbor Address Resolution Request */
+#define PCU_IF_MSG_NEIGH_ADDR_CNF 0x82 /* Neighbor Address Resolution Confirmation */
+
/* sapi */
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
@@ -226,6 +230,30 @@ struct gsm_pcu_if_container {
uint8_t data[0];
} __attribute__ ((packed));
+/*** Used inside container: NOTE: values must be network byte order here! ***/
+/* Neighbor Address Resolution Request */
+struct gsm_pcu_if_neigh_addr_req {
+ uint16_t local_lac;
+ uint16_t local_ci;
+ uint16_t tgt_arfcn;
+ uint8_t tgt_bsic;
+} __attribute__ ((packed));
+
+/* Neighbor Address Resolution Confirmation */
+struct gsm_pcu_if_neigh_addr_cnf {
+ struct gsm_pcu_if_neigh_addr_req orig_req;
+ uint8_t err_code; /* 0 success, !0 failed & below unset */
+ /* RAI + CI (CGI-PS): */
+ struct __attribute__ ((packed)) {
+ uint16_t mcc;
+ uint16_t mnc;
+ uint8_t mnc_3_digits;
+ uint16_t lac;
+ uint8_t rac;
+ uint16_t cell_identity;
+ } cgi_ps;
+} __attribute__ ((packed));
+
struct gsm_pcu_if {
/* context based information */
uint8_t msg_type; /* message type */
diff --git a/include/osmocom/bsc/power_control.h b/include/osmocom/bsc/power_control.h
new file mode 100644
index 000000000..82cbcb096
--- /dev/null
+++ b/include/osmocom/bsc/power_control.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/meas_rep.h>
+
+int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, const struct gsm_meas_rep *mr);
diff --git a/include/osmocom/bsc/signal.h b/include/osmocom/bsc/signal.h
index 4933703a8..084c30dd6 100644
--- a/include/osmocom/bsc/signal.h
+++ b/include/osmocom/bsc/signal.h
@@ -134,9 +134,9 @@ struct nm_statechg_signal_data {
struct gsm_nm_state *old_state;
struct gsm_nm_state *new_state;
- /* This pointer is vaold for TS 12.21 MO */
+ /* This pointer is valid for TS 12.21 MO */
struct abis_om_obj_inst *obj_inst;
- /* This pointer is vaold for RBS2000 MO */
+ /* This pointer is valid for RBS2000 MO */
struct abis_om2k_mo *om2k_mo;
};
diff --git a/include/osmocom/bsc/timeslot_fsm.h b/include/osmocom/bsc/timeslot_fsm.h
index f5e4b4ccb..526f3cf00 100644
--- a/include/osmocom/bsc/timeslot_fsm.h
+++ b/include/osmocom/bsc/timeslot_fsm.h
@@ -17,7 +17,7 @@
gsm_ts_name(ts), \
## args, \
(!fmt || !*fmt || fmt[strlen(fmt)-1] != '\n') ? "\n" : ""); \
- } while(0)
+ } while (0)
enum ts_fsm_state {
TS_ST_NOT_INITIALIZED,
@@ -42,9 +42,8 @@ enum ts_fsm_event {
TS_EV_PDCH_DEACT_NACK,
};
-void ts_fsm_init();
-
void ts_fsm_alloc(struct gsm_bts_trx_ts *ts);
+void ts_fsm_free(struct gsm_bts_trx_ts *ts);
bool ts_is_capable_of_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan);
bool ts_is_capable_of_lchant(struct gsm_bts_trx_ts *ts, enum gsm_chan_t type);
diff --git a/include/osmocom/bsc/vty.h b/include/osmocom/bsc/vty.h
index 9ea983541..70b973db3 100644
--- a/include/osmocom/bsc/vty.h
+++ b/include/osmocom/bsc/vty.h
@@ -23,6 +23,7 @@ struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
enum bsc_vty_node {
GSMNET_NODE = _LAST_OSMOVTY_NODE + 1,
+ MGW_NODE,
BTS_NODE,
TRX_NODE,
TS_NODE,
diff --git a/src/ipaccess/ipaccess-config.c b/src/ipaccess/ipaccess-config.c
index 02501bdb5..2d72d0e41 100644
--- a/src/ipaccess/ipaccess-config.c
+++ b/src/ipaccess/ipaccess-config.c
@@ -58,8 +58,6 @@
#include <osmocom/bsc/bss.h>
#include <osmocom/bsc/bts.h>
-struct gsm_network *bsc_gsmnet;
-
static int net_listen_testnr;
static int restart;
static bool get_attr;
@@ -503,7 +501,7 @@ static const struct value_string ipa_nvflag_strs[] = {
{ 0x0002, "static-gw" },
{ 0x0004, "no-dhcp-vsi" },
{ 0x0008, "dhcp-enabled" },
- { 0x0040, "led-disabled" },
+ { 0x0040, "led-enabled" },
{ 0x0100, "secondary-oml-enabled" },
{ 0x0200, "diag-enabled" },
{ 0x0400, "cli-enabled" },
@@ -518,8 +516,10 @@ static int ipa_nvflag_set(uint16_t *flags, uint16_t *mask, const char *name, int
{
int rc;
rc = get_string_value(ipa_nvflag_strs, name);
- if (rc < 0)
+ if (rc < 0) {
+ fprintf(stderr, "Unknown attribute '%s'\n", name);
return rc;
+ }
*mask |= rc;
if (en)
@@ -927,7 +927,7 @@ static const struct log_info_cat log_categories[] = {
.name = "DNM",
.description = "A-bis Network Management / O&M (NM/OML)",
.color = "\033[1;36m",
- .loglevel = LOGL_DEBUG,
+ .loglevel = LOGL_NOTICE,
.enabled = 1,
},
};
@@ -1131,15 +1131,3 @@ int main(int argc, char **argv)
exit(0);
}
-
-/* Stub */
-int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{ return 0; }
-int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{ return 0; }
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/src/ipaccess/ipaccess-proxy.c b/src/ipaccess/ipaccess-proxy.c
index d67603833..7ede28396 100644
--- a/src/ipaccess/ipaccess-proxy.c
+++ b/src/ipaccess/ipaccess-proxy.c
@@ -1253,11 +1253,3 @@ int main(int argc, char **argv)
osmo_select_main(0);
}
}
-
-/* Stub */
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/src/ipaccess/stubs.c b/src/ipaccess/stubs.c
index cb561051a..60fa62627 100644
--- a/src/ipaccess/stubs.c
+++ b/src/ipaccess/stubs.c
@@ -30,17 +30,3 @@ bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts)
/* No TS init required here. */
return true;
}
-
-int abis_rsl_rcvmsg(struct msgb *msg)
-{
- /* No RSL handling here */
- return 0;
-}
-
-void paging_flush_bts(struct gsm_bts *bts, struct bsc_msc_data *msc)
-{
- /* No paging flushing */
-}
-
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts)
-{}
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am
index c149f47ef..583fb79d9 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -39,6 +39,7 @@ libbsc_la_SOURCES = \
bsc_rf_ctrl.c \
bsc_rll.c \
bsc_sccp.c \
+ bsc_stats.c \
bsc_subscr_conn_fsm.c \
bsc_subscriber.c \
bsc_vty.c \
@@ -56,6 +57,7 @@ libbsc_la_SOURCES = \
bts_vty.c \
bts_trx_vty.c \
chan_alloc.c \
+ chan_counts.c \
codec_pref.c \
e1_config.c \
gsm_04_08_rr.c \
@@ -77,6 +79,7 @@ libbsc_la_SOURCES = \
meas_rep.c \
neighbor_ident.c \
neighbor_ident_vty.c \
+ neighbor_ident_ctrl.c \
net_init.c \
nm_common_fsm.c \
nm_bb_transc_fsm.c \
@@ -105,6 +108,7 @@ libbsc_la_SOURCES = \
smscb.c \
cbch_scheduler.c \
cbsp_link.c \
+ power_control.c \
$(NULL)
libbsc_la_LIBADD = \
diff --git a/src/osmo-bsc/a_reset.c b/src/osmo-bsc/a_reset.c
index 0befd7227..d23ffa799 100644
--- a/src/osmo-bsc/a_reset.c
+++ b/src/osmo-bsc/a_reset.c
@@ -25,6 +25,7 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
#include <osmocom/bsc/bssmap_reset.h>
+#include <osmocom/bsc/bsc_stats.h>
static void a_reset_tx_reset(void *data)
{
@@ -43,6 +44,7 @@ static void a_reset_link_up(void *data)
struct bsc_msc_data *msc = data;
LOGP(DMSC, LOGL_NOTICE, "(msc%d) BSSMAP assocation is up\n", msc->nr);
osmo_stat_item_inc(osmo_stat_item_group_get_item(msc->msc_statg, MSC_STAT_MSC_LINKS_ACTIVE), 1);
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(msc->network->bsc_statg, BSC_STAT_NUM_MSC_CONNECTED), 1);
osmo_signal_dispatch(SS_MSC, S_MSC_CONNECTED, msc);
}
@@ -51,6 +53,7 @@ static void a_reset_link_lost(void *data)
struct bsc_msc_data *msc = data;
LOGP(DMSC, LOGL_NOTICE, "(msc%d) BSSMAP assocation is down\n", msc->nr);
osmo_stat_item_dec(osmo_stat_item_group_get_item(msc->msc_statg, MSC_STAT_MSC_LINKS_ACTIVE), 1);
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(msc->network->bsc_statg, BSC_STAT_NUM_MSC_CONNECTED), 1);
osmo_signal_dispatch(SS_MSC, S_MSC_LOST, msc);
osmo_bsc_sigtran_reset(msc);
}
@@ -76,6 +79,7 @@ void a_reset_alloc(struct bsc_msc_data *msc, const char *name)
}
msc->a.bssmap_reset = bssmap_reset_alloc(msc, name, &cfg);
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(msc->network->bsc_statg, BSC_STAT_NUM_MSC_TOTAL), 1);
}
/* Confirm that we successfully received a reset acknowledge message */
diff --git a/src/osmo-bsc/abis_osmo.c b/src/osmo-bsc/abis_osmo.c
index 39caac6df..48774b4e8 100644
--- a/src/osmo-bsc/abis_osmo.c
+++ b/src/osmo-bsc/abis_osmo.c
@@ -32,6 +32,7 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/pcuif_proto.h>
+#include <osmocom/bsc/neighbor_ident.h>
#define OM_HEADROOM_SIZE 128
@@ -40,7 +41,6 @@
///////////////////////////////////////
#define PCUIF_HDR_SIZE ( sizeof(struct gsm_pcu_if) - sizeof(((struct gsm_pcu_if *)0)->u) )
-#if 0
static struct msgb *abis_osmo_pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr, size_t extra_size)
{
struct msgb *msg;
@@ -62,7 +62,65 @@ static int abis_osmo_pcu_sendmsg(struct gsm_bts *bts, struct msgb *msg)
ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_PCU);
return abis_osmo_sendmsg(bts, msg);
}
-#endif
+
+static int abis_osmo_pcu_tx_neigh_addr_cnf(struct gsm_bts *bts, const struct gsm_pcu_if_neigh_addr_req *naddr_req,
+ uint8_t err_code, const struct osmo_cell_global_id_ps *cgi_ps)
+{
+ struct msgb *msg = abis_osmo_pcu_msgb_alloc(PCU_IF_MSG_CONTAINER, bts->bts_nr, sizeof(struct gsm_pcu_if_neigh_addr_cnf));
+ struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msgb_data(msg);
+ struct gsm_pcu_if_neigh_addr_cnf *naddr_cnf = (struct gsm_pcu_if_neigh_addr_cnf *)&pcu_prim->u.container.data[0];
+
+ msgb_put(msg, sizeof(pcu_prim->u.container) + sizeof(struct gsm_pcu_if_neigh_addr_cnf));
+ pcu_prim->u.container.msg_type = PCU_IF_MSG_NEIGH_ADDR_CNF;
+ osmo_store16be(sizeof(struct gsm_pcu_if_neigh_addr_cnf), &pcu_prim->u.container.length);
+
+ naddr_cnf->orig_req = *naddr_req;
+ naddr_cnf->err_code = err_code;
+ if (err_code == 0) {
+ osmo_store16be(cgi_ps->rai.lac.plmn.mcc, &naddr_cnf->cgi_ps.mcc);
+ osmo_store16be(cgi_ps->rai.lac.plmn.mnc, &naddr_cnf->cgi_ps.mnc);
+ naddr_cnf->cgi_ps.mnc_3_digits = cgi_ps->rai.lac.plmn.mnc_3_digits;
+ osmo_store16be(cgi_ps->rai.lac.lac, &naddr_cnf->cgi_ps.lac);
+ naddr_cnf->cgi_ps.rac = cgi_ps->rai.rac;
+ osmo_store16be(cgi_ps->cell_identity, &naddr_cnf->cgi_ps.cell_identity);
+ }
+
+ return abis_osmo_pcu_sendmsg(bts, msg);
+}
+
+static int rcvmsg_pcu_neigh_addr_req(struct gsm_bts *bts, const struct gsm_pcu_if_neigh_addr_req *naddr_req)
+{
+
+ struct cell_ab ab;
+ uint16_t local_lac, local_ci;
+ struct osmo_cell_global_id_ps cgi_ps;
+ int rc;
+
+ local_lac = osmo_load16be(&naddr_req->local_lac);
+ local_ci = osmo_load16be(&naddr_req->local_ci);
+ ab = (struct cell_ab){
+ .arfcn = osmo_load16be(&naddr_req->tgt_arfcn),
+ .bsic = naddr_req->tgt_bsic,
+ };
+
+ LOGP(DNM, LOGL_INFO, "(bts=%d) Rx Neighbor Address Resolution Req (ARFCN=%u,BSIC=%u) from (LAC=%u,CI=%u)\n",
+ bts->nr, ab.arfcn, ab.bsic, local_lac, local_ci);
+
+ if (!cell_ab_valid(&ab)) {
+ rc = 2;
+ goto do_fail;
+ }
+
+ if (neighbor_address_resolution(bts->network, &ab, local_lac, local_ci, &cgi_ps) < 0) {
+ rc = 1;
+ goto do_fail;
+ }
+ return abis_osmo_pcu_tx_neigh_addr_cnf(bts, naddr_req, 0, &cgi_ps);
+
+do_fail:
+ return abis_osmo_pcu_tx_neigh_addr_cnf(bts, naddr_req, rc, NULL);
+}
+
static int rcvmsg_pcu_container(struct gsm_bts *bts, struct gsm_pcu_if_container *container, size_t container_len)
{
@@ -79,6 +137,13 @@ static int rcvmsg_pcu_container(struct gsm_bts *bts, struct gsm_pcu_if_container
bts->nr, container->msg_type);
switch (container->msg_type) {
+ case PCU_IF_MSG_NEIGH_ADDR_REQ:
+ if (data_length < sizeof(struct gsm_pcu_if_neigh_addr_req)) {
+ LOGP(DNM, LOGL_ERROR, "ABIS_OSMO_PCU CONTAINER ANR_CNF message too short\n");
+ return -EINVAL;
+ }
+ rc = rcvmsg_pcu_neigh_addr_req(bts, (struct gsm_pcu_if_neigh_addr_req *)&container->data);
+ break;
default:
LOGP(DNM, LOGL_NOTICE, "(bts=%d) Rx ABIS_OSMO_PCU unexpected msg type (%u) inside container!\n",
bts->nr, container->msg_type);
diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c
index d5b0d532e..52008a5ba 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -55,6 +55,8 @@
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/smscb.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/power_control.h>
+#include <osmocom/bsc/chan_counts.h>
static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,
struct gsm_meas_rep *resp)
@@ -259,7 +261,9 @@ static void add_power_control_params(struct msgb *msg, enum abis_rsl_ie iei,
}
/* Send a BCCH_INFO message as per Chapter 8.5.1 */
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
+/* Allow test to overwrite it */
+__attribute__((weak)) int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type,
+ const uint8_t *data, int len)
{
struct abis_rsl_dchan_hdr *dh;
const struct gsm_bts *bts = trx->bts;
@@ -293,7 +297,8 @@ int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type,
return abis_rsl_sendmsg(msg);
}
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type,
+/* Allow test to overwrite it */
+__attribute__((weak)) int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type,
const uint8_t *data, int len)
{
struct abis_rsl_common_hdr *ch;
@@ -521,11 +526,11 @@ static int put_mr_config_for_bts(struct msgb *msg, const struct gsm48_multi_rate
/* indicate FACCH/SACCH Repetition to be performed by BTS,
* see also: 3GPP TS 44.006, section 10 and 11 */
-static void rep_acch_cap_for_bts(struct gsm_lchan *lchan,
- struct msgb *msg)
+static void put_rep_acch_cap_ie(const struct gsm_lchan *lchan,
+ struct msgb *msg)
{
struct abis_rsl_osmo_rep_acch_cap *cap;
- struct gsm_bts *bts = lchan->ts->trx->bts;
+ const struct gsm_bts *bts = lchan->ts->trx->bts;
/* The RSL_IE_OSMO_REP_ACCH_CAP IE is a proprietary IE, that can only
* be used with osmo-bts type BTSs */
@@ -535,7 +540,7 @@ static void rep_acch_cap_for_bts(struct gsm_lchan *lchan,
cap = (struct abis_rsl_osmo_rep_acch_cap*) msg->tail;
msgb_tlv_put(msg, RSL_IE_OSMO_REP_ACCH_CAP, sizeof(*cap),
- (uint8_t*) &bts->repeated_acch_policy);
+ (uint8_t *)&bts->rep_acch_cap);
if (!(lchan->conn && lchan->conn->cm3_valid
&& lchan->conn->cm3.repeated_acch_capability)) {
@@ -548,6 +553,34 @@ static void rep_acch_cap_for_bts(struct gsm_lchan *lchan,
}
}
+/* indicate Temporary overpower of SACCH and FACCH channels */
+static void put_top_acch_cap_ie(const struct gsm_lchan *lchan,
+ const struct rsl_ie_chan_mode *cm,
+ struct msgb *msg)
+{
+ const struct gsm_bts *bts = lchan->ts->trx->bts;
+
+ /* The BTS_FEAT_ACCH_TEMP_OVP IE is a proprietary IE, that can only be used with osmo-bts type BTSs */
+ if (!(bts->model->type == GSM_BTS_TYPE_OSMOBTS && osmo_bts_has_feature(&bts->features, BTS_FEAT_ACCH_TEMP_OVP)))
+ return;
+
+ /* Check if TOP is permitted for the given Channel Mode */
+ switch (bts->top_acch_chan_mode) {
+ case TOP_ACCH_CHAN_MODE_SPEECH_V3:
+ if (cm->spd_ind != RSL_CMOD_SPD_SPEECH)
+ return;
+ if (cm->chan_rate != RSL_CMOD_SP_GSM3)
+ return;
+ break;
+ case TOP_ACCH_CHAN_MODE_ANY:
+ break;
+ }
+
+ msgb_tlv_put(msg, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP,
+ sizeof(bts->top_acch_cap),
+ (void *)&bts->top_acch_cap);
+}
+
/* Write RSL_IE_OSMO_TRAINING_SEQUENCE to msgb. The tsc_set argument's range is 1-4, tsc argument range is 0-7. */
static void put_osmo_training_sequence_ie(struct msgb *msg, uint8_t tsc_set, uint8_t tsc)
{
@@ -658,13 +691,19 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
if (bts->type == GSM_BTS_TYPE_BS11)
ta <<= 2;
msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
+ } else if ((act_type & 0x06) == 0x00) {
+ /* Note '4)' in section 8.4.1: The Timing Advance element must be
+ * included if activation type is intra cell channel change. */
+ LOG_LCHAN(lchan, LOGL_NOTICE, "Timing Advance IE shall be present, "
+ "but the actual value is not known => assuming 0\n");
+ msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, 0);
}
/* BS/MS Power Control Parameters (if supported by BTS model) */
add_power_control_params(msg, RSL_IE_BS_POWER_PARAM, lchan);
add_power_control_params(msg, RSL_IE_MS_POWER_PARAM, lchan);
- if (gsm48_chan_mode_to_non_vamos(lchan->activate.ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ if (cm.chan_rate == RSL_CMOD_SP_GSM3) {
rc = put_mr_config_for_bts(msg, &lchan->activate.mr_conf_filtered,
(lchan->type == GSM_LCHAN_TCH_F) ? &bts->mr_full : &bts->mr_half);
if (rc) {
@@ -674,7 +713,8 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
}
}
- rep_acch_cap_for_bts(lchan, msg);
+ put_rep_acch_cap_ie(lchan, msg);
+ put_top_acch_cap_ie(lchan, &cm, msg);
/* Selecting a specific TSC Set is only applicable to VAMOS mode */
if (lchan->activate.info.vamos && lchan->activate.tsc_set >= 1)
@@ -735,7 +775,7 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
}
}
- if (gsm48_chan_mode_to_non_vamos(lchan->modify.ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ if (cm.chan_rate == RSL_CMOD_SP_GSM3) {
rc = put_mr_config_for_bts(msg, &lchan->modify.mr_conf_filtered,
(lchan->type == GSM_LCHAN_TCH_F) ? &bts->mr_full : &bts->mr_half);
if (rc) {
@@ -745,7 +785,8 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
}
}
- rep_acch_cap_for_bts(lchan, msg);
+ put_rep_acch_cap_ie(lchan, msg);
+ put_top_acch_cap_ie(lchan, &cm, msg);
/* Selecting a specific TSC Set is only applicable to VAMOS mode. Send this Osmocom specific IE only to OsmoBTS
* types. */
@@ -980,6 +1021,106 @@ int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci)
}
+/* For 3GPP TS 52.402 unsuccReqsForService, we need to decode the DTAP and count CM Service Reject messages. */
+static void count_unsucc_reqs_for_service(const struct msgb *msg)
+{
+ struct gsm_bts *bts = msg->lchan->ts->trx->bts;
+ const struct gsm48_hdr *gh;
+ uint8_t pdisc, mtype;
+ uint8_t cause;
+
+ if (msgb_l3len(msg) < sizeof(*gh))
+ return;
+
+ gh = msgb_l3(msg);
+ pdisc = gsm48_hdr_pdisc(gh);
+ mtype = gsm48_hdr_msg_type(gh);
+
+ if (pdisc != GSM48_PDISC_MM || mtype != GSM48_MT_MM_CM_SERV_REJ)
+ return;
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ));
+
+ cause = gh->data[0];
+ switch (cause) {
+ case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_HLR));
+ break;
+ case GSM48_REJECT_ILLEGAL_MS:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_ILLEGAL_MS));
+ break;
+ case GSM48_REJECT_IMSI_UNKNOWN_IN_VLR:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_VLR));
+ break;
+ case GSM48_REJECT_IMEI_NOT_ACCEPTED:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_IMEI_NOT_ACCEPTED));
+ break;
+ case GSM48_REJECT_ILLEGAL_ME:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_ILLEGAL_ME));
+ break;
+ case GSM48_REJECT_PLMN_NOT_ALLOWED:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_PLMN_NOT_ALLOWED));
+ break;
+ case GSM48_REJECT_LOC_NOT_ALLOWED:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_LOC_NOT_ALLOWED));
+ break;
+ case GSM48_REJECT_ROAMING_NOT_ALLOWED:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_ROAMING_NOT_ALLOWED));
+ break;
+ case GSM48_REJECT_NETWORK_FAILURE:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_NETWORK_FAILURE));
+ break;
+ case GSM48_REJECT_SYNCH_FAILURE:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_SYNCH_FAILURE));
+ break;
+ case GSM48_REJECT_CONGESTION:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_CONGESTION));
+ break;
+ case GSM48_REJECT_SRV_OPT_NOT_SUPPORTED:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_SRV_OPT_NOT_SUPPORTED));
+ break;
+ case GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_RQD_SRV_OPT_NOT_SUPPORTED));
+ break;
+ case GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_SRV_OPT_TMP_OUT_OF_ORDER));
+ break;
+ case GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_CALL_CAN_NOT_BE_IDENTIFIED));
+ break;
+ case GSM48_REJECT_INCORRECT_MESSAGE:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_INCORRECT_MESSAGE));
+ break;
+ case GSM48_REJECT_INVALID_MANDANTORY_INF:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_INVALID_MANDANTORY_INF));
+ break;
+ case GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_IMPLEMENTED));
+ break;
+ case GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_COMPATIBLE));
+ break;
+ case GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_INF_ELEME_NOT_IMPLEMENTED));
+ break;
+ case GSM48_REJECT_CONDTIONAL_IE_ERROR:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_CONDTIONAL_IE_ERROR));
+ break;
+ case GSM48_REJECT_MSG_NOT_COMPATIBLE:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_MSG_NOT_COMPATIBLE));
+ break;
+ default:
+ if (cause >= 48 && cause <= 63) {
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_RETRY_IN_NEW_CELL));
+ break;
+ }
+ /* else fall thru */
+ case GSM48_REJECT_PROTOCOL_ERROR:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CM_SERV_REJ_PROTOCOL_ERROR));
+ break;
+ }
+}
+
/* Send "DATA REQUEST" message with given L3 Info payload */
/* Chapter 8.3.1 */
int rsl_data_request(struct msgb *msg, uint8_t link_id)
@@ -992,6 +1133,8 @@ int rsl_data_request(struct msgb *msg, uint8_t link_id)
return -EINVAL;
}
+ count_unsucc_reqs_for_service(msg);
+
chan_nr = gsm_lchan2chan_nr(msg->lchan, true);
if (chan_nr < 0) {
msgb_free(msg);
@@ -1127,7 +1270,9 @@ static int rsl_rx_conn_fail(struct msgb *msg)
return 0;
}
-static void print_meas_rep_uni(struct osmo_strbuf *sb, struct gsm_meas_rep_unidir *mru, const char *prefix)
+static void print_meas_rep_uni(struct osmo_strbuf *sb,
+ const struct gsm_meas_rep_unidir *mru,
+ const char *prefix)
{
OSMO_STRBUF_PRINTF(*sb, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ",
prefix, rxlev2dbm(mru->full.rx_lev),
@@ -1136,21 +1281,11 @@ static void print_meas_rep_uni(struct osmo_strbuf *sb, struct gsm_meas_rep_unidi
prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual);
}
-static int print_meas_rep_buf(char *buf, size_t len, struct gsm_lchan *lchan, struct gsm_meas_rep *mr)
+static int print_meas_rep_buf(char *buf, size_t len, const struct gsm_meas_rep *mr)
{
- const char *name = "";
- struct bsc_subscr *bsub = NULL;
struct osmo_strbuf sb = { .buf = buf, .len = len };
- if (lchan && lchan->conn) {
- bsub = lchan->conn->bsub;
- if (bsub) {
- name = bsc_subscr_name(bsub);
- } else
- name = lchan->name;
- }
-
- OSMO_STRBUF_PRINTF(sb, "[%s] MEASUREMENT RESULT NR=%d ", name, mr->nr);
+ OSMO_STRBUF_PRINTF(sb, "MEASUREMENT RESULT NR=%d ", mr->nr);
if (mr->flags & MEAS_REP_F_DL_DTX)
OSMO_STRBUF_PRINTF(sb, "DTXd ");
@@ -1181,30 +1316,35 @@ static int print_meas_rep_buf(char *buf, size_t len, struct gsm_lchan *lchan, st
return sb.chars_needed;
}
-static char *print_meas_rep_c(void *ctx, struct gsm_lchan *lchan, struct gsm_meas_rep *mr)
+static char *print_meas_rep_c(void *ctx, const struct gsm_meas_rep *mr)
{
/* A naive count of required characters gets me to ~200, so 256 should be safe to get a large enough buffer on
* the first time. */
- OSMO_NAME_C_IMPL(ctx, 256, "ERROR", print_meas_rep_buf, lchan, mr)
+ OSMO_NAME_C_IMPL(ctx, 256, "ERROR", print_meas_rep_buf, mr)
}
-static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr)
+static void print_meas_rep(struct gsm_lchan *lchan, const struct gsm_meas_rep *mr)
{
int i;
+ const char *name = "";
struct bsc_subscr *bsub = NULL;
if (lchan && lchan->conn) {
bsub = lchan->conn->bsub;
- if (bsub)
+ if (bsub) {
log_set_context(LOG_CTX_BSC_SUBSCR, bsub);
+ name = bsc_subscr_name(bsub);
+ } else {
+ name = lchan->name;
+ }
}
- DEBUGP(DMEAS, "%s\n", print_meas_rep_c(OTC_SELECT, lchan, mr));
+ DEBUGP(DMEAS, "[%s] %s\n", name, print_meas_rep_c(OTC_SELECT, mr));
if (mr->num_cell != 7
&& log_check_level(DMEAS, LOGL_DEBUG)) {
for (i = 0; i < mr->num_cell; i++) {
- struct gsm_meas_rep_cell *mrc = &mr->cell[i];
+ const struct gsm_meas_rep_cell *mrc = &mr->cell[i];
DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n",
mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev));
}
@@ -1288,9 +1428,10 @@ static int rsl_rx_meas_res(struct msgb *msg)
if (val[0] & 0x04)
mr->flags |= MEAS_REP_F_FPC;
mr->ms_l1.ta = val[1];
- /* BS11 and Nokia reports TA shifted by 2 bits */
+ /* BS11, Nokia and RBS report TA shifted by 2 bits */
if (msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11
- || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE)
+ || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE
+ || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_RBS2000)
mr->ms_l1.ta >>= 2;
/* store TA for handover decision, and for intra-cell re-assignment */
mr->lchan->last_ta = mr->ms_l1.ta;
@@ -1309,6 +1450,8 @@ static int rsl_rx_meas_res(struct msgb *msg)
print_meas_rep(msg->lchan, mr);
+ lchan_ms_pwr_ctrl(msg->lchan, mr);
+
send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr);
return 0;
@@ -1476,9 +1619,7 @@ static int rsl_rx_resource_indication(struct msgb *msg)
struct gsm_bts_trx *trx = sign_link->trx;
struct gsm_lchan *lchan;
int ts_nr;
- int i;
-
- rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg) - sizeof(*rslh));
+ int rc, i;
LOGP(DRSL, LOGL_DEBUG, "%s Rx Resource Indication\n", gsm_trx_name(trx));
@@ -1492,6 +1633,12 @@ static int rsl_rx_resource_indication(struct msgb *msg)
}
}
+ rc = rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg) - sizeof(*rslh));
+ if (rc < 0) {
+ LOGP(DRSL, LOGL_ERROR, "Rx Resource Indication: failed to parse the message\n");
+ return -EINVAL;
+ }
+
res_info_ie = TLVP_GET(&tp, RSL_IE_RESOURCE_INFO);
if (!res_info_ie) {
LOGP(DRSL, LOGL_ERROR, "Rx Resource Indication: missing Resource Info IE\n");
@@ -1916,6 +2063,7 @@ static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)
struct gsm_lchan *_select_sdcch_for_call(struct gsm_bts *bts, const struct chan_rqd *rqd, enum gsm_chan_t lctype)
{
+ struct chan_counts bts_counts;
struct gsm_lchan *lchan = NULL;
int free_tchf, free_tchh;
bool needs_dyn_switch;
@@ -1927,8 +2075,9 @@ struct gsm_lchan *_select_sdcch_for_call(struct gsm_bts *bts, const struct chan_
needs_dyn_switch = lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN &&
lchan->ts->pchan_is != GSM_PCHAN_SDCCH8_SACCH8C;
- free_tchf = bts_count_free_ts(bts, GSM_PCHAN_TCH_F);
- free_tchh = bts_count_free_ts(bts, GSM_PCHAN_TCH_H);
+ chan_counts_for_bts(&bts_counts, bts);
+ free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
if (free_tchf == 0 && free_tchh == 0) {
LOG_BTS(bts, DRSL, LOGL_INFO,
"CHAN RQD: 0x%x Requesting %s reason=call but no TCH available\n",
@@ -2049,14 +2198,14 @@ void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts)
gsm_chreq_name(rqd->reason), rqd->ref.ra, rqd->ta);
info = (struct lchan_activate_info){
.activ_for = ACTIVATE_FOR_MS_CHANNEL_REQUEST,
+ .chreq_reason = rqd->reason,
.ch_mode_rate = {
.chan_mode = GSM48_CMODE_SIGN,
.chan_rate = CH_RATE_SDCCH,
},
.ta = rqd->ta,
.ta_known = true,
- .tsc_set = -1,
- .tsc = -1,
+ .imm_ass_time = bts->imm_ass_time,
};
lchan_activate(lchan, &info);
@@ -2065,12 +2214,42 @@ void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts)
return;
}
+static void imm_ass_rate_ctr(struct gsm_lchan *lchan)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_SUCCESSFUL));
+ switch (lchan->activate.info.chreq_reason) {
+ case GSM_CHREQ_REASON_EMERG:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_ATTEMPTED_EMERG));
+ break;
+ case GSM_CHREQ_REASON_CALL:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_ATTEMPTED_CALL));
+ break;
+ case GSM_CHREQ_REASON_LOCATION_UPD:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_ATTEMPTED_LOCATION_UPD));
+ break;
+ case GSM_CHREQ_REASON_PAG:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_ATTEMPTED_PAG));
+ break;
+ case GSM_CHREQ_REASON_PDCH:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_ATTEMPTED_PDCH));
+ break;
+ case GSM_CHREQ_REASON_OTHER:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_ATTEMPTED_OTHER));
+ break;
+ default:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_ATTEMPTED_UNKNOWN));
+ break;
+ }
+}
+
int rsl_tx_imm_assignment(struct gsm_lchan *lchan)
{
int rc;
struct gsm_bts *bts = lchan->ts->trx->bts;
uint8_t buf[GSM_MACBLOCK_LEN];
struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf;
+ enum gsm_phys_chan_config pchan;
/* create IMMEDIATE ASSIGN 04.08 message */
memset(ia, 0, sizeof(*ia));
@@ -2078,7 +2257,15 @@ int rsl_tx_imm_assignment(struct gsm_lchan *lchan)
ia->proto_discr = GSM48_PDISC_RR;
ia->msg_type = GSM48_MT_RR_IMM_ASS;
ia->page_mode = GSM48_PM_SAME;
- rc = gsm48_lchan2chan_desc(&ia->chan_desc, lchan, lchan->tsc, true);
+
+ /* In case the dyn TS is not ready yet, ts->pchan_is still reflects the previous pchan type; so get the pchan
+ * kind from lchan->type, which already reflects the target type. This only happens for dynamic timeslots.
+ * gsm_pchan_by_lchan_type() isn't always exact, which is fine for dyn TS with their limited pchan kinds. */
+ if (lchan_state_is(lchan, LCHAN_ST_WAIT_TS_READY))
+ pchan = gsm_pchan_by_lchan_type(lchan->type);
+ else
+ pchan = lchan->ts->pchan_is;
+ rc = gsm48_lchan_and_pchan2chan_desc(&ia->chan_desc, lchan, pchan, lchan->tsc, true);
if (rc) {
LOG_LCHAN(lchan, LOGL_ERROR, "Error encoding Channel Number\n");
return rc;
@@ -2100,7 +2287,7 @@ int rsl_tx_imm_assignment(struct gsm_lchan *lchan)
rc = rsl_imm_assign_cmd(bts, sizeof(*ia)+ia->mob_alloc_len, (uint8_t *) ia);
if (!rc)
- rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_SUCCESSFUL));
+ imm_ass_rate_ctr(lchan);
return rc;
}
diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c
index 2c52d22d8..656bd3e94 100644
--- a/src/osmo-bsc/assignment_fsm.c
+++ b/src/osmo-bsc/assignment_fsm.c
@@ -36,6 +36,7 @@
#include <osmocom/bsc/lchan_select.h>
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/bsc/assignment_fsm.h>
@@ -73,7 +74,7 @@ static const struct osmo_tdef_state_timeout assignment_fsm_timeouts[32] = {
osmo_fsm_inst_state_name(fi), gsm0808_cause_name(cause), ## args); \
assignment_count_result(CTR_ASSIGNMENT_ERROR); \
on_assignment_failure(_conn); \
- } while(0)
+ } while (0)
/* Assume presence of local var 'conn' as struct gsm_subscriber_connection */
#define assignment_count(counter) do { \
@@ -97,7 +98,7 @@ static const struct osmo_tdef_state_timeout assignment_fsm_timeouts[32] = {
break; \
} \
} \
- } while(0)
+ } while (0)
#define assignment_count_result(counter) do { \
if (!conn->assignment.result_rate_ctr_done) { \
@@ -108,7 +109,7 @@ static const struct osmo_tdef_state_timeout assignment_fsm_timeouts[32] = {
"result rate counter already recorded, NOT counting as: %s %s\n", \
bsc_ctr_description[BSC_##counter].name, \
bsc_ctr_description[BSC_##counter].description); \
- } while(0)
+ } while (0)
void assignment_reset(struct gsm_subscriber_connection *conn)
{
@@ -451,8 +452,14 @@ static int _reassignment_request(enum assign_for assign_for, struct gsm_lchan *l
.n_ch_mode_rate = 1,
.ch_mode_rate_list = { lchan->current_ch_mode_rate },
.target_lchan = to_lchan,
- .tsc_set = tsc_set,
- .tsc = tsc,
+ .tsc_set = {
+ .present = (tsc_set >= 0),
+ .val = tsc_set,
+ },
+ .tsc = {
+ .present = (tsc >= 0),
+ .val = tsc,
+ },
};
if (to_lchan)
@@ -677,6 +684,15 @@ static void assignment_fsm_wait_rr_ass_complete_onenter(struct osmo_fsm_inst *fi
int rc;
struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
+ /* There may be situations where the SDCCH gets released while the TCH is still being activated. We will then
+ * receive ChanActivAck message from the BTS when the TCH is ready. Since the SDCCH is already released by
+ * then conn->lchan will be NULL in this case. */
+ if (!conn->lchan) {
+ assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE,
+ "Unable to send RR Assignment Command: conn without lchan");
+ return;
+ }
+
rc = gsm48_send_rr_ass_cmd(conn->lchan, conn->assignment.new_lchan,
conn->lchan->ms_power);
@@ -820,9 +836,16 @@ static void assignment_fsm_wait_lchan_modified_onenter(struct osmo_fsm_inst *fi,
.ch_mode_rate = conn->assignment.selected_ch_mode_rate,
.requires_voice_stream = conn->assignment.requires_voice_stream,
.msc_assigned_cic = req->msc_assigned_cic,
- /* keep previous training sequence code */
- .tsc_set = lchan->tsc_set,
- .tsc = lchan->tsc,
+ /* keep previous training sequence code. TSC is always present, TSC Set may or may not be an explicit
+ * value. */
+ .tsc_set = {
+ .present = (lchan->tsc_set >= 0),
+ .val = lchan->tsc_set,
+ },
+ .tsc = {
+ .present = true,
+ .val = lchan->tsc,
+ },
};
lchan_mode_modify(lchan, &modif_info);
}
@@ -915,7 +938,7 @@ static const struct value_string assignment_fsm_event_names[] = {
{}
};
-void assignment_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+static void assignment_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
@@ -936,7 +959,7 @@ void assignment_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, vo
assignment_fail(new_lchan->activate.gsm0808_error_cause,
"Failed to %s lchan %s",
conn->assignment.new_lchan ? "activate" : "modify",
- conn->assignment.new_lchan ? gsm_lchan_name(conn->assignment.new_lchan) : "");
+ gsm_lchan_name(new_lchan));
return;
default:
@@ -944,7 +967,7 @@ void assignment_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, vo
}
}
-int assignment_fsm_timer_cb(struct osmo_fsm_inst *fi)
+static int assignment_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
assignment_count_result(CTR_ASSIGNMENT_TIMEOUT);
diff --git a/src/osmo-bsc/bsc_ctrl_commands.c b/src/osmo-bsc/bsc_ctrl_commands.c
index be83b1b49..fb8bd0cec 100644
--- a/src/osmo-bsc/bsc_ctrl_commands.c
+++ b/src/osmo-bsc/bsc_ctrl_commands.c
@@ -35,6 +35,7 @@
#include <osmocom/bsc/osmo_bsc_rf.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/neighbor_ident.h>
static int verify_net_apply_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
{
@@ -420,6 +421,44 @@ static int get_bts_rf_state(struct ctrl_cmd *cmd, void *data)
}
CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state");
+/* Return a list of the states of each TRX for a given BTS.
+ * <bts_nr>,<trx_nr>,<opstate>,<adminstate>,<rf_policy>,<rsl_status>;<bts_nr>,<trx_nr>,...;...;
+ * For details on the string, see bsc_rf_states_c();
+ */
+static int get_bts_rf_states(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ if (!bts) {
+ cmd->reply = "bts not found.";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = bsc_rf_states_of_bts_c(cmd, bts);
+ if (!cmd->reply) {
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(bts_rf_states, "rf_states");
+
+/* Return a list of the states of each TRX for all BTS:
+ * <bts_nr>,<trx_nr>,<opstate>,<adminstate>,<rf_policy>,<rsl_status>;<bts_nr>,<trx_nr>,...;...;
+ * For details on the string, see bsc_rf_states_c();
+ */
+static int get_net_rf_states(struct ctrl_cmd *cmd, void *data)
+{
+ cmd->reply = bsc_rf_states_c(cmd);
+ if (!cmd->reply) {
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
+ }
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(net_rf_states, "rf_states");
+
static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data)
{
struct gsm_network *net = cmd->node;
@@ -500,6 +539,37 @@ static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *dat
}
CTRL_CMD_DEFINE(net_rf_lock, "rf_locked");
+static int get_trx_rf_locked(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts_trx *trx = cmd->node;
+ /* Return rf_locked = 1 only if it is explicitly locked. If it is in shutdown or null state, do not "trick" the
+ * caller into thinking that sending "rf_locked 0" is necessary to bring the TRX up. */
+ cmd->reply = (trx->mo.nm_state.administrative == NM_STATE_LOCKED) ? "1" : "0";
+ return CTRL_CMD_REPLY;
+}
+
+static int set_trx_rf_locked(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts_trx *trx = cmd->node;
+ int locked;
+ if (osmo_str_to_int(&locked, cmd->value, 10, 0, 1)) {
+ cmd->reply = "Invalid value";
+ return CTRL_CMD_ERROR;
+ }
+
+ gsm_trx_lock_rf(trx, locked, "ctrl");
+
+ /* Let's not assume the nm FSM has already switched its state, just return the intended rf_locked value. */
+ cmd->reply = locked ? "1" : "0";
+ return CTRL_CMD_REPLY;
+}
+
+static int verify_trx_rf_locked(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ return osmo_str_to_int(NULL, value, 10, 0, 1);
+}
+CTRL_CMD_DEFINE(trx_rf_locked, "rf_locked");
+
static int get_net_bts_num(struct ctrl_cmd *cmd, void *data)
{
struct gsm_network *net = cmd->node;
@@ -598,6 +668,120 @@ static int set_bts_c0_power_red(struct ctrl_cmd *cmd, void *data)
CTRL_CMD_DEFINE(bts_c0_power_red, "c0-power-reduction");
+static int verify_bts_neighbor_list_add_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int arfcn;
+
+ if (osmo_str_to_int(&arfcn, value, 10, 0, 1023) < 0) {
+ cmd->reply = "Invalid ARFCN value";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int set_bts_neighbor_list_add_del(struct ctrl_cmd *cmd, void *data, bool add)
+{
+ struct gsm_bts *bts = cmd->node;
+ struct bitvec *bv = &bts->si_common.neigh_list;
+ int arfcn_int;
+ uint16_t arfcn;
+ enum gsm_band unused;
+
+ if (osmo_str_to_int(&arfcn_int, cmd->value, 10, 0, 1023) < 0) {
+ cmd->reply = "Failed to parse ARFCN value";
+ return CTRL_CMD_ERROR;
+ }
+ arfcn = (uint16_t) arfcn_int;
+
+ if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) {
+ cmd->reply = "Neighbor list not in manual mode";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ cmd->reply = "Invalid arfcn detected";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (add)
+ bitvec_set_bit_pos(bv, arfcn, 1);
+ else
+ bitvec_set_bit_pos(bv, arfcn, 0);
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+static int verify_bts_neighbor_list_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return verify_bts_neighbor_list_add_del(cmd, value, _data);
+}
+
+static int set_bts_neighbor_list_add(struct ctrl_cmd *cmd, void *data)
+{
+ return set_bts_neighbor_list_add_del(cmd, data, true);
+}
+
+CTRL_CMD_DEFINE_WO(bts_neighbor_list_add, "neighbor-list add");
+
+static int verify_bts_neighbor_list_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return verify_bts_neighbor_list_add_del(cmd, value, _data);
+}
+
+static int set_bts_neighbor_list_del(struct ctrl_cmd *cmd, void *data)
+{
+ return set_bts_neighbor_list_add_del(cmd, data, false);
+}
+
+CTRL_CMD_DEFINE_WO(bts_neighbor_list_del, "neighbor-list del");
+
+static int verify_bts_neighbor_list_mode(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (!strcmp(value, "automatic"))
+ return 0;
+ if (!strcmp(value, "manual"))
+ return 0;
+ if (!strcmp(value, "manual-si5"))
+ return 0;
+
+ cmd->reply = "Invalid mode";
+ return 1;
+}
+
+static int set_bts_neighbor_list_mode(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int mode = NL_MODE_AUTOMATIC;
+
+ if (!strcmp(cmd->value, "automatic"))
+ mode = NL_MODE_AUTOMATIC;
+ else if (!strcmp(cmd->value, "manual"))
+ mode = NL_MODE_MANUAL;
+ else if (!strcmp(cmd->value, "manual-si5"))
+ mode = NL_MODE_MANUAL_SI5SEP;
+
+ switch (mode) {
+ case NL_MODE_MANUAL_SI5SEP:
+ case NL_MODE_MANUAL:
+ /* make sure we clear the current list when switching to
+ * manual mode */
+ if (bts->neigh_list_manual_mode == 0)
+ memset(&bts->si_common.data.neigh_list, 0, sizeof(bts->si_common.data.neigh_list));
+ break;
+ default:
+ break;
+ }
+
+ bts->neigh_list_manual_mode = mode;
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO(bts_neighbor_list_mode, "neighbor-list mode");
+
int bsc_base_ctrl_cmds_install(void)
{
int rc = 0;
@@ -609,6 +793,7 @@ int bsc_base_ctrl_cmds_install(void)
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc_mnc_apply);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_lock);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_bts_num);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_states);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci);
@@ -619,10 +804,17 @@ int bsc_base_ctrl_cmds_install(void)
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_up);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_gprs_mode);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_states);
rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_c0_power_red);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_mode);
+
+ rc |= neighbor_ident_ctrl_init();
rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power);
rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn);
+ rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_rf_locked);
return rc;
}
diff --git a/src/osmo-bsc/bsc_init.c b/src/osmo-bsc/bsc_init.c
index 24596f4eb..0412f6b6b 100644
--- a/src/osmo-bsc/bsc_init.c
+++ b/src/osmo-bsc/bsc_init.c
@@ -38,6 +38,7 @@
#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lb.h>
+#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/bsc/smscb.h>
#include <osmocom/gsm/protocol/gsm_48_049.h>
@@ -46,17 +47,7 @@
#include <limits.h>
#include <stdbool.h>
-static const struct osmo_stat_item_desc bsc_stat_desc[] = {
- { "num_bts:total", "Number of configured BTS for this BSC", "", 16, 0 },
-};
-
-static const struct osmo_stat_item_group_desc bsc_statg_desc = {
- .group_name_prefix = "bsc",
- .group_description = "base station controller",
- .class_id = OSMO_STATS_CLASS_GLOBAL,
- .num_items = ARRAY_SIZE(bsc_stat_desc),
- .item_desc = bsc_stat_desc,
-};
+struct gsm_network *bsc_gsmnet;
int bsc_shutdown_net(struct gsm_network *net)
{
@@ -129,6 +120,51 @@ static struct gsm_network *bsc_network_init(void *ctx)
if (!net->bts_unknown_statg)
goto err_free_all;
+ net->all_allocated_sdcch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(net->bsc_ctrs, BSC_CTR_ALL_ALLOCATED_SDCCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+ net->all_allocated_static_sdcch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(net->bsc_ctrs, BSC_CTR_ALL_ALLOCATED_STATIC_SDCCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+ net->all_allocated_tch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(net->bsc_ctrs, BSC_CTR_ALL_ALLOCATED_TCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+ net->all_allocated_static_tch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(net->bsc_ctrs, BSC_CTR_ALL_ALLOCATED_STATIC_TCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+
INIT_LLIST_HEAD(&net->bts_rejected);
gsm_net_update_ctype(net);
diff --git a/src/osmo-bsc/bsc_rf_ctrl.c b/src/osmo-bsc/bsc_rf_ctrl.c
index a845859be..749d2eb49 100644
--- a/src/osmo-bsc/bsc_rf_ctrl.c
+++ b/src/osmo-bsc/bsc_rf_ctrl.c
@@ -124,6 +124,96 @@ enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts)
}
}
+enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_trx(struct gsm_bts_trx *trx)
+{
+ if (trx->mo.nm_state.operational == NM_OPSTATE_ENABLED)
+ return OSMO_BSC_RF_OPSTATE_OPERATIONAL;
+ return OSMO_BSC_RF_OPSTATE_INOPERATIONAL;
+}
+
+enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_trx(struct gsm_bts_trx *trx)
+{
+ if (trx->mo.nm_state.administrative == NM_STATE_UNLOCKED)
+ return OSMO_BSC_RF_ADMINSTATE_UNLOCKED;
+ return OSMO_BSC_RF_ADMINSTATE_LOCKED;
+}
+
+/* Return a string listing the state of the given TRX.
+ * For details, see bsc_rf_states_c().
+ */
+static int bsc_rf_state_of_trx_buf(char *buf, size_t buflen, struct gsm_bts_trx *trx)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ OSMO_STRBUF_PRINTF(sb, "%u,%u,%s,%s,%s,%s;",
+ trx->bts->nr, trx->nr,
+ osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_trx(trx)),
+ osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_trx(trx)),
+ osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(trx->bts)),
+ trx->rsl_link_primary ? "rsl-up" : "rsl-down");
+ return sb.chars_needed;
+}
+
+/* Same as bsc_rf_states_of_bts_c() but return result in a fixed-size buffer.
+ * For details, see bsc_rf_states_c().
+ * Return the amount of characters that would be written to the buffer if it is large enough, like snprintf(). */
+static int bsc_rf_states_of_bts_buf(char *buf, size_t buflen, struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ OSMO_STRBUF_APPEND(sb, bsc_rf_state_of_trx_buf, trx);
+ }
+ return sb.chars_needed;
+}
+
+/* Return a string listing the states of each TRX for the given BTS.
+ * For details, see bsc_rf_states_c().
+ *
+ * \param ctx Talloc context to allocate the returned string from.
+ * \param bts BTS of which to list the TRX states.
+ * \return talloc allocated string.
+ */
+char *bsc_rf_states_of_bts_c(void *ctx, struct gsm_bts *bts)
+{
+ OSMO_NAME_C_IMPL(ctx, 256, "ERROR", bsc_rf_states_of_bts_buf, bts);
+}
+
+/* Same as bsc_rf_states_c() but return result in a fixed-size buffer.
+ * For details, see bsc_rf_states_c().
+ * Return the amount of characters that would be written to the buffer if it is large enough, like snprintf(). */
+static int bsc_rf_states_buf(char *buf, size_t buflen)
+{
+ struct gsm_bts *bts;
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
+ OSMO_STRBUF_APPEND(sb, bsc_rf_states_of_bts_buf, bts);
+ }
+ return sb.chars_needed;
+}
+
+/* Return a string listing the states of all TRX of all BTS.
+ * The string has the form:
+ * <bts_nr>,<trx_nr>,<opstate>,<adminstate>,<rf_policy>,<rsl_status>;<bts_nr>,<trx_nr>,...;...;
+ * (always terminates in a semicolon).
+ *
+ * Meaning of the elements:
+ * - bts_nr: 0..255 -- BTS index.
+ * - trx_nr: 0..255 -- TRX index.
+ * - opstate: inoperational|operational -- whether RF is active.
+ * - adminstate: unlocked|locked -- whether the TRX is configured as RF-locked.
+ * - rf_policy: off|on|grace|unknown -- which state RF should be in according to user rf_lock requests.
+ * - rsl_status: rsl-up|rsl-down -- 'rsl-up' if an RSL link to the TRX is currently present.
+ *
+ * \param ctx Talloc context to allocate the returned string from.
+ * \return talloc allocated string.
+ */
+char *bsc_rf_states_c(void *ctx)
+{
+ OSMO_NAME_C_IMPL(ctx, 4096, "ERROR", bsc_rf_states_buf);
+}
+
static int lock_each_trx(struct gsm_network *net, bool lock)
{
struct gsm_bts *bts;
diff --git a/src/osmo-bsc/bsc_stats.c b/src/osmo-bsc/bsc_stats.c
new file mode 100644
index 000000000..c789aead4
--- /dev/null
+++ b/src/osmo-bsc/bsc_stats.c
@@ -0,0 +1,260 @@
+/* osmo-bsc statistics */
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/bsc/bsc_stats.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/core/stat_item.h>
+
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/chan_counts.h>
+
+const struct rate_ctr_desc bsc_ctr_description[] = {
+ [BSC_CTR_ASSIGNMENT_ATTEMPTED] = {"assignment:attempted", "Assignment attempts"},
+ [BSC_CTR_ASSIGNMENT_COMPLETED] = {"assignment:completed", "Assignment completed"},
+ [BSC_CTR_ASSIGNMENT_STOPPED] = {"assignment:stopped", "Connection ended during Assignment"},
+ [BSC_CTR_ASSIGNMENT_NO_CHANNEL] = {"assignment:no_channel", "Failure to allocate lchan for Assignment"},
+ [BSC_CTR_ASSIGNMENT_TIMEOUT] = {"assignment:timeout", "Assignment timed out"},
+ [BSC_CTR_ASSIGNMENT_FAILED] = {"assignment:failed", "Received Assignment Failure message"},
+ [BSC_CTR_ASSIGNMENT_ERROR] = {"assignment:error", "Assignment failed for other reason"},
+
+ [BSC_CTR_HANDOVER_ATTEMPTED] = {"handover:attempted", "Handover attempts"},
+ [BSC_CTR_HANDOVER_COMPLETED] = {"handover:completed", "Handover completed"},
+ [BSC_CTR_HANDOVER_STOPPED] = {"handover:stopped", "Connection ended during HO"},
+ [BSC_CTR_HANDOVER_NO_CHANNEL] = {"handover:no_channel", "Failure to allocate lchan for HO"},
+ [BSC_CTR_HANDOVER_TIMEOUT] = {"handover:timeout", "Handover timed out"},
+ [BSC_CTR_HANDOVER_FAILED] = {"handover:failed", "Received Handover Fail messages"},
+ [BSC_CTR_HANDOVER_ERROR] = {"handover:error", "Handover failed for other reason"},
+
+ [BSC_CTR_INTRA_CELL_HO_ATTEMPTED] = {"intra_cell_ho:attempted", "Intra-Cell handover attempts"},
+ [BSC_CTR_INTRA_CELL_HO_COMPLETED] = {"intra_cell_ho:completed", "Intra-Cell handover completed"},
+ [BSC_CTR_INTRA_CELL_HO_STOPPED] = {"intra_cell_ho:stopped", "Connection ended during HO"},
+ [BSC_CTR_INTRA_CELL_HO_NO_CHANNEL] = {"intra_cell_ho:no_channel", "Failure to allocate lchan for HO"},
+ [BSC_CTR_INTRA_CELL_HO_TIMEOUT] = {"intra_cell_ho:timeout", "Handover timed out"},
+ [BSC_CTR_INTRA_CELL_HO_FAILED] = {"intra_cell_ho:failed", "Received Handover Fail messages"},
+ [BSC_CTR_INTRA_CELL_HO_ERROR] = {"intra_cell_ho:error", "Intra-cell handover failed for other reason"},
+
+ [BSC_CTR_INTRA_BSC_HO_ATTEMPTED] = {"intra_bsc_ho:attempted", "Intra-BSC inter-cell handover attempts"},
+ [BSC_CTR_INTRA_BSC_HO_COMPLETED] = {"intra_bsc_ho:completed", "Intra-BSC inter-cell handover completed"},
+ [BSC_CTR_INTRA_BSC_HO_STOPPED] = {"intra_bsc_ho:stopped", "Connection ended during HO"},
+ [BSC_CTR_INTRA_BSC_HO_NO_CHANNEL] = {"intra_bsc_ho:no_channel", "Failure to allocate lchan for HO"},
+ [BSC_CTR_INTRA_BSC_HO_TIMEOUT] = {"intra_bsc_ho:timeout", "Handover timed out"},
+ [BSC_CTR_INTRA_BSC_HO_FAILED] = {"intra_bsc_ho:failed", "Received Handover Fail messages"},
+ [BSC_CTR_INTRA_BSC_HO_ERROR] = {"intra_bsc_ho:error", "Intra-BSC inter-cell HO failed for other reason"},
+
+ [BSC_CTR_INTER_BSC_HO_OUT_ATTEMPTED] = {"interbsc_ho_out:attempted",
+ "Attempts to handover to remote BSS"},
+ [BSC_CTR_INTER_BSC_HO_OUT_COMPLETED] = {"interbsc_ho_out:completed",
+ "Handover to remote BSS completed"},
+ [BSC_CTR_INTER_BSC_HO_OUT_STOPPED] = {"interbsc_ho_out:stopped", "Connection ended during HO"},
+ [BSC_CTR_INTER_BSC_HO_OUT_TIMEOUT] = {"interbsc_ho_out:timeout", "Handover timed out"},
+ [BSC_CTR_INTER_BSC_HO_OUT_FAILED] = {"interbsc_ho_out:failed", "Received Handover Fail message"},
+ [BSC_CTR_INTER_BSC_HO_OUT_ERROR] = {"interbsc_ho_out:error",
+ "Handover to remote BSS failed for other reason"},
+
+ [BSC_CTR_INTER_BSC_HO_IN_ATTEMPTED] = {"interbsc_ho_in:attempted",
+ "Attempts to handover from remote BSS"},
+ [BSC_CTR_INTER_BSC_HO_IN_COMPLETED] = {"interbsc_ho_in:completed",
+ "Handover from remote BSS completed"},
+ [BSC_CTR_INTER_BSC_HO_IN_STOPPED] = {"interbsc_ho_in:stopped", "Connection ended during HO"},
+ [BSC_CTR_INTER_BSC_HO_IN_NO_CHANNEL] = {"interbsc_ho_in:no_channel",
+ "Failure to allocate lchan for HO"},
+ [BSC_CTR_INTER_BSC_HO_IN_TIMEOUT] = {"interbsc_ho_in:timeout", "Handover from remote BSS timed out"},
+ [BSC_CTR_INTER_BSC_HO_IN_FAILED] = {"interbsc_ho_in:failed", "Received Handover Fail message"},
+ [BSC_CTR_INTER_BSC_HO_IN_ERROR] = {"interbsc_ho_in:error",
+ "Handover from remote BSS failed for other reason"},
+
+ [BSC_CTR_SRVCC_ATTEMPTED] = {"srvcc:attempted", "Intra-BSC SRVCC attempts"},
+ [BSC_CTR_SRVCC_COMPLETED] = {"srvcc:completed", "Intra-BSC SRVCC completed"},
+ [BSC_CTR_SRVCC_STOPPED] = {"srvcc:stopped", "Connection ended during HO"},
+ [BSC_CTR_SRVCC_NO_CHANNEL] = {"srvcc:no_channel", "Failure to allocate lchan for HO"},
+ [BSC_CTR_SRVCC_TIMEOUT] = {"srvcc:timeout", "SRVCC timed out"},
+ [BSC_CTR_SRVCC_FAILED] = {"srvcc:failed", "Received SRVCC Fail messages"},
+ [BSC_CTR_SRVCC_ERROR] = {"srvcc:error", "Re-assignment failed for other reason"},
+
+ [BSC_CTR_PAGING_ATTEMPTED] = {"paging:attempted", "Paging attempts for a subscriber"},
+ [BSC_CTR_PAGING_DETACHED] = {"paging:detached", "Paging request send failures because no responsible BTS was found"},
+ [BSC_CTR_PAGING_RESPONDED] = {"paging:responded", "Paging attempts with successful response"},
+ [BSC_CTR_PAGING_NO_ACTIVE_PAGING] = {"paging:no_active_paging", "Paging response without an active paging request (arrived after paging expiration?)"},
+
+ [BSC_CTR_UNKNOWN_UNIT_ID] = {"abis:unknown_unit_id", "Connection attempts from unknown IPA CCM Unit ID"},
+
+ [BSC_CTR_MSCPOOL_SUBSCR_NO_MSC] = {"mscpool:subscr:no_msc",
+ "Complete Layer 3 requests lost because no connected MSC is found available"},
+ [BSC_CTR_MSCPOOL_EMERG_FORWARDED] = {"mscpool:emerg:forwarded",
+ "Emergency call requests forwarded to an MSC (see also per-MSC counters"},
+ [BSC_CTR_MSCPOOL_EMERG_LOST] = {"mscpool:emerg:lost",
+ "Emergency call requests lost because no MSC was found available"},
+ [BSC_CTR_ALL_ALLOCATED_SDCCH] = {"all_allocated:sdcch", "Cumulative counter of seconds where all SDCCH channels were allocated"},
+ [BSC_CTR_ALL_ALLOCATED_STATIC_SDCCH] = {"all_allocated:static_sdcch",
+ "Cumulative counter of seconds where all non-dynamic SDCCH channels were allocated"},
+ [BSC_CTR_ALL_ALLOCATED_TCH] = {"all_allocated:tch", "Cumulative counter of seconds where all TCH channels were allocated"},
+ [BSC_CTR_ALL_ALLOCATED_STATIC_TCH] = {"all_allocated:static_tch",
+ "Cumulative counter of seconds where all non-dynamic TCH channels were allocated"},
+};
+
+const struct rate_ctr_group_desc bsc_ctrg_desc = {
+ "bsc",
+ "base station controller",
+ OSMO_STATS_CLASS_GLOBAL,
+ ARRAY_SIZE(bsc_ctr_description),
+ bsc_ctr_description,
+};
+
+static const struct osmo_stat_item_desc bsc_stat_desc[] = {
+ [BSC_STAT_NUM_BTS_OML_CONNECTED] = { "num_bts:oml_connected", "Number of BTS for this BSC where OML is up", "", 16, 0 },
+ [BSC_STAT_NUM_BTS_ALL_TRX_RSL_CONNECTED] = { "num_bts:all_trx_rsl_connected", "Number of BTS for this BSC where RSL is up for all TRX", "", 16, 0 },
+ [BSC_STAT_NUM_BTS_TOTAL] = { "num_bts:total", "Number of configured BTS for this BSC", "", 16, 0 },
+ [BSC_STAT_NUM_TRX_RSL_CONNECTED] = { "num_trx:rsl_connected", "Number of TRX where RSL is up, total sum across all BTS", "", 16, 0 },
+ [BSC_STAT_NUM_TRX_TOTAL] = { "num_trx:total", "Number of configured TRX, total sum across all BTS", "", 1, 0 },
+ [BSC_STAT_NUM_MSC_CONNECTED] = { "num_msc:connected", "Number of actively connected MSCs", "", 16, 0 },
+ [BSC_STAT_NUM_MSC_TOTAL] = { "num_msc:total", "Number of configured MSCs, not necessarily connected", "", 1, 0 },
+};
+
+const struct osmo_stat_item_group_desc bsc_statg_desc = {
+ .group_name_prefix = "bsc",
+ .group_description = "base station controller",
+ .class_id = OSMO_STATS_CLASS_GLOBAL,
+ .num_items = ARRAY_SIZE(bsc_stat_desc),
+ .item_desc = bsc_stat_desc,
+};
+
+/* Count all BTS and TRX OML and RSL stati and update stat items */
+void bsc_update_connection_stats(struct gsm_network *net)
+{
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+
+ /* Nr of configured BTS and total sum of configured TRX across all BTS */
+ int num_bts = 0;
+ int num_trx_total = 0;
+ /* Nr of BTS where OML is up */
+ int bts_oml_connected = 0;
+ /* Nr of TRX across all BTS where RSL is up */
+ int trx_rsl_connected_total = 0;
+ /* Nr of BTS that have all TRX RSL up */
+ int bts_rsl_all_trx_connected = 0;
+
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ bool oml_connected = false;
+ int num_trx = 0;
+ int trx_rsl_connected = 0;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ /* If any one trx is usable, it means OML for this BTS is connected */
+ if (trx_is_usable(trx))
+ oml_connected = true;
+
+ /* Count nr of TRX for this BTS */
+ num_trx++;
+ if (trx->ts[0].is_rsl_ready)
+ trx_rsl_connected++;
+ }
+
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_NUM_TRX_RSL_CONNECTED),
+ trx_rsl_connected);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_NUM_TRX_TOTAL),
+ num_trx);
+
+ num_trx_total += num_trx;
+ trx_rsl_connected_total += trx_rsl_connected;
+
+ num_bts++;
+ if (oml_connected)
+ bts_oml_connected++;
+ if (trx_rsl_connected == num_trx)
+ bts_rsl_all_trx_connected++;
+ }
+
+ osmo_stat_item_set(osmo_stat_item_group_get_item(net->bsc_statg, BSC_STAT_NUM_BTS_OML_CONNECTED),
+ bts_oml_connected);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(net->bsc_statg, BSC_STAT_NUM_BTS_ALL_TRX_RSL_CONNECTED),
+ bts_rsl_all_trx_connected);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(net->bsc_statg, BSC_STAT_NUM_BTS_TOTAL), num_bts);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(net->bsc_statg, BSC_STAT_NUM_TRX_RSL_CONNECTED),
+ trx_rsl_connected_total);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(net->bsc_statg, BSC_STAT_NUM_TRX_TOTAL), num_trx_total);
+
+ /* Make sure to notice cells that become disconnected */
+ bsc_update_time_cc_all_allocated(net);
+}
+
+void bsc_update_time_cc_all_allocated(struct gsm_network *net)
+{
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+
+ struct chan_counts bsc_counts;
+ chan_counts_zero(&bsc_counts);
+
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ struct chan_counts bts_counts;
+ chan_counts_zero(&bts_counts);
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct chan_counts trx_counts;
+ chan_counts_for_trx(&trx_counts, trx);
+ chan_counts_add(&bts_counts, &trx_counts);
+ }
+
+ osmo_time_cc_set_flag(&bts->all_allocated_sdcch,
+ bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_SDCCH]
+ && !bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_SDCCH]);
+
+ osmo_time_cc_set_flag(&bts->all_allocated_static_sdcch,
+ bts_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_SDCCH]
+ && !bts_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_SDCCH]);
+
+ osmo_time_cc_set_flag(&bts->all_allocated_tch,
+ (bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_F]
+ + bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_H])
+ && !(bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F]
+ + bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H]));
+
+ osmo_time_cc_set_flag(&bts->all_allocated_static_tch,
+ (bts_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_F]
+ + bts_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_H])
+ && !(bts_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F]
+ + bts_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H]));
+
+ chan_counts_add(&bsc_counts, &bts_counts);
+ }
+
+ osmo_time_cc_set_flag(&net->all_allocated_sdcch,
+ bsc_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_SDCCH]
+ && !bsc_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_SDCCH]);
+
+ osmo_time_cc_set_flag(&net->all_allocated_static_sdcch,
+ bsc_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_SDCCH]
+ && !bsc_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_SDCCH]);
+
+ osmo_time_cc_set_flag(&net->all_allocated_tch,
+ (bsc_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_F]
+ + bsc_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_H])
+ && !(bsc_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F]
+ + bsc_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H]));
+
+ osmo_time_cc_set_flag(&net->all_allocated_static_tch,
+ (bsc_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_F]
+ + bsc_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_H])
+ && !(bsc_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F]
+ + bsc_counts.val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H]));
+}
diff --git a/src/osmo-bsc/bsc_subscr_conn_fsm.c b/src/osmo-bsc/bsc_subscr_conn_fsm.c
index 004572c60..b6fdf6ee9 100644
--- a/src/osmo-bsc/bsc_subscr_conn_fsm.c
+++ b/src/osmo-bsc/bsc_subscr_conn_fsm.c
@@ -45,6 +45,7 @@
#include <osmocom/bsc/assignment_fsm.h>
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
+#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/bsc/lb.h>
#include <osmocom/bsc/lcs_loc_req.h>
@@ -66,7 +67,8 @@ enum gscon_fsm_states {
ST_ASSIGNMENT,
ST_HANDOVER,
/* BSSMAP CLEAR has been received */
- ST_CLEARING,
+ ST_WAIT_CLEAR_CMD,
+ ST_WAIT_SCCP_RLSD,
};
static const struct value_string gscon_fsm_event_names[] = {
@@ -94,7 +96,8 @@ static const struct value_string gscon_fsm_event_names[] = {
struct osmo_tdef_state_timeout conn_fsm_timeouts[32] = {
[ST_WAIT_CC] = { .T = -3210 },
- [ST_CLEARING] = { .T = -4 },
+ [ST_WAIT_CLEAR_CMD] = { .T = -4 },
+ [ST_WAIT_SCCP_RLSD] = { .T = -4 },
};
/* Transition to a state, using the T timer defined in conn_fsm_timeouts.
@@ -142,44 +145,71 @@ static void gscon_bssmap_clear(struct gsm_subscriber_connection *conn,
{
/* already clearing? */
switch (conn->fi->state) {
- case ST_CLEARING:
+ case ST_WAIT_CLEAR_CMD:
+ case ST_WAIT_SCCP_RLSD:
return;
default:
break;
}
conn->clear_cause = cause;
- conn_fsm_state_chg(ST_CLEARING);
+ conn_fsm_state_chg(ST_WAIT_CLEAR_CMD);
}
-static void gscon_fsm_clearing_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+static void gscon_fsm_wait_clear_cmd_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct msgb *resp;
int rc;
struct gsm_subscriber_connection *conn = fi->priv;
enum gsm0808_cause cause = conn->clear_cause;
- if (conn->rx_clear_command) {
- LOGPFSML(conn->fi, LOGL_DEBUG, "Not sending BSSMAP CLEAR REQUEST, already got CLEAR COMMAND from MSC\n");
- return;
- }
-
if (!conn->sccp.msc) {
LOGPFSML(fi, LOGL_ERROR, "Unable to deliver BSSMAP Clear Request message, no MSC for this conn\n");
- return;
+ goto nothing_sent;
}
LOGPFSML(fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST(%s) to MSC\n", gsm0808_cause_name(cause));
resp = gsm0808_create_clear_rqst(cause);
if (!resp) {
LOGPFSML(fi, LOGL_ERROR, "Unable to compose BSSMAP Clear Request message\n");
- return;
+ goto nothing_sent;
}
rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_CLEAR_RQST));
rc = osmo_bsc_sigtran_send(conn, resp);
- if (rc < 0)
+ if (rc < 0) {
LOGPFSML(conn->fi, LOGL_ERROR, "Unable to deliver BSSMAP Clear Request message\n");
+ goto nothing_sent;
+ }
+ return;
+
+nothing_sent:
+ /* Normally, we request a CLEAR from the MSC and terminate as soon as the CLEAR COMMAND has been issued by the
+ * MSC. But if we are trying to clear without being able to send anything to the MSC, we might as well shut down
+ * the conn right away now. */
+ conn_fsm_state_chg(ST_WAIT_SCCP_RLSD);
+}
+
+void gscon_fsm_wait_sccp_rlsd_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ /* According to 3GPP 48.008 3.1.9.1. "The BSS need not wait for the radio channel
+ * release to be completed or for the guard timer to expire before returning the
+ * CLEAR COMPLETE message" */
+ if (!gscon_sigtran_send(conn, gsm0808_create_clear_complete()))
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_CLEAR_COMPLETE));
+
+ /* Give the handover_fsm a chance to book this as handover success before tearing down everything,
+ * making it look like a sudden death failure. */
+ if (conn->ho.fi)
+ osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_CONN_RELEASING, NULL);
+
+ if (conn->lcs.loc_req)
+ osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_CONN_CLEAR, NULL);
+
+ gscon_release_lchans(conn, true, bsc_gsm48_rr_cause_from_gsm0808_cause(conn->clear_cause));
+ osmo_mgcpc_ep_clear(conn->user_plane.mgw_endpoint);
}
/* forward MO DTAP from RSL side to BSSAP side */
@@ -530,39 +560,47 @@ struct osmo_mgcpc_ep *gscon_ensure_mgw_endpoint(struct gsm_subscriber_connection
uint16_t msc_assigned_cic, struct gsm_lchan *for_lchan)
{
const char *epname;
+ struct mgcp_client *mgcp_client = NULL;
if (conn->user_plane.mgw_endpoint)
return conn->user_plane.mgw_endpoint;
+ if (gscon_is_sccplite(conn) || gscon_is_aoip(conn)) {
+ /* Get MGCP client from pool */
+ mgcp_client = mgcp_client_pool_get(conn->network->mgw.mgw_pool);
+ if (!mgcp_client) {
+ LOGPFSML(conn->fi, LOGL_ERROR,
+ "cannot ensure MGW endpoint -- no MGW configured, check configuration!\n");
+ conn->user_plane.mgw_endpoint = NULL;
+ return NULL;
+ }
+ }
+
if (gscon_is_sccplite(conn)) {
/* derive endpoint name from CIC on A interface side */
conn->user_plane.mgw_endpoint =
osmo_mgcpc_ep_alloc(conn->fi, GSCON_EV_FORGET_MGW_ENDPOINT,
- conn->network->mgw.client,
+ mgcp_client,
conn->network->mgw.tdefs,
conn->fi->id,
"%x@%s", msc_assigned_cic,
- mgcp_client_endpoint_domain(conn->network->mgw.client));
+ mgcp_client_endpoint_domain(mgcp_client));
LOGPFSML(conn->fi, LOGL_DEBUG, "MGW endpoint name derived from CIC 0x%x: %s\n",
msc_assigned_cic, osmo_mgcpc_ep_name(conn->user_plane.mgw_endpoint));
} else if (gscon_is_aoip(conn)) {
-
if (is_ipaccess_bts(for_lchan->ts->trx->bts))
/* use dynamic RTPBRIDGE endpoint allocation in MGW */
- epname = mgcp_client_rtpbridge_wildcard(conn->network->mgw.client);
+ epname = mgcp_client_rtpbridge_wildcard(mgcp_client);
else {
- epname = mgcp_client_e1_epname(conn, conn->network->mgw.client, for_lchan->ts->e1_link.e1_nr,
+ epname = mgcp_client_e1_epname(conn, mgcp_client, for_lchan->ts->e1_link.e1_nr,
for_lchan->ts->e1_link.e1_ts, 16,
for_lchan->ts->e1_link.e1_ts_ss*2);
}
conn->user_plane.mgw_endpoint =
- osmo_mgcpc_ep_alloc(conn->fi, GSCON_EV_FORGET_MGW_ENDPOINT,
- conn->network->mgw.client,
- conn->network->mgw.tdefs,
- conn->fi->id,
- "%s", epname);
+ osmo_mgcpc_ep_alloc(conn->fi, GSCON_EV_FORGET_MGW_ENDPOINT, mgcp_client,
+ conn->network->mgw.tdefs, conn->fi->id, "%s", epname);
} else {
LOGPFSML(conn->fi, LOGL_ERROR, "Conn is neither SCCPlite nor AoIP!?\n");
return NULL;
@@ -668,13 +706,13 @@ static const struct osmo_fsm_state gscon_fsm_states[] = {
.name = "INIT",
.in_event_mask = S(GSCON_EV_MO_COMPL_L3) | S(GSCON_EV_A_CONN_IND)
| S(GSCON_EV_HANDOVER_END),
- .out_state_mask = S(ST_WAIT_CC) | S(ST_ACTIVE) | S(ST_CLEARING),
+ .out_state_mask = S(ST_WAIT_CC) | S(ST_ACTIVE) | S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD),
.action = gscon_fsm_init,
},
[ST_WAIT_CC] = {
.name = "WAIT_CC",
.in_event_mask = S(GSCON_EV_A_CONN_CFM),
- .out_state_mask = S(ST_ACTIVE) | S(ST_CLEARING),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD),
.action = gscon_fsm_wait_cc,
},
[ST_ACTIVE] = {
@@ -684,27 +722,32 @@ static const struct osmo_fsm_state gscon_fsm_states[] = {
| S(GSCON_EV_LCS_LOC_REQ_END)
| S(GSCON_EV_MO_COMPL_L3)
,
- .out_state_mask = S(ST_CLEARING) | S(ST_ASSIGNMENT) |
+ .out_state_mask = S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD) | S(ST_ASSIGNMENT) |
S(ST_HANDOVER),
.action = gscon_fsm_active,
},
[ST_ASSIGNMENT] = {
.name = "ASSIGNMENT",
.in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_ASSIGNMENT_END),
- .out_state_mask = S(ST_ACTIVE) | S(ST_CLEARING),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD),
.action = gscon_fsm_assignment,
},
[ST_HANDOVER] = {
.name = "HANDOVER",
.in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_HANDOVER_END),
- .out_state_mask = S(ST_ACTIVE) | S(ST_CLEARING),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD),
.action = gscon_fsm_handover,
},
- [ST_CLEARING] = {
- .name = "CLEARING",
- .onenter = gscon_fsm_clearing_onenter,
- /* dead end state */
- },
+ [ST_WAIT_CLEAR_CMD] = {
+ .name = "WAIT_CLEAR_CMD",
+ .onenter = gscon_fsm_wait_clear_cmd_onenter,
+ .out_state_mask = S(ST_WAIT_SCCP_RLSD),
+ },
+ [ST_WAIT_SCCP_RLSD] = {
+ .name = "WAIT_SCCP_RLSD",
+ .onenter = gscon_fsm_wait_sccp_rlsd_onenter,
+ .in_event_mask = S(GSCON_EV_HANDOVER_END),
+ },
};
void gscon_change_primary_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan *new_lchan)
@@ -808,6 +851,12 @@ void gscon_forget_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan
static void gscon_forget_mgw_endpoint(struct gsm_subscriber_connection *conn)
{
+ struct mgcp_client *mgcp_client;
+
+ /* Put MGCP client back into MGW pool */
+ mgcp_client = osmo_mgcpc_ep_client(conn->user_plane.mgw_endpoint);
+ mgcp_client_pool_put(mgcp_client);
+
conn->user_plane.mgw_endpoint = NULL;
conn->user_plane.mgw_endpoint_ci_msc = NULL;
conn->ho.created_ci_for_msc = NULL;
@@ -828,44 +877,19 @@ void gscon_forget_mgw_endpoint_ci(struct gsm_subscriber_connection *conn, struct
static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
- const enum gsm0808_cause *cause_0808;
const struct tlv_parsed *tp;
struct osmo_mobile_identity mi_imsi;
/* Regular allstate event processing */
switch (event) {
case GSCON_EV_A_CLEAR_CMD:
- conn->rx_clear_command = true;
-
- /* Give the handover_fsm a chance to book this as handover success before tearing down everything,
- * making it look like a sudden death failure. */
- if (conn->ho.fi)
- osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_CONN_RELEASING, NULL);
-
- if (conn->lcs.loc_req)
- osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_CONN_CLEAR, NULL);
-
OSMO_ASSERT(data);
- cause_0808 = data;
- /* MSC tells us to cleanly shut down */
- if (conn->fi->state != ST_CLEARING)
- osmo_fsm_inst_state_chg(fi, ST_CLEARING, 60, -4);
- LOGPFSML(fi, LOGL_DEBUG, "Releasing all lchans (if any) after BSSMAP Clear Command\n");
- gscon_release_lchans(conn, true, bsc_gsm48_rr_cause_from_gsm0808_cause(*cause_0808));
- /* FIXME: Release all terestrial resources in ST_CLEARING */
- /* According to 3GPP 48.008 3.1.9.1. "The BSS need not wait for the radio channel
- * release to be completed or for the guard timer to expire before returning the
- * CLEAR COMPLETE message" */
-
- /* Close MGCP connections */
- osmo_mgcpc_ep_clear(conn->user_plane.mgw_endpoint);
-
- rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_CLEAR_COMPLETE));
- gscon_sigtran_send(conn, gsm0808_create_clear_complete());
+ conn->clear_cause = *(const enum gsm0808_cause*)data;
+ conn_fsm_state_chg(ST_WAIT_SCCP_RLSD);
break;
case GSCON_EV_A_DISC_IND:
- /* MSC or SIGTRAN network has hard-released SCCP connection,
- * terminate the FSM now. */
+ /* MSC or SIGTRAN network has hard-released SCCP connection, terminate the FSM now.
+ * Cleanup is done in gscon_pre_term() and gscon_cleanup(). */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, data);
break;
case GSCON_EV_FORGET_MGW_ENDPOINT:
@@ -947,8 +971,21 @@ static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau
static void gscon_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct gsm_subscriber_connection *conn = fi->priv;
+ struct mgcp_client *mgcp_client;
+
+ /* Put MGCP client back into MGW pool */
+ mgcp_client = osmo_mgcpc_ep_client(conn->user_plane.mgw_endpoint);
+ mgcp_client_pool_put(mgcp_client);
osmo_mgcpc_ep_clear(conn->user_plane.mgw_endpoint);
+ conn->user_plane.mgw_endpoint = NULL;
+ conn->user_plane.mgw_endpoint = NULL;
+
+ if (conn->ho.fi)
+ osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_CONN_RELEASING, NULL);
+
+ if (conn->lcs.loc_req)
+ osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_CONN_CLEAR, NULL);
if (conn->lcls.fi) {
/* request termination of LCLS FSM */
diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c
index 6d104264b..c1a6e4404 100644
--- a/src/osmo-bsc/bsc_vty.c
+++ b/src/osmo-bsc/bsc_vty.c
@@ -37,6 +37,7 @@
#include <osmocom/gsm/gsm0502.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
+#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/gsm_data.h>
@@ -62,6 +63,7 @@
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/assignment_fsm.h>
+#include <osmocom/bsc/bssmap_reset.h>
#include <inttypes.h>
@@ -847,6 +849,7 @@ static int ho_or_as(struct vty *vty, const char *argv[], int argc)
return CMD_WARNING;
}
+/* tsc_set and tsc: -1 to automatically determine which TSC Set / which TSC to use. */
static int trigger_vamos_mode_modify(struct vty *vty, struct gsm_lchan *lchan, bool vamos, int tsc_set, int tsc)
{
struct lchan_modify_info info = {
@@ -854,8 +857,14 @@ static int trigger_vamos_mode_modify(struct vty *vty, struct gsm_lchan *lchan, b
.ch_mode_rate = lchan->current_ch_mode_rate,
.requires_voice_stream = (lchan->fi_rtp != NULL),
.vamos = vamos,
- .tsc_set = tsc_set,
- .tsc = tsc,
+ .tsc_set = {
+ .present = (tsc_set >= 0),
+ .val = tsc_set,
+ },
+ .tsc = {
+ .present = (tsc >= 0),
+ .val = tsc,
+ },
};
lchan_mode_modify(lchan, &info);
@@ -1354,6 +1363,12 @@ DEFUN(bts_resend_power_ctrl_params,
return CMD_WARNING;
}
+ if (bts->ms_power_ctrl.mode != GSM_PWR_CTRL_MODE_DYN_BTS) {
+ vty_out(vty, "%% Not Sending default MS/BS Power control parameters "
+ "because BTS%d is not using dyn-bts mode%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
if (bts->model->power_ctrl_send_def_params == NULL) {
vty_out(vty, "%% Sending default MS/BS Power control parameters "
"for BTS%d is not implemented%s", bts_nr, VTY_NEWLINE);
@@ -1538,10 +1553,7 @@ DEFUN(pdch_act, pdch_act_cmd,
/* Activate / Deactivate a single lchan with a specific codec mode */
static int lchan_act_single(struct vty *vty, struct gsm_lchan *lchan, const char *codec_str, int amr_mode, int activate)
{
- struct lchan_activate_info info = {
- .tsc_set = -1,
- .tsc = -1,
- };
+ struct lchan_activate_info info = {0};
uint16_t amr_modes[8] =
{ GSM0808_SC_CFG_AMR_4_75, GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20, GSM0808_SC_CFG_AMR_5_90,
GSM0808_SC_CFG_AMR_6_70, GSM0808_SC_CFG_AMR_7_40, GSM0808_SC_CFG_AMR_7_95, GSM0808_SC_CFG_AMR_10_2,
@@ -1587,53 +1599,35 @@ static int lchan_act_single(struct vty *vty, struct gsm_lchan *lchan, const char
/* configure the lchan */
lchan_select_set_type(lchan, lchan_t);
if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr")) {
- info = (struct lchan_activate_info) {
- .activ_for = ACTIVATE_FOR_VTY,
- .ch_mode_rate = {
- .chan_mode = GSM48_CMODE_SPEECH_V1,
- },
- .requires_voice_stream = false,
- };
+ info.ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1;
} else if (!strcmp(codec_str, "efr")) {
- info = (struct lchan_activate_info) {
- .activ_for = ACTIVATE_FOR_VTY,
- .ch_mode_rate = {
- .chan_mode = GSM48_CMODE_SPEECH_EFR,
- },
- .requires_voice_stream = false,
- };
+ info.ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_EFR;
} else if (!strcmp(codec_str, "amr")) {
if (amr_mode == -1) {
vty_out(vty, "%% AMR requires specification of AMR mode%s", VTY_NEWLINE);
return CMD_WARNING;
}
- info = (struct lchan_activate_info) {
- .activ_for = ACTIVATE_FOR_VTY,
- .ch_mode_rate = {
- .chan_mode = GSM48_CMODE_SPEECH_AMR,
- .s15_s0 = amr_modes[amr_mode],
- },
- .requires_voice_stream = false,
- };
+ info.ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_AMR;
+ info.ch_mode_rate.s15_s0 = amr_modes[amr_mode];
} else if (!strcmp(codec_str, "sig")) {
- info = (struct lchan_activate_info) {
- .activ_for = ACTIVATE_FOR_VTY,
- .ch_mode_rate = {
- .chan_mode = GSM48_CMODE_SIGN,
- },
- .requires_voice_stream = false,
- };
+ info.ch_mode_rate.chan_mode = GSM48_CMODE_SIGN;
} else {
vty_out(vty, "%% Invalid channel mode specified!%s", VTY_NEWLINE);
return CMD_WARNING;
}
+ info.activ_for = ACTIVATE_FOR_VTY;
+ info.requires_voice_stream = false;
info.ch_mode_rate.chan_rate = chan_t_to_chan_rate(lchan_t);
if (activate == 2 || lchan->vamos.is_secondary) {
info.vamos = true;
- info.tsc_set = lchan->vamos.is_secondary ? 1 : 0;
- info.tsc = 0;
+ if (lchan->vamos.is_secondary) {
+ info.tsc_set.present = true;
+ info.tsc_set.val = 1;
+ }
+ info.tsc.present = true;
+ info.tsc.val = 0;
info.ch_mode_rate.chan_mode = gsm48_chan_mode_to_vamos(info.ch_mode_rate.chan_mode);
}
@@ -1904,6 +1898,57 @@ DEFUN_HIDDEN(lchan_act_all_trx, lchan_act_all_trx_cmd,
return CMD_SUCCESS;
}
+DEFUN(lchan_set_mspower, lchan_set_mspower_cmd,
+ "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> ms-power <0-40> [verify]\n",
+ BTS_NR_TRX_TS_SS_STR2
+ "Manually force MS Uplink Power Level in dBm on the lchan (for testing)\n"
+ "Set transmit power of the MS in dBm\n"
+ "Check requested level against BAND and UE Power Class.\n")
+{
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+ struct gsm_lchan *lchan;
+ int bts_nr = atoi(argv[0]);
+ int trx_nr = atoi(argv[1]);
+ int ss_nr = atoi(argv[3]);
+ bool verify = (argc > 5);
+
+ bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
+ if (!bts) {
+ vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ trx = gsm_bts_trx_num(bts, trx_nr);
+ if (!trx) {
+ vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
+ if (!ts) {
+ vty_out(vty, "%% No such TS (%d)%s", atoi(argv[2]), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (ss_nr >= ts->max_primary_lchans) {
+ vty_out(vty, "%% Invalid sub-slot number for this timeslot type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ lchan = &ts->lchan[ss_nr];
+ if (!lchan->fi)
+ return CMD_WARNING;
+
+ if (verify) {
+ lchan_update_ms_power_ctrl_level(lchan, atoi(argv[4]));
+ return CMD_SUCCESS;
+ }
+ lchan->ms_power = ms_pwr_ctl_lvl(ts->trx->bts->band, atoi(argv[4]));
+ rsl_chan_ms_power_ctrl(lchan);
+ return CMD_SUCCESS;
+}
+
DEFUN(vamos_modify_lchan, vamos_modify_lchan_cmd,
"bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> modify (vamos|non-vamos) " TSC_ARGS_OPT,
BTS_NR_TRX_TS_SS_STR2
@@ -3324,6 +3369,28 @@ DEFUN_HIDDEN(mscpool_roundrobin_next, mscpool_roundrobin_next_cmd,
return CMD_SUCCESS;
}
+DEFUN(msc_bssmap_reset, msc_bssmap_reset_cmd,
+ "msc " MSC_NR_RANGE " bssmap reset",
+ "Query or manipulate a specific A-interface link\n"
+ "MSC nr\n"
+ "Query or manipulate BSSMAP layer of A-interface\n"
+ "Flip this MSC to disconnected state and re-send BSSMAP RESET\n")
+{
+ int msc_nr = atoi(argv[0]);
+ struct bsc_msc_data *msc;
+
+ msc = osmo_msc_data_find(bsc_gsmnet, msc_nr);
+
+ if (!msc) {
+ vty_out(vty, "%% No such MSC: nr %d\n", msc_nr);
+ return CMD_WARNING;
+ }
+
+ LOGP(DMSC, LOGL_NOTICE, "(msc%d) VTY requests BSSMAP RESET\n", msc_nr);
+ bssmap_reset_resend_reset(msc->a.bssmap_reset);
+ return CMD_SUCCESS;
+}
+
int bsc_vty_init(struct gsm_network *network)
{
OSMO_ASSERT(vty_global_gsm_network == NULL);
@@ -3383,6 +3450,7 @@ int bsc_vty_init(struct gsm_network *network)
install_element(GSMNET_NODE, &cfg_net_nri_null_del_cmd);
bts_vty_init();
+ mgcp_client_pool_vty_init(GSMNET_NODE, MGW_NODE, " ", vty_global_gsm_network->mgw.mgw_pool);
install_element(ENABLE_NODE, &drop_bts_cmd);
install_element(ENABLE_NODE, &restart_bts_cmd);
@@ -3399,6 +3467,7 @@ int bsc_vty_init(struct gsm_network *network)
install_element(ENABLE_NODE, &lchan_mdcx_cmd);
install_element(ENABLE_NODE, &lchan_set_borken_cmd);
install_element(ENABLE_NODE, &lchan_reassign_cmd);
+ install_element(ENABLE_NODE, &lchan_set_mspower_cmd);
install_element(ENABLE_NODE, &handover_subscr_conn_cmd);
install_element(ENABLE_NODE, &assignment_subscr_conn_cmd);
@@ -3477,6 +3546,7 @@ int bsc_vty_init(struct gsm_network *network)
install_element(ENABLE_NODE, &gen_position_trap_cmd);
install_element(ENABLE_NODE, &mscpool_roundrobin_next_cmd);
+ install_element(ENABLE_NODE, &msc_bssmap_reset_cmd);
install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd);
diff --git a/src/osmo-bsc/bssmap_reset.c b/src/osmo-bsc/bssmap_reset.c
index 6c54560e7..beb89684a 100644
--- a/src/osmo-bsc/bssmap_reset.c
+++ b/src/osmo-bsc/bssmap_reset.c
@@ -243,6 +243,12 @@ bool bssmap_reset_is_conn_ready(const struct bssmap_reset *bssmap_reset)
return bssmap_reset->fi->state == BSSMAP_RESET_ST_CONN;
}
+void bssmap_reset_resend_reset(struct bssmap_reset *bssmap_reset)
+{
+ /* Immediately (1ms) kick off reset sending mechanism */
+ osmo_fsm_inst_state_chg_ms(bssmap_reset->fi, BSSMAP_RESET_ST_DISC, 1, 0);
+}
+
static __attribute__((constructor)) void bssmap_reset_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&bssmap_reset_fsm) == 0);
diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c
index c0d663420..11b1ec384 100644
--- a/src/osmo-bsc/bts.c
+++ b/src/osmo-bsc/bts.c
@@ -211,6 +211,51 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, struct gsm_bts_sm *bts_sm
}
bts->bts_statg = osmo_stat_item_group_alloc(bts, &bts_statg_desc, bts->nr);
+ bts->all_allocated_sdcch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_SDCCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+ bts->all_allocated_static_sdcch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_STATIC_SDCCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+ bts->all_allocated_tch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_TCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+ bts->all_allocated_static_tch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_STATIC_TCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+
/* create our primary TRX */
bts->c0 = gsm_bts_trx_alloc(bts);
if (!bts->c0) {
@@ -266,10 +311,14 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, struct gsm_bts_sm *bts_sm
bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */
bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */
bts->si_common.rach_control.t2 = 4; /* no emergency calls */
+ bts->si_common.chan_desc.mscr = 1; /* Indicate R99 MSC in SI3 */
bts->si_common.chan_desc.att = 1; /* attachment required */
bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */
bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */
bts->si_common.chan_desc.t3212 = osmo_tdef_get(net->T_defs, 3212, OSMO_TDEF_CUSTOM, -1);
+ bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
+ bts->si_common.cell_sel_par.acs = 0;
+ bts->si_common.ncc_permitted = 0xff;
gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */
INIT_LLIST_HEAD(&bts->abis_queue);
@@ -348,15 +397,18 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, struct gsm_bts_sm *bts_sm
acc_mgr_init(&bts->acc_mgr, bts);
acc_ramp_init(&bts->acc_ramp, bts);
- bts->repeated_acch_policy.rxqual = 4;
+ /* Default RxQual threshold for ACCH repetition/overpower */
+ bts->rep_acch_cap.rxqual = 4;
+ bts->top_acch_cap.rxqual = 4;
+
+ /* Permit ACCH overpower only for speech channels using AMR */
+ bts->top_acch_chan_mode = TOP_ACCH_CHAN_MODE_SPEECH_V3;
/* MS Power Control parameters (defaults) */
- bts->ms_power_ctrl = power_ctrl_params_def;
- bts->ms_power_ctrl.dir = GSM_PWR_CTRL_DIR_UL;
+ power_ctrl_params_def_reset(&bts->ms_power_ctrl, GSM_PWR_CTRL_DIR_UL);
/* BS Power Control parameters (defaults) */
- bts->bs_power_ctrl = power_ctrl_params_def;
- bts->bs_power_ctrl.dir = GSM_PWR_CTRL_DIR_DL;
+ power_ctrl_params_def_reset(&bts->bs_power_ctrl, GSM_PWR_CTRL_DIR_DL);
/* Interference Measurement Parameters (defaults) */
bts->interf_meas_params_cfg = interf_meas_params_def;
@@ -715,19 +767,6 @@ void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data)
gsm_trx_all_ts_dispatch(trx, ts_ev, data);
}
-
-/* Count number of free TS of given pchan type */
-int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
-{
- struct gsm_bts_trx *trx;
- int count = 0;
-
- llist_for_each_entry(trx, &bts->trx_list, list)
- count += trx_count_free_ts(trx, pchan);
-
- return count;
-}
-
/* set all system information types for a BTS */
int gsm_bts_set_system_infos(struct gsm_bts *bts)
{
@@ -845,6 +884,27 @@ const struct rate_ctr_desc bts_ctr_description[] = {
[BTS_CTR_CHREQ_SUCCESSFUL] = \
{ "chreq:successful",
"Successful channel requests (immediate assign sent)" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_EMERG] = \
+ { "chreq:successful_emerg",
+ "Sent Immediate Assignment for EMERG" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_CALL] = \
+ { "chreq:successful_call",
+ "Sent Immediate Assignment for CALL" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_LOCATION_UPD] = \
+ { "chreq:successful_location_upd",
+ "Sent Immediate Assignment for LOCATION_UPD" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_PAG] = \
+ { "chreq:successful_pag",
+ "Sent Immediate Assignment for PAG" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_PDCH] = \
+ { "chreq:successful_pdch",
+ "Sent Immediate Assignment for PDCH" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_OTHER] = \
+ { "chreq:successful_other",
+ "Sent Immediate Assignment for OTHER" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_UNKNOWN] = \
+ { "chreq:successful_unknown",
+ "Sent Immediate Assignment for UNKNOWN" },
[BTS_CTR_CHREQ_NO_CHANNEL] = \
{ "chreq:no_channel",
"Sent to MS no channel available" },
@@ -1121,10 +1181,10 @@ const struct rate_ctr_desc bts_ctr_description[] = {
[BTS_CTR_INTRA_BSC_HO_ATTEMPTED] = \
{ "intra_bsc_ho:attempted",
- "Intra-BSC handover attempts" },
+ "Intra-BSC inter-cell handover attempts" },
[BTS_CTR_INTRA_BSC_HO_COMPLETED] = \
{ "intra_bsc_ho:completed",
- "Intra-BSC handover completed" },
+ "Intra-BSC inter-cell handover completed" },
[BTS_CTR_INTRA_BSC_HO_STOPPED] = \
{ "intra_bsc_ho:stopped",
"Connection ended during HO" },
@@ -1139,7 +1199,29 @@ const struct rate_ctr_desc bts_ctr_description[] = {
"Received Handover Fail messages" },
[BTS_CTR_INTRA_BSC_HO_ERROR] = \
{ "intra_bsc_ho:error",
- "Re-assignment failed for other reason" },
+ "Intra-BSC inter-cell HO failed for other reason" },
+
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_ATTEMPTED] = \
+ { "incoming_intra_bsc_ho:attempted",
+ "Incoming intra-BSC inter-cell handover attempts" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_COMPLETED] = \
+ { "incoming_intra_bsc_ho:completed",
+ "Incoming intra-BSC inter-cell handover completed" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_STOPPED] = \
+ { "incoming_intra_bsc_ho:stopped",
+ "Connection ended during HO" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_NO_CHANNEL] = \
+ { "incoming_intra_bsc_ho:no_channel",
+ "Failure to allocate lchan for HO" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_TIMEOUT] = \
+ { "incoming_intra_bsc_ho:timeout",
+ "Handover timed out" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_FAILED] = \
+ { "incoming_intra_bsc_ho:failed",
+ "Received Handover Fail messages" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_ERROR] = \
+ { "incoming_intra_bsc_ho:error",
+ "Incoming intra-BSC inter-cell HO failed for other reason" },
[BTS_CTR_INTER_BSC_HO_OUT_ATTEMPTED] = \
{ "interbsc_ho_out:attempted",
@@ -1202,6 +1284,93 @@ const struct rate_ctr_desc bts_ctr_description[] = {
[BTS_CTR_SRVCC_ERROR] = \
{ "srvcc:error",
"Re-assignment failed for other reason" },
+ [BTS_CTR_ALL_ALLOCATED_SDCCH] = \
+ { "all_allocated:sdcch",
+ "Cumulative counter of seconds where all SDCCH channels were allocated" },
+ [BTS_CTR_ALL_ALLOCATED_STATIC_SDCCH] = \
+ { "all_allocated:static_sdcch",
+ "Cumulative counter of seconds where all non-dynamic SDCCH channels were allocated" },
+ [BTS_CTR_ALL_ALLOCATED_TCH] = \
+ { "all_allocated:tch",
+ "Cumulative counter of seconds where all TCH channels were allocated" },
+ [BTS_CTR_ALL_ALLOCATED_STATIC_TCH] = \
+ { "all_allocated:static_tch",
+ "Cumulative counter of seconds where all non-dynamic TCH channels were allocated" },
+
+ [BTS_CTR_CM_SERV_REJ] = \
+ { "cm_serv_rej", "MSC sent CM Service Reject" },
+ [BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_HLR] = \
+ { "cm_serv_rej:imsi_unknown_in_hlr",
+ "MSC sent CM Service Reject with cause IMSI_UNKNOWN_IN_HLR" },
+ [BTS_CTR_CM_SERV_REJ_ILLEGAL_MS] = \
+ { "cm_serv_rej:illegal_ms",
+ "MSC sent CM Service Reject with cause ILLEGAL_MS" },
+ [BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_VLR] = \
+ { "cm_serv_rej:imsi_unknown_in_vlr",
+ "MSC sent CM Service Reject with cause IMSI_UNKNOWN_IN_VLR" },
+ [BTS_CTR_CM_SERV_REJ_IMEI_NOT_ACCEPTED] = \
+ { "cm_serv_rej:imei_not_accepted",
+ "MSC sent CM Service Reject with cause IMEI_NOT_ACCEPTED" },
+ [BTS_CTR_CM_SERV_REJ_ILLEGAL_ME] = \
+ { "cm_serv_rej:illegal_me",
+ "MSC sent CM Service Reject with cause ILLEGAL_ME" },
+ [BTS_CTR_CM_SERV_REJ_PLMN_NOT_ALLOWED] = \
+ { "cm_serv_rej:plmn_not_allowed",
+ "MSC sent CM Service Reject with cause PLMN_NOT_ALLOWED" },
+ [BTS_CTR_CM_SERV_REJ_LOC_NOT_ALLOWED] = \
+ { "cm_serv_rej:loc_not_allowed",
+ "MSC sent CM Service Reject with cause LOC_NOT_ALLOWED" },
+ [BTS_CTR_CM_SERV_REJ_ROAMING_NOT_ALLOWED] = \
+ { "cm_serv_rej:roaming_not_allowed",
+ "MSC sent CM Service Reject with cause ROAMING_NOT_ALLOWED" },
+ [BTS_CTR_CM_SERV_REJ_NETWORK_FAILURE] = \
+ { "cm_serv_rej:network_failure",
+ "MSC sent CM Service Reject with cause NETWORK_FAILURE" },
+ [BTS_CTR_CM_SERV_REJ_SYNCH_FAILURE] = \
+ { "cm_serv_rej:synch_failure",
+ "MSC sent CM Service Reject with cause SYNCH_FAILURE" },
+ [BTS_CTR_CM_SERV_REJ_CONGESTION] = \
+ { "cm_serv_rej:congestion",
+ "MSC sent CM Service Reject with cause CONGESTION" },
+ [BTS_CTR_CM_SERV_REJ_SRV_OPT_NOT_SUPPORTED] = \
+ { "cm_serv_rej:srv_opt_not_supported",
+ "MSC sent CM Service Reject with cause SRV_OPT_NOT_SUPPORTED" },
+ [BTS_CTR_CM_SERV_REJ_RQD_SRV_OPT_NOT_SUPPORTED] = \
+ { "cm_serv_rej:rqd_srv_opt_not_supported",
+ "MSC sent CM Service Reject with cause RQD_SRV_OPT_NOT_SUPPORTED" },
+ [BTS_CTR_CM_SERV_REJ_SRV_OPT_TMP_OUT_OF_ORDER] = \
+ { "cm_serv_rej:srv_opt_tmp_out_of_order",
+ "MSC sent CM Service Reject with cause SRV_OPT_TMP_OUT_OF_ORDER" },
+ [BTS_CTR_CM_SERV_REJ_CALL_CAN_NOT_BE_IDENTIFIED] = \
+ { "cm_serv_rej:call_can_not_be_identified",
+ "MSC sent CM Service Reject with cause CALL_CAN_NOT_BE_IDENTIFIED" },
+ [BTS_CTR_CM_SERV_REJ_INCORRECT_MESSAGE] = \
+ { "cm_serv_rej:incorrect_message",
+ "MSC sent CM Service Reject with cause INCORRECT_MESSAGE" },
+ [BTS_CTR_CM_SERV_REJ_INVALID_MANDANTORY_INF] = \
+ { "cm_serv_rej:invalid_mandantory_inf",
+ "MSC sent CM Service Reject with cause INVALID_MANDANTORY_INF" },
+ [BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_IMPLEMENTED] = \
+ { "cm_serv_rej:msg_type_not_implemented",
+ "MSC sent CM Service Reject with cause MSG_TYPE_NOT_IMPLEMENTED" },
+ [BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_COMPATIBLE] = \
+ { "cm_serv_rej:msg_type_not_compatible",
+ "MSC sent CM Service Reject with cause MSG_TYPE_NOT_COMPATIBLE" },
+ [BTS_CTR_CM_SERV_REJ_INF_ELEME_NOT_IMPLEMENTED] = \
+ { "cm_serv_rej:inf_eleme_not_implemented",
+ "MSC sent CM Service Reject with cause INF_ELEME_NOT_IMPLEMENTED" },
+ [BTS_CTR_CM_SERV_REJ_CONDTIONAL_IE_ERROR] = \
+ { "cm_serv_rej:condtional_ie_error",
+ "MSC sent CM Service Reject with cause CONDTIONAL_IE_ERROR" },
+ [BTS_CTR_CM_SERV_REJ_MSG_NOT_COMPATIBLE] = \
+ { "cm_serv_rej:msg_not_compatible",
+ "MSC sent CM Service Reject with cause MSG_NOT_COMPATIBLE" },
+ [BTS_CTR_CM_SERV_REJ_PROTOCOL_ERROR] = \
+ { "cm_serv_rej:protocol_error",
+ "MSC sent CM Service Reject with cause PROTOCOL_ERROR" },
+ [BTS_CTR_CM_SERV_REJ_RETRY_IN_NEW_CELL] = \
+ { "cm_serv_rej:retry_in_new_cell",
+ "MSC sent CM Service Reject with cause 00110000..00111111, Retry upon entry in a new cell" },
};
const struct rate_ctr_group_desc bts_ctrg_desc = {
@@ -1303,7 +1472,7 @@ const struct osmo_stat_item_desc bts_stat_desc[] = {
"", 16, 0 },
[BTS_STAT_RSL_CONNECTED] = \
{ "rsl_connected",
- "Number of RSL links connected",
+ "Number of RSL links connected (same as num_trx:rsl_connected)",
"", 16, 0 },
[BTS_STAT_LCHAN_BORKEN] = \
{ "lchan_borken",
@@ -1313,6 +1482,14 @@ const struct osmo_stat_item_desc bts_stat_desc[] = {
{ "ts_borken",
"Number of timeslots in the BORKEN state",
"", 16, 0 },
+ [BTS_STAT_NUM_TRX_RSL_CONNECTED] = \
+ { "num_trx:rsl_connected",
+ "Number of TRX in this BTS where RSL is up",
+ "" },
+ [BTS_STAT_NUM_TRX_TOTAL] = \
+ { "num_trx:total",
+ "Number of configured TRX in this BTS",
+ "" },
};
const struct osmo_stat_item_group_desc bts_statg_desc = {
diff --git a/src/osmo-bsc/bts_ipaccess_nanobts.c b/src/osmo-bsc/bts_ipaccess_nanobts.c
index 27f6aeee5..9607068d5 100644
--- a/src/osmo-bsc/bts_ipaccess_nanobts.c
+++ b/src/osmo-bsc/bts_ipaccess_nanobts.c
@@ -51,6 +51,7 @@
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/bts_sm.h>
#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/bsc_stats.h>
static int bts_model_nanobts_start(struct gsm_network *net);
static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line);
@@ -919,13 +920,17 @@ static void enc_power_params(struct msgb *msg, const struct gsm_power_ctrl_param
thresh_comp->red_step_size = cp->red_step_size_db & 0x0f;
}
+void osmobts_enc_power_params_osmo_ext(struct msgb *msg, const struct gsm_power_ctrl_params *cp);
static void add_power_params_ie(struct msgb *msg, enum abis_rsl_ie iei,
+ const struct gsm_bts_trx *trx,
const struct gsm_power_ctrl_params *cp)
{
uint8_t *ie_len = msgb_tl_put(msg, iei);
uint8_t msg_len = msgb_length(msg);
enc_power_params(msg, cp);
+ if (iei == RSL_IE_MS_POWER_PARAM && is_osmobts(trx->bts))
+ osmobts_enc_power_params_osmo_ext(msg, cp);
*ie_len = msgb_length(msg) - msg_len;
}
@@ -957,9 +962,9 @@ static int power_ctrl_send_def_params(const struct gsm_bts_trx *trx)
/* MS/BS Power Parameters IEs */
if (ms_power_ctrl->mode == GSM_PWR_CTRL_MODE_DYN_BTS)
- add_power_params_ie(msg, RSL_IE_MS_POWER_PARAM, ms_power_ctrl);
+ add_power_params_ie(msg, RSL_IE_MS_POWER_PARAM, trx, ms_power_ctrl);
if (bs_power_ctrl->mode == GSM_PWR_CTRL_MODE_DYN_BTS)
- add_power_params_ie(msg, RSL_IE_BS_POWER_PARAM, bs_power_ctrl);
+ add_power_params_ie(msg, RSL_IE_BS_POWER_PARAM, trx, bs_power_ctrl);
msg->dst = trx->rsl_link_primary;
diff --git a/src/osmo-bsc/bts_osmobts.c b/src/osmo-bsc/bts_osmobts.c
index 9813a265d..5c535beca 100644
--- a/src/osmo-bsc/bts_osmobts.c
+++ b/src/osmo-bsc/bts_osmobts.c
@@ -44,6 +44,121 @@ extern struct gsm_bts_model bts_model_nanobts;
static struct gsm_bts_model model_osmobts;
+static void enc_osmo_meas_proc_params(struct msgb *msg, const struct gsm_power_ctrl_params *mp)
+{
+ struct osmo_preproc_ave_cfg *ave_cfg;
+ uint8_t *ie_len;
+
+ /* No averaging => no Measurement Averaging parameters */
+ if (mp->ci_fr_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_hr_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_amr_fr_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_amr_hr_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_sdcch_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_gprs_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE)
+ return;
+
+ /* (TLV) Measurement Averaging parameters for RxLev/RxQual */
+ ie_len = msgb_tl_put(msg, RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG);
+
+ ave_cfg = (struct osmo_preproc_ave_cfg *) msgb_put(msg, sizeof(*ave_cfg));
+
+#define ENC_PROC(PARAMS, TO, TYPE) do { \
+ (TO)->TYPE.ave_enabled = (PARAMS)->TYPE##_meas.algo != GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE; \
+ if ((TO)->TYPE.ave_enabled) { \
+ /* H_REQAVE and H_REQT */ \
+ (TO)->TYPE.h_reqave = (PARAMS)->TYPE##_meas.h_reqave & 0x1f; \
+ (TO)->TYPE.h_reqt = (PARAMS)->TYPE##_meas.h_reqt & 0x1f; \
+ /* Averaging method and parameters */ \
+ (TO)->TYPE.ave_method = ((PARAMS)->TYPE##_meas.algo - 1) & 0x07; \
+ switch ((PARAMS)->TYPE##_meas.algo) { \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA: \
+ msgb_v_put(msg, (PARAMS)->TYPE##_meas.ewma.alpha); \
+ break; \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED: \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN: \
+ /* FIXME: unknown format */ \
+ break; \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED: \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE: \
+ /* No parameters here */ \
+ break; \
+ } \
+ } \
+ } while (0)
+ ENC_PROC(mp, ave_cfg, ci_fr);
+ ENC_PROC(mp, ave_cfg, ci_hr);
+ ENC_PROC(mp, ave_cfg, ci_amr_fr);
+ ENC_PROC(mp, ave_cfg, ci_amr_hr);
+ ENC_PROC(mp, ave_cfg, ci_sdcch);
+ ENC_PROC(mp, ave_cfg, ci_gprs);
+#undef ENC_PROC
+
+ /* Update length part of the containing IE */
+ *ie_len = msg->tail - (ie_len + 1);
+}
+
+/* Appends Osmocom specific extension IEs into RSL_IE_MS_POWER_PARAM */
+void osmobts_enc_power_params_osmo_ext(struct msgb *msg, const struct gsm_power_ctrl_params *cp)
+{
+ struct osmo_preproc_pc_thresh *osmo_thresh;
+ struct osmo_preproc_pc_comp *osmo_thresh_comp;
+ uint8_t *ie_len;
+
+ /* (TLV) Measurement Averaging Configure (C/I) */
+ enc_osmo_meas_proc_params(msg, cp);
+
+ /* (TLV) Thresholds (C/I) */
+ ie_len = msgb_tl_put(msg, RSL_IPAC_EIE_OSMO_MS_PWR_CTL);
+ osmo_thresh = (struct osmo_preproc_pc_thresh *) msgb_put(msg, sizeof(*osmo_thresh));
+ #define ENC_THRESH_CI(TYPE) \
+ do { \
+ if (cp->TYPE##_meas.enabled) { \
+ osmo_thresh->l_##TYPE = cp->TYPE##_meas.lower_thresh; \
+ osmo_thresh->u_##TYPE = cp->TYPE##_meas.upper_thresh; \
+ } else { \
+ osmo_thresh->l_##TYPE = 0; \
+ osmo_thresh->u_##TYPE = 0; \
+ } \
+ } while (0)
+ ENC_THRESH_CI(ci_fr);
+ ENC_THRESH_CI(ci_hr);
+ ENC_THRESH_CI(ci_amr_fr);
+ ENC_THRESH_CI(ci_amr_hr);
+ ENC_THRESH_CI(ci_sdcch);
+ ENC_THRESH_CI(ci_gprs);
+ #undef ENC_THRESH_CI
+ /* Update length part of the containing IE */
+ *ie_len = msg->tail - (ie_len + 1);
+
+ /* (TLV) PC Threshold Comparators (C/I) */
+ ie_len = msgb_tl_put(msg, RSL_IPAC_EIE_OSMO_PC_THRESH_COMP);
+ osmo_thresh_comp = (struct osmo_preproc_pc_comp *) msgb_put(msg, sizeof(*osmo_thresh_comp));
+ #define ENC_THRESH_CI(TYPE) \
+ do { \
+ if (cp->TYPE##_meas.enabled) { \
+ osmo_thresh_comp->TYPE.lower_p = cp->TYPE##_meas.lower_cmp_p & 0x1f; \
+ osmo_thresh_comp->TYPE.lower_n = cp->TYPE##_meas.lower_cmp_n & 0x1f; \
+ osmo_thresh_comp->TYPE.upper_p = cp->TYPE##_meas.upper_cmp_p & 0x1f; \
+ osmo_thresh_comp->TYPE.upper_n = cp->TYPE##_meas.upper_cmp_n & 0x1f; \
+ } else { \
+ osmo_thresh_comp->TYPE.lower_p = 0; \
+ osmo_thresh_comp->TYPE.lower_n = 0; \
+ osmo_thresh_comp->TYPE.upper_p = 0; \
+ osmo_thresh_comp->TYPE.upper_n = 0; \
+ } \
+ } while (0)
+ ENC_THRESH_CI(ci_fr);
+ ENC_THRESH_CI(ci_hr);
+ ENC_THRESH_CI(ci_amr_fr);
+ ENC_THRESH_CI(ci_amr_hr);
+ ENC_THRESH_CI(ci_sdcch);
+ ENC_THRESH_CI(ci_gprs);
+ #undef ENC_THRESH_CI
+ /* Update length part of the containing IE */
+ *ie_len = msg->tail - (ie_len + 1);
+}
+
static int power_ctrl_set_c0_power_red(const struct gsm_bts *bts,
const uint8_t red)
{
diff --git a/src/osmo-bsc/bts_trx.c b/src/osmo-bsc/bts_trx.c
index f183d4ba3..0333f703a 100644
--- a/src/osmo-bsc/bts_trx.c
+++ b/src/osmo-bsc/bts_trx.c
@@ -50,6 +50,7 @@ static int gsm_bts_trx_talloc_destructor(struct gsm_bts_trx *trx)
osmo_fsm_inst_free(ts->mo.fi);
ts->mo.fi = NULL;
}
+ ts_fsm_free(ts);
}
return 0;
}
@@ -183,11 +184,17 @@ struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
lch_idx = 0; /* TCH/F */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_F)
|| ts->pchan_on_init == GSM_PCHAN_PDCH; /* PDCH? really? */
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr %x cbits %x: ts %s is not capable of GSM_PCHAN_TCH_F\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
break;
case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0):
case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(1):
lch_idx = cbits & 0x1; /* TCH/H */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_H);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_TCH_H\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
break;
case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0):
case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(1):
@@ -195,6 +202,9 @@ struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(3):
lch_idx = cbits & 0x3; /* SDCCH/4 */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_CCCH_SDCCH4);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_CCCH_SDCCH4\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
break;
case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0):
case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(1):
@@ -206,17 +216,26 @@ struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(7):
lch_idx = cbits & 0x7; /* SDCCH/8 */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_SDCCH8_SACCH8C);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_SDCCH8_SACCH8C\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
break;
case ABIS_RSL_CHAN_NR_CBITS_BCCH:
case ABIS_RSL_CHAN_NR_CBITS_RACH:
case ABIS_RSL_CHAN_NR_CBITS_PCH_AGCH:
lch_idx = 0; /* CCCH? */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_CCCH);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_CCCH\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
/* FIXME: we should not return first sdcch4 !!! */
break;
case ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH:
lch_idx = 0;
- ok = (ts->pchan_on_init == GSM_PCHAN_OSMO_DYN);
+ ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_PDCH);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_PDCH\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
break;
default:
return NULL;
@@ -273,54 +292,6 @@ void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data
}
}
-int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
-{
- struct gsm_bts_trx_ts *ts;
- struct gsm_lchan *lchan;
- int j;
- int count = 0;
-
- if (!trx_is_usable(trx))
- return 0;
-
- for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
- ts = &trx->ts[j];
- if (!ts_is_usable(ts))
- continue;
-
- if (ts->pchan_is == GSM_PCHAN_PDCH) {
- /* Dynamic timeslots in PDCH mode will become TCH if needed. */
- switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_PDCH:
- if (pchan == GSM_PCHAN_TCH_F)
- count++;
- continue;
-
- case GSM_PCHAN_OSMO_DYN:
- if (pchan == GSM_PCHAN_TCH_F)
- count++;
- else if (pchan == GSM_PCHAN_TCH_H)
- count += 2;
- continue;
-
- default:
- /* Not dynamic, not applicable. */
- continue;
- }
- }
-
- if (ts->pchan_is != pchan)
- continue;
-
- ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {
- if (lchan_state_is(lchan, LCHAN_ST_UNUSED))
- count++;
- }
- }
-
- return count;
-}
-
bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx)
{
bool combined = false;
diff --git a/src/osmo-bsc/bts_vty.c b/src/osmo-bsc/bts_vty.c
index 3695d2838..0c7259b7d 100644
--- a/src/osmo-bsc/bts_vty.c
+++ b/src/osmo-bsc/bts_vty.c
@@ -49,6 +49,7 @@
#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/smscb.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
#include <inttypes.h>
@@ -122,7 +123,6 @@ DEFUN_ATTR(cfg_bts,
/* allocate a new one */
bts = bsc_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN,
HARDCODED_BSIC);
- osmo_stat_item_inc(osmo_stat_item_group_get_item(gsmnet->bsc_statg, BSC_STAT_NUM_BTS_TOTAL), 1);
} else
bts = gsm_bts_num(gsmnet, bts_nr);
@@ -650,11 +650,11 @@ DEFUN_USRATTR(cfg_bts_rep_dl_facch,
}
if (!strcmp(argv[0], "command")) {
- bts->repeated_acch_policy.dl_facch_cmd = true;
- bts->repeated_acch_policy.dl_facch_all = false;
+ bts->rep_acch_cap.dl_facch_cmd = true;
+ bts->rep_acch_cap.dl_facch_all = false;
} else {
- bts->repeated_acch_policy.dl_facch_cmd = true;
- bts->repeated_acch_policy.dl_facch_all = true;
+ bts->rep_acch_cap.dl_facch_cmd = true;
+ bts->rep_acch_cap.dl_facch_all = true;
}
return CMD_SUCCESS;
}
@@ -668,8 +668,8 @@ DEFUN_USRATTR(cfg_bts_rep_no_dl_facch,
{
struct gsm_bts *bts = vty->index;
- bts->repeated_acch_policy.dl_facch_cmd = false;
- bts->repeated_acch_policy.dl_facch_all = false;
+ bts->rep_acch_cap.dl_facch_cmd = false;
+ bts->rep_acch_cap.dl_facch_all = false;
return CMD_SUCCESS;
}
@@ -691,9 +691,9 @@ DEFUN_USRATTR(cfg_bts_rep_ul_dl_sacch,
}
if (strcmp(argv[0], "ul-sacch") == 0)
- bts->repeated_acch_policy.ul_sacch = true;
+ bts->rep_acch_cap.ul_sacch = true;
else
- bts->repeated_acch_policy.dl_sacch = true;
+ bts->rep_acch_cap.dl_sacch = true;
return CMD_SUCCESS;
}
@@ -709,28 +709,32 @@ DEFUN_USRATTR(cfg_bts_rep_no_ul_dl_sacch,
struct gsm_bts *bts = vty->index;
if (strcmp(argv[0], "ul-sacch") == 0)
- bts->repeated_acch_policy.ul_sacch = false;
+ bts->rep_acch_cap.ul_sacch = false;
else
- bts->repeated_acch_policy.dl_sacch = false;
+ bts->rep_acch_cap.dl_sacch = false;
return CMD_SUCCESS;
}
+/* See 3GPP TS 45.008, section 8.2.4 */
+#define RXQUAL_THRESH_CMD \
+ "rxqual (0|1|2|3|4|5|6|7)"
+#define RXQUAL_THRESH_CMD_DESC \
+ "Set RxQual (BER) threshold (default 4)\n" \
+ "BER >= 0% (always on)\n" \
+ "BER >= 0.2%\n" \
+ "BER >= 0.4%\n" \
+ "BER >= 0.8%\n" \
+ "BER >= 1.6% (default)\n" \
+ "BER >= 3.2%\n" \
+ "BER >= 6.4%\n" \
+ "BER >= 12.8%\n"
+
DEFUN_USRATTR(cfg_bts_rep_rxqual,
cfg_bts_rep_rxqual_cmd,
X(BSC_VTY_ATTR_NEW_LCHAN),
- "repeat rxqual (0|1|2|3|4|5|6|7)",
- REP_ACCH_STR
- "Set UL-SACCH/DL-FACCH rxqual threshold-ber\n"
- "0 disabled (always on)\n"
- "1 BER >= 0.2%\n"
- "2 BER >= 0.4%\n"
- "3 BER >= 0.8%\n"
- "4 BER >= 1.6% (default)\n"
- "5 BER >= 3.2%\n"
- "6 BER >= 6.4%\n"
- "7 BER >= 12.8%\n")
- /* See also: GSM 05.08, section 8.2.4 */
+ "repeat " RXQUAL_THRESH_CMD,
+ REP_ACCH_STR RXQUAL_THRESH_CMD_DESC)
{
struct gsm_bts *bts = vty->index;
@@ -741,11 +745,106 @@ DEFUN_USRATTR(cfg_bts_rep_rxqual,
}
/* See also: GSM 05.08, section 8.2.4 */
- bts->repeated_acch_policy.rxqual = atoi(argv[0]);
+ bts->rep_acch_cap.rxqual = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+#define TOP_ACCH_STR "Temporary ACCH overpower\n"
+
+DEFUN_USRATTR(cfg_bts_top_dl_acch,
+ cfg_bts_top_dl_acch_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "overpower (dl-acch|dl-sacch|dl-facch) <1-4>",
+ TOP_ACCH_STR
+ "Enable overpower for both SACCH and FACCH\n"
+ "Enable overpower for SACCH only\n"
+ "Enable overpower for FACCH only\n"
+ "Overpower value in dB\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% ACCH overpower is not supported by BTS %u%s",
+ bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->top_acch_cap.sacch_enable = 0;
+ bts->top_acch_cap.facch_enable = 0;
+
+ if (!strcmp(argv[0], "dl-acch") || !strcmp(argv[0], "dl-sacch"))
+ bts->top_acch_cap.sacch_enable = 1;
+ if (!strcmp(argv[0], "dl-acch") || !strcmp(argv[0], "dl-facch"))
+ bts->top_acch_cap.facch_enable = 1;
+
+ bts->top_acch_cap.overpower_db = atoi(argv[1]);
return CMD_SUCCESS;
}
+DEFUN_USRATTR(cfg_bts_top_no_dl_acch,
+ cfg_bts_top_no_dl_acch_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no overpower dl-acch",
+ NO_STR TOP_ACCH_STR
+ "Disable ACCH overpower for this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->top_acch_cap.overpower_db = 0;
+ bts->top_acch_cap.sacch_enable = 0;
+ bts->top_acch_cap.facch_enable = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_top_dl_acch_rxqual,
+ cfg_bts_top_dl_acch_rxqual_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "overpower " RXQUAL_THRESH_CMD,
+ TOP_ACCH_STR RXQUAL_THRESH_CMD_DESC)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% ACCH overpower is not supported by BTS %u%s",
+ bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->top_acch_cap.rxqual = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+static const struct value_string top_acch_chan_mode_name[] = {
+ { TOP_ACCH_CHAN_MODE_ANY, "any" },
+ { TOP_ACCH_CHAN_MODE_SPEECH_V3, "speech-amr" },
+ { 0, NULL }
+};
+
+DEFUN_USRATTR(cfg_bts_top_dl_acch_chan_mode,
+ cfg_bts_top_dl_acch_chan_mode_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "overpower chan-mode (speech-amr|any)",
+ TOP_ACCH_STR
+ "Allow temporary overpower for specific Channel mode(s)\n"
+ "Speech channels using AMR codec (default)\n"
+ "Any kind of channel mode\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% ACCH overpower is not supported by BTS %u%s",
+ bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->top_acch_chan_mode = get_string_value(top_acch_chan_mode_name, argv[0]);
+
+ return CMD_SUCCESS;
+}
#define CD_STR "Channel Description\n"
@@ -2806,6 +2905,27 @@ DEFUN_ATTR(cfg_bts_srvcc_fast_return, cfg_bts_srvcc_fast_return_cmd,
return CMD_SUCCESS;
}
+DEFUN_ATTR(cfg_bts_immediate_assignment, cfg_bts_immediate_assignment_cmd,
+ "immediate-assignment (post-chan-ack|pre-chan-ack|pre-ts-ack)",
+ "Configure time of Immediate Assignment after ChanRqd RACH (Abis optimization)\n"
+ "Send the Immediate Assignment after the Channel Activation ACK (normal sequence)\n"
+ "Send the Immediate Assignment directly after Channel Activation (early), without waiting for the ACK;"
+ " This may help with double allocations on high latency Abis links\n"
+ "EXPERIMENTAL: If a dynamic timeslot switch is necessary, send the Immediate Assignment even before the"
+ " timeslot is switched, i.e. even before the Channel Activation is sent (very early)\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (!strcmp(argv[0], "pre-ts-ack"))
+ bts->imm_ass_time = IMM_ASS_TIME_PRE_TS_ACK;
+ else if (!strcmp(argv[0], "pre-chan-ack"))
+ bts->imm_ass_time = IMM_ASS_TIME_PRE_CHAN_ACK;
+ else
+ bts->imm_ass_time = IMM_ASS_TIME_POST_CHAN_ACK;
+ return CMD_SUCCESS;
+}
+
#define BS_POWER_CONTROL_CMD \
"bs-power-control"
#define MS_POWER_CONTROL_CMD \
@@ -2859,10 +2979,11 @@ DEFUN(cfg_bts_power_ctrl,
DEFUN_USRATTR(cfg_power_ctrl_mode,
cfg_power_ctrl_mode_cmd,
X(BSC_VTY_ATTR_NEW_LCHAN),
- "mode (static|dyn-bts) [reset]",
+ "mode (static|dyn-bts|dyn-bsc) [reset]",
"Power control mode\n"
"Instruct the MS/BTS to use a static power level\n"
"Power control to be performed dynamically by the BTS itself\n"
+ "Power control to be performed dynamically at this BSC\n"
"Reset to default parameters for the given mode\n")
{
struct gsm_power_ctrl_params *params = vty->index;
@@ -2870,15 +2991,20 @@ DEFUN_USRATTR(cfg_power_ctrl_mode,
/* Do we need to reset? */
if (argc > 1) {
vty_out(vty, "%% Reset to default parameters%s", VTY_NEWLINE);
- enum gsm_power_ctrl_dir dir = params->dir;
- *params = power_ctrl_params_def;
- params->dir = dir;
+ power_ctrl_params_def_reset(params, params->dir);
}
if (strcmp(argv[0], "static") == 0)
params->mode = GSM_PWR_CTRL_MODE_STATIC;
else if (strcmp(argv[0], "dyn-bts") == 0)
params->mode = GSM_PWR_CTRL_MODE_DYN_BTS;
+ else if (strcmp(argv[0], "dyn-bsc") == 0) {
+ if (params->dir == GSM_PWR_CTRL_DIR_DL) {
+ vty_out(vty, "%% mode dyn-bsc not supported for Downlink.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ params->mode = GSM_PWR_CTRL_MODE_DYN_BSC;
+ }
return CMD_SUCCESS;
}
@@ -2922,7 +3048,7 @@ DEFUN_USRATTR(cfg_power_ctrl_ctrl_interval,
X(BSC_VTY_ATTR_NEW_LCHAN),
"ctrl-interval <0-31>",
"Set power control interval (for dynamic mode)\n"
- "P_CON_INTERVAL, in units of 2 SACCH periods (0.96 seconds)\n")
+ "P_CON_INTERVAL, in units of 2 SACCH periods (0.96 seconds)(default=1)\n")
{
struct gsm_power_ctrl_params *params = vty->index;
@@ -2978,6 +3104,8 @@ DEFUN_USRATTR(cfg_power_ctrl_step_size,
"RxLev value (signal strength, 0 is worst, 63 is best)\n"
#define POWER_CONTROL_MEAS_RXQUAL_DESC \
"RxQual value (signal quality, 0 is best, 7 is worst)\n"
+#define POWER_CONTROL_MEAS_CI_DESC \
+ "C/I value (Carrier-to-Interference (dB), 0 is worst, 30 is best)\n"
DEFUN_USRATTR(cfg_power_ctrl_rxlev_thresh,
cfg_power_ctrl_rxlev_thresh_cmd,
@@ -3034,10 +3162,111 @@ DEFUN_USRATTR(cfg_power_ctrl_rxqual_thresh,
return CMD_SUCCESS;
}
+#define VTY_CMD_CI_TYPE "(fr-efr|hr|amr-fr|amr-hr|sdcch|gprs)"
+#define VTY_CMD_CI_OR_ALL_TYPE "(fr-efr|hr|amr-fr|amr-hr|sdcch|gprs|all)"
+#define VTY_DESC_CI_TYPE \
+ "Channel Type FR/EFR\n" \
+ "Channel Type HR\n" \
+ "Channel Type AMR FR\n" \
+ "Channel Type AMR HR\n" \
+ "Channel Type SDCCH\n" \
+ "Channel Type (E)GPRS\n"
+#define VTY_DESC_CI_OR_ALL_TYPE VTY_DESC_CI_TYPE "All Channel Types\n"
+
+static struct gsm_power_ctrl_meas_params *ci_thresh_by_conn_type(struct gsm_power_ctrl_params *params, const char *type)
+{
+ if (!strcmp(type, "fr-efr"))
+ return &params->ci_fr_meas;
+ if (!strcmp(type, "hr"))
+ return &params->ci_hr_meas;
+ if (!strcmp(type, "amr-fr"))
+ return &params->ci_amr_fr_meas;
+ if (!strcmp(type, "amr-hr"))
+ return &params->ci_amr_hr_meas;
+ if (!strcmp(type, "sdcch"))
+ return &params->ci_sdcch_meas;
+ if (!strcmp(type, "gprs"))
+ return &params->ci_gprs_meas;
+ OSMO_ASSERT(false);
+ return NULL;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_thresh,
+ cfg_power_ctrl_ci_thresh_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ci-thresh " VTY_CMD_CI_TYPE " lower <0-30> upper <0-30>",
+ "Set target C/I thresholds (for dynamic mode), only available in ms-power-control\n"
+ VTY_DESC_CI_TYPE
+ "Lower C/I value\n"
+ "Lower " POWER_CONTROL_MEAS_RXQUAL_DESC
+ "Upper C/I value\n"
+ "Upper " POWER_CONTROL_MEAS_RXQUAL_DESC)
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ const char *type = argv[0];
+ int lower = atoi(argv[1]);
+ int upper = atoi(argv[2]);
+ struct gsm_power_ctrl_meas_params *meas_params;
+
+ if (params->mode == GSM_PWR_CTRL_MODE_DYN_BSC) {
+ vty_out(vty, "%% C/I based power loop not possible in dyn-bsc mode!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (params->dir != GSM_PWR_CTRL_DIR_UL) {
+ vty_out(vty, "%% C/I based power loop only possible in Uplink!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (lower > upper) {
+ vty_out(vty, "%% Lower 'rxqual-rxqual' (%d) must be less than upper (%d)%s",
+ upper, lower, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ meas_params = ci_thresh_by_conn_type(params, type);
+
+ meas_params->lower_thresh = lower;
+ meas_params->upper_thresh = upper;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_thresh_disable,
+ cfg_power_ctrl_ci_thresh_disable_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ci-thresh " VTY_CMD_CI_OR_ALL_TYPE " (enable|disable)",
+ "Set target C/I thresholds (for dynamic mode), only available in ms-power-control\n"
+ VTY_DESC_CI_OR_ALL_TYPE
+ "Enable C/I comparison in control loop\n"
+ "Disable C/I comparison in control loop\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+
+ bool enable = strcmp(argv[1], "enable") == 0;
+
+ if (strcmp(argv[0], "all") == 0) {
+ params->ci_fr_meas.enabled = enable;
+ params->ci_hr_meas.enabled = enable;
+ params->ci_amr_fr_meas.enabled = enable;
+ params->ci_amr_hr_meas.enabled = enable;
+ params->ci_sdcch_meas.enabled = enable;
+ params->ci_gprs_meas.enabled = enable;
+ } else {
+ struct gsm_power_ctrl_meas_params *meas_params = ci_thresh_by_conn_type(params, argv[0]);
+ meas_params->enabled = enable;
+ }
+
+ return CMD_SUCCESS;
+}
+
#define POWER_CONTROL_MEAS_THRESH_COMP_CMD(meas) \
meas " lower <0-31> <0-31> upper <0-31> <0-31>"
-#define POWER_CONTROL_MEAS_THRESH_COMP_DESC(meas, lp, ln, up, un) \
+#define POWER_CONTROL_MEAS_THRESH_COMP_DESC(meas, opt_param, lp, ln, up, un) \
"Set " meas " threshold comparators (for dynamic mode)\n" \
+ opt_param \
"Lower " meas " threshold comparators (see 3GPP TS 45.008, A.3.2.1)\n" lp ln \
"Upper " meas " threshold comparators (see 3GPP TS 45.008, A.3.2.1)\n" up un
@@ -3046,7 +3275,7 @@ DEFUN_USRATTR(cfg_power_ctrl_rxlev_thresh_comp,
X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
X(BSC_VTY_ATTR_NEW_LCHAN),
POWER_CONTROL_MEAS_THRESH_COMP_CMD("rxlev-thresh-comp"),
- POWER_CONTROL_MEAS_THRESH_COMP_DESC("RxLev",
+ POWER_CONTROL_MEAS_THRESH_COMP_DESC("RxLev", /*empty*/,
"P1 (default 10)\n", "N1 (default 12)\n",
"P2 (default 10)\n", "N2 (default 12)\n"))
{
@@ -3064,7 +3293,7 @@ DEFUN_USRATTR(cfg_power_ctrl_rxlev_thresh_comp,
if (upper_cmp_p > upper_cmp_n) {
vty_out(vty, "%% Upper RxLev P2 %d must be less than N2 %d%s",
- lower_cmp_p, lower_cmp_n, VTY_NEWLINE);
+ upper_cmp_p, upper_cmp_n, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -3081,7 +3310,7 @@ DEFUN_USRATTR(cfg_power_ctrl_rxqual_thresh_comp,
X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
X(BSC_VTY_ATTR_NEW_LCHAN),
POWER_CONTROL_MEAS_THRESH_COMP_CMD("rxqual-thresh-comp"),
- POWER_CONTROL_MEAS_THRESH_COMP_DESC("RxQual",
+ POWER_CONTROL_MEAS_THRESH_COMP_DESC("RxQual", /*empty*/,
"P3 (default 5)\n", "N3 (default 7)\n",
"P4 (default 15)\n", "N4 (default 18)\n"))
{
@@ -3099,7 +3328,7 @@ DEFUN_USRATTR(cfg_power_ctrl_rxqual_thresh_comp,
if (upper_cmp_p > upper_cmp_n) {
vty_out(vty, "%% Upper RxQual P4 %d must be less than N4 %d%s",
- lower_cmp_p, lower_cmp_n, VTY_NEWLINE);
+ upper_cmp_p, upper_cmp_n, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -3111,6 +3340,45 @@ DEFUN_USRATTR(cfg_power_ctrl_rxqual_thresh_comp,
return CMD_SUCCESS;
}
+DEFUN_USRATTR(cfg_power_ctrl_ci_thresh_comp,
+ cfg_power_ctrl_ci_thresh_comp_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ POWER_CONTROL_MEAS_THRESH_COMP_CMD("ci-thresh-comp " VTY_CMD_CI_TYPE),
+ POWER_CONTROL_MEAS_THRESH_COMP_DESC("Carrier-to_interference (C/I)",
+ VTY_DESC_CI_TYPE,
+ "Lower P (default 5)\n", "Lower N (default 7)\n",
+ "Upper P (default 15)\n", "Upper N (default 18)\n"))
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *meas_params;
+ int lower_cmp_p = atoi(argv[1]);
+ int lower_cmp_n = atoi(argv[2]);
+ int upper_cmp_p = atoi(argv[3]);
+ int upper_cmp_n = atoi(argv[4]);
+
+ if (lower_cmp_p > lower_cmp_n) {
+ vty_out(vty, "%% Lower C/I P %d must be less than N %d%s",
+ lower_cmp_p, lower_cmp_n, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (upper_cmp_p > upper_cmp_n) {
+ vty_out(vty, "%% Upper C/I P %d must be less than N %d%s",
+ upper_cmp_p, upper_cmp_n, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ meas_params = ci_thresh_by_conn_type(params, argv[0]);
+
+ meas_params->lower_cmp_p = lower_cmp_p;
+ meas_params->lower_cmp_n = lower_cmp_n;
+ meas_params->upper_cmp_p = upper_cmp_p;
+ meas_params->upper_cmp_n = upper_cmp_n;
+
+ return CMD_SUCCESS;
+}
+
#define POWER_CONTROL_MEAS_AVG_CMD \
"(rxlev-avg|rxqual-avg)"
#define POWER_CONTROL_MEAS_AVG_DESC \
@@ -3222,6 +3490,114 @@ DEFUN_USRATTR(cfg_power_ctrl_avg_osmo_ewma,
return CMD_SUCCESS;
}
+/* C/I related power control measurements */
+#define POWER_CONTROL_CI_MEAS_AVG_DESC \
+ "C/I (Carrier-to-Interference) measurement averaging (for dynamic mode)\n"
+
+DEFUN_USRATTR(cfg_power_ctrl_no_ci_avg,
+ cfg_power_ctrl_no_ci_avg_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no ci-avg " VTY_CMD_CI_TYPE,
+ NO_STR POWER_CONTROL_CI_MEAS_AVG_DESC VTY_DESC_CI_TYPE)
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+
+ avg_params = ci_thresh_by_conn_type(params, argv[0]);
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_avg_params,
+ cfg_power_ctrl_ci_avg_params_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ci-avg " VTY_CMD_CI_TYPE " params hreqave <1-31> hreqt <1-31>",
+ POWER_CONTROL_CI_MEAS_AVG_DESC VTY_DESC_CI_TYPE
+ "Configure general averaging parameters\n"
+ "Hreqave: the period over which an average is produced\n"
+ "Hreqave value (so that Hreqave * Hreqt < 32)\n"
+ "Hreqt: the number of averaged results that are maintained\n"
+ "Hreqt value (so that Hreqave * Hreqt < 32)\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+ int h_reqave = atoi(argv[1]);
+ int h_reqt = atoi(argv[2]);
+
+ if (h_reqave * h_reqt > 31) {
+ vty_out(vty, "%% Hreqave (%d) * Hreqt (%d) = %d must be < 32%s",
+ h_reqave, h_reqt, h_reqave * h_reqt, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ avg_params = ci_thresh_by_conn_type(params, argv[0]);
+ avg_params->h_reqave = h_reqave;
+ avg_params->h_reqt = h_reqt;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_avg_algo,
+ cfg_power_ctrl_ci_avg_algo_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ /* FIXME: add algorithm specific parameters */
+ "ci-avg " VTY_CMD_CI_TYPE " algo (unweighted|weighted|mod-median)",
+ POWER_CONTROL_CI_MEAS_AVG_DESC VTY_DESC_CI_TYPE
+ "Select the averaging algorithm\n"
+ "Un-weighted average\n" "Weighted average\n"
+ "Modified median calculation\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+
+ avg_params = ci_thresh_by_conn_type(params, argv[0]);
+ if (strcmp(argv[1], "unweighted") == 0)
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED;
+ else if (strcmp(argv[1], "weighted") == 0)
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED;
+ else if (strcmp(argv[1], "mod-median") == 0)
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_avg_osmo_ewma,
+ cfg_power_ctrl_ci_avg_osmo_ewma_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ci-avg " VTY_CMD_CI_TYPE " algo osmo-ewma beta <1-99>",
+ POWER_CONTROL_CI_MEAS_AVG_DESC VTY_DESC_CI_TYPE
+ "Select the averaging algorithm\n"
+ "Exponentially Weighted Moving Average (EWMA)\n"
+ "Smoothing factor (in %): beta = (100 - alpha)\n"
+ "1% - lowest smoothing, 99% - highest smoothing\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+ const struct gsm_bts *bts;
+
+ if (params->dir == GSM_PWR_CTRL_DIR_UL)
+ bts = container_of(params, struct gsm_bts, ms_power_ctrl);
+ else
+ bts = container_of(params, struct gsm_bts, bs_power_ctrl);
+
+ if (bts->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% EWMA is an OsmoBTS specific algorithm, "
+ "it's not usable for other BTS types%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ avg_params = ci_thresh_by_conn_type(params, argv[0]);
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA;
+ avg_params->ewma.alpha = 100 - atoi(argv[1]);
+
+ return CMD_SUCCESS;
+}
+
static void vty_out_neigh_list(struct vty *vty, struct bitvec *bv)
{
int count = 0;
@@ -3597,31 +3973,20 @@ static void config_write_bts_amr(struct vty *vty, struct gsm_bts *bts,
vty_out(vty, "%*s" fmt, indent, "", ##args);
static void config_write_power_ctrl_meas(struct vty *vty, unsigned int indent,
- const struct gsm_power_ctrl_params *cp,
- uint8_t ptype)
+ const struct gsm_power_ctrl_meas_params *mp,
+ const char *param, const char *param2)
{
- const struct gsm_power_ctrl_meas_params *mp;
- const char *param;
-
- switch (ptype) {
- case IPAC_RXLEV_AVE:
- mp = &cp->rxlev_meas;
- param = "rxlev";
- break;
- case IPAC_RXQUAL_AVE:
- mp = &cp->rxqual_meas;
- param = "rxqual";
- break;
- default:
- /* Shall not happen */
- OSMO_ASSERT(0);
+ if (strcmp(param, "ci") == 0) {
+ cfg_out("%s-thresh%s %s%s",
+ param, param2, mp->enabled ? "enable" : "disable",
+ VTY_NEWLINE);
}
- cfg_out("%s-thresh lower %u upper %u%s",
- param, mp->lower_thresh, mp->upper_thresh,
+ cfg_out("%s-thresh%s lower %u upper %u%s",
+ param, param2, mp->lower_thresh, mp->upper_thresh,
VTY_NEWLINE);
- cfg_out("%s-thresh-comp lower %u %u upper %u %u%s",
- param, mp->lower_cmp_p, mp->lower_cmp_n,
+ cfg_out("%s-thresh-comp%s lower %u %u upper %u %u%s",
+ param, param2, mp->lower_cmp_p, mp->lower_cmp_n,
mp->upper_cmp_p, mp->upper_cmp_n,
VTY_NEWLINE);
@@ -3630,27 +3995,28 @@ static void config_write_power_ctrl_meas(struct vty *vty, unsigned int indent,
/* Do not print any averaging parameters */
return; /* we're done */
case GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED:
- cfg_out("%s-avg algo unweighted%s", param, VTY_NEWLINE);
+ cfg_out("%s-avg%s algo unweighted%s", param, param2, VTY_NEWLINE);
break;
case GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED:
- cfg_out("%s-avg algo weighted%s", param, VTY_NEWLINE);
+ cfg_out("%s-avg%s algo weighted%s", param, param2, VTY_NEWLINE);
break;
case GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN:
- cfg_out("%s-avg algo mod-median%s", param, VTY_NEWLINE);
+ cfg_out("%s-avg%s algo mod-median%s", param, param2, VTY_NEWLINE);
break;
case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:
- cfg_out("%s-avg algo osmo-ewma beta %u%s",
- param, 100 - mp->ewma.alpha,
+ cfg_out("%s-avg%s algo osmo-ewma beta %u%s",
+ param, param2, 100 - mp->ewma.alpha,
VTY_NEWLINE);
break;
}
- cfg_out("%s-avg params hreqave %u hreqt %u%s",
- param, mp->h_reqave, mp->h_reqt,
+ cfg_out("%s-avg%s params hreqave %u hreqt %u%s",
+ param, param2, mp->h_reqave, mp->h_reqt,
VTY_NEWLINE);
}
static void config_write_power_ctrl(struct vty *vty, unsigned int indent,
+ const struct gsm_bts *bts,
const struct gsm_power_ctrl_params *cp)
{
const char *node_name;
@@ -3671,20 +4037,30 @@ static void config_write_power_ctrl(struct vty *vty, unsigned int indent,
cfg_out(" bs-power static %u%s", cp->bs_power_val_db, VTY_NEWLINE);
break;
case GSM_PWR_CTRL_MODE_DYN_BTS:
+ case GSM_PWR_CTRL_MODE_DYN_BSC:
cfg_out("%s%s", node_name, VTY_NEWLINE);
- cfg_out(" mode dyn-bts%s", VTY_NEWLINE);
+ cfg_out(" mode %s%s",
+ cp->mode == GSM_PWR_CTRL_MODE_DYN_BTS ? "dyn-bts" : "dyn-bsc", VTY_NEWLINE);
if (cp->dir == GSM_PWR_CTRL_DIR_DL)
cfg_out(" bs-power dyn-max %u%s", cp->bs_power_max_db, VTY_NEWLINE);
- if (cp->ctrl_interval > 0)
- cfg_out(" ctrl-interval %u%s", cp->ctrl_interval, VTY_NEWLINE);
+ cfg_out(" ctrl-interval %u%s", cp->ctrl_interval, VTY_NEWLINE);
cfg_out(" step-size inc %u red %u%s",
cp->inc_step_size_db, cp->red_step_size_db,
VTY_NEWLINE);
/* Measurement processing / averaging parameters */
- config_write_power_ctrl_meas(vty, indent + 1, cp, IPAC_RXLEV_AVE);
- config_write_power_ctrl_meas(vty, indent + 1, cp, IPAC_RXQUAL_AVE);
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->rxlev_meas, "rxlev", "");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->rxqual_meas, "rxqual", "");
+ if (cp->dir == GSM_PWR_CTRL_DIR_UL && is_osmobts(bts)
+ && cp->mode == GSM_PWR_CTRL_MODE_DYN_BTS) {
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_fr_meas, "ci", " fr-efr");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_hr_meas, "ci", " hr");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_amr_fr_meas, "ci", " amr-fr");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_amr_hr_meas, "ci", " amr-hr");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_sdcch_meas, "ci", " sdcch");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_gprs_meas, "ci", " gprs");
+ }
break;
}
}
@@ -3945,18 +4321,42 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
ho_vty_write_bts(vty, bts);
- if (bts->repeated_acch_policy.dl_facch_all)
+ if (bts->top_acch_cap.overpower_db > 0) {
+ const struct abis_rsl_osmo_temp_ovp_acch_cap *top = \
+ &bts->top_acch_cap;
+ const char *mode = NULL;
+
+ if (top->sacch_enable && top->facch_enable)
+ mode = "dl-acch";
+ else if (top->sacch_enable)
+ mode = "dl-sacch";
+ else if (top->facch_enable)
+ mode = "dl-facch";
+ else /* shall not happen */
+ OSMO_ASSERT(0);
+
+ vty_out(vty, " overpower %s %u%s",
+ mode, top->overpower_db, VTY_NEWLINE);
+ vty_out(vty, " overpower rxqual %u%s",
+ top->rxqual, VTY_NEWLINE);
+ vty_out(vty, " overpower chan-mode %s%s",
+ get_value_string(top_acch_chan_mode_name,
+ bts->top_acch_chan_mode),
+ VTY_NEWLINE);
+ }
+
+ if (bts->rep_acch_cap.dl_facch_all)
vty_out(vty, " repeat dl-facch all%s", VTY_NEWLINE);
- else if (bts->repeated_acch_policy.dl_facch_cmd)
+ else if (bts->rep_acch_cap.dl_facch_cmd)
vty_out(vty, " repeat dl-facch command%s", VTY_NEWLINE);
- if (bts->repeated_acch_policy.dl_sacch)
+ if (bts->rep_acch_cap.dl_sacch)
vty_out(vty, " repeat dl-sacch%s", VTY_NEWLINE);
- if (bts->repeated_acch_policy.ul_sacch)
+ if (bts->rep_acch_cap.ul_sacch)
vty_out(vty, " repeat ul-sacch%s", VTY_NEWLINE);
- if (bts->repeated_acch_policy.ul_sacch
- || bts->repeated_acch_policy.dl_facch_cmd
- || bts->repeated_acch_policy.dl_facch_cmd)
- vty_out(vty, " repeat rxqual %u%s", bts->repeated_acch_policy.rxqual, VTY_NEWLINE);
+ if (bts->rep_acch_cap.ul_sacch
+ || bts->rep_acch_cap.dl_facch_cmd
+ || bts->rep_acch_cap.dl_facch_cmd)
+ vty_out(vty, " repeat rxqual %u%s", bts->rep_acch_cap.rxqual, VTY_NEWLINE);
if (bts->interf_meas_params_cfg.avg_period != interf_meas_params_def.avg_period) {
vty_out(vty, " interference-meas avg-period %u%s",
@@ -3980,9 +4380,22 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
if (!bts->srvcc_fast_return_allowed)
vty_out(vty, " srvcc fast-return forbid%s", VTY_NEWLINE);
+ switch (bts->imm_ass_time) {
+ default:
+ case IMM_ASS_TIME_POST_CHAN_ACK:
+ /* default value */
+ break;
+ case IMM_ASS_TIME_PRE_CHAN_ACK:
+ vty_out(vty, " immediate-assignment pre-chan-ack%s", VTY_NEWLINE);
+ break;
+ case IMM_ASS_TIME_PRE_TS_ACK:
+ vty_out(vty, " immediate-assignment pre-ts-ack%s", VTY_NEWLINE);
+ break;
+ }
+
/* BS/MS Power Control parameters */
- config_write_power_ctrl(vty, 2, &bts->bs_power_ctrl);
- config_write_power_ctrl(vty, 2, &bts->ms_power_ctrl);
+ config_write_power_ctrl(vty, 2, bts, &bts->bs_power_ctrl);
+ config_write_power_ctrl(vty, 2, bts, &bts->ms_power_ctrl);
config_write_bts_model(vty, bts);
}
@@ -4147,9 +4560,14 @@ int bts_vty_init(void)
install_element(BTS_NODE, &cfg_bts_rep_ul_dl_sacch_cmd);
install_element(BTS_NODE, &cfg_bts_rep_no_ul_dl_sacch_cmd);
install_element(BTS_NODE, &cfg_bts_rep_rxqual_cmd);
+ install_element(BTS_NODE, &cfg_bts_top_dl_acch_cmd);
+ install_element(BTS_NODE, &cfg_bts_top_no_dl_acch_cmd);
+ install_element(BTS_NODE, &cfg_bts_top_dl_acch_rxqual_cmd);
+ install_element(BTS_NODE, &cfg_bts_top_dl_acch_chan_mode_cmd);
install_element(BTS_NODE, &cfg_bts_interf_meas_avg_period_cmd);
install_element(BTS_NODE, &cfg_bts_interf_meas_level_bounds_cmd);
install_element(BTS_NODE, &cfg_bts_srvcc_fast_return_cmd);
+ install_element(BTS_NODE, &cfg_bts_immediate_assignment_cmd);
neighbor_ident_vty_init();
/* See also handover commands added on bts level from handover_vty.c */
@@ -4163,12 +4581,19 @@ int bts_vty_init(void)
install_element(POWER_CTRL_NODE, &cfg_power_ctrl_step_size_cmd);
install_element(POWER_CTRL_NODE, &cfg_power_ctrl_rxlev_thresh_cmd);
install_element(POWER_CTRL_NODE, &cfg_power_ctrl_rxqual_thresh_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_thresh_disable_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_thresh_cmd);
install_element(POWER_CTRL_NODE, &cfg_power_ctrl_rxlev_thresh_comp_cmd);
install_element(POWER_CTRL_NODE, &cfg_power_ctrl_rxqual_thresh_comp_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_thresh_comp_cmd);
install_element(POWER_CTRL_NODE, &cfg_power_ctrl_no_avg_cmd);
install_element(POWER_CTRL_NODE, &cfg_power_ctrl_avg_params_cmd);
install_element(POWER_CTRL_NODE, &cfg_power_ctrl_avg_algo_cmd);
install_element(POWER_CTRL_NODE, &cfg_power_ctrl_avg_osmo_ewma_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_no_ci_avg_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_avg_params_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_avg_algo_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_avg_osmo_ewma_cmd);
return bts_trx_vty_init();
diff --git a/src/osmo-bsc/chan_counts.c b/src/osmo-bsc/chan_counts.c
new file mode 100644
index 000000000..99e6e7649
--- /dev/null
+++ b/src/osmo-bsc/chan_counts.c
@@ -0,0 +1,142 @@
+/* count total, allocated and free channels of all types.
+ *
+ * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bts_trx.h>
+#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/chan_counts.h>
+
+static const unsigned int lchans_per_pchan[_GSM_PCHAN_MAX][_GSM_LCHAN_MAX] = {
+ [GSM_PCHAN_NONE] = {0},
+ [GSM_PCHAN_CCCH] = { [GSM_LCHAN_CCCH] = 1, },
+ [GSM_PCHAN_PDCH] = { [GSM_LCHAN_PDTCH] = 1, },
+ [GSM_PCHAN_CCCH_SDCCH4] = {
+ [GSM_LCHAN_CCCH] = 1,
+ [GSM_LCHAN_SDCCH] = 3,
+ },
+ [GSM_PCHAN_TCH_F] = { [GSM_LCHAN_TCH_F] = 1, },
+ [GSM_PCHAN_TCH_H] = { [GSM_LCHAN_TCH_H] = 2, },
+ [GSM_PCHAN_SDCCH8_SACCH8C] = { [GSM_LCHAN_SDCCH] = 8, },
+ [GSM_PCHAN_CCCH_SDCCH4_CBCH] = {
+ [GSM_LCHAN_CCCH] = 1,
+ [GSM_LCHAN_SDCCH] = 3,
+ [GSM_LCHAN_CBCH] = 1,
+ },
+ [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = {
+ [GSM_LCHAN_SDCCH] = 8,
+ [GSM_LCHAN_CBCH] = 1,
+ },
+ [GSM_PCHAN_OSMO_DYN] = {
+ [GSM_LCHAN_TCH_F] = 1,
+ [GSM_LCHAN_TCH_H] = 2,
+ [GSM_LCHAN_SDCCH] = 8,
+ [GSM_LCHAN_PDTCH] = 1,
+ },
+ [GSM_PCHAN_TCH_F_PDCH] = {
+ [GSM_LCHAN_TCH_F] = 1,
+ [GSM_LCHAN_PDTCH] = 1,
+ },
+};
+
+static inline void chan_counts_per_pchan_add(struct chan_counts *dst,
+ enum chan_counts_dim1 dim1, enum chan_counts_dim2 dim2,
+ enum gsm_phys_chan_config pchan)
+{
+ int i;
+ for (i = 0; i < _GSM_LCHAN_MAX; i++)
+ dst->val[dim1][dim2][i] += lchans_per_pchan[pchan][i];
+}
+
+void chan_counts_for_trx(struct chan_counts *trx_counts, const struct gsm_bts_trx *trx)
+{
+ const struct gsm_bts_trx_ts *ts;
+ const struct gsm_lchan *lchan;
+ int i;
+
+ chan_counts_zero(trx_counts);
+
+ if (!trx_is_usable(trx))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ bool ts_is_dynamic;
+ struct chan_counts ts_count = {0};
+ ts = &trx->ts[i];
+ if (!ts_is_usable(ts))
+ continue;
+
+ /* Count the full potential nr of lchans for dynamic TS */
+ chan_counts_per_pchan_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL, ts->pchan_on_init);
+
+ switch (ts->pchan_on_init) {
+ case GSM_PCHAN_TCH_F_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
+ ts_is_dynamic = true;
+ break;
+ default:
+ ts_is_dynamic = false;
+ break;
+ }
+
+ if (ts_is_dynamic && ts->pchan_is == GSM_PCHAN_PDCH) {
+ /* Dynamic timeslots in PDCH mode can become TCH or SDCCH immediately,
+ * so set CURRENT_TOTAL = MAX_TOTAL. */
+ chan_counts_dim3_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL,
+ &ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL);
+ } else {
+ /* Static TS, or dyn TS that are currently fixed on a specific pchan: count lchans for the
+ * current pchan mode. */
+ chan_counts_per_pchan_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL, ts->pchan_is);
+ }
+
+ /* Count currently allocated lchans */
+ ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {
+ if (!lchan_state_is(lchan, LCHAN_ST_UNUSED))
+ ts_count.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_ALLOCATED][lchan->type]++;
+ }
+
+ chan_counts_dim3_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,
+ &ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL);
+ chan_counts_dim3_sub(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,
+ &ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_ALLOCATED);
+
+ if (ts_is_dynamic)
+ chan_counts_dim2_add(trx_counts, CHAN_COUNTS1_DYNAMIC, &ts_count, CHAN_COUNTS1_ALL);
+ else
+ chan_counts_dim2_add(trx_counts, CHAN_COUNTS1_STATIC, &ts_count, CHAN_COUNTS1_ALL);
+ chan_counts_dim2_add(trx_counts, CHAN_COUNTS1_ALL, &ts_count, CHAN_COUNTS1_ALL);
+ }
+}
+
+void chan_counts_for_bts(struct chan_counts *bts_counts, const struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+ chan_counts_zero(bts_counts);
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct chan_counts trx_counts;
+ chan_counts_for_trx(&trx_counts, trx);
+ chan_counts_add(bts_counts, &trx_counts);
+ }
+}
diff --git a/src/osmo-bsc/gsm_04_08_rr.c b/src/osmo-bsc/gsm_04_08_rr.c
index 16fe99930..6c0615e80 100644
--- a/src/osmo-bsc/gsm_04_08_rr.c
+++ b/src/osmo-bsc/gsm_04_08_rr.c
@@ -1027,6 +1027,9 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
case GSM48_MT_RR_CLSM_CHG:
handle_classmark_chg(conn, msg);
break;
+ case GSM48_MT_RR_UTRAN_CLSM_CHG:
+ /* TODO: forward to the MSC? */
+ break;
case GSM48_MT_RR_APP_INFO:
/* Passing RR APP INFO to MSC, not quite
* according to spec */
diff --git a/src/osmo-bsc/gsm_08_08.c b/src/osmo-bsc/gsm_08_08.c
index 1f6b38ef6..1715533e2 100644
--- a/src/osmo-bsc/gsm_08_08.c
+++ b/src/osmo-bsc/gsm_08_08.c
@@ -26,6 +26,7 @@
#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/a_reset.h>
@@ -100,23 +101,6 @@ void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *
msgb_free(resp);
}
-/* 9.2.5 CM service accept */
-int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACK");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-
- msg->lchan = conn->lchan;
-
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_CM_SERV_ACC;
-
- DEBUGP(DMM, "-> CM SERVICE ACK\n");
-
- gscon_submit_rsl_dtap(conn, msg, 0, 0);
- return 0;
-}
-
static bool is_cm_service_for_emerg(struct msgb *msg)
{
struct gsm48_service_request *cm;
diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c
index 4db70f060..988d70efa 100644
--- a/src/osmo-bsc/gsm_data.c
+++ b/src/osmo-bsc/gsm_data.c
@@ -562,7 +562,7 @@ int gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
/* For RSL, to talk to osmo-bts, we introduce Osmocom specific channel number cbits to indicate VAMOS secondary lchans.
* However, in RR, which is sent to the MS, these special cbits must not be sent, but their "normal" equivalent; for RR
* messages, pass allow_osmo_cbits = false. */
-int gsm_lchan2chan_nr(const struct gsm_lchan *lchan, bool allow_osmo_cbits)
+int gsm_lchan_and_pchan2chan_nr(const struct gsm_lchan *lchan, enum gsm_phys_chan_config pchan, bool allow_osmo_cbits)
{
int rc;
uint8_t lchan_nr = lchan->nr;
@@ -582,7 +582,7 @@ int gsm_lchan2chan_nr(const struct gsm_lchan *lchan, bool allow_osmo_cbits)
* a primary ts->lchan[0] and a VAMOS ts->lchan[1]. Still, the VAMOS lchan should send chan_nr = 0. */
if (lchan->vamos.is_secondary)
lchan_nr -= lchan->ts->max_primary_lchans;
- rc = gsm_pchan2chan_nr(lchan->ts->pchan_is, lchan->ts->nr, lchan_nr,
+ rc = gsm_pchan2chan_nr(pchan, lchan->ts->nr, lchan_nr,
allow_osmo_cbits ? lchan->vamos.is_secondary : false);
/* Log an error so that we don't need to add logging to each caller of this function */
if (rc < 0)
@@ -593,6 +593,11 @@ int gsm_lchan2chan_nr(const struct gsm_lchan *lchan, bool allow_osmo_cbits)
return rc;
}
+int gsm_lchan2chan_nr(const struct gsm_lchan *lchan, bool allow_osmo_cbits)
+{
+ return gsm_lchan_and_pchan2chan_nr(lchan, lchan->ts->pchan_is, allow_osmo_cbits);
+}
+
static const uint8_t subslots_per_pchan[] = {
[GSM_PCHAN_NONE] = 0,
[GSM_PCHAN_CCCH] = 0,
@@ -604,7 +609,7 @@ static const uint8_t subslots_per_pchan[] = {
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8,
/* Dyn TS: maximum allowed subslots */
- [GSM_PCHAN_OSMO_DYN] = 2,
+ [GSM_PCHAN_OSMO_DYN] = 8,
[GSM_PCHAN_TCH_F_PDCH] = 1,
};
@@ -628,7 +633,7 @@ static const uint8_t subslots_per_pchan_vamos[] = {
[GSM_PCHAN_SDCCH8_SACCH8C] = 0,
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = 0,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 0,
- [GSM_PCHAN_OSMO_DYN] = 2,
+ [GSM_PCHAN_OSMO_DYN] = 0,
[GSM_PCHAN_TCH_F_PDCH] = 2,
};
@@ -712,35 +717,17 @@ static void _chan_desc_fill_tail(struct gsm48_chan_desc *cd, const struct gsm_lc
}
}
-int gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
- const struct gsm_lchan *lchan,
- uint8_t tsc, bool allow_osmo_cbits)
-{
- int chan_nr = gsm_lchan2chan_nr(lchan, allow_osmo_cbits);
- if (chan_nr < 0) {
- /* Log an error so that we don't need to add logging to each caller of this function */
- LOG_LCHAN(lchan, LOGL_ERROR, "Error encoding Channel Number\n");
- return chan_nr;
- }
- cd->chan_nr = chan_nr;
- _chan_desc_fill_tail(cd, lchan, tsc);
- return 0;
-}
-
-/* like gsm48_lchan2chan_desc() above, but use ts->pchan_from_config to
- * return a channel description based on what is configured, rather than
- * what the current state of the pchan type is */
-int gsm48_lchan2chan_desc_as_configured(struct gsm48_chan_desc *cd,
- const struct gsm_lchan *lchan,
- uint8_t tsc)
+int gsm48_lchan_and_pchan2chan_desc(struct gsm48_chan_desc *cd,
+ const struct gsm_lchan *lchan,
+ enum gsm_phys_chan_config pchan,
+ uint8_t tsc, bool allow_osmo_cbits)
{
- int chan_nr = gsm_pchan2chan_nr(lchan->ts->pchan_from_config, lchan->ts->nr, lchan->nr,
- lchan->vamos.is_secondary);
+ int chan_nr = gsm_lchan_and_pchan2chan_nr(lchan, pchan, allow_osmo_cbits);
if (chan_nr < 0) {
/* Log an error so that we don't need to add logging to each caller of this function */
LOG_LCHAN(lchan, LOGL_ERROR,
"Error encoding Channel Number: pchan %s ts %u ss %u%s (rc = %d)\n",
- gsm_pchan_name(lchan->ts->pchan_from_config), lchan->ts->nr, lchan->nr,
+ gsm_pchan_name(pchan), lchan->ts->nr, lchan->nr,
lchan->vamos.is_secondary ? " (VAMOS shadow)" : "", chan_nr);
return chan_nr;
}
@@ -749,6 +736,13 @@ int gsm48_lchan2chan_desc_as_configured(struct gsm48_chan_desc *cd,
return 0;
}
+int gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
+ const struct gsm_lchan *lchan,
+ uint8_t tsc, bool allow_osmo_cbits)
+{
+ return gsm48_lchan_and_pchan2chan_desc(cd, lchan, lchan->ts->pchan_is, tsc, allow_osmo_cbits);
+}
+
uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts)
{
if (ts->tsc != -1)
@@ -1126,12 +1120,12 @@ enum gsm48_rr_cause bsc_gsm48_rr_cause_from_rsl_cause(uint8_t c)
const struct gsm_interf_meas_params interf_meas_params_def = {
.avg_period = 6, /* 6 SACCH periods */
.bounds_dbm = {
- 85, /* 0: -85 dBm */
- 91, /* X1: -91 dBm */
- 97, /* X2: -97 dBm */
- 103, /* X3: -103 dBm */
- 109, /* X4: -109 dBm */
- 115, /* X5: -115 dBm */
+ 115, /* 0: -115 dBm */
+ 109, /* X1: -109 dBm */
+ 103, /* X2: -103 dBm */
+ 97, /* X3: -97 dBm */
+ 91, /* X4: -91 dBm */
+ 85, /* X5: -85 dBm */
},
};
@@ -1148,8 +1142,11 @@ const struct gsm_power_ctrl_params power_ctrl_params_def = {
.inc_step_size_db = 4, /* 2, 4, or 6 dB */
.red_step_size_db = 2, /* 2 or 4 dB */
+ .ctrl_interval = 1, /* Trigger loop every second SACCH block. TS 45.008 sec 4.7.1 */
+
/* RxLev measurement parameters */
.rxlev_meas = {
+ .enabled = true,
/* 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) */
@@ -1174,16 +1171,155 @@ const struct gsm_power_ctrl_params power_ctrl_params_def = {
/* RxQual measurement parameters */
.rxqual_meas = {
+ .enabled = true,
/* 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%) */
/* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
- * out of LOWER_CMP_N averages are lower than L_RXLEV_XX_P */
+ * out of LOWER_CMP_N averages are lower than L_RXQUAL_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_RXLEV_XX_P */
+ * out of UPPER_CMP_N averages are greater than L_RXQUAL_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 */
+ },
+
+ /* 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 */
+ .enabled = true,
+ .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 */
+ .enabled = true,
+ .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 */
+ .enabled = true,
+ .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 */
+ .enabled = true,
+ .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 */
+ .enabled = true,
+ .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 */
+ .enabled = true,
+ .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) */
@@ -1197,6 +1333,16 @@ const struct gsm_power_ctrl_params power_ctrl_params_def = {
},
};
+void power_ctrl_params_def_reset(struct gsm_power_ctrl_params *params,
+ enum gsm_power_ctrl_dir dir)
+{
+ *params = power_ctrl_params_def;
+ params->dir = dir;
+ if (dir == GSM_PWR_CTRL_DIR_UL)
+ /* Trigger loop every fourth SACCH block (1.92s). TS 45.008 sec 4.7.1: */
+ params->ctrl_interval = 2;
+}
+
enum rsl_cmod_spd chan_mode_to_rsl_cmod_spd(enum gsm48_chan_mode chan_mode)
{
switch (gsm48_chan_mode_to_non_vamos(chan_mode)) {
diff --git a/src/osmo-bsc/handover_decision_2.c b/src/osmo-bsc/handover_decision_2.c
index d08173cb9..6730f26ae 100644
--- a/src/osmo-bsc/handover_decision_2.c
+++ b/src/osmo-bsc/handover_decision_2.c
@@ -42,6 +42,7 @@
#include <osmocom/bsc/timeslot_fsm.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lchan_select.h>
+#include <osmocom/bsc/chan_counts.h>
#define LOGPHOBTS(bts, level, fmt, args...) \
LOGP(DHODEC, level, "(BTS %u) " fmt, bts->nr, ## args)
@@ -86,7 +87,7 @@
LOGPHOLCHANTOBTS((candidate)->current.lchan, (candidate)->target.bts, level, fmt, ## args); \
else if ((candidate)->target.cell_ids.id_list_len) \
LOGPHOLCHANTOREMOTE((candidate)->current.lchan, &(candidate)->target.cell_ids, level, fmt, ## args); \
- } while(0)
+ } while (0)
#define REQUIREMENT_A_TCHF 0x01
@@ -990,12 +991,15 @@ static inline void debug_candidate(struct ho_candidate *candidate)
static void candidate_set_free_tch(struct ho_candidate *c)
{
+ struct chan_counts bts_counts;
struct gsm_lchan *next_lchan;
- c->current.free_tchf = bts_count_free_ts(c->current.bts, GSM_PCHAN_TCH_F);
+ chan_counts_for_bts(&bts_counts, c->current.bts);
+ c->current.free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
c->current.min_free_tchf = ho_get_hodec2_tchf_min_slots(c->current.bts->ho);
- c->current.free_tchh = bts_count_free_ts(c->current.bts, GSM_PCHAN_TCH_H);
+ c->current.free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
c->current.min_free_tchh = ho_get_hodec2_tchh_min_slots(c->current.bts->ho);
+
switch (c->current.lchan->ts->pchan_is) {
case GSM_PCHAN_TCH_F:
c->current.free_tch = c->current.free_tchf;
@@ -1023,9 +1027,10 @@ static void candidate_set_free_tch(struct ho_candidate *c)
break;
}
- c->target.free_tchf = bts_count_free_ts(c->target.bts, GSM_PCHAN_TCH_F);
+ chan_counts_for_bts(&bts_counts, c->target.bts);
+ c->target.free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
c->target.min_free_tchf = ho_get_hodec2_tchf_min_slots(c->target.bts->ho);
- c->target.free_tchh = bts_count_free_ts(c->target.bts, GSM_PCHAN_TCH_H);
+ c->target.free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
c->target.min_free_tchh = ho_get_hodec2_tchh_min_slots(c->target.bts->ho);
/* Would the next TCH/F lchan occupy a dynamic timeslot that currently counts for free TCH/H timeslots? */
@@ -1928,6 +1933,7 @@ exit:
static void bts_congestion_check(struct gsm_bts *bts)
{
+ struct chan_counts bts_counts;
int min_free_tchf, min_free_tchh;
int free_tchf, free_tchh;
@@ -1955,8 +1961,9 @@ static void bts_congestion_check(struct gsm_bts *bts)
return;
}
- free_tchf = bts_count_free_ts(bts, GSM_PCHAN_TCH_F);
- free_tchh = bts_count_free_ts(bts, GSM_PCHAN_TCH_H);
+ chan_counts_for_bts(&bts_counts, bts);
+ free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
LOGPHOBTS(bts, LOGL_INFO, "Congestion check: (free/want-free) TCH/F=%d/%d TCH/H=%d/%d\n",
free_tchf, min_free_tchf, free_tchh, min_free_tchh);
diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c
index 797a336e8..37e74176d 100644
--- a/src/osmo-bsc/handover_fsm.c
+++ b/src/osmo-bsc/handover_fsm.c
@@ -45,6 +45,7 @@
#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lcs_loc_req.h>
+#include <osmocom/bsc/bsc_stats.h>
#define LOG_FMT_BTS "bts %u lac-ci %u-%u arfcn-bsic %d-%d"
#define LOG_ARGS_BTS(bts) \
@@ -89,7 +90,7 @@
bsc_ctr_description[counter].name, \
bsc_ctr_description[counter].description); \
rate_ctr_inc(rate_ctr_group_get_ctr(conn->network->bsc_ctrs, counter)); \
- } while(0)
+ } while (0)
/* Assume presence of local var 'conn' as struct gsm_subscriber_connection.
* Handles bts == NULL gracefully
@@ -105,12 +106,15 @@
rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, counter)); \
else \
rate_ctr_inc(rate_ctr_group_get_ctr(conn->network->bts_unknown_ctrs, counter)); \
- } while(0)
+ } while (0)
+/* Count handover result on both bts and bsc level.
+ * Call with 'counter' being the counter name without the "BSC_"/"BTS_" part,
+ * e.g. ho_count(conn_get_bts(conn), CTR_HANDOVER_ATTEMPTED); */
#define ho_count(bts, counter) do { \
- ho_count_bsc(BSC_##counter); \
- ho_count_bts(bts, BTS_##counter); \
-} while(0)
+ ho_count_bsc(BSC_##counter); \
+ ho_count_bts(bts, BTS_##counter); \
+ } while (0)
static uint8_t g_next_ho_ref = 1;
@@ -211,12 +215,12 @@ static const struct osmo_tdef_state_timeout ho_fsm_timeouts[32] = {
LOG_HO(conn, LOGL_ERROR, "Handover failed in state %s, %s: " fmt "\n", \
osmo_fsm_inst_state_name(conn->fi), handover_result_name(result), ## args); \
handover_end(conn, result); \
- } while(0)
+ } while (0)
#define ho_success() do { \
LOG_HO(conn, LOGL_DEBUG, "Handover succeeded\n"); \
handover_end(conn, HO_RESULT_OK); \
- } while(0)
+ } while (0)
/* issue handover to a cell identified by ARFCN and BSIC */
int handover_request(struct handover_out_req *req)
@@ -383,6 +387,7 @@ static void handover_start_intra_bsc(struct gsm_subscriber_connection *conn)
} else {
ho_count(bts, CTR_INTRA_BSC_HO_ATTEMPTED);
ho_fsm_update_id(fi, "intraBSC");
+ ho_count_bts(ho->new_bts, BTS_CTR_INCOMING_INTRA_BSC_HO_ATTEMPTED);
}
if (!ho->new_lchan) {
@@ -747,6 +752,23 @@ void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
lchan_activate(ho->new_lchan, &info);
}
+/* Create functions result_counter_{BSC,BTS}_{HANDOVER,...}(), to evaluate the handover result and return
+ * BSC_CTR_HANDOVER_ATTEMPTED,
+ * BSC_CTR_HANDOVER_COMPLETED,
+ * BSC_CTR_HANDOVER_STOPPED,
+ * BSC_CTR_HANDOVER_NO_CHANNEL,
+ * BSC_CTR_HANDOVER_TIMEOUT,
+ * BSC_CTR_HANDOVER_FAILED,
+ * BSC_CTR_HANDOVER_ERROR,
+ * or
+ * BTS_CTR_HANDOVER_ATTEMPTED,
+ * BTS_CTR_HANDOVER_COMPLETED,
+ * BTS_CTR_HANDOVER_STOPPED,
+ * BTS_CTR_HANDOVER_NO_CHANNEL,
+ * BTS_CTR_HANDOVER_TIMEOUT,
+ * BTS_CTR_HANDOVER_FAILED,
+ * BTS_CTR_HANDOVER_ERROR,
+ */
#define FUNC_RESULT_COUNTER(obj, name) \
static int result_counter_##obj##_##name(enum handover_result result) \
{ \
@@ -772,6 +794,7 @@ FUNC_RESULT_COUNTER(BSC, INTRA_CELL_HO)
FUNC_RESULT_COUNTER(BSC, INTRA_BSC_HO)
FUNC_RESULT_COUNTER(BSC, INTER_BSC_HO_IN)
+/* INTRA_BSC_HO_OUT does not have a NO_CHANNEL result, so can't do this with FUNC_RESULT_COUNTER() macro. */
static int result_counter_BSC_INTER_BSC_HO_OUT(enum handover_result result) {
switch (result) {
case HO_RESULT_OK:
@@ -807,8 +830,10 @@ static int result_counter_bsc(enum handover_scope scope, enum handover_result re
FUNC_RESULT_COUNTER(BTS, HANDOVER)
FUNC_RESULT_COUNTER(BTS, INTRA_CELL_HO)
FUNC_RESULT_COUNTER(BTS, INTRA_BSC_HO)
+FUNC_RESULT_COUNTER(BTS, INCOMING_INTRA_BSC_HO)
FUNC_RESULT_COUNTER(BTS, INTER_BSC_HO_IN)
+/* INTRA_BSC_HO_OUT does not have a NO_CHANNEL result, so can't do this with FUNC_RESULT_COUNTER() macro. */
static int result_counter_BTS_INTER_BSC_HO_OUT(enum handover_result result) {
switch (result) {
case HO_RESULT_OK:
@@ -997,6 +1022,9 @@ void handover_end(struct gsm_subscriber_connection *conn, enum handover_result r
ho_count_bsc(result_counter_bsc(ho->scope, result));
ho_count_bts(bts, result_counter_BTS_HANDOVER(result));
ho_count_bts(bts, result_counter_bts(ho->scope, result));
+ /* For inter-cell HO, also increment the "INCOMING" counters on the target BTS. */
+ if (ho->scope & HO_INTRA_BSC)
+ ho_count_bts(ho->new_bts, result_counter_BTS_INCOMING_INTRA_BSC_HO(result));
if (ho->scope & HO_INTER_BSC_IN && conn->fast_return.last_eutran_plmn_valid) {
/* From outside local BSC and with Last EUTRAN PLMN Id => SRVCC */
ho_count_bsc(result_counter_BSC_SRVCC(result));
diff --git a/src/osmo-bsc/lchan_fsm.c b/src/osmo-bsc/lchan_fsm.c
index aa616d09c..5ae6df651 100644
--- a/src/osmo-bsc/lchan_fsm.c
+++ b/src/osmo-bsc/lchan_fsm.c
@@ -40,6 +40,7 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
static struct osmo_fsm lchan_fsm;
@@ -97,6 +98,11 @@ static void _lchan_on_mode_modify_failure(struct gsm_lchan *lchan, enum lchan_mo
case MODIFY_FOR_ASSIGNMENT:
LOG_LCHAN(lchan, LOGL_NOTICE, "Signalling Assignment FSM of error (%s)\n",
lchan->last_error ? : "unknown error");
+ if (!for_conn) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "lchan Channel Mode Modify failed, "
+ "but modify request has no conn\n");
+ break;
+ }
_osmo_fsm_inst_dispatch(for_conn->assignment.fi, ASSIGNMENT_EV_LCHAN_ERROR, lchan,
file, line);
return;
@@ -297,7 +303,7 @@ struct osmo_tdef_state_timeout lchan_fsm_timeouts[32] = {
else \
LOG_LCHAN(_lchan, LOGL_DEBUG, "After failure handling, already in state %s\n", \
osmo_fsm_state_name(fsm, state_chg)); \
- } while(0)
+ } while (0)
/* Which state to transition to when lchan_fail() is called in a given state. */
uint32_t lchan_fsm_on_error[34] = {
@@ -512,6 +518,8 @@ static void lchan_fsm_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta
lchan_reset(lchan);
osmo_fsm_inst_dispatch(lchan->ts->fi, TS_EV_LCHAN_UNUSED, lchan);
+ bsc_update_time_cc_all_allocated(bts->network);
+
/* Poll the channel request queue, so that waiting calls can make use of the lchan that just
* has become unused now. */
abis_rsl_chan_rqd_queue_poll(bts);
@@ -627,6 +635,10 @@ static void lchan_fsm_unused(struct osmo_fsm_inst *fi, uint32_t event, void *dat
lchan->release.requested = false;
lchan->activate.info = *info;
+ /* To avoid confusion, invalidate info.chreq_reason value if it isn't for a CHREQ */
+ if (lchan->activate.info.activ_for != ACTIVATE_FOR_MS_CHANNEL_REQUEST)
+ lchan->activate.info.chreq_reason = -1;
+
lchan->activate.concluded = false;
lchan_fsm_state_chg(LCHAN_ST_WAIT_TS_READY);
break;
@@ -657,6 +669,23 @@ static int lchan_activate_set_ch_mode_rate_and_mr_config(struct gsm_lchan *lchan
return 0;
}
+static int lchan_send_imm_ass(struct osmo_fsm_inst *fi)
+{
+ int rc;
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ rc = rsl_tx_imm_assignment(lchan);
+ if (rc) {
+ lchan_fail("Failed to Tx RR Immediate Assignment message (rc=%d %s)",
+ rc, strerror(-rc));
+ return rc;
+ }
+ LOG_LCHAN(lchan, LOGL_DEBUG, "Tx RR Immediate Assignment\n");
+ lchan->activate.immediate_assignment_sent = true;
+ return rc;
+}
+
+static void post_activ_ack_accept_preliminary_settings(struct gsm_lchan *lchan);
+
static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
@@ -664,34 +693,29 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
struct osmo_mgcpc_ep_ci *use_mgwep_ci;
struct gsm_lchan *old_lchan = lchan->activate.info.re_use_mgw_endpoint_from_lchan;
struct lchan_activate_info *info = &lchan->activate.info;
- int ms_power_dbm;
+ int ms_power_dbm = bts->ms_max_power;
if (lchan->release.requested) {
lchan_fail("Release requested while activating");
return;
}
+ bsc_update_time_cc_all_allocated(bts->network);
+
lchan->conn = info->for_conn;
/* If there is a previous lchan, and the new lchan is on the same cell as previous one,
- * take over power and TA values. Otherwise, use max power and zero TA. */
+ * take over MS power values. Otherwise, use configured MS max power. */
if (old_lchan && old_lchan->ts->trx->bts == bts) {
- ms_power_dbm = ms_pwr_dbm(bts->band, old_lchan->ms_power);
- lchan_update_ms_power_ctrl_level(lchan, ms_power_dbm >= 0 ? ms_power_dbm : bts->ms_max_power);
- lchan->bs_power_db = old_lchan->bs_power_db;
- } else {
- lchan_update_ms_power_ctrl_level(lchan, bts->ms_max_power);
- /* Upon last entering the UNUSED state, from lchan_reset():
- * - bs_power_db is still zero, 0dB reduction, output power = Pn.
- * - TA is still zero, to be determined by RACH. */
-
- /* Default BS Power reduction value (in 2 dB steps) */
- if (bts->bs_power_ctrl.mode == GSM_PWR_CTRL_MODE_DYN_BTS)
- lchan->bs_power_db = bts->bs_power_ctrl.bs_power_max_db;
- else
- lchan->bs_power_db = bts->bs_power_ctrl.bs_power_val_db;
+ if ((ms_power_dbm = ms_pwr_dbm(bts->band, old_lchan->ms_power)) == 0)
+ ms_power_dbm = bts->ms_max_power;
}
+ lchan_update_ms_power_ctrl_level(lchan, ms_power_dbm);
+ /* Default BS Power reduction value (in dB) */
+ lchan->bs_power_db = (bts->bs_power_ctrl.mode == GSM_PWR_CTRL_MODE_DYN_BTS) ?
+ bts->bs_power_ctrl.bs_power_max_db :
+ bts->bs_power_ctrl.bs_power_val_db;
/* BS Power Control is generally not allowed on the BCCH/CCCH carrier.
* However, we allow it in the BCCH carrier power reduction mode of operation. */
if (lchan->ts->trx == bts->c0) {
@@ -702,6 +726,16 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
if (lchan_activate_set_ch_mode_rate_and_mr_config(lchan))
return;
+ /* If enabling VAMOS mode and no specific TSC Set was selected, make sure to select a sane TSC Set by
+ * default: Set 1 for the primary and Set 2 for the shadow lchan. For non-VAMOS lchans, TSC Set 1. */
+ if (lchan->activate.info.tsc_set.present)
+ lchan->activate.tsc_set = lchan->activate.info.tsc_set.val;
+ else
+ lchan->activate.tsc_set = lchan->vamos.is_secondary ? 2 : 1;
+
+ /* Use the TSC provided in the activation request, if any. Otherwise use the timeslot's configured TSC. */
+ lchan->activate.tsc = lchan->activate.info.tsc.present ? lchan->activate.info.tsc.val : gsm_ts_tsc(lchan->ts);
+
use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);
LOG_LCHAN(lchan, LOGL_INFO,
@@ -724,6 +758,18 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
/* Prepare an MGW endpoint CI if appropriate. */
if (lchan->activate.info.requires_voice_stream)
lchan_rtp_fsm_start(lchan);
+
+ if (lchan->activate.info.imm_ass_time == IMM_ASS_TIME_PRE_TS_ACK) {
+ /* Send the Immediate Assignment even before the timeslot is ready, saving a dyn TS timeslot roundtrip
+ * on Abis (experimental).
+ *
+ * Until the Channel Activation ACK is received, various values still are preliminary and hence live in
+ * lchan->activate.*. We're doing things early here and need e.g. an accurate lchan->tsc, so already
+ * copy the preliminary values from lchan->activate.* into the operative places in lchan->* prematurely.
+ */
+ post_activ_ack_accept_preliminary_settings(lchan);
+ lchan_send_imm_ass(fi);
+ }
}
static void lchan_fsm_wait_ts_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -774,25 +820,19 @@ static void lchan_fsm_wait_activ_ack_onenter(struct osmo_fsm_inst *fi, uint32_t
act_type = lchan->conn->ho.async ? RSL_ACT_INTER_ASYNC : RSL_ACT_INTER_SYNC;
ho_ref = lchan->conn->ho.ho_ref;
break;
- default:
case ACTIVATE_FOR_ASSIGNMENT:
+ default:
act_type = RSL_ACT_INTRA_NORM_ASS;
break;
}
+ /* rsl_tx_chan_activ() and build_encr_info() access lchan->encr, make sure it reflects the values requested for
+ * activation.
+ * TODO: rather leave it in lchan->activate.info.encr until the ACK is received, which means that
+ * rsl_tx_chan_activ() should use lchan->activate.info.encr and build_encr_info() should be passed encr as an
+ * explicit argument. */
lchan->encr = lchan->activate.info.encr;
- /* If enabling VAMOS mode and no specific TSC Set was selected, make sure to select a sane TSC Set by
- * default: Set 1 for the primary and Set 2 for the shadow lchan. For non-VAMOS lchans, TSC Set 1. */
- if (lchan->activate.info.tsc_set > 0)
- lchan->activate.tsc_set = lchan->activate.info.tsc_set;
- else
- lchan->activate.tsc_set = lchan->vamos.is_secondary ? 2 : 1;
-
- /* Use the TSC provided in the modification request, if any. Otherwise use the timeslot's configured
- * TSC. */
- lchan->activate.tsc = (lchan->activate.info.tsc >= 0) ? lchan->activate.info.tsc : gsm_ts_tsc(lchan->ts);
-
rc = rsl_tx_chan_activ(lchan, act_type, ho_ref);
if (rc) {
lchan_fail_to(LCHAN_ST_UNUSED, "Tx Chan Activ failed: %s (%d)", strerror(-rc), rc);
@@ -801,6 +841,18 @@ static void lchan_fsm_wait_activ_ack_onenter(struct osmo_fsm_inst *fi, uint32_t
if (lchan->activate.info.ta_known)
lchan->last_ta = lchan->activate.info.ta;
+
+ if (lchan->activate.info.imm_ass_time == IMM_ASS_TIME_PRE_CHAN_ACK) {
+ /* Send the Immediate Assignment directly after the Channel Activation request, saving one Abis
+ * roundtrip between ChanRqd and Imm Ass.
+ *
+ * Until the Channel Activation ACK is received, various values still are preliminary and hence live in
+ * lchan->activate.*. We're doing things early here and need e.g. an accurate lchan->tsc, so already
+ * copy the preliminary values from lchan->activate.* into the operative places in lchan->* prematurely.
+ */
+ post_activ_ack_accept_preliminary_settings(lchan);
+ lchan_send_imm_ass(fi);
+ }
}
static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi);
@@ -859,16 +911,20 @@ static void lchan_fsm_wait_activ_ack(struct osmo_fsm_inst *fi, uint32_t event, v
}
}
-static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)
+static void post_activ_ack_accept_preliminary_settings(struct gsm_lchan *lchan)
{
- int rc;
- struct gsm_lchan *lchan = lchan_fi_lchan(fi);
-
lchan->current_ch_mode_rate = lchan->activate.ch_mode_rate;
lchan->current_mr_conf = lchan->activate.mr_conf_filtered;
lchan->vamos.enabled = lchan->activate.info.vamos;
lchan->tsc_set = lchan->activate.tsc_set;
lchan->tsc = lchan->activate.tsc;
+}
+
+static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)
+{
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+
+ post_activ_ack_accept_preliminary_settings(lchan);
LOG_LCHAN(lchan, LOGL_INFO, "Rx Activ ACK %s\n",
gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode));
@@ -880,14 +936,12 @@ static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)
switch (lchan->activate.info.activ_for) {
case ACTIVATE_FOR_MS_CHANNEL_REQUEST:
- rc = rsl_tx_imm_assignment(lchan);
- if (rc) {
- lchan_fail("Failed to Tx RR Immediate Assignment message (rc=%d %s)",
- rc, strerror(-rc));
- return;
+ if (lchan->activate.info.imm_ass_time == IMM_ASS_TIME_POST_CHAN_ACK) {
+ if (lchan_send_imm_ass(fi)) {
+ /* lchan_fail() was already called in lchan_send_imm_ass() */
+ return;
+ }
}
- LOG_LCHAN(lchan, LOGL_DEBUG, "Tx RR Immediate Assignment\n");
- lchan->activate.immediate_assignment_sent = true;
break;
case ACTIVATE_FOR_ASSIGNMENT:
@@ -896,14 +950,14 @@ static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)
"lchan activation for assignment succeeded, but lchan has no conn:"
" cannot trigger appropriate actions. Release.\n");
lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
- break;
+ return;
}
if (!lchan->conn->assignment.fi) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for assignment succeeded, but lchan has no"
" assignment ongoing: cannot trigger appropriate actions. Release.\n");
lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
- break;
+ return;
}
/* After the Chan Activ Ack, the MS expects to receive an RR Assignment Command.
* Let the assignment_fsm handle that. */
@@ -916,14 +970,14 @@ static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)
"lchan activation for handover succeeded, but lchan has no conn:"
" cannot trigger appropriate actions. Release.\n");
lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
- break;
+ return;
}
if (!lchan->conn->ho.fi) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for handover succeeded, but lchan has no"
" handover ongoing: cannot trigger appropriate actions. Release.\n");
lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
- break;
+ return;
}
/* After the Chan Activ Ack of the new lchan, send the MS an RR Handover Command on the
* old channel. The handover_fsm handles that. */
@@ -1055,8 +1109,6 @@ static void lchan_fsm_wait_rsl_chan_mode_modify_ack(struct osmo_fsm_inst *fi, ui
.ch_mode_rate = lchan->modify.ch_mode_rate,
.requires_voice_stream = true,
.msc_assigned_cic = lchan->modify.info.msc_assigned_cic,
- .tsc_set = -1,
- .tsc = -1,
};
if (lchan_activate_set_ch_mode_rate_and_mr_config(lchan))
return;
@@ -1210,14 +1262,14 @@ static void lchan_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void
/* If enabling VAMOS mode and no specific TSC Set was selected, make sure to select a sane TSC Set by
* default: Set 1 for the primary and Set 2 for the shadow lchan. For non-VAMOS lchans, TSC Set 1. */
- if (lchan->modify.info.tsc_set > 0)
- lchan->modify.tsc_set = lchan->modify.info.tsc_set;
+ if (lchan->modify.info.tsc_set.present)
+ lchan->modify.tsc_set = lchan->modify.info.tsc_set.val;
else
lchan->modify.tsc_set = lchan->vamos.is_secondary ? 2 : 1;
/* Use the TSC provided in the modification request, if any. Otherwise use the timeslot's configured
* TSC. */
- lchan->modify.tsc = (lchan->modify.info.tsc >= 0) ? lchan->modify.info.tsc : gsm_ts_tsc(lchan->ts);
+ lchan->modify.tsc = lchan->modify.info.tsc.present ? lchan->modify.info.tsc.val : gsm_ts_tsc(lchan->ts);
LOG_LCHAN(lchan, LOGL_INFO,
"Modification requested: %s voice=%s MGW-ci=%s type=%s tch-mode=%s tsc=%d/%u\n",
diff --git a/src/osmo-bsc/lchan_rtp_fsm.c b/src/osmo-bsc/lchan_rtp_fsm.c
index 7bd8209c7..62cd100f8 100644
--- a/src/osmo-bsc/lchan_rtp_fsm.c
+++ b/src/osmo-bsc/lchan_rtp_fsm.c
@@ -66,7 +66,7 @@ struct osmo_tdef_state_timeout lchan_rtp_fsm_timeouts[32] = {
LCHAN_SET_LAST_ERROR(_lchan, "lchan-rtp failure in state %s: " fmt, \
osmo_fsm_state_name(fi->fsm, state_was), ## args); \
osmo_fsm_inst_dispatch(_lchan->fi, LCHAN_EV_RTP_ERROR, 0); \
- } while(0)
+ } while (0)
/* Called from lchan_fsm_init(), does not need to be visible in lchan_rtp_fsm.h */
void lchan_rtp_fsm_init()
diff --git a/src/osmo-bsc/lcs_loc_req.c b/src/osmo-bsc/lcs_loc_req.c
index 7153ce6b7..d2ea72d65 100644
--- a/src/osmo-bsc/lcs_loc_req.c
+++ b/src/osmo-bsc/lcs_loc_req.c
@@ -81,7 +81,7 @@ static const struct osmo_tdef_state_timeout lcs_loc_req_fsm_timeouts[32] = {
.cause_val = cause, \
}; \
lcs_loc_req_fsm_state_chg(lcs_loc_req->fi, LCS_LOC_REQ_ST_FAILED); \
- } while(0)
+ } while (0)
static struct lcs_loc_req *lcs_loc_req_alloc(struct osmo_fsm_inst *parent_fi, uint32_t parent_event_term)
{
@@ -110,7 +110,7 @@ static bool parse_bssmap_perf_loc_req(struct lcs_loc_req *lcs_loc_req, struct ms
#define PARSE_ERR(ERRMSG) do { \
lcs_loc_req_fail(LCS_CAUSE_PROTOCOL_ERROR, "rx BSSMAP Perform Location Request: " ERRMSG); \
return false; \
- } while(0)
+ } while (0)
payload_length = msg->tail - msg->l4h;
if (tlv_parse2(tp_arr, 1, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0) <= 0)
diff --git a/src/osmo-bsc/lcs_ta_req.c b/src/osmo-bsc/lcs_ta_req.c
index bcee6e1c8..6252d4632 100644
--- a/src/osmo-bsc/lcs_ta_req.c
+++ b/src/osmo-bsc/lcs_ta_req.c
@@ -63,7 +63,7 @@ static const struct osmo_tdef_state_timeout lcs_ta_req_fsm_timeouts[32] = {
lcs_ta_req ? osmo_fsm_inst_state_name(lcs_ta_req->fi) : "NULL", ## args); \
lcs_ta_req->failure_cause = cause; \
lcs_ta_req_fsm_state_chg(lcs_ta_req->fi, LCS_TA_REQ_ST_FAILED); \
- } while(0)
+ } while (0)
static struct osmo_fsm lcs_ta_req_fsm;
diff --git a/src/osmo-bsc/neighbor_ident.c b/src/osmo-bsc/neighbor_ident.c
index 6e706258d..44b0b4381 100644
--- a/src/osmo-bsc/neighbor_ident.c
+++ b/src/osmo-bsc/neighbor_ident.c
@@ -366,47 +366,17 @@ static int gsm_bts_get_cgi_ps(const struct gsm_bts *bts, struct osmo_cell_global
return 0;
}
-static int get_neighbor_resolve_cgi_ps_from_lac_ci(struct ctrl_cmd *cmd, void *data)
+/* Attempt resolution of cgi_ps from ARFCN+BSIC of neighbor from BTS identified by LAC+CI */
+int neighbor_address_resolution(const struct gsm_network *net, const struct cell_ab *ab,
+ uint16_t lac, uint16_t cell_id,
+ struct osmo_cell_global_id_ps *res_cgi_ps)
{
- struct gsm_network *net = (struct gsm_network *)data;
struct gsm_bts *bts_tmp, *bts_found = NULL;
- char *tmp = NULL, *tok, *saveptr;
- struct cell_ab ab;
- unsigned lac, cell_id;
struct osmo_cell_global_id_ps local_cgi_ps;
const struct osmo_cell_global_id_ps *cgi_ps = NULL;
struct gsm_bts *local_neighbor = NULL;
struct gsm0808_cell_id_list2 remote_neighbors = { 0 };
- if (!cmd->variable)
- goto fmt_err;
-
- tmp = talloc_strdup(cmd, cmd->variable);
- if (!tmp) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
-
- if (!(tok = strtok_r(tmp, ".", &saveptr)))
- goto fmt_err;
- OSMO_ASSERT(strcmp(tok, "neighbor_resolve_cgi_ps_from_lac_ci") == 0);
-
- if (!(tok = strtok_r(NULL, ".", &saveptr)))
- goto fmt_err;
- lac = atoi(tok);
-
- if (!(tok = strtok_r(NULL, ".", &saveptr)))
- goto fmt_err;
- cell_id = atoi(tok);
-
- if (!(tok = strtok_r(NULL, ".", &saveptr)))
- goto fmt_err;
- ab.arfcn = atoi(tok);
-
- if (!(tok = strtok_r(NULL, "\0", &saveptr)))
- goto fmt_err;
- ab.bsic = atoi(tok);
-
llist_for_each_entry(bts_tmp, &net->bts_list, list) {
if (bts_tmp->location_area_code != lac)
continue;
@@ -420,12 +390,9 @@ static int get_neighbor_resolve_cgi_ps_from_lac_ci(struct ctrl_cmd *cmd, void *d
goto notfound_err;
LOG_BTS(bts_found, DLINP, LOGL_DEBUG, "Resolving neighbor BTS %u -> %s\n", bts_found->nr,
- cell_ab_to_str_c(OTC_SELECT, &ab));
-
- if (!cell_ab_valid(&ab))
- goto fmt_err;
+ cell_ab_to_str_c(OTC_SELECT, ab));
- if (resolve_neighbors(&local_neighbor, &remote_neighbors, bts_found, &ab, true))
+ if (resolve_neighbors(&local_neighbor, &remote_neighbors, bts_found, ab, true))
goto notfound_err;
/* resolve_neighbors() returns either a local_neighbor or remote_neighbors.
@@ -448,7 +415,57 @@ static int get_neighbor_resolve_cgi_ps_from_lac_ci(struct ctrl_cmd *cmd, void *d
if (!cgi_ps)
goto notfound_err;
- ctrl_cmd_reply_printf(cmd, "%s", osmo_cgi_ps_name(cgi_ps));
+ *res_cgi_ps = *cgi_ps;
+ return 0;
+
+notfound_err:
+ return -1;
+}
+
+static int get_neighbor_resolve_cgi_ps_from_lac_ci(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_network *net = (struct gsm_network *)data;
+ char *tmp = NULL, *tok, *saveptr;
+ struct cell_ab ab;
+ unsigned int lac, cell_id;
+ struct osmo_cell_global_id_ps cgi_ps;
+
+ if (!cmd->variable)
+ goto fmt_err;
+
+ tmp = talloc_strdup(cmd, cmd->variable);
+ if (!tmp) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!(tok = strtok_r(tmp, ".", &saveptr)))
+ goto fmt_err;
+ OSMO_ASSERT(strcmp(tok, "neighbor_resolve_cgi_ps_from_lac_ci") == 0);
+
+ if (!(tok = strtok_r(NULL, ".", &saveptr)))
+ goto fmt_err;
+ lac = atoi(tok);
+
+ if (!(tok = strtok_r(NULL, ".", &saveptr)))
+ goto fmt_err;
+ cell_id = atoi(tok);
+
+ if (!(tok = strtok_r(NULL, ".", &saveptr)))
+ goto fmt_err;
+ ab.arfcn = atoi(tok);
+
+ if (!(tok = strtok_r(NULL, "\0", &saveptr)))
+ goto fmt_err;
+ ab.bsic = atoi(tok);
+
+ if (!cell_ab_valid(&ab))
+ goto fmt_err;
+
+ if (neighbor_address_resolution(net, &ab, lac, cell_id, &cgi_ps) < 0)
+ goto notfound_err;
+
+ ctrl_cmd_reply_printf(cmd, "%s", osmo_cgi_ps_name(&cgi_ps));
talloc_free(tmp);
return CTRL_CMD_REPLY;
@@ -472,6 +489,7 @@ int neighbor_ctrl_cmds_install(struct gsm_network *net)
struct ctrl_handle *neighbor_controlif_setup(struct gsm_network *net)
{
+ /* See also osmobsc-usermanual.pdf, section 14.1 Neighbor Address Resolution Service */
return ctrl_interface_setup_dynip2(net, net->neigh_ctrl.addr, net->neigh_ctrl.port,
NULL, _LAST_CTRL_NODE_NEIGHBOR);
}
diff --git a/src/osmo-bsc/neighbor_ident_ctrl.c b/src/osmo-bsc/neighbor_ident_ctrl.c
new file mode 100644
index 000000000..8e5e04875
--- /dev/null
+++ b/src/osmo-bsc/neighbor_ident_ctrl.c
@@ -0,0 +1,713 @@
+/* CTRL interface implementation to manage identity of neighboring BSS cells for inter-BSC handover. */
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier <pmaier@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <time.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/vty.h>
+
+/* Continue to parse ARFCN and BSIC, which are optional parameters at the end of the parameter string in most of the
+ * commands. The result is ignored when parameter n is set to NULL. */
+static int continue_parse_arfcn_and_bsic(char **saveptr, struct neighbor *n)
+{
+ int arfcn;
+ int bsic;
+ char *tok;
+
+ tok = strtok_r(NULL, "-", saveptr);
+
+ /* No ARFCN and BSIC persent - stop */
+ if (!tok)
+ return 0;
+
+ if (osmo_str_to_int(&arfcn, tok, 10, 0, 1023) < 0)
+ return -EINVAL;
+
+ tok = strtok_r(NULL, "-", saveptr);
+
+ /* When an ARFCN is given, then the BSIC parameter is
+ * mandatory */
+ if (!tok)
+ return -EINVAL;
+
+ if (strcmp(tok, "any") == 0) {
+ bsic = BSIC_ANY;
+ } else {
+ if (osmo_str_to_int(&bsic, tok, 10, 0, 63) < 0)
+ return 1;
+ }
+
+ /* Make sure there are no excess parameters */
+ if (strtok_r(NULL, "-", saveptr))
+ return -EINVAL;
+
+ if (n) {
+ n->cell_id.ab_present = true;
+ n->cell_id.ab.arfcn = arfcn;
+ n->cell_id.ab.bsic = bsic;
+ }
+
+ return 0;
+}
+
+/* This and the following: Add/Remove a BTS as neighbor */
+static int verify_neighbor_bts(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ struct gsm_bts *bts = cmd->node;
+ const int neigh_bts_nr = atoi(value);
+ struct gsm_bts *neigh_bts = gsm_bts_num(bts->network, neigh_bts_nr);
+
+ if (!neigh_bts) {
+ cmd->reply = "Invalid Neighbor BTS number - no such BTS";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int verify_neighbor_bts_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return verify_neighbor_bts(cmd, value, _data);
+}
+
+static int set_neighbor_bts_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ const int bts_nr = atoi(cmd->value);
+ int rc;
+
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_BTS_NR,
+ .bts_nr = bts_nr,
+ };
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<num>"
+ * num: BTS number (0-255) */
+CTRL_CMD_DEFINE_WO(neighbor_bts_add, "neighbor-bts add");
+
+static int verify_neighbor_bts_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return verify_neighbor_bts(cmd, value, _data);
+}
+
+static int set_neighbor_bts_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ const int bts_nr = atoi(cmd->value);
+ int rc;
+
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_BTS_NR,
+ .bts_nr = bts_nr,
+ };
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_bts_del, "neighbor-bts del");
+
+/* This and the following: Add/Remove a LAC as neighbor */
+static int parse_lac(void *ctx, struct neighbor *n, const char *value)
+{
+ char *tmp = NULL, *tok, *saveptr;
+ int rc = 0;
+ int lac;
+
+ if (n)
+ memset(n, 0, sizeof(*n));
+
+ tmp = talloc_strdup(ctx, value);
+ if (!tmp)
+ return -EINVAL;
+
+ /* Parse LAC */
+ tok = strtok_r(tmp, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Optional parameters: ARFCN and BSIC */
+ if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (n) {
+ n->type = NEIGHBOR_TYPE_CELL_ID;
+ n->cell_id.id.id_discr = CELL_IDENT_LAC;
+ n->cell_id.id.id.lac = lac;
+ }
+
+exit:
+ talloc_free(tmp);
+ return rc;
+}
+
+static int verify_neighbor_lac_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_lac(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_lac_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+
+ parse_lac(cmd, &n, cmd->value);
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<lac>[-<arfcn>-<bsic>]"
+ * lac: Location area of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_lac_add, "neighbor-lac add");
+
+static int verify_neighbor_lac_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_lac(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_lac_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+ parse_lac(cmd, &n, cmd->value);
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_lac_del, "neighbor-lac del");
+
+/* This and the following: Add/Remove a LAC-CI as neighbor */
+static int parse_lac_ci(void *ctx, struct neighbor *n, const char *value)
+{
+ char *tmp = NULL, *tok, *saveptr;
+ int rc = 0;
+ int lac;
+ int ci;
+
+ if (n)
+ memset(n, 0, sizeof(*n));
+
+ tmp = talloc_strdup(ctx, value);
+ if (!tmp)
+ return -EINVAL;
+
+ /* Parse LAC */
+ tok = strtok_r(tmp, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse CI */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Optional parameters: ARFCN and BSIC */
+ if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (n) {
+ n->type = NEIGHBOR_TYPE_CELL_ID;
+ n->cell_id.id.id_discr = CELL_IDENT_LAC_AND_CI;
+ n->cell_id.id.id.lac = lac;
+ n->cell_id.id.id.ci = ci;
+ }
+
+exit:
+ talloc_free(tmp);
+ return rc;
+}
+
+static int verify_neighbor_lac_ci_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_lac_ci(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_lac_ci_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+
+ parse_lac_ci(cmd, &n, cmd->value);
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<lac>-<ci>[-<arfcn>-<bsic>]"
+ * lac: Location area of neighbor cell (0-65535)
+ * ci: Cell ID of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_lac_ci_add, "neighbor-lac-ci add");
+
+static int verify_neighbor_lac_ci_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_lac_ci(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_lac_ci_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+ parse_lac_ci(cmd, &n, cmd->value);
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_lac_ci_del, "neighbor-lac-ci del");
+
+/* This and the following: Add/Remove a CGI as neighbor */
+static int parse_cgi(void *ctx, struct neighbor *n, const char *value)
+{
+ char *tmp = NULL, *tok, *saveptr;
+ int rc = 0;
+ uint16_t mcc;
+ uint16_t mnc;
+ bool mnc_3_digits;
+ int lac;
+ int ci;
+
+ if (n)
+ memset(n, 0, sizeof(*n));
+
+ tmp = talloc_strdup(ctx, value);
+ if (!tmp)
+ return -EINVAL;
+
+ /* Parse MCC */
+ tok = strtok_r(tmp, "-", &saveptr);
+ if (tok) {
+ if (osmo_mcc_from_str(tok, &mcc)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse MNC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_mnc_from_str(tok, &mnc, &mnc_3_digits)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse LAC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse CI */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Optional parameters: ARFCN and BSIC */
+ if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (n) {
+ n->type = NEIGHBOR_TYPE_CELL_ID;
+ n->cell_id.id.id_discr = CELL_IDENT_WHOLE_GLOBAL;
+ n->cell_id.id.id.global.lai.lac = lac;
+ n->cell_id.id.id.global.lai.plmn.mcc = mcc;
+ n->cell_id.id.id.global.lai.plmn.mnc = mnc;
+ n->cell_id.id.id.global.lai.plmn.mnc_3_digits = mnc_3_digits;
+ n->cell_id.id.id.global.cell_identity = ci;
+ }
+
+exit:
+ talloc_free(tmp);
+ return rc;
+}
+
+static int verify_neighbor_cgi_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_cgi(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_cgi_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+
+ parse_cgi(cmd, &n, cmd->value);
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"
+ * mcc: Mobile country code of neighbor cell (0-999)
+ * mnc: Mobile network code of neighbor cell (0-999)
+ * lac: Location area of neighbor cell (0-65535)
+ * ci: Cell ID of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_add, "neighbor-cgi add");
+
+static int verify_neighbor_cgi_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_cgi(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_cgi_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+ parse_cgi(cmd, &n, cmd->value);
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_del, "neighbor-cgi del");
+
+/* This and the following: Add/Remove a CGI-PS as neighbor */
+static int parse_cgi_ps(void *ctx, struct neighbor *n, const char *value)
+{
+ char *tmp = NULL, *tok, *saveptr;
+ int rc = 0;
+ uint16_t mcc;
+ uint16_t mnc;
+ bool mnc_3_digits;
+ int lac;
+ int rac;
+ int ci;
+
+ if (n)
+ memset(n, 0, sizeof(*n));
+
+ tmp = talloc_strdup(ctx, value);
+ if (!tmp)
+ return -EINVAL;
+
+ /* Parse MCC */
+ tok = strtok_r(tmp, "-", &saveptr);
+ if (tok) {
+ if (osmo_mcc_from_str(tok, &mcc)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse MNC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_mnc_from_str(tok, &mnc, &mnc_3_digits)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse LAC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse RAC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&rac, tok, 10, 0, 255) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse CI */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Optional parameters: ARFCN and BSIC */
+ if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (n) {
+ n->type = NEIGHBOR_TYPE_CELL_ID;
+ n->cell_id.id.id_discr = CELL_IDENT_WHOLE_GLOBAL_PS;
+ n->cell_id.id.id.global_ps.rai.lac.lac = lac;
+ n->cell_id.id.id.global_ps.rai.rac = lac;
+ n->cell_id.id.id.global_ps.rai.lac.plmn.mcc = mcc;
+ n->cell_id.id.id.global_ps.rai.lac.plmn.mnc = mnc;
+ n->cell_id.id.id.global_ps.rai.lac.plmn.mnc_3_digits = mnc_3_digits;
+ n->cell_id.id.id.global_ps.cell_identity = ci;
+ }
+
+exit:
+ talloc_free(tmp);
+ return rc;
+}
+
+static int verify_neighbor_cgi_ps_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_cgi_ps(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_cgi_ps_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+
+ parse_cgi_ps(cmd, &n, cmd->value);
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"
+ * mcc: Mobile country code of neighbor cell (0-999)
+ * mnc: Mobile network code of neighbor cell (0-999)
+ * lac: Location area of neighbor cell (0-65535)
+ * rac: Routing area of neighbor cell (0-65535)
+ * ci: Cell ID of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_ps_add, "neighbor-cgi-ps add");
+
+static int verify_neighbor_cgi_ps_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_cgi_ps(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_cgi_ps_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+ parse_cgi_ps(cmd, &n, cmd->value);
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_ps_del, "neighbor-cgi-ps del");
+
+/* This and the following: clear all neighbor cell information */
+static int set_neighbor_clear(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ struct neighbor *neighbor;
+ struct neighbor *neighbor_tmp;
+
+ llist_for_each_entry_safe(neighbor, neighbor_tmp, &bts->neighbors, entry) {
+ llist_del(&neighbor->entry);
+ talloc_free(neighbor);
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(neighbor_clear, "neighbor-clear");
+
+/* Register control interface commands implemented above */
+int neighbor_ident_ctrl_init(void)
+{
+ int rc = 0;
+
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_bts_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_bts_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_ci_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_ci_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_ps_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_ps_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_clear);
+
+ return rc;
+}
diff --git a/src/osmo-bsc/neighbor_ident_vty.c b/src/osmo-bsc/neighbor_ident_vty.c
index b9160ec67..e40fa8c09 100644
--- a/src/osmo-bsc/neighbor_ident_vty.c
+++ b/src/osmo-bsc/neighbor_ident_vty.c
@@ -146,23 +146,32 @@ void neighbor_ident_vty_parse_arfcn_bsic(struct cell_ab *ab, const char **argv)
};
}
-static int add_neighbor(struct vty *vty, struct neighbor *n)
+#define LOGPORVTY(vty, fmt, args...) \
+{ \
+ if (vty) \
+ vty_out(vty, "%% " fmt "%s", ## args, VTY_NEWLINE); \
+ else \
+ LOGP(DLINP, LOGL_NOTICE, fmt "\n", ## args); \
+} while (0) \
+
+/* Delete a neighbor from neighborlist. When the parameter *vty is set to NULL all error messages are redirected to the
+ * logtext. */
+int neighbor_ident_add_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n)
{
- struct gsm_bts *bts = vty->index;
struct neighbor *neighbor;
- OSMO_ASSERT((vty->node == BTS_NODE) && bts);
+ OSMO_ASSERT(bts);
+ OSMO_ASSERT(!vty || (vty->node == BTS_NODE));
llist_for_each_entry(neighbor, &bts->neighbors, entry) {
/* Check against duplicates */
if (neighbor_same(neighbor, n, false)) {
/* Found a match on Cell ID or BTS number, without ARFCN+BSIC. If they are fully identical, ignore the
* duplicate. If the ARFCN+BSIC part differs, it's an error. */
- vty_out(vty, "%% BTS %u already had neighbor %s%s", bts->nr, neighbor_to_str_c(OTC_SELECT, neighbor),
- VTY_NEWLINE);
+ LOGPORVTY(vty, "BTS %u already had neighbor %s", bts->nr, neighbor_to_str_c(OTC_SELECT, neighbor));
if (!neighbor_same(neighbor, n, true)) {
- vty_out(vty, "%% ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: %s%s",
- neighbor_to_str_c(OTC_SELECT, n), VTY_NEWLINE);
+ LOGPORVTY(vty, "ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: %s",
+ neighbor_to_str_c(OTC_SELECT, n));
return CMD_WARNING;
}
/* Exact same neighbor again, just ignore. */
@@ -173,9 +182,9 @@ static int add_neighbor(struct vty *vty, struct neighbor *n)
if (n->type == NEIGHBOR_TYPE_CELL_ID
&& n->cell_id.ab_present && neighbor->cell_id.ab_present
&& cell_ab_match(&n->cell_id.ab, &neighbor->cell_id.ab, true)) {
- vty_out(vty, "%% Error: only one Cell Identifier entry is allowed per remote neighbor."
- " Already have: BTS %u -> %s%s", bts->nr,
- neighbor_to_str_c(OTC_SELECT, neighbor), VTY_NEWLINE);
+ LOGPORVTY(vty, "Error: only one Cell Identifier entry is allowed per remote neighbor."
+ " Already have: BTS %u -> %s", bts->nr,
+ neighbor_to_str_c(OTC_SELECT, neighbor));
return CMD_WARNING;
}
}
@@ -186,12 +195,14 @@ static int add_neighbor(struct vty *vty, struct neighbor *n)
return CMD_SUCCESS;
}
-static int del_neighbor(struct vty *vty, struct neighbor *n)
+/* Delete a neighbor from neighborlist. When the parameter *vty is set to NULL all error messages are redirected to the
+ * logtext. */
+int neighbor_ident_del_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n)
{
- struct gsm_bts *bts = vty->index;
struct neighbor *neighbor;
- OSMO_ASSERT((vty->node == BTS_NODE) && bts);
+ OSMO_ASSERT(bts);
+ OSMO_ASSERT(!vty || (vty->node == BTS_NODE));
llist_for_each_entry(neighbor, &bts->neighbors, entry) {
if (neighbor->type != n->type)
@@ -216,8 +227,8 @@ static int del_neighbor(struct vty *vty, struct neighbor *n)
return CMD_SUCCESS;
}
- vty_out(vty, "%% Error: no such neighbor on BTS %d: %s%s",
- bts->nr, neighbor_to_str_c(OTC_SELECT, n), VTY_NEWLINE);
+ LOGPORVTY(vty, "Error: no such neighbor on BTS %d: %s",
+ bts->nr, neighbor_to_str_c(OTC_SELECT, n));
return CMD_WARNING;
}
@@ -271,7 +282,7 @@ DEFUN(cfg_neighbor_add_bts_nr, cfg_neighbor_add_bts_nr_cmd,
.type = NEIGHBOR_TYPE_BTS_NR,
.bts_nr = atoi(argv[0]),
};
- return add_neighbor(vty, &n);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_lac, cfg_neighbor_add_lac_cmd,
@@ -283,7 +294,7 @@ DEFUN(cfg_neighbor_add_lac, cfg_neighbor_add_lac_cmd,
};
if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return add_neighbor(vty, &n);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_lac_ci, cfg_neighbor_add_lac_ci_cmd,
@@ -295,7 +306,7 @@ DEFUN(cfg_neighbor_add_lac_ci, cfg_neighbor_add_lac_ci_cmd,
};
if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return add_neighbor(vty, &n);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_cgi, cfg_neighbor_add_cgi_cmd,
@@ -307,7 +318,7 @@ DEFUN(cfg_neighbor_add_cgi, cfg_neighbor_add_cgi_cmd,
};
if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return add_neighbor(vty, &n);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_cgi_ps, cfg_neighbor_add_cgi_ps_cmd,
@@ -319,7 +330,7 @@ DEFUN(cfg_neighbor_add_cgi_ps, cfg_neighbor_add_cgi_ps_cmd,
};
if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return add_neighbor(vty, &n);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
static int neighbor_del_all(struct vty *vty)
@@ -354,7 +365,7 @@ DEFUN(cfg_neighbor_add_lac_arfcn_bsic, cfg_neighbor_add_lac_arfcn_bsic_cmd,
if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
return CMD_WARNING;
neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + LAC_ARGC);
- return add_neighbor(vty, &n);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_lac_ci_arfcn_bsic, cfg_neighbor_add_lac_ci_arfcn_bsic_cmd,
@@ -368,7 +379,7 @@ DEFUN(cfg_neighbor_add_lac_ci_arfcn_bsic, cfg_neighbor_add_lac_ci_arfcn_bsic_cmd
if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
return CMD_WARNING;
neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + LAC_CI_ARGC);
- return add_neighbor(vty, &n);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_cgi_arfcn_bsic, cfg_neighbor_add_cgi_arfcn_bsic_cmd,
@@ -382,7 +393,7 @@ DEFUN(cfg_neighbor_add_cgi_arfcn_bsic, cfg_neighbor_add_cgi_arfcn_bsic_cmd,
if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
return CMD_WARNING;
neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + CGI_ARGC);
- return add_neighbor(vty, &n);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_cgi_ps_arfcn_bsic, cfg_neighbor_add_cgi_ps_arfcn_bsic_cmd,
@@ -396,7 +407,7 @@ DEFUN(cfg_neighbor_add_cgi_ps_arfcn_bsic, cfg_neighbor_add_cgi_ps_arfcn_bsic_cmd
if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
return CMD_WARNING;
neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + CGI_PS_ARGC);
- return add_neighbor(vty, &n);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_bts_nr, cfg_neighbor_del_bts_nr_cmd,
@@ -407,7 +418,7 @@ DEFUN(cfg_neighbor_del_bts_nr, cfg_neighbor_del_bts_nr_cmd,
.type = NEIGHBOR_TYPE_BTS_NR,
.bts_nr = atoi(argv[0]),
};
- return del_neighbor(vty, &n);
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_lac, cfg_neighbor_del_lac_cmd,
@@ -419,7 +430,7 @@ DEFUN(cfg_neighbor_del_lac, cfg_neighbor_del_lac_cmd,
};
if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return del_neighbor(vty, &n);
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_lac_ci, cfg_neighbor_del_lac_ci_cmd,
@@ -431,7 +442,7 @@ DEFUN(cfg_neighbor_del_lac_ci, cfg_neighbor_del_lac_ci_cmd,
};
if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return del_neighbor(vty, &n);
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_cgi, cfg_neighbor_del_cgi_cmd,
@@ -443,7 +454,7 @@ DEFUN(cfg_neighbor_del_cgi, cfg_neighbor_del_cgi_cmd,
};
if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return del_neighbor(vty, &n);
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_cgi_ps, cfg_neighbor_del_cgi_ps_cmd,
@@ -455,7 +466,7 @@ DEFUN(cfg_neighbor_del_cgi_ps, cfg_neighbor_del_cgi_ps_cmd,
};
if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return del_neighbor(vty, &n);
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_arfcn_bsic, cfg_neighbor_del_arfcn_bsic_cmd,
@@ -481,6 +492,10 @@ DEFUN(cfg_neighbor_bind, cfg_neighbor_bind_cmd,
NEIGHBOR_DOC "Bind Neighbor Resolution Service (CTRL interface) to given ip and port\n"
IP_STR IPV6_STR "Port to bind the service to [defaults to 4248 if not provided]\n")
{
+ vty_out(vty, "%% Warning: The CTRL interface for Neighbor Address Resolution is now deprecated."
+ "Upgrade osmo-pcu and drop the 'neighbor-resolution bind " VTY_IPV46_CMD " [<0-65535>]' VTY "
+ "option in order to let osmo-pcu use the new resoluton method using the PCUIF over IPA "
+ "multiplex, which will work out of the box without required configuration.%s", VTY_NEWLINE);
osmo_talloc_replace_string(bsc_gsmnet, &bsc_gsmnet->neigh_ctrl.addr, argv[0]);
if (argc > 1)
bsc_gsmnet->neigh_ctrl.port = atoi(argv[1]);
diff --git a/src/osmo-bsc/net_init.c b/src/osmo-bsc/net_init.c
index 6d88adb3f..ec2f881e3 100644
--- a/src/osmo-bsc/net_init.c
+++ b/src/osmo-bsc/net_init.c
@@ -56,6 +56,20 @@ static struct osmo_tdef gsm_network_T_defs[] = {
{ .T=-12, .default_val=5, .desc="Timeout for obtaining TA after BSSLAP TA Request" },
{ .T=-13, .default_val=5, .desc="Timeout for RR Channel Mode Modify ACK (BSC <-> MS)" },
{ .T=-14, .default_val=5, .desc="Timeout for RSL Channel Mode Modify ACK (BSC <-> BTS)" },
+ { .T = -16, .default_val = 1000, .unit = OSMO_TDEF_MS,
+ .desc = "Granularity for all_allocated:* rate counters: amount of milliseconds that one counter increment"
+ " represents. See also X17, X18" },
+ { .T = -17, .default_val = 0, .unit = OSMO_TDEF_MS,
+ .desc = "Rounding threshold for all_allocated:* rate counters: round up to the next counter increment"
+ " after this many milliseconds. If set to half of X16 (or 0), employ the usual round() behavior:"
+ " round up after half of a granularity period. If set to 1, behave like ceil(): already"
+ " increment the counter immediately when all channels are allocated. If set >= X16, behave like"
+ " floor(): only increment after a full X16 period of all channels being occupied."
+ " See also X16, X18" },
+ { .T = -18, .default_val = 60000, .unit = OSMO_TDEF_MS,
+ .desc = "Forget-sum period for all_allocated:* rate counters:"
+ " after this amount of idle time, forget internally cumulated time remainders. Zero to always"
+ " keep remainders. See also X16, X17." },
{ .T=-3111, .default_val=4, .desc="Wait time after lchan was released in error (should be T3111 + 2s)" },
{ .T=-3210, .default_val=20, .desc="After L3 Complete, wait for MSC to confirm" },
{}
diff --git a/src/osmo-bsc/nm_channel_fsm.c b/src/osmo-bsc/nm_channel_fsm.c
index 31b8c0372..52ff4374f 100644
--- a/src/osmo-bsc/nm_channel_fsm.c
+++ b/src/osmo-bsc/nm_channel_fsm.c
@@ -95,16 +95,8 @@ static void configure_loop(struct gsm_bts_trx_ts *ts, struct gsm_nm_state *state
if (!ts->mo.set_attr_sent && !ts->mo.set_attr_ack_received) {
ts->mo.set_attr_sent = true;
ccomb = abis_nm_chcomb4pchan(ts->pchan_from_config);
- if (abis_nm_set_channel_attr(ts, ccomb) == -EINVAL) {
- /* FIXME: using this here makes crazy lots of .o
- dependencies be fulled in, ending up in
- osmo_bsc_main.o which conficts due to containing its
- own main() */
- LOGPFSML(ts->mo.fi, LOGL_ERROR,
- "FIXME: Here OML link should br dropped, "
- "something is wrong in your setup!\n");
- //ipaccess_drop_oml_deferred(trx->bts);
- }
+ if (abis_nm_set_channel_attr(ts, ccomb) == -EINVAL)
+ ipaccess_drop_oml_deferred(trx->bts);
}
if (state->administrative != NM_STATE_UNLOCKED && !ts->mo.adm_unlock_sent) {
diff --git a/src/osmo-bsc/nm_rcarrier_fsm.c b/src/osmo-bsc/nm_rcarrier_fsm.c
index 4bb62fca6..b9e67c1bd 100644
--- a/src/osmo-bsc/nm_rcarrier_fsm.c
+++ b/src/osmo-bsc/nm_rcarrier_fsm.c
@@ -120,7 +120,7 @@ static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_
* be sent during Dependency, so we simply move to OFFLINE state here to
* avoid duplicating code. However, RadioCarrier seems to be implemented
* correctly and goes to Offline state during startup. If some HW
- * version is found with the aboev estated bug, this code needs to be
+ * version is found with the above estated bug, this code needs to be
* enabled, similar to what we do in nm_bb_transc_fsm:
*/
/*if (trx->bts->site_mgr.peer_has_no_avstate_offline) {
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index 6f0caa76d..aab251424 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -34,6 +34,7 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808.h>
@@ -939,8 +940,6 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn,
.msc_assigned_cic = cic,
.use_osmux = use_osmux,
.osmux_cid = osmux_cid,
- .tsc_set = -1,
- .tsc = -1,
};
/* Match codec information from the assignment command against the
@@ -967,8 +966,6 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn,
req = (struct assignment_request){
.assign_for = ASSIGN_FOR_BSSMAP_REQ,
.aoip = aoip,
- .tsc_set = -1,
- .tsc = -1,
};
rc = select_sign_chan(&req, &ct);
diff --git a/src/osmo-bsc/osmo_bsc_ctrl.c b/src/osmo-bsc/osmo_bsc_ctrl.c
index 1eea6901b..969efb5a3 100644
--- a/src/osmo-bsc/osmo_bsc_ctrl.c
+++ b/src/osmo-bsc/osmo_bsc_ctrl.c
@@ -428,6 +428,20 @@ static int set_bts_loc(struct ctrl_cmd *cmd, void *data)
if (!tmp)
goto oom;
+ tstamp = strtok_r(tmp, ",", &saveptr);
+ valid = strtok_r(NULL, ",", &saveptr);
+ lat = strtok_r(NULL, ",", &saveptr);
+ lon = strtok_r(NULL, ",", &saveptr);
+ height = strtok_r(NULL, "\0", &saveptr);
+
+ /* Check if one of the strtok results was NULL. This will probably never occur since we will only see verified
+ * input in this code path */
+ if ((tstamp == NULL) || (valid == NULL) || (lat == NULL) || (lon == NULL) || (height == NULL)) {
+ talloc_free(tmp);
+ cmd->reply = "parse error";
+ return CTRL_CMD_ERROR;
+ }
+
curloc = talloc_zero(tall_bsc_ctx, struct bts_location);
if (!curloc) {
talloc_free(tmp);
@@ -435,13 +449,6 @@ static int set_bts_loc(struct ctrl_cmd *cmd, void *data)
}
INIT_LLIST_HEAD(&curloc->list);
-
- tstamp = strtok_r(tmp, ",", &saveptr);
- valid = strtok_r(NULL, ",", &saveptr);
- lat = strtok_r(NULL, ",", &saveptr);
- lon = strtok_r(NULL, ",", &saveptr);
- height = strtok_r(NULL, "\0", &saveptr);
-
curloc->tstamp = atol(tstamp);
curloc->valid = get_string_value(bts_loc_fix_names, valid);
curloc->lat = atof(lat);
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index b97c7a1b7..243664117 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -57,6 +57,7 @@
#include <osmocom/vty/cpu_sched_vty.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
+#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include <osmocom/abis/abis.h>
#include <osmocom/bsc/abis_om2000.h>
@@ -67,8 +68,10 @@
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/mgcp_client/mgcp_client.h>
+#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include <osmocom/sigtran/xua_msg.h>
@@ -84,7 +87,6 @@
#include "../../bscconfig.h"
-struct gsm_network *bsc_gsmnet = 0;
static const char *config_file = "osmo-bsc.cfg";
static const char *rf_ctrl = NULL;
static int daemonize = 0;
@@ -106,7 +108,6 @@ static void print_help()
printf(" -c --config-file filename The config file to use.\n");
printf(" -e --log-level number Set a global loglevel.\n");
printf(" -r --rf-ctl NAME A unix domain socket to listen for cmds.\n");
- printf(" -t --testmode A special mode to provoke failures at the MSC.\n");
printf("\nVTY reference generation:\n");
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
@@ -153,13 +154,12 @@ static void handle_options(int argc, char **argv)
{"version", 0, 0, 'V' },
{"log-level", 1, 0, 'e'},
{"rf-ctl", 1, 0, 'r'},
- {"testmode", 0, 0, 't'},
{"vty-ref-mode", 1, &long_option, 1},
{"vty-ref-xml", 0, &long_option, 2},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, "hd:DsTVc:e:r:t",
+ c = getopt_long(argc, argv, "hd:DsTVc:e:r:",
long_options, &option_index);
if (c == -1)
break;
@@ -377,100 +377,54 @@ static void all_ts_dispatch_event(struct gsm_bts_trx *trx, uint32_t event)
}
}
-/* Callback function to be called every time we receive a signal from INPUT */
-static int inp_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct input_signal_data *isd = signal_data;
- struct gsm_bts_trx *trx = isd->trx;
-
- if (subsys != SS_L_INPUT)
- return -EINVAL;
-
- LOGP(DLMI, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__,
- get_value_string(e1inp_signal_names, signal));
- switch (signal) {
- case S_L_INP_TEI_UP:
- if (isd->link_type == E1INP_SIGN_OML) {
- /* Generate Mobile Allocation bit-masks for all timeslots.
- * This needs to be done here, because it's used for TS configuration. */
- generate_ma_for_bts(trx->bts);
- }
- if (isd->link_type == E1INP_SIGN_RSL)
- bootstrap_rsl(trx);
- break;
- case S_L_INP_TEI_DN:
- LOG_TRX(trx, DLMI, LOGL_ERROR, "Lost E1 %s link\n", e1inp_signtype_name(isd->link_type));
-
- if (isd->link_type == E1INP_SIGN_OML) {
- rate_ctr_inc(rate_ctr_group_get_ctr(trx->bts->bts_ctrs, BTS_CTR_BTS_OML_FAIL));
- all_ts_dispatch_event(trx, TS_EV_OML_DOWN);
- } else if (isd->link_type == E1INP_SIGN_RSL) {
- rate_ctr_inc(rate_ctr_group_get_ctr(trx->bts->bts_ctrs, BTS_CTR_BTS_RSL_FAIL));
- acc_ramp_abort(&trx->bts->acc_ramp);
- all_ts_dispatch_event(trx, TS_EV_RSL_DOWN);
- if (trx->nr == 0)
- osmo_timer_del(&trx->bts->cbch_timer);
- }
+struct osmo_timer_list update_connection_stats_timer;
- gsm_bts_sm_mo_reset(trx->bts->site_mgr);
-
- abis_nm_clear_queue(trx->bts);
- break;
- default:
- break;
- }
-
- return 0;
+/* Periodically call bsc_update_connection_stats() to keep stat items updated.
+ * It would be nicer to trigger this only when OML or RSL state is seen to flip. I tried hard to find all code paths
+ * that should call this and failed to get accurate results; this trivial timer covers all of them. */
+static void update_connection_stats_cb(void *data)
+{
+ bsc_update_connection_stats(bsc_gsmnet);
+ osmo_timer_setup(&update_connection_stats_timer, update_connection_stats_cb, NULL);
+ osmo_timer_schedule(&update_connection_stats_timer, 1, 0);
}
-static int bootstrap_bts(struct gsm_bts *bts)
+static int check_bts(struct gsm_bts *bts)
{
struct gsm_bts_trx *trx;
- unsigned int n = 0;
if (!bts->model)
return -EFAULT;
- if (bts->model->start && !bts->model->started) {
- int ret = bts->model->start(bts->network);
- if (ret < 0)
- return ret;
-
- bts->model->started = true;
- }
-
- /* FIXME: What about secondary TRX of a BTS? What about a BTS that has TRX
- * in different bands? Why is 'band' a parameter of the BTS and not of the TRX? */
switch (bts->band) {
case GSM_BAND_1800:
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
- LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n");
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM1800 channel (%u) must be between 512-885.\n",
+ bts->nr, bts->c0->arfcn);
return -EINVAL;
}
break;
case GSM_BAND_1900:
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) {
- LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n");
- return -EINVAL;
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM1900 channel (%u) must be between 512-810.\n",
+ bts->nr, bts->c0->arfcn);
}
break;
case GSM_BAND_900:
if ((bts->c0->arfcn > 124 && bts->c0->arfcn < 955) ||
bts->c0->arfcn > 1023) {
- LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 0-124, 955-1023.\n");
- return -EINVAL;
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM900 channel (%u) must be between 0-124, 955-1023.\n",
+ bts->nr, bts->c0->arfcn);
}
break;
case GSM_BAND_850:
if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) {
- LOGP(DNM, LOGL_ERROR, "GSM850 channel must be between 128-251.\n");
- return -EINVAL;
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM850 channel (%u) must be between 128-251.\n",
+ bts->nr, bts->c0->arfcn);
}
break;
default:
- LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n");
- return -EINVAL;
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) Unsupported frequency band.\n", bts->nr);
}
/* Verify the physical channel mapping */
@@ -482,10 +436,14 @@ static int bootstrap_bts(struct gsm_bts *bts)
}
}
- /* Control Channel Description is set from vty/config */
+ return 0;
+}
- /* Indicate R99 MSC in SI3 */
- bts->si_common.chan_desc.mscr = 1;
+static void bootstrap_bts(struct gsm_bts *bts)
+{
+ unsigned int n = 0;
+
+ /* Control Channel Description is set from vty/config */
/* Determine the value of CCCH_CONF. Is TS0/C0 combined? */
if (bts->c0->ts[0].pchan_from_config != GSM_PCHAN_CCCH) {
@@ -507,18 +465,72 @@ static int bootstrap_bts(struct gsm_bts *bts)
bts->si_common.chan_desc.ccch_conf = (n << 1);
}
- bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
+ /* ACC ramping is initialized from vty/config */
- bts->si_common.cell_sel_par.acs = 0;
+ /* Initialize the BTS state */
+ gsm_bts_sm_mo_reset(bts->site_mgr);
- bts->si_common.ncc_permitted = 0xff;
+ /* Generate Mobile Allocation bit-masks for all timeslots.
+ * This needs to be done here, because it's used for TS configuration. */
+ generate_ma_for_bts(bts);
+}
- bts->chan_load_samples_idx = 0;
+/* Callback function to be called every time we receive a signal from INPUT */
+static int inp_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct input_signal_data *isd = signal_data;
+ struct gsm_bts_trx *trx = isd->trx;
+ int rc;
- /* ACC ramping is initialized from vty/config */
+ if (subsys != SS_L_INPUT)
+ return -EINVAL;
- /* Initialize the BTS state */
- gsm_bts_sm_mo_reset(bts->site_mgr);
+ LOGP(DLMI, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__,
+ get_value_string(e1inp_signal_names, signal));
+ switch (signal) {
+ case S_L_INP_TEI_UP:
+ if (isd->link_type == E1INP_SIGN_OML) {
+ /* Check parameters and apply vty config dependent parameters */
+ rc = check_bts(trx->bts);
+ if (rc < 0) {
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) Error in BTS configuration -- cannot bootstrap BTS\n",
+ trx->bts->nr);
+ return rc;
+ }
+ bootstrap_bts(trx->bts);
+ }
+ if (isd->link_type == E1INP_SIGN_RSL) {
+ rc = check_bts(trx->bts);
+ if (rc < 0) {
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) Error in BTS configuration -- cannot bootstrap RSL\n",
+ trx->bts->nr);
+ return rc;
+ }
+ bootstrap_rsl(trx);
+ }
+ break;
+ case S_L_INP_TEI_DN:
+ LOG_TRX(trx, DLMI, LOGL_ERROR, "Lost E1 %s link\n", e1inp_signtype_name(isd->link_type));
+
+ if (isd->link_type == E1INP_SIGN_OML) {
+ rate_ctr_inc(rate_ctr_group_get_ctr(trx->bts->bts_ctrs, BTS_CTR_BTS_OML_FAIL));
+ all_ts_dispatch_event(trx, TS_EV_OML_DOWN);
+ } else if (isd->link_type == E1INP_SIGN_RSL) {
+ rate_ctr_inc(rate_ctr_group_get_ctr(trx->bts->bts_ctrs, BTS_CTR_BTS_RSL_FAIL));
+ acc_ramp_abort(&trx->bts->acc_ramp);
+ all_ts_dispatch_event(trx, TS_EV_RSL_DOWN);
+ if (trx->nr == 0)
+ osmo_timer_del(&trx->bts->cbch_timer);
+ }
+
+ gsm_bts_sm_mo_reset(trx->bts->site_mgr);
+
+ abis_nm_clear_queue(trx->bts);
+ break;
+ default:
+ break;
+ }
return 0;
}
@@ -544,11 +556,12 @@ static int bsc_network_configure(const char *config_file)
osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
- rc = bootstrap_bts(bts);
+ rc = check_bts(bts);
if (rc < 0) {
- LOGP(DNM, LOGL_FATAL, "Error bootstrapping BTS\n");
+ LOGP(DNM, LOGL_FATAL, "(bts=%u) cannot bootstrap BTS, invalid BTS configuration\n", bts->nr);
return rc;
}
+ bootstrap_bts(bts);
rc = e1_reconfig_bts(bts);
if (rc < 0) {
LOGP(DNM, LOGL_FATAL, "Error enabling E1 input driver\n");
@@ -840,6 +853,12 @@ static const struct log_info_cat osmo_bsc_categories[] = {
.description = "RESET/ACK on A and Lb interfaces",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
+ [DLOOP] = {
+ .name = "DLOOP",
+ .description = "Control loops",
+ .color = "\033[0;34m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
static int filter_fn(const struct log_context *ctx, struct log_target *tar)
@@ -866,6 +885,41 @@ extern void *tall_fle_ctx;
extern void *tall_tqe_ctx;
extern void *tall_ctr_ctx;
+static int bsc_mgw_setup(void)
+{
+ struct mgcp_client *mgcp_client_single;
+ unsigned int pool_members_initalized;
+
+ /* Initalize MGW pool. This initalizes and connects all MGCP clients that are currently configured in
+ * the pool. Adding additional MGCP clients to the pool is possible but the user has to configure and
+ * (re)connect them manually from the VTY. */
+ pool_members_initalized = mgcp_client_pool_connect(bsc_gsmnet->mgw.mgw_pool);
+ if (pool_members_initalized) {
+ LOGP(DNM, LOGL_NOTICE,
+ "MGW pool with %u pool members configured, (ignoring MGW configuration in VTY node 'msc').\n",
+ pool_members_initalized);
+ return 0;
+ }
+
+ /* Initialize and connect a single MGCP client. This MGCP client will appear as the one and only pool
+ * member if there is no MGW pool configured. */
+ LOGP(DNM, LOGL_NOTICE, "No MGW pool configured, using MGW configuration in VTY node 'msc'\n");
+ mgcp_client_single = mgcp_client_init(bsc_gsmnet, bsc_gsmnet->mgw.conf);
+ if (!mgcp_client_single) {
+ LOGP(DNM, LOGL_ERROR, "MGW (single) client initalization failed\n");
+ return -EINVAL;
+ }
+ if (mgcp_client_connect(mgcp_client_single)) {
+ LOGP(DNM, LOGL_ERROR, "MGW (single) connect failed at (%s:%u)\n",
+ bsc_gsmnet->mgw.conf->remote_addr,
+ bsc_gsmnet->mgw.conf->remote_port);
+ return -EINVAL;
+ }
+ mgcp_client_pool_register_single(bsc_gsmnet->mgw.mgw_pool, mgcp_client_single);
+
+ return 0;
+}
+
int main(int argc, char **argv)
{
struct bsc_msc_data *msc;
@@ -896,6 +950,7 @@ int main(int argc, char **argv)
}
bsc_gsmnet->mgw.conf = talloc_zero(bsc_gsmnet, struct mgcp_client_conf);
+ bsc_gsmnet->mgw.mgw_pool = mgcp_client_pool_alloc(bsc_gsmnet);
mgcp_client_conf_init(bsc_gsmnet->mgw.conf);
bts_init();
@@ -923,7 +978,6 @@ int main(int argc, char **argv)
/* seed the PRNG */
srand(time(NULL));
- ts_fsm_init();
lchan_fsm_init();
bsc_subscr_conn_fsm_init();
assignment_fsm_init();
@@ -999,14 +1053,8 @@ int main(int argc, char **argv)
}
}
- bsc_gsmnet->mgw.client = mgcp_client_init(bsc_gsmnet, bsc_gsmnet->mgw.conf);
-
- if (mgcp_client_connect(bsc_gsmnet->mgw.client)) {
- LOGP(DNM, LOGL_ERROR, "MGW connect failed at (%s:%u)\n",
- bsc_gsmnet->mgw.conf->remote_addr,
- bsc_gsmnet->mgw.conf->remote_port);
+ if (bsc_mgw_setup() != 0)
exit(1);
- }
if (osmo_bsc_sigtran_init(&bsc_gsmnet->mscs) != 0) {
LOGP(DNM, LOGL_ERROR, "Failed to initialize sigtran backhaul.\n");
@@ -1025,6 +1073,8 @@ int main(int argc, char **argv)
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
+ update_connection_stats_cb(NULL);
+
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
diff --git a/src/osmo-bsc/osmo_bsc_msc.c b/src/osmo-bsc/osmo_bsc_msc.c
index 772bea588..baef4e524 100644
--- a/src/osmo-bsc/osmo_bsc_msc.c
+++ b/src/osmo-bsc/osmo_bsc_msc.c
@@ -147,8 +147,8 @@ static const struct rate_ctr_group_desc msc_ctrg_desc = {
};
static const struct osmo_stat_item_desc msc_stat_desc[] = {
- { "msc_links:active", "Number of active MSC links", "", 16, 0 },
- { "msc_links:total", "Number of configured MSC links", "", 16, 0 },
+ [MSC_STAT_MSC_LINKS_ACTIVE] = { "msc_links:active", "Number of active MSC links", "", 16, 0 },
+ [MSC_STAT_MSC_LINKS_TOTAL] = { "msc_links:total", "Number of configured MSC links", "", 16, 0 },
};
static const struct osmo_stat_item_group_desc msc_statg_desc = {
@@ -165,6 +165,10 @@ int osmo_bsc_msc_init(struct bsc_msc_data *msc)
uint16_t mgw_port;
int rc;
+ /* Everything below refers to SCCP-Lite MSC connections only. */
+ if (msc_is_aoip(msc))
+ return 0;
+
if (net->mgw.conf->remote_port >= 0)
mgw_port = net->mgw.conf->remote_port;
else
diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c
index d3c636df8..04c2e9912 100644
--- a/src/osmo-bsc/osmo_bsc_sigtran.c
+++ b/src/osmo-bsc/osmo_bsc_sigtran.c
@@ -312,7 +312,8 @@ enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, s
}
/* Open a new connection oriented sigtran connection */
-int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg)
+/* Allow test to overwrite it */
+__attribute__((weak)) int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
struct osmo_ss7_instance *ss7;
struct bsc_msc_data *msc;
diff --git a/src/osmo-bsc/paging.c b/src/osmo-bsc/paging.c
index d2dd5eade..04512be92 100644
--- a/src/osmo-bsc/paging.c
+++ b/src/osmo-bsc/paging.c
@@ -54,6 +54,7 @@
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
void *tall_paging_ctx = NULL;
diff --git a/src/osmo-bsc/pcu_sock.c b/src/osmo-bsc/pcu_sock.c
index 329bab0f8..6f7ee7715 100644
--- a/src/osmo-bsc/pcu_sock.c
+++ b/src/osmo-bsc/pcu_sock.c
@@ -263,7 +263,8 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
return pcu_sock_send(bts, msg);
}
-void pcu_info_update(struct gsm_bts *bts)
+/* Allow test to overwrite it */
+__attribute__((weak)) void pcu_info_update(struct gsm_bts *bts)
{
if (pcu_connected(bts))
pcu_tx_info_ind(bts);
diff --git a/src/osmo-bsc/power_control.c b/src/osmo-bsc/power_control.c
new file mode 100644
index 000000000..6fe445544
--- /dev/null
+++ b/src/osmo-bsc/power_control.c
@@ -0,0 +1,261 @@
+/* MS Power Control Loop L1 */
+
+/* (C) 2014 by Holger Hans Peter Freyther
+ * (C) 2020-2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bsc_subscriber.h>
+#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/meas_rep.h>
+#include <osmocom/bsc/power_control.h>
+
+/* 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:
+ *
+ * Avg[n] = a * Val[n] + (1 - a) * Avg[n - 1]
+ *
+ * where parameter 'a' determines how much weight of the latest measurement value
+ * 'Val[n]' carries vs the weight of the accumulated average 'Avg[n - 1]'. The
+ * value of 'a' is usually a float in range 0 .. 1, so:
+ *
+ * - value 0.5 gives equal weight to both 'Val[n]' and 'Avg[n - 1]';
+ * - value 1.0 means no filtering at all (pass through);
+ * - value 0.0 makes no sense.
+ *
+ * Further optimization:
+ *
+ * Avg[n] = a * Val[n] + Avg[n - 1] - a * Avg[n - 1]
+ * ^^^^^^ ^^^^^^^^^^
+ *
+ * a) this can be implemented in C using '+=' operator:
+ *
+ * Avg += a * Val - a * Avg
+ * Avg += a * (Val - Avg)
+ *
+ * b) everything is scaled up by 100 to avoid floating point stuff:
+ *
+ * Avg100 += A * (Val - Avg)
+ *
+ * where 'Avg100' is 'Avg * 100' and 'A' is 'a * 100'.
+ *
+ * For more details, see:
+ *
+ * https://en.wikipedia.org/wiki/Moving_average
+ * https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
+ * https://tomroelandts.com/articles/low-pass-single-pole-iir-filter
+ */
+static int do_pf_ewma(const struct gsm_power_ctrl_meas_params *mp,
+ struct gsm_power_ctrl_meas_proc_state *mps,
+ const int Val)
+{
+ const uint8_t A = mp->ewma.alpha;
+ int *Avg100 = &mps->ewma.Avg100;
+
+ /* We don't have 'Avg[n - 1]' if this is the first run */
+ if (mps->meas_num++ == 0) {
+ *Avg100 = Val * EWMA_SCALE_FACTOR;
+ return Val;
+ }
+
+ *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
+
+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 val_avg;
+ switch (mp->algo) {
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:
+ 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) */
+ val_avg = val;
+ }
+ 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 >= 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;
+
+ /* 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
+ * change. A higher value means a lower level (and vice versa) */
+ if (delta > params->inc_step_size_db)
+ delta = params->inc_step_size_db;
+ else if (delta < -params->red_step_size_db)
+ delta = -params->red_step_size_db;
+
+ 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;
+
+ /* Can we be sure if ONE Report is always going to correspond
+ * to ONE SACCH block at the BTS? - If not this is as approximation
+ * but it should not hurt. */
+
+ /* 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;
+}
+
+int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, const struct gsm_meas_rep *mr)
+{
+ struct lchan_power_ctrl_state *state = &lchan->ms_power_ctrl;
+ struct gsm_bts_trx *trx = lchan->ts->trx;
+ struct gsm_bts *bts = trx->bts;
+ enum gsm_band band = bts->band;
+ const struct gsm_power_ctrl_params *params = &bts->ms_power_ctrl;
+ 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;
+ uint8_t ms_power_lvl = ms_pwr_ctl_lvl(band, mr->ms_l1.pwr);
+ int8_t ul_rssi_dbm;
+ bool ignore;
+
+ if (params == NULL)
+ return 0;
+ /* Not doing the power loop here if we are not handling it */
+ if (params->mode != GSM_PWR_CTRL_MODE_DYN_BSC)
+ return 0;
+
+ /* Shall we skip current block based on configured interval? */
+ if (ctrl_interval_skip_block(params, state))
+ return 0;
+
+ /* If DTx is active on Uplink,
+ * use the '-SUB', otherwise '-FULL': */
+ if (mr->flags & MEAS_REP_F_UL_DTX)
+ ul_rssi_dbm = rxlev2dbm(mr->ul.sub.rx_lev);
+ else
+ ul_rssi_dbm = rxlev2dbm(mr->ul.full.rx_lev);
+
+ ms_dbm = ms_pwr_dbm(band, ms_power_lvl);
+ if (ms_dbm < 0) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE,
+ "Failed to calculate dBm for power ctl level %" PRIu8 " on band %s\n",
+ ms_power_lvl, gsm_band_name(band));
+ return 0;
+ }
+
+ bsc_max_dbm = bts->ms_max_power;
+ rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, dbm2rxlev(ul_rssi_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. */
+ if (new_dbm < 0)
+ new_dbm = 0;
+ /* Don't ask for smaller ms power level than the one set by ms max power for this BTS */
+ if (new_dbm > bsc_max_dbm)
+ new_dbm = bsc_max_dbm;
+
+ new_power_lvl = ms_pwr_ctl_lvl(band, new_dbm);
+ if (new_power_lvl < 0) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE,
+ "Failed to retrieve power level for %" PRId8 " dBm on band %d\n",
+ new_dbm, band);
+ return 0;
+ }
+
+ current_dbm = ms_pwr_dbm(band, lchan->ms_power);
+
+ /* 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 (lchan->ms_power == 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\n",
+ new_power_lvl, ms_dbm, ms_power_lvl, bsc_max_dbm, ul_rssi_dbm, rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh));
+ return 0;
+ }
+
+ 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\n",
+ (new_dbm > current_dbm) ? "Raising" : "Lowering",
+ lchan->ms_power, current_dbm, new_power_lvl, new_dbm, ms_power_lvl,
+ bsc_max_dbm, ul_rssi_dbm, rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh));
+
+ lchan_update_ms_power_ctrl_level(lchan, new_dbm);
+
+ return 1;
+
+}
diff --git a/src/osmo-bsc/system_information.c b/src/osmo-bsc/system_information.c
index e8b3b7d7d..974af3a84 100644
--- a/src/osmo-bsc/system_information.c
+++ b/src/osmo-bsc/system_information.c
@@ -1017,8 +1017,10 @@ static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts)
const struct gsm_bts_trx_ts *ts = cbch_lchan->ts;
struct gsm48_chan_desc cd;
- /* 10.5.2.5 (TV) CBCH Channel Description IE */
- if (gsm48_lchan2chan_desc_as_configured(&cd, cbch_lchan, gsm_ts_tsc(cbch_lchan->ts)))
+ /* 10.5.2.5 (TV) CBCH Channel Description IE.
+ * CBCH is never in VAMOS mode, so just pass allow_osmo_cbits == false. */
+ if (gsm48_lchan_and_pchan2chan_desc(&cd, cbch_lchan, cbch_lchan->ts->pchan_from_config,
+ gsm_ts_tsc(cbch_lchan->ts), false))
return -EINVAL;
tail = tv_fixed_put(tail, GSM48_IE_CBCH_CHAN_DESC,
sizeof(cd), (uint8_t *) &cd);
diff --git a/src/osmo-bsc/timeslot_fsm.c b/src/osmo-bsc/timeslot_fsm.c
index adca31d8c..86fea3c8d 100644
--- a/src/osmo-bsc/timeslot_fsm.c
+++ b/src/osmo-bsc/timeslot_fsm.c
@@ -52,7 +52,7 @@ static void ts_fsm_update_id(struct gsm_bts_trx_ts *ts)
gsm_pchan_id(ts->pchan_on_init));
}
-void ts_fsm_init()
+static __attribute__((constructor)) void ts_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&ts_fsm) == 0);
}
@@ -66,6 +66,14 @@ void ts_fsm_alloc(struct gsm_bts_trx_ts *ts)
ts_fsm_update_id(ts);
}
+void ts_fsm_free(struct gsm_bts_trx_ts *ts)
+{
+ if (ts->fi) {
+ osmo_fsm_inst_free(ts->fi);
+ ts->fi = NULL;
+ }
+}
+
enum lchan_sanity {
LCHAN_IS_INSANE = -1,
LCHAN_IS_READY_TO_GO,
@@ -193,19 +201,24 @@ void ts_set_pchan_is(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan_
{
int i;
struct gsm_lchan *lchan;
+ uint8_t max_lchans_possible_vamos;
+
ts->pchan_is = pchan_is;
ts->max_primary_lchans = pchan_subslots(ts->pchan_is);
- LOG_TS(ts, LOGL_DEBUG, "pchan_is=%s max_primary_lchans=%d max_lchans_possible=%d\n",
- gsm_pchan_name(ts->pchan_is), ts->max_primary_lchans, ts->max_lchans_possible);
+ max_lchans_possible_vamos = pchan_subslots_vamos(ts->pchan_is);
+ LOG_TS(ts, LOGL_DEBUG, "pchan_is=%s max_primary_lchans=%d max_lchans_possible=%d (%u VAMOS)\n",
+ gsm_pchan_name(ts->pchan_is), ts->max_primary_lchans, ts->max_lchans_possible,
+ max_lchans_possible_vamos);
switch (ts->pchan_is) {
case GSM_PCHAN_TCH_F:
case GSM_PCHAN_TCH_H:
for (i = 0; i < ts->max_lchans_possible; i++) {
lchan = &ts->lchan[i];
- if (i < ts->max_primary_lchans)
- lchan->vamos.is_secondary = false;
- else
+ if (i >= ts->max_primary_lchans &&
+ (i - ts->max_primary_lchans) < (int)max_lchans_possible_vamos)
lchan->vamos.is_secondary = true;
+ else
+ lchan->vamos.is_secondary = false;
lchan_fsm_update_id(lchan);
}
break;
@@ -235,6 +248,7 @@ static void ts_setup_lchans(struct gsm_bts_trx_ts *ts)
ts->max_lchans_possible = max_lchans + max_lchans_vamos;
ts->max_primary_lchans = 0;
+ OSMO_ASSERT(ts->max_lchans_possible <= TS_MAX_LCHAN);
for (i = 0; i < ts->max_lchans_possible; i++) {
/* If we receive more than one Channel OPSTART ACK, don't fail on the second init. */
if (ts->lchan[i].fi)
diff --git a/src/utils/bs11_config.c b/src/utils/bs11_config.c
index c279179d5..e79507642 100644
--- a/src/utils/bs11_config.c
+++ b/src/utils/bs11_config.c
@@ -974,24 +974,3 @@ int main(int argc, char **argv)
exit(0);
}
-
-/* Stub */
-int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- return 0;
-}
-
-/* Stub */
-int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- return 0;
-}
-
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int abis_rsl_rcvmsg(struct msgb *msg) { return 0; }
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/src/utils/meas_json.c b/src/utils/meas_json.c
index 82b1e570b..00b90e8f5 100644
--- a/src/utils/meas_json.c
+++ b/src/utils/meas_json.c
@@ -201,12 +201,3 @@ int main(int argc, char **argv)
exit(0);
}
-
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int abis_rsl_rcvmsg(struct msgb *msg) { return 0; }
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0831dee5d..a5f92efcc 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -42,6 +42,7 @@ EXTRA_DIST = \
timer.vty \
power_ctrl.vty \
interf_meas.vty \
+ acch_overpower.vty \
ctrl/osmo-bsc-neigh-test.cfg \
ctrl/osmo-bsc-apply-config-file.cfg \
ctrl/osmo-bsc-apply-config-file-invalid.cfg \
@@ -60,7 +61,6 @@ python-tests: $(BUILT_SOURCES)
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
$(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
$(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v
- rm -f $(top_builddir)/sms.db $(top_builddir)/gsn_restart $(top_builddir)/gtphub_restart_count
else
python-tests: $(BUILT_SOURCES)
echo "Not running python-based tests (determined at configure-time)"
diff --git a/tests/abis/abis_test.c b/tests/abis/abis_test.c
index 9d26eddcd..a48154bac 100644
--- a/tests/abis/abis_test.c
+++ b/tests/abis/abis_test.c
@@ -183,16 +183,3 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { return true; }
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/tests/acc/acc_test.c b/tests/acc/acc_test.c
index 759ff139f..6357d4b4f 100644
--- a/tests/acc/acc_test.c
+++ b/tests/acc/acc_test.c
@@ -524,8 +524,8 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
-/* Whenever ACC code changes the set of barred ACCs, gsm_bts_set_system_infos()
- * is called which ends up calling pcu_info_update */
+/* stub: Whenever ACC code changes the set of barred ACCs, gsm_bts_set_system_infos()
+ * is called which ends up calling pcu_info_update. */
void pcu_info_update(struct gsm_bts *bts) {
struct gsm48_rach_control rach_control = {0};
@@ -545,15 +545,15 @@ void pcu_info_update(struct gsm_bts *bts) {
);
}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { return true; }
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
+/* stub: Whenever ACC code changes the set of barred ACCs, gsm_bts_set_system_infos()
+ * is called which ends up calling rsl_bcch_info. We need to return success to
+ * have pcu_info_update() called. */
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
+
+/* stub: Whenever ACC code changes the set of barred ACCs, gsm_bts_set_system_infos()
+ * is called which ends up calling rsl_sacch_filling. We need to return success to
+ * have pcu_info_update() called. */
+int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type,
+ const uint8_t *data, int len)
+{ return 0; }
diff --git a/tests/acch_overpower.vty b/tests/acch_overpower.vty
new file mode 100644
index 000000000..70090a438
--- /dev/null
+++ b/tests/acch_overpower.vty
@@ -0,0 +1,88 @@
+OsmoBSC> enable
+
+OsmoBSC# ### Default configuration (overpower disabled)
+OsmoBSC# show running-config
+... !overpower
+
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)# list with-flags
+...
+ . ..l overpower (dl-acch|dl-sacch|dl-facch) <1-4>
+ . ..l no overpower dl-acch
+ . ..l overpower rxqual (0|1|2|3|4|5|6|7)
+ . ..l overpower chan-mode (speech-amr|any)
+...
+
+OsmoBSC(config-net-bts)# overpower?
+ overpower Temporary ACCH overpower
+OsmoBSC(config-net-bts)# overpower ?
+ dl-acch Enable overpower for both SACCH and FACCH
+ dl-sacch Enable overpower for SACCH only
+ dl-facch Enable overpower for FACCH only
+ rxqual Set RxQual (BER) threshold (default 4)
+ chan-mode Allow temporary overpower for specific Channel mode(s)
+
+OsmoBSC(config-net-bts)# overpower dl-acch ?
+ <1-4> Overpower value in dB
+OsmoBSC(config-net-bts)# overpower dl-acch 2
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-acch 2
+ overpower rxqual 4
+ overpower chan-mode speech-amr
+...
+OsmoBSC(config-net-bts)# overpower dl-sacch 3
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-sacch 3
+ overpower rxqual 4
+ overpower chan-mode speech-amr
+...
+OsmoBSC(config-net-bts)# overpower dl-facch 4
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-facch 4
+ overpower rxqual 4
+ overpower chan-mode speech-amr
+...
+
+OsmoBSC(config-net-bts)# overpower rxqual ?
+ 0 BER >= 0% (always on)
+ 1 BER >= 0.2%
+ 2 BER >= 0.4%
+ 3 BER >= 0.8%
+ 4 BER >= 1.6% (default)
+ 5 BER >= 3.2%
+ 6 BER >= 6.4%
+ 7 BER >= 12.8%
+OsmoBSC(config-net-bts)# overpower rxqual 0
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-facch 4
+ overpower rxqual 0
+ overpower chan-mode speech-amr
+...
+OsmoBSC(config-net-bts)# overpower rxqual 7
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-facch 4
+ overpower rxqual 7
+ overpower chan-mode speech-amr
+...
+
+OsmoBSC(config-net-bts)# overpower chan-mode ?
+ speech-amr Speech channels using AMR codec (default)
+ any Any kind of channel mode
+OsmoBSC(config-net-bts)# overpower chan-mode any
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-facch 4
+ overpower rxqual 7
+ overpower chan-mode any
+...
+
+OsmoBSC(config-net-bts)# no overpower dl-acch
+OsmoBSC(config-net-bts)# show running-config
+... !overpower
diff --git a/tests/bsc/bsc_test.c b/tests/bsc/bsc_test.c
index dbb547e82..4a97b9f5d 100644
--- a/tests/bsc/bsc_test.c
+++ b/tests/bsc/bsc_test.c
@@ -39,7 +39,6 @@
#include <search.h>
void *ctx = NULL;
-struct gsm_network *bsc_gsmnet = NULL;
enum test {
TEST_SCAN_TO_BTS,
@@ -222,30 +221,3 @@ int main(int argc, char **argv)
talloc_free(ctx);
return 0;
}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, uint8_t dlci, enum gsm0808_cause cause) {}
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) {}
-int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel)
-{ return 0; }
-void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) {}
-void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause) {}
-void bsc_assign_fail(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause) {}
-void bsc_cm_update(struct gsm_subscriber_connection *conn,
- const uint8_t *cm2, uint8_t cm2_len,
- const uint8_t *cm3, uint8_t cm3_len) {}
-void gscon_submit_rsl_dtap(struct gsm_subscriber_connection *conn,
- struct msgb *msg, int link_id, int allow_sacch) {}
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-void lchan_activate(struct gsm_lchan *lchan, void *info) {}
-bool neighbor_ident_bts_entry_exists(uint8_t from_bts) { return false; }
-const char *handover_status(struct gsm_subscriber_connection *conn) { return "x"; }
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/tests/ctrl_test_runner.py b/tests/ctrl_test_runner.py
index bd2cb1061..4c07d5e1e 100755
--- a/tests/ctrl_test_runner.py
+++ b/tests/ctrl_test_runner.py
@@ -525,6 +525,46 @@ class TestCtrlBSC(TestCtrlBase):
self.assertEqual(r['var'], 'apply-config-file')
self.assertEqual(r['value'], 'OK')
+ def testNeighborList(self):
+ # Enter manual neighbor-list mode
+ r = self.do_set('bts.0.neighbor-list.mode', 'manual')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-list.mode')
+ self.assertEqual(r['value'], 'OK')
+
+ # Add an ARFCN
+ r = self.do_set('bts.0.neighbor-list.add', '123')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-list.add')
+ self.assertEqual(r['value'], 'OK')
+
+ # Delete the ARFCN again
+ r = self.do_set('bts.0.neighbor-list.del', '123')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-list.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # Go back to automatic neighbor-list mode
+ r = self.do_set('bts.0.neighbor-list.mode', 'automatic')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-list.mode')
+ self.assertEqual(r['value'], 'OK')
+
+ # This must not work as we are in automatic neighbor-list mode
+ r = self.do_set('bts.0.neighbor-list.add', '123')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Neighbor list not in manual mode')
+
+ # Try an invalid neighbor-list mode
+ r = self.do_set('bts.0.neighbor-list.mode', 'qwertzuiop')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Invalid mode')
class TestCtrlBSCNeighbor(TestCtrlBase):
@@ -562,6 +602,177 @@ class TestCtrlBSCNeighbor(TestCtrlBase):
self.assertEqual(r['var'], 'neighbor_resolve_cgi_ps_from_lac_ci.1.6969.23.32')
self.assertEqual(r['value'], '023-42-423-2-5')
+
+class TestCtrlBSCNeighborCell(TestCtrlBase):
+
+ def tearDown(self):
+ TestCtrlBase.tearDown(self)
+ os.unlink("tmp_dummy_sock")
+
+ def ctrl_command(self):
+ return ["./src/osmo-bsc/osmo-bsc", "-r", "tmp_dummy_sock", "-c",
+ "tests/ctrl/osmo-bsc-neigh-test.cfg"]
+
+ def ctrl_app(self):
+ return (4249, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc")
+
+ def testCtrlAddDelBTS(self):
+ r = self.do_set('bts.0.neighbor-bts.add', '1')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-bts.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-bts.del', '1')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-bts.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlAddDelLAC(self):
+ # without ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-lac.add', '100')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-lac.del', '100')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # with ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-lac.add', '100-123-4')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-lac.del', '100-123-4')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlAddDelLACCI(self):
+ # without ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-lac-ci.add', '100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-lac-ci.del', '100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # with ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-lac-ci.add', '100-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-lac-ci.del', '100-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlAddDelCGI(self):
+ # without ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-cgi.add', '001-01-100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-cgi.del', '001-01-100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # with ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-cgi.add', '001-01-100-200-123-4')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-cgi.del', '001-01-100-200-123-4')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlAddDelCGIPS(self):
+ # without ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-cgi-ps.add', '001-01-100-33-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # with ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-cgi-ps.add', '001-01-100-33-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlClearNeighbors(self):
+ r = self.do_set('bts.0.neighbor-clear', 'ignored')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-clear')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlErrs(self):
+ # Missing BSIC
+ r = self.do_set('bts.0.neighbor-lac.add', '100-123')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Short value (missing RAC)
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-200-123-1')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Long value
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-1-2')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Out of range values
+ r = self.do_set('bts.0.neighbor-cgi.add', '100001-1123401-100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Garbage
+ r = self.do_set('bts.0.neighbor-lac-ci.add', '0G1-Z1-1U0-a3-2p0')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Delete something that shouldn't be there
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Failed to delete neighbor')
+
+
def add_bsc_test(suite, workdir, klass):
if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")):
print("Skipping the BSC test")
@@ -601,5 +812,6 @@ if __name__ == '__main__':
suite = unittest.TestSuite()
add_bsc_test(suite, workdir, TestCtrlBSC)
add_bsc_test(suite, workdir, TestCtrlBSCNeighbor)
+ add_bsc_test(suite, workdir, TestCtrlBSCNeighborCell)
res = unittest.TextTestRunner(verbosity=verbose_level).run(suite)
sys.exit(len(res.errors) + len(res.failures))
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index 4255ab26e..90b294e83 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -57,9 +57,6 @@
__FILE__, __LINE__, (int) res, # cmp, (int) wanted); \
}
-
-struct gsm_network *bsc_gsmnet = NULL;
-
static inline void gen(struct gsm_bts *bts, const char *s)
{
int r;
@@ -598,60 +595,3 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-void gscon_release_lchans(struct gsm_subscriber_connection *conn, bool do_rr_release) {
- OSMO_ASSERT(0);
-}
-
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts)
-{
- return true;
-}
-
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-
-void bsc_cm_update(struct gsm_subscriber_connection *conn,
- const uint8_t *cm2, uint8_t cm2_len,
- const uint8_t *cm3, uint8_t cm3_len) {}
-
-int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci)
-{ return 0; }
-
-int rsl_chan_mode_modify_req(struct gsm_lchan *ts) { return 0; }
-
-int rsl_tx_ipacc_crcx(const struct gsm_lchan *lchan) { return 0; }
-
-void gscon_submit_rsl_dtap(struct gsm_subscriber_connection *conn,
- struct msgb *msg, int link_id, int allow_sacch) {}
-
-bool lchan_may_receive_data(struct gsm_lchan *lchan) { return true; }
-
-int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel)
-{ return 0; }
-
-void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id,
- struct msgb *msg) {}
-
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn,
- struct msgb *msg, uint8_t chosen_encr) {}
-
-const char *bsc_subscr_name(struct bsc_subscr *bsub) { return NULL; }
-
-void lchan_release(struct gsm_lchan *lchan, bool do_rr_release,
- bool err, enum gsm48_rr_cause cause_rr) {}
-
-int rsl_data_request(struct msgb *msg, uint8_t link_id) { return 0; }
-
-int rsl_encryption_cmd(struct msgb *msg) { return 0; }
-
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-struct e1inp_sign_link *rsl_chan_link(const struct gsm_lchan *lchan) { return NULL; }
diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok
index b22d0c241..0bb7ee037 100644
--- a/tests/gsm0408/gsm0408_test.ok
+++ b/tests/gsm0408/gsm0408_test.ok
@@ -169,7 +169,7 @@ generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 4
BTS deallocated OK in test_si2q_long()
BTS allocation OK in test_si_ba_ind()
Testing if BA-IND is set as expected in SI2xxx and SI5xxx
-SI2: 59 06 1a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e5 04 00
+SI2: 59 06 1a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff e5 04 00
SI2bis: 55 06 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e5 04 00 2b
SI2ter: 49 06 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2b 2b 2b 2b
SI5: 06 1d 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c
index 315fc1043..17bc048be 100644
--- a/tests/handover/handover_test.c
+++ b/tests/handover/handover_test.c
@@ -52,13 +52,12 @@
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/vty.h>
+#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include "../../bscconfig.h"
void *ctx;
-struct gsm_network *bsc_gsmnet;
-
/* override, requires '-Wl,--wrap=osmo_mgcpc_ep_ci_request'.
* Catch modification of an MGCP connection. */
void __real_osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
@@ -1617,11 +1616,16 @@ int main(int argc, char **argv)
if (!bsc_gsmnet)
exit(1);
+ /* The MGCP client which is handling the pool (mgcp_client_pool_vty_init) is used from the bsc_vty_init, so
+ * we must allocate an empty mgw pool even though we do not need it for this test. */
+ bsc_gsmnet->mgw.mgw_pool = mgcp_client_pool_alloc(bsc_gsmnet);
+ if (!bsc_gsmnet->mgw.mgw_pool)
+ exit(1);
+
vty_init(&vty_info);
bsc_vty_init(bsc_gsmnet);
ho_test_vty_init();
- ts_fsm_init();
lchan_fsm_init();
bsc_subscr_conn_fsm_init();
handover_fsm_init();
@@ -1678,34 +1682,17 @@ void trau_mux_unmap() {}
void trau_mux_map_lchan() {}
void trau_recv_lchan() {}
void trau_send_frame() {}
-int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; }
+/* Stub */
int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; }
void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, uint8_t dlci, enum gsm0808_cause cause) {}
void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) {}
int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel)
{ return 0; }
-int bsc_paging_start(struct bsc_paging_params *params)
-{ return 0; }
void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) {}
void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause) {}
void bsc_cm_update(struct gsm_subscriber_connection *conn,
const uint8_t *cm2, uint8_t cm2_len,
const uint8_t *cm3, uint8_t cm3_len) {}
-struct gsm0808_handover_required;
-int bsc_tx_bssmap_ho_required(struct gsm_lchan *lchan, const struct gsm0808_cell_id_list2 *target_cells)
-{ return 0; }
-int bsc_tx_bssmap_ho_request_ack(struct gsm_subscriber_connection *conn, struct msgb *rr_ho_command)
-{ return 0; }
-int bsc_tx_bssmap_ho_detect(struct gsm_subscriber_connection *conn) { return 0; }
-enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection *conn,
- struct gsm_lchan *lchan) { return HO_RESULT_OK; }
-void bsc_tx_bssmap_ho_failure(struct gsm_subscriber_connection *conn) {}
-void osmo_bsc_sigtran_tx_reset(void) {}
-void osmo_bsc_sigtran_tx_reset_ack(void) {}
-void osmo_bsc_sigtran_reset(void) {}
-void bssmap_reset_alloc(void) {}
-void bssmap_reset_is_conn_ready(void) {}
-void bssmap_reset_term_and_free(void) {}
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
{
return "fake-ep";
diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.c b/tests/nanobts_omlattr/nanobts_omlattr_test.c
index 73ba86950..1b3cc8e5f 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.c
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.c
@@ -32,76 +32,7 @@
#include <stdio.h>
#include <string.h>
-struct gsm_bts_model bts_model_nanobts = {
- .type = GSM_BTS_TYPE_NANOBTS,
- .name = "nanobts",
- .start = NULL,
- .oml_rcvmsg = NULL,
- .e1line_bind_ops = NULL,
- .nm_att_tlvdef = {
- .def = {
- /* ip.access specifics */
- [NM_ATT_IPACC_DST_IP] = {TLV_TYPE_FIXED, 4},
- [NM_ATT_IPACC_DST_IP_PORT] =
- {TLV_TYPE_FIXED, 2},
- [NM_ATT_IPACC_STREAM_ID] = {TLV_TYPE_TV,},
- [NM_ATT_IPACC_SEC_OML_CFG] =
- {TLV_TYPE_FIXED, 6},
- [NM_ATT_IPACC_IP_IF_CFG] =
- {TLV_TYPE_FIXED, 8},
- [NM_ATT_IPACC_IP_GW_CFG] =
- {TLV_TYPE_FIXED, 12},
- [NM_ATT_IPACC_IN_SERV_TIME] =
- {TLV_TYPE_FIXED, 4},
- [NM_ATT_IPACC_LOCATION] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_PAGING_CFG] =
- {TLV_TYPE_FIXED, 2},
- [NM_ATT_IPACC_UNIT_ID] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_UNIT_NAME] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_SNMP_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_PRIM_OML_CFG_LIST] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NV_FLAGS] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_FREQ_CTRL] =
- {TLV_TYPE_FIXED, 2},
- [NM_ATT_IPACC_PRIM_OML_FB_TOUT] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_CUR_SW_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_TIMING_BUS] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_CGI] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_RAC] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_OBJ_VERSION] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_GPRS_PAGING_CFG] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NSEI] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_BVCI] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NSVCI] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NS_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_BSSGP_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NS_LINK_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_RLC_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_ALM_THRESH_LIST] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_MONIT_VAL_LIST] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_TIB_CONTROL] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_SUPP_FEATURES] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_CODING_SCHEMES] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_RLC_CFG_2] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_HEARTB_TOUT] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_UPTIME] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_RLC_CFG_3] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_SSL_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_SEC_POSSIBLE] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_IML_SSL_STATE] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_REVOC_DATE] = {TLV_TYPE_TL16V},
- },
- },
-};
+extern struct gsm_bts_model bts_model_nanobts;
static void test_nanobts_attr_bts_get(struct gsm_bts *bts, uint8_t *expected)
{
@@ -232,7 +163,7 @@ int main(int argc, char **argv)
bts->location_area_code = 1;
bts->gprs.rac = 0;
uint8_t attr_bts_expected[] =
- { 0x19, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, 0x18, 0x06, 0x0e, 0x00,
+ { 0x19, 0x73, 0x6d, 0x67, 0x61, 0x5b, 0x55, 0x18, 0x06, 0x0e, 0x00,
0x02, 0x01, 0x20, 0x33, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21,
0xa8, 0x1f, 0x3f, 0x25,
0x00, 0x01, 0x0a, 0x0c, 0x0a, 0x0b, 0x01, 0x2a, 0x5a, 0x2b,
@@ -323,25 +254,3 @@ int main(int argc, char **argv)
talloc_free(ctx);
return 0;
}
-
-/* stubs */
-struct osmo_prim_hdr;
-int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
-{
- abort();
-}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts)
-{ return true; }
-
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.ok b/tests/nanobts_omlattr/nanobts_omlattr_test.ok
index 2f3ff771e..e1029a4bf 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.ok
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.ok
@@ -1,6 +1,6 @@
Testing nanobts_attr_bts_get()...
-result= 19555b61676d7318060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539
-expected=19555b61676d7318060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539
+result= 19736d67615b5518060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539
+expected=19736d67615b5518060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539
ok.
Testing nanobts_attr_nse_get()...
diff --git a/tests/osmo-bsc.vty b/tests/osmo-bsc.vty
index 7351056da..ace21dfb3 100644
--- a/tests/osmo-bsc.vty
+++ b/tests/osmo-bsc.vty
@@ -12,6 +12,7 @@ OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 ?
modify Manually send Channel Mode Modify (for debugging)
mdcx Modify RTP Connection
reassign-to Trigger Assignment to an unused lchan on the same cell
+ ms-power Manually force MS Uplink Power Level in dBm on the lchan (for testing)
handover Manually trigger handover (for debugging)
assignment Manually trigger assignment (for debugging)
@@ -79,6 +80,7 @@ OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 ?
modify Manually send Channel Mode Modify (for debugging)
mdcx Modify RTP Connection
reassign-to Trigger Assignment to an unused lchan on the same cell
+ ms-power Manually force MS Uplink Power Level in dBm on the lchan (for testing)
handover Manually trigger handover (for debugging)
assignment Manually trigger assignment (for debugging)
@@ -204,3 +206,29 @@ OsmoBSC(config-net-bts)# show running-config
OsmoBSC(config-net-bts)# channel allocator allow-tch-for-signalling 1
OsmoBSC(config-net-bts)# show running-config
... !channel allocator allow-tch-for-signalling
+
+OsmoBSC(config-net-bts)# immediate-assignment?
+ immediate-assignment Configure time of Immediate Assignment after ChanRqd RACH (Abis optimization)
+OsmoBSC(config-net-bts)# immediate-assignment ?
+ post-chan-ack Send the Immediate Assignment after the Channel Activation ACK (normal sequence)
+ pre-chan-ack Send the Immediate Assignment directly after Channel Activation (early), without waiting for the ACK; This may help with double allocations on high latency Abis links
+ pre-ts-ack EXPERIMENTAL: If a dynamic timeslot switch is necessary, send the Immediate Assignment even before the timeslot is switched, i.e. even before the Channel Activation is sent (very early)
+OsmoBSC(config-net-bts)# show running-config
+... !immediate-assignment
+OsmoBSC(config-net-bts)# immediate-assignment pre-chan-ack
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ immediate-assignment pre-chan-ack
+...
+OsmoBSC(config-net-bts)# immediate-assignment pre-ts-ack
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ immediate-assignment pre-ts-ack
+...
+OsmoBSC(config-net-bts)# immediate-assignment post-chan-ack
+OsmoBSC(config-net-bts)# show running-config
+... !immediate-assignment
diff --git a/tests/power_ctrl.vty b/tests/power_ctrl.vty
index ff387ce33..374002d82 100644
--- a/tests/power_ctrl.vty
+++ b/tests/power_ctrl.vty
@@ -7,6 +7,7 @@ OsmoBSC# show running-config
mode static
ms-power-control
mode dyn-bts
+ ctrl-interval 2
step-size inc 4 red 2
rxlev-thresh lower 32 upper 38
rxlev-thresh-comp lower 10 12 upper 19 20
@@ -27,18 +28,25 @@ OsmoBSC(config-net-bts)# list with-flags
OsmoBSC(config-net-bts)# bs-power-control
OsmoBSC(config-bs-power-ctrl)# list with-flags
...
- . l. mode (static|dyn-bts) [reset]
+ . l. mode (static|dyn-bts|dyn-bsc) [reset]
. l. bs-power (static|dyn-max) <0-30>
. lv ctrl-interval <0-31>
. lv step-size inc <2-6> red <2-4>
. lv rxlev-thresh lower <0-63> upper <0-63>
. lv rxqual-thresh lower <0-7> upper <0-7>
+ . lv ci-thresh (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs|all) (enable|disable)
+ . lv ci-thresh (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-30> upper <0-30>
. lv rxlev-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
. lv rxqual-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
+ . lv ci-thresh-comp (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-31> <0-31> upper <0-31> <0-31>
. lv no (rxlev-avg|rxqual-avg)
. lv (rxlev-avg|rxqual-avg) params hreqave <1-31> hreqt <1-31>
. lv (rxlev-avg|rxqual-avg) algo (unweighted|weighted|mod-median)
. lv (rxlev-avg|rxqual-avg) algo osmo-ewma beta <1-99>
+ . lv no ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) params hreqave <1-31> hreqt <1-31>
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo (unweighted|weighted|mod-median)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo osmo-ewma beta <1-99>
OsmoBSC(config-bs-power-ctrl)# bs-power ?
static Fixed BS Power reduction value (for static mode)
@@ -64,6 +72,7 @@ OsmoBSC(config-bs-power-ctrl)# show running-config
bs-power-control
mode dyn-bts
bs-power dyn-max 12
+ ctrl-interval 1
step-size inc 4 red 2
rxlev-thresh lower 32 upper 38
rxlev-thresh-comp lower 10 12 upper 19 20
@@ -90,26 +99,35 @@ OsmoBSC(config-bs-power-ctrl)# show running-config
bs-power-control
...
ctrl-interval 31
-... !ctrl-interval
+...
ms-power-control
-... !ctrl-interval
+...
+ ctrl-interval 2
+...
OsmoBSC(config-bs-power-ctrl)# exit
OsmoBSC(config-net-bts)# ms-power-control
OsmoBSC(config-ms-power-ctrl)# list with-flags
...
- . l. mode (static|dyn-bts) [reset]
+ . l. mode (static|dyn-bts|dyn-bsc) [reset]
. l. bs-power (static|dyn-max) <0-30>
. lv ctrl-interval <0-31>
. lv step-size inc <2-6> red <2-4>
. lv rxlev-thresh lower <0-63> upper <0-63>
. lv rxqual-thresh lower <0-7> upper <0-7>
+ . lv ci-thresh (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs|all) (enable|disable)
+ . lv ci-thresh (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-30> upper <0-30>
. lv rxlev-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
. lv rxqual-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
+ . lv ci-thresh-comp (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-31> <0-31> upper <0-31> <0-31>
. lv no (rxlev-avg|rxqual-avg)
. lv (rxlev-avg|rxqual-avg) params hreqave <1-31> hreqt <1-31>
. lv (rxlev-avg|rxqual-avg) algo (unweighted|weighted|mod-median)
. lv (rxlev-avg|rxqual-avg) algo osmo-ewma beta <1-99>
+ . lv no ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) params hreqave <1-31> hreqt <1-31>
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo (unweighted|weighted|mod-median)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo osmo-ewma beta <1-99>
OsmoBSC(config-ms-power-ctrl)# ### Check default MS Power Parameters
OsmoBSC(config-ms-power-ctrl)# show running-config
@@ -119,11 +137,30 @@ OsmoBSC(config-ms-power-ctrl)# show running-config
...
ms-power-control
mode dyn-bts
+ ctrl-interval 2
step-size inc 4 red 2
rxlev-thresh lower 32 upper 38
rxlev-thresh-comp lower 10 12 upper 19 20
rxqual-thresh lower 3 upper 0
rxqual-thresh-comp lower 5 7 upper 15 18
+ ci-thresh fr-efr enable
+ ci-thresh fr-efr lower 13 upper 17
+ ci-thresh-comp fr-efr lower 5 7 upper 15 18
+ ci-thresh hr enable
+ ci-thresh hr lower 16 upper 21
+ ci-thresh-comp hr lower 5 7 upper 15 18
+ ci-thresh amr-fr enable
+ ci-thresh amr-fr lower 7 upper 11
+ ci-thresh-comp amr-fr lower 5 7 upper 15 18
+ ci-thresh amr-hr enable
+ ci-thresh amr-hr lower 13 upper 17
+ ci-thresh-comp amr-hr lower 5 7 upper 15 18
+ ci-thresh sdcch enable
+ ci-thresh sdcch lower 12 upper 16
+ ci-thresh-comp sdcch lower 5 7 upper 15 18
+ ci-thresh gprs enable
+ ci-thresh gprs lower 18 upper 24
+ ci-thresh-comp gprs lower 5 7 upper 15 18
...
OsmoBSC(config-ms-power-ctrl)# bs-power static 30
@@ -259,9 +296,61 @@ OsmoBSC(config-net-bts)# show running-config
mode static
ms-power-control
mode dyn-bts
+ ctrl-interval 2
step-size inc 4 red 2
rxlev-thresh lower 32 upper 38
rxlev-thresh-comp lower 10 12 upper 19 20
rxqual-thresh lower 3 upper 0
rxqual-thresh-comp lower 5 7 upper 15 18
...
+
+OsmoBSC(config-net-bts)# ### Check 'ci-thresh-comp disable all' works properly:
+OsmoBSC(config-net-bts)# ms-power-control
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ ms-power-control
+...
+ ci-thresh fr-efr enable
+ ci-thresh fr-efr lower 13 upper 17
+ ci-thresh-comp fr-efr lower 5 7 upper 15 18
+ ci-thresh hr enable
+ ci-thresh hr lower 16 upper 21
+ ci-thresh-comp hr lower 5 7 upper 15 18
+ ci-thresh amr-fr enable
+ ci-thresh amr-fr lower 7 upper 11
+ ci-thresh-comp amr-fr lower 5 7 upper 15 18
+ ci-thresh amr-hr enable
+ ci-thresh amr-hr lower 13 upper 17
+ ci-thresh-comp amr-hr lower 5 7 upper 15 18
+ ci-thresh sdcch enable
+ ci-thresh sdcch lower 12 upper 16
+ ci-thresh-comp sdcch lower 5 7 upper 15 18
+ ci-thresh gprs enable
+ ci-thresh gprs lower 18 upper 24
+ ci-thresh-comp gprs lower 5 7 upper 15 18
+...
+
+OsmoBSC(config-ms-power-ctrl)# ci-thresh all disable
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ ms-power-control
+...
+ ci-thresh fr-efr disable
+ ci-thresh fr-efr lower 13 upper 17
+ ci-thresh-comp fr-efr lower 5 7 upper 15 18
+ ci-thresh hr disable
+ ci-thresh hr lower 16 upper 21
+ ci-thresh-comp hr lower 5 7 upper 15 18
+ ci-thresh amr-fr disable
+ ci-thresh amr-fr lower 7 upper 11
+ ci-thresh-comp amr-fr lower 5 7 upper 15 18
+ ci-thresh amr-hr disable
+ ci-thresh amr-hr lower 13 upper 17
+ ci-thresh-comp amr-hr lower 5 7 upper 15 18
+ ci-thresh sdcch disable
+ ci-thresh sdcch lower 12 upper 16
+ ci-thresh-comp sdcch lower 5 7 upper 15 18
+ ci-thresh gprs disable
+ ci-thresh gprs lower 18 upper 24
+ ci-thresh-comp gprs lower 5 7 upper 15 18
+...
diff --git a/tests/timer.vty b/tests/timer.vty
index 71dd2c19f..12c13adb1 100644
--- a/tests/timer.vty
+++ b/tests/timer.vty
@@ -29,6 +29,9 @@ net: X11 = 5 s Timeout for Perform Location Response from SMLC (default: 5 s)
net: X12 = 5 s Timeout for obtaining TA after BSSLAP TA Request (default: 5 s)
net: X13 = 5 s Timeout for RR Channel Mode Modify ACK (BSC <-> MS) (default: 5 s)
net: X14 = 5 s Timeout for RSL Channel Mode Modify ACK (BSC <-> BTS) (default: 5 s)
+net: X16 = 1000 ms Granularity for all_allocated:* rate counters: amount of milliseconds that one counter increment represents. See also X17, X18 (default: 1000 ms)
+net: X17 = 0 ms Rounding threshold for all_allocated:* rate counters: round up to the next counter increment after this many milliseconds. If set to half of X16 (or 0), employ the usual round() behavior: round up after half of a granularity period. If set to 1, behave like ceil(): already increment the counter immediately when all channels are allocated. If set >= X16, behave like floor(): only increment after a full X16 period of all channels being occupied. See also X16, X18 (default: 0 ms)
+net: X18 = 60000 ms Forget-sum period for all_allocated:* rate counters: after this amount of idle time, forget internally cumulated time remainders. Zero to always keep remainders. See also X16, X17. (default: 60000 ms)
net: X3111 = 4 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
net: X3210 = 20 s After L3 Complete, wait for MSC to confirm (default: 20 s)
mgw: X2427 = 5 s timeout for MGCP response from MGW (default: 5 s)
@@ -78,6 +81,9 @@ net: X11 = 5 s Timeout for Perform Location Response from SMLC (default: 5 s)
net: X12 = 5 s Timeout for obtaining TA after BSSLAP TA Request (default: 5 s)
net: X13 = 5 s Timeout for RR Channel Mode Modify ACK (BSC <-> MS) (default: 5 s)
net: X14 = 5 s Timeout for RSL Channel Mode Modify ACK (BSC <-> BTS) (default: 5 s)
+net: X16 = 1000 ms Granularity for all_allocated:* rate counters: amount of milliseconds that one counter increment represents. See also X17, X18 (default: 1000 ms)
+net: X17 = 0 ms Rounding threshold for all_allocated:* rate counters: round up to the next counter increment after this many milliseconds. If set to half of X16 (or 0), employ the usual round() behavior: round up after half of a granularity period. If set to 1, behave like ceil(): already increment the counter immediately when all channels are allocated. If set >= X16, behave like floor(): only increment after a full X16 period of all channels being occupied. See also X16, X18 (default: 0 ms)
+net: X18 = 60000 ms Forget-sum period for all_allocated:* rate counters: after this amount of idle time, forget internally cumulated time remainders. Zero to always keep remainders. See also X16, X17. (default: 60000 ms)
net: X3111 = 4 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
net: X3210 = 20 s After L3 Complete, wait for MSC to confirm (default: 20 s)
mgw: X2427 = 5 s timeout for MGCP response from MGW (default: 5 s)