aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--TODO-RELEASE12
-rw-r--r--configure.ac37
-rwxr-xr-xcontrib/jenkins.sh7
-rw-r--r--contrib/osmo-bsc.spec.in19
-rw-r--r--debian/changelog588
-rw-r--r--debian/control14
-rw-r--r--doc/Makefile.am1
-rw-r--r--doc/assignment-fsm.dot10
-rw-r--r--doc/assignment.msc2
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc-minimal.cfg2
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc.cfg2
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg2
-rw-r--r--doc/handover-inter-bsc-in-fsm.dot9
-rw-r--r--doc/handover-inter-bsc-in.msc2
-rw-r--r--doc/handover-intra-bsc-fsm.dot4
-rw-r--r--doc/handover.msc2
-rw-r--r--doc/lchan.msc24
-rw-r--r--doc/location_services_ta.msc49
-rw-r--r--doc/manuals/aoip-mgw-options-docinfo.xml10
-rw-r--r--doc/manuals/aoip-mgw-options.adoc30
-rw-r--r--doc/manuals/chapters/aoip-flows.adoc107
-rw-r--r--doc/manuals/chapters/bsc.adoc4
-rw-r--r--doc/manuals/chapters/bts.adoc136
-rw-r--r--doc/manuals/chapters/control.adoc62
-rw-r--r--doc/manuals/chapters/handover.adoc139
-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.adoc634
-rw-r--r--doc/manuals/chapters/qos-example.adoc46
-rw-r--r--doc/manuals/chapters/running.adoc22
-rw-r--r--doc/manuals/chapters/smlc.adoc90
-rw-r--r--doc/manuals/chapters/smscb.adoc26
-rw-r--r--doc/manuals/message-sequences/a_interface_bringup.msc31
-rw-r--r--doc/manuals/message-sequences/location_services_ta.msc49
-rw-r--r--doc/manuals/message-sequences/mo_call-bsc-msc-mgw-aoip.msc75
-rw-r--r--doc/manuals/mgw/osmo-bsc-new-mgw-e1.msc4
-rw-r--r--doc/manuals/mgw/osmo-bsc-new-mgw.msc4
-rw-r--r--doc/manuals/osmobsc-usermanual.adoc12
-rw-r--r--include/osmocom/bsc/Makefile.am13
-rw-r--r--include/osmocom/bsc/a_reset.h2
-rw-r--r--include/osmocom/bsc/abis_nm.h3
-rw-r--r--include/osmocom/bsc/abis_om2000.h33
-rw-r--r--include/osmocom/bsc/abis_osmo.h (renamed from include/osmocom/bsc/osmo_bsc_reset.h)25
-rw-r--r--include/osmocom/bsc/abis_rsl.h11
-rw-r--r--include/osmocom/bsc/arfcn_range_encode.h26
-rw-r--r--include/osmocom/bsc/assignment_fsm.h9
-rw-r--r--include/osmocom/bsc/bsc_msc_data.h4
-rw-r--r--include/osmocom/bsc/bsc_stats.h112
-rw-r--r--include/osmocom/bsc/bsc_subscr_conn_fsm.h15
-rw-r--r--include/osmocom/bsc/bss.h2
-rw-r--r--include/osmocom/bsc/bssmap_reset.h31
-rw-r--r--include/osmocom/bsc/bts.h391
-rw-r--r--include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h8
-rw-r--r--include/osmocom/bsc/bts_sm.h79
-rw-r--r--include/osmocom/bsc/bts_trx.h22
-rw-r--r--include/osmocom/bsc/chan_counts.h76
-rw-r--r--include/osmocom/bsc/debug.h2
-rw-r--r--include/osmocom/bsc/gsm_04_08_rr.h6
-rw-r--r--include/osmocom/bsc/gsm_data.h695
-rw-r--r--include/osmocom/bsc/handover.h9
-rw-r--r--include/osmocom/bsc/handover_cfg.h35
-rw-r--r--include/osmocom/bsc/handover_ctrl.h3
-rw-r--r--include/osmocom/bsc/handover_fsm.h2
-rw-r--r--include/osmocom/bsc/lb.h24
-rw-r--r--include/osmocom/bsc/lchan_fsm.h22
-rw-r--r--include/osmocom/bsc/lchan_rtp_fsm.h2
-rw-r--r--include/osmocom/bsc/lchan_select.h4
-rw-r--r--include/osmocom/bsc/lcs_loc_req.h10
-rw-r--r--include/osmocom/bsc/lcs_ta_req.h2
-rw-r--r--include/osmocom/bsc/meas_rep.h32
-rw-r--r--include/osmocom/bsc/neighbor_ident.h105
-rw-r--r--include/osmocom/bsc/nm_common_fsm.h113
-rw-r--r--include/osmocom/bsc/osmo_bsc_rf.h5
-rw-r--r--include/osmocom/bsc/osmo_bsc_sigtran.h4
-rw-r--r--include/osmocom/bsc/pcuif_proto.h48
-rw-r--r--include/osmocom/bsc/penalty_timers.h47
-rw-r--r--include/osmocom/bsc/power_control.h7
-rw-r--r--include/osmocom/bsc/rest_octets.h121
-rw-r--r--include/osmocom/bsc/signal.h8
-rw-r--r--include/osmocom/bsc/system_information.h5
-rw-r--r--include/osmocom/bsc/timeslot_fsm.h9
-rw-r--r--include/osmocom/bsc/vty.h63
-rw-r--r--src/ipaccess/Makefile.am15
-rw-r--r--src/ipaccess/abisip-find.c11
-rw-r--r--src/ipaccess/ipaccess-config.c41
-rw-r--r--src/ipaccess/ipaccess-proxy.c53
-rw-r--r--src/ipaccess/stubs.c14
-rw-r--r--src/osmo-bsc/Makefile.am51
-rw-r--r--src/osmo-bsc/a_reset.c221
-rw-r--r--src/osmo-bsc/abis_nm.c183
-rw-r--r--src/osmo-bsc/abis_nm_vty.c5
-rw-r--r--src/osmo-bsc/abis_om2000.c584
-rw-r--r--src/osmo-bsc/abis_om2000_vty.c226
-rw-r--r--src/osmo-bsc/abis_osmo.c226
-rw-r--r--src/osmo-bsc/abis_rsl.c1111
-rw-r--r--src/osmo-bsc/acc.c2
-rw-r--r--src/osmo-bsc/arfcn_range_encode.c340
-rw-r--r--src/osmo-bsc/assignment_fsm.c422
-rw-r--r--src/osmo-bsc/bsc_ctrl_commands.c321
-rw-r--r--src/osmo-bsc/bsc_init.c77
-rw-r--r--src/osmo-bsc/bsc_rf_ctrl.c98
-rw-r--r--src/osmo-bsc/bsc_sccp.c3
-rw-r--r--src/osmo-bsc/bsc_stats.c260
-rw-r--r--src/osmo-bsc/bsc_subscr_conn_fsm.c98
-rw-r--r--src/osmo-bsc/bsc_vty.c4844
-rw-r--r--src/osmo-bsc/bssmap_reset.c255
-rw-r--r--src/osmo-bsc/bts.c973
-rw-r--r--src/osmo-bsc/bts_ericsson_rbs2000.c36
-rw-r--r--src/osmo-bsc/bts_init.c2
-rw-r--r--src/osmo-bsc/bts_ipaccess_nanobts.c591
-rw-r--r--src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c53
-rw-r--r--src/osmo-bsc/bts_nokia_site.c3
-rw-r--r--src/osmo-bsc/bts_osmobts.c217
-rw-r--r--src/osmo-bsc/bts_siemens_bs11.c3
-rw-r--r--src/osmo-bsc/bts_sm.c112
-rw-r--r--src/osmo-bsc/bts_sysmobts.c67
-rw-r--r--src/osmo-bsc/bts_trx.c185
-rw-r--r--src/osmo-bsc/bts_trx_vty.c849
-rw-r--r--src/osmo-bsc/bts_vty.c4600
-rw-r--r--src/osmo-bsc/cbch_scheduler.c5
-rw-r--r--src/osmo-bsc/chan_alloc.c63
-rw-r--r--src/osmo-bsc/chan_counts.c142
-rw-r--r--src/osmo-bsc/codec_pref.c2
-rw-r--r--src/osmo-bsc/e1_config.c12
-rw-r--r--src/osmo-bsc/gsm_04_08_rr.c204
-rw-r--r--src/osmo-bsc/gsm_08_08.c89
-rw-r--r--src/osmo-bsc/gsm_data.c518
-rw-r--r--src/osmo-bsc/handover_ctrl.c216
-rw-r--r--src/osmo-bsc/handover_decision.c18
-rw-r--r--src/osmo-bsc/handover_decision_2.c1221
-rw-r--r--src/osmo-bsc/handover_fsm.c186
-rw-r--r--src/osmo-bsc/handover_logic.c111
-rw-r--r--src/osmo-bsc/handover_vty.c27
-rw-r--r--src/osmo-bsc/lb.c380
-rw-r--r--src/osmo-bsc/lchan_fsm.c679
-rw-r--r--src/osmo-bsc/lchan_rtp_fsm.c22
-rw-r--r--src/osmo-bsc/lchan_select.c230
-rw-r--r--src/osmo-bsc/lcs_loc_req.c38
-rw-r--r--src/osmo-bsc/lcs_ta_req.c8
-rw-r--r--src/osmo-bsc/meas_feed.c4
-rw-r--r--src/osmo-bsc/meas_rep.c79
-rw-r--r--src/osmo-bsc/neighbor_ident.c554
-rw-r--r--src/osmo-bsc/neighbor_ident_ctrl.c713
-rw-r--r--src/osmo-bsc/neighbor_ident_vty.c860
-rw-r--r--src/osmo-bsc/net_init.c17
-rw-r--r--src/osmo-bsc/nm_bb_transc_fsm.c364
-rw-r--r--src/osmo-bsc/nm_bts_fsm.c397
-rw-r--r--src/osmo-bsc/nm_bts_sm_fsm.c285
-rw-r--r--src/osmo-bsc/nm_channel_fsm.c352
-rw-r--r--src/osmo-bsc/nm_common_fsm.c36
-rw-r--r--src/osmo-bsc/nm_gprs_cell_fsm.c370
-rw-r--r--src/osmo-bsc/nm_gprs_nse_fsm.c372
-rw-r--r--src/osmo-bsc/nm_gprs_nsvc_fsm.c383
-rw-r--r--src/osmo-bsc/nm_rcarrier_fsm.c356
-rw-r--r--src/osmo-bsc/osmo_bsc_bssap.c336
-rw-r--r--src/osmo-bsc/osmo_bsc_ctrl.c25
-rw-r--r--src/osmo-bsc/osmo_bsc_filter.c47
-rw-r--r--src/osmo-bsc/osmo_bsc_lcls.c15
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c421
-rw-r--r--src/osmo-bsc/osmo_bsc_msc.c14
-rw-r--r--src/osmo-bsc/osmo_bsc_sigtran.c47
-rw-r--r--src/osmo-bsc/paging.c13
-rw-r--r--src/osmo-bsc/pcu_sock.c40
-rw-r--r--src/osmo-bsc/penalty_timers.c121
-rw-r--r--src/osmo-bsc/power_control.c261
-rw-r--r--src/osmo-bsc/rest_octets.c899
-rw-r--r--src/osmo-bsc/smscb.c4
-rw-r--r--src/osmo-bsc/system_information.c214
-rw-r--r--src/osmo-bsc/timeslot_fsm.c144
-rw-r--r--src/utils/Makefile.am15
-rw-r--r--src/utils/bs11_config.c21
-rw-r--r--src/utils/meas_db.c2
-rw-r--r--src/utils/meas_json.c11
-rw-r--r--src/utils/meas_vis.c2
-rw-r--r--tests/Makefile.am7
-rw-r--r--tests/abis/Makefile.am7
-rw-r--r--tests/abis/abis_test.c13
-rw-r--r--tests/acc/Makefile.am7
-rw-r--r--tests/acc/acc_test.c33
-rw-r--r--tests/acc/acc_test.ok928
-rw-r--r--tests/acch_overpower.vty88
-rw-r--r--tests/bsc/Makefile.am15
-rw-r--r--tests/bsc/bsc_test.c29
-rw-r--r--tests/codec_pref/Makefile.am2
-rw-r--r--tests/ctrl/osmo-bsc-apply-config-file-invalid.cfg2
-rw-r--r--tests/ctrl/osmo-bsc-apply-config-file.cfg55
-rw-r--r--tests/ctrl/osmo-bsc-neigh-test.cfg155
-rwxr-xr-xtests/ctrl_test_runner.py300
-rw-r--r--tests/gsm0408/Makefile.am14
-rw-r--r--tests/gsm0408/gsm0408_test.c447
-rw-r--r--tests/gsm0408/gsm0408_test.ok66
-rw-r--r--tests/handover/Makefile.am77
-rw-r--r--tests/handover/handover_test.c2521
-rw-r--r--tests/handover/handover_test.ok1
-rw-r--r--tests/handover/handover_tests.ok56
-rwxr-xr-xtests/handover/handover_tests.sh61
-rw-r--r--tests/handover/neighbor_ident_test.c270
-rw-r--r--tests/handover/neighbor_ident_test.err0
-rw-r--r--tests/handover/neighbor_ident_test.ok186
-rw-r--r--tests/handover/test_amr_tch_f_to_h.ho_vty14
-rw-r--r--tests/handover/test_amr_tch_f_to_h_balance_congestion.ho_vty16
-rw-r--r--tests/handover/test_amr_tch_f_to_h_congestion.ho_vty19
-rw-r--r--tests/handover/test_amr_tch_f_to_h_congestion_assignment.ho_vty18
-rw-r--r--tests/handover/test_amr_tch_f_to_h_congestion_assignment_2.ho_vty27
-rw-r--r--tests/handover/test_amr_tch_f_to_h_congestion_assignment_3.ho_vty15
-rw-r--r--tests/handover/test_amr_tch_h_and_afs_bias.ho_vty13
-rw-r--r--tests/handover/test_amr_tch_h_to_f_congestion.ho_vty14
-rw-r--r--tests/handover/test_amr_tch_h_to_f_congestion_two_cells.ho_vty17
-rw-r--r--tests/handover/test_amr_tch_h_to_f_rxlev.ho_vty16
-rw-r--r--tests/handover/test_amr_tch_h_to_f_rxlev_congested.ho_vty63
-rw-r--r--tests/handover/test_amr_tch_h_to_f_rxlev_oscillation.ho_vty20
-rw-r--r--tests/handover/test_amr_tch_h_to_f_rxqual.ho_vty39
-rw-r--r--tests/handover/test_amr_tch_h_to_f_rxqual_congested.ho_vty68
-rw-r--r--tests/handover/test_amr_tch_h_to_f_rxqual_oscillation.ho_vty20
-rw-r--r--tests/handover/test_balance_congestion.ho_vty20
-rw-r--r--tests/handover/test_balance_congestion_2.ho_vty18
-rw-r--r--tests/handover/test_balance_congestion_by_percentage.ho_vty52
-rw-r--r--tests/handover/test_balance_congestion_tchf_tchh.ho_vty53
-rw-r--r--tests/handover/test_bs_power.ho_vty11
-rw-r--r--tests/handover/test_congestion.ho_vty21
-rw-r--r--tests/handover/test_congestion_favor_best_target_rxlev.ho_vty33
-rw-r--r--tests/handover/test_congestion_intra_vs_inter_cell.ho_vty122
-rw-r--r--tests/handover/test_congestion_no_oscillation.ho_vty28
-rw-r--r--tests/handover/test_congestion_no_oscillation2.ho_vty28
-rw-r--r--tests/handover/test_disabled_ho_and_as.ho_vty36
-rw-r--r--tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment.ho_vty78
-rw-r--r--tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment_2.ho_vty47
-rw-r--r--tests/handover/test_dyn_ts_amr_tch_h_to_f_congestion_assignment_2.ho_vty27
-rw-r--r--tests/handover/test_dyn_ts_balance_congestion.ho_vty37
-rw-r--r--tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty74
-rw-r--r--tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty34
-rw-r--r--tests/handover/test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty12
-rw-r--r--tests/handover/test_dyn_ts_favor_moving_half_used_tch_h.ho_vty42
-rw-r--r--tests/handover/test_dyn_ts_favor_static_ts_as_target.ho_vty38
-rw-r--r--tests/handover/test_ho_to_better_cell.ho_vty8
-rw-r--r--tests/handover/test_ho_to_better_cell_2.ho_vty10
-rw-r--r--tests/handover/test_hysteresis.ho_vty13
-rw-r--r--tests/handover/test_insufficient_measurements.ho_vty46
-rw-r--r--tests/handover/test_keep_efr_codec.ho_vty21
-rw-r--r--tests/handover/test_keep_fr_codec.ho_vty21
-rw-r--r--tests/handover/test_keep_hr_codec.ho_vty20
-rw-r--r--tests/handover/test_max_handovers.ho_vty18
-rw-r--r--tests/handover/test_max_ta.ho_vty37
-rw-r--r--tests/handover/test_min_rxlev_vs_congestion.ho_vty18
-rw-r--r--tests/handover/test_min_rxlev_vs_hysteresis.ho_vty20
-rw-r--r--tests/handover/test_neighbor_congested.ho_vty21
-rw-r--r--tests/handover/test_neighbor_full.ho_vty9
-rw-r--r--tests/handover/test_no_congestion.ho_vty17
-rw-r--r--tests/handover/test_penalty_timer.ho_vty45
-rw-r--r--tests/handover/test_resource_indication.ho_vty67
-rw-r--r--tests/handover/test_rxqual.ho_vty21
-rw-r--r--tests/handover/test_rxqual_vs_congestion.ho_vty19
-rw-r--r--tests/handover/test_stay_in_better_cell.ho_vty6
-rw-r--r--tests/handover/test_stay_in_better_cell_2.ho_vty10
-rw-r--r--tests/handover/test_story.ho_vty72
-rw-r--r--tests/handover_cfg.vty35
-rw-r--r--tests/interf_meas.vty42
-rw-r--r--tests/nanobts_omlattr/Makefile.am7
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.c125
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.ok10
-rw-r--r--tests/neighbor_ident.vty171
-rw-r--r--tests/nri_cfg.vty32
-rw-r--r--tests/osmo-bsc.vty215
-rw-r--r--tests/power_ctrl.vty356
-rw-r--r--tests/smlc.vty73
-rw-r--r--tests/subscr/Makefile.am2
-rw-r--r--tests/subscr/bsc_subscr_test.c3
-rw-r--r--tests/testsuite.at189
-rw-r--r--tests/timer.vty12
-rwxr-xr-xtests/vty_test_runner.py15
272 files changed, 29189 insertions, 13110 deletions
diff --git a/.gitignore b/.gitignore
index c7b14bff3..0bbe4782b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,12 +2,14 @@ debian/*.log
*.o
*.lo
*.a
+*.la
.deps
Makefile
Makefile.in
bscconfig.h
bscconfig.h.in
*.pc
+*~
*.*~
*.sw?
@@ -46,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
@@ -71,6 +72,9 @@ tests/package.m4
tests/testsuite
tests/testsuite.log
+tests/handover/test*.ok
+tests/handover/test*.err
+
writtenconfig/
# manuals
@@ -80,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 f5d70c2b4..cfb9cf9ab 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -7,5 +7,13 @@
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
-manual needs common chapter cs7-config.adoc, vty_cpu_sched.adoc from osmo-gsm-manuals > 0.3.0
-osmo-bsc Mobile Identity Coding OsmoBSC is stricter in rejecting invalid coding of Mobile Identity IEs
+libosmocore >1.5.1 needs osmo_bts_features_name(), osmo_bts_features_desc()
+libosmogsm >1.5.1 enum entry GSM0808_FE_IE_LAST_USED_EUTRAN_PLMN_ID
+libosmogsm >1.5.1 introduced struct needed gsm0808_old_bss_to_new_bss_info->last_eutran_plmn_id
+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 ed9b2cb3f..bcf91bacd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,6 +9,8 @@ AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE([dist-bzip2])
AC_CONFIG_TESTDIR(tests)
+CFLAGS="$CFLAGS -std=gnu11"
+
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -34,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)
@@ -49,15 +46,14 @@ AC_ARG_ENABLE([ipaccess-utils], [AS_HELP_STRING([--enable-ipaccess-utils], [Buil
AM_CONDITIONAL(BUILD_IPA_UTILS, test "x$osmo_ac_ipa_utils" = "xyes")
AC_SUBST(osmo_ac_ipa_utils)
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.4.0)
-PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.4.0)
-PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.4.0)
-PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.4.0)
-PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
-PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0)
-PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 0.10.0)
-PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.10.0)
-PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
+PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
+PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
+PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.1.0)
+PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
+PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.4.0)
+PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.8.0)
dnl checks for header files
AC_HEADER_STDC
@@ -119,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,
@@ -154,9 +143,9 @@ AC_ARG_ENABLE([external_tests],
[Include the VTY/CTRL tests in make check [default=no]]),
[enable_ext_tests="$enableval"],[enable_ext_tests="no"])
if test "x$enable_ext_tests" = "xyes" ; then
- AC_CHECK_PROG(PYTHON2_AVAIL,python2,yes)
- if test "x$PYTHON2_AVAIL" != "xyes" ; then
- AC_MSG_ERROR([Please install python2 to run the VTY/CTRL tests.])
+ AC_CHECK_PROG(PYTHON3_AVAIL,python3,yes)
+ if test "x$PYTHON3_AVAIL" != "xyes" ; then
+ AC_MSG_ERROR([Please install python3 to run the VTY/CTRL tests.])
fi
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index 73f117428..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"
@@ -39,7 +39,6 @@ osmo-build-dep.sh osmo-mgw
# Additional configure options and depends
CONFIG=""
if [ "$WITH_MANUALS" = "1" ]; then
- osmo-build-dep.sh osmo-gsm-manuals
CONFIG="--enable-manuals"
fi
@@ -59,12 +58,12 @@ LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|| cat-testlogs.sh
LD_LIBRARY_PATH="$inst/lib" \
DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests --enable-werror $CONFIG" \
- $MAKE distcheck \
+ $MAKE $PARALLEL_MAKE distcheck \
|| cat-testlogs.sh
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
-$MAKE maintainer-clean
+$MAKE $PARALLEL_MAKE maintainer-clean
osmo-clean-workspace.sh
diff --git a/contrib/osmo-bsc.spec.in b/contrib/osmo-bsc.spec.in
index 8293c5195..9f7ef4862 100644
--- a/contrib/osmo-bsc.spec.in
+++ b/contrib/osmo-bsc.spec.in
@@ -31,16 +31,15 @@ BuildRequires: pkgconfig >= 0.20
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libcrypto) >= 0.9.5
-BuildRequires: pkgconfig(libosmo-mgcp-client) >= 1.5.0
-BuildRequires: pkgconfig(libosmo-netif) >= 0.6.0
-BuildRequires: pkgconfig(libosmo-sccp) >= 0.10.0
-BuildRequires: pkgconfig(libosmo-sigtran) >= 0.10.0
-BuildRequires: pkgconfig(libosmoabis) >= 0.6.0
-BuildRequires: pkgconfig(libosmocore) >= 1.2.0
-BuildRequires: pkgconfig(libosmoctrl) >= 1.2.0
-BuildRequires: pkgconfig(libosmogb)
-BuildRequires: pkgconfig(libosmogsm) >= 1.2.0
-BuildRequires: pkgconfig(libosmovty) >= 1.2.0
+BuildRequires: pkgconfig(libosmo-mgcp-client) >= 1.8.0
+BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
+BuildRequires: pkgconfig(libosmo-sigtran) >= 1.4.0
+BuildRequires: pkgconfig(libosmoabis) >= 1.1.0
+BuildRequires: pkgconfig(libosmocore) >= 1.5.0
+BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
+BuildRequires: pkgconfig(libosmogb) >= 1.5.0
+BuildRequires: pkgconfig(libosmogsm) >= 1.5.0
+BuildRequires: pkgconfig(libosmovty) >= 1.5.0
BuildRequires: pkgconfig(talloc)
%{?systemd_requires}
diff --git a/debian/changelog b/debian/changelog
index b65bc0dd8..8fc8b7618 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,591 @@
+osmo-bsc (1.7.0) unstable; urgency=medium
+
+ [ Harald Welte ]
+ * Introduce nm_fail_rep_signal_data for "SS_NM, S_NM_FAIL_REP" signal
+ * OM2000: Add some more message types and IEs we now understand
+ * remove 'NAT' log category
+ * handorer.h: Fix compilation with gcc-10
+ * gsm_data.h: Comment the 'nokia' BTS fields
+ * bts_nokia_site: Fix LAPD segfault during reset procedure
+ * [cosmetic] system_information: Values are not guesses; more comments
+ * bs11_config: Print "Not Equipped" for MBCCU (TRX) that don't exist
+ * Count RSL DELETE INDICATION received from BTS
+ * system_information: Set BSS_PAGING_COORDINATION in SI13 for osmo-bts
+ * vty/bts_resend_cmd: Use gsm_bts_set_system_infos() to increment changemark
+ * osmo-bsc.spec.in: Use %config(noreplace) to retain current config file
+ * abis_nm: Avoid various "Unhandled message" errors on BS-11 startup
+ * Bring timeslot FSMs on BS-11 out of NOT_INITIALIZED state
+ * Don't print 'bogus channel load sample' message if total == 0
+ * bts_nokia_site: Clean up logging
+ * Add example configuration files for E1 BTS
+ * remove examples/osmo-bsc/ericsson/osmo-bsc.cfg
+ * osmo-bsc.spec.in: Package E1 config files
+ * osmo-bsc.spec.in: Add missing directories to package
+ * debian: Package E1 configuration file examples
+ * lchan_fsm: silently ignore LCHAN_EV_RLL_ERR_IND
+ * Add example config files for Ericsson DUG20 based BTS
+ * acc.c: Don't use C99 constructs, this breaks builds on Debian 8
+ * BS-11: Fix "CONNECT TERRESTRIAL TRAFFIC"
+ * Implement support for receiving BSSMAP CommonID from MSC
+ * osmo_bsc_sigtran.c: Remove unused #defines
+ * use osmo_fd_setup() whenever applicable
+ * debian/control: Recommend installation of osmo-mgw
+ * Use osmo_fd_*_{disable,enable}
+ * fix some size-t format string characters
+ * update aoip-mgw-options document with reality of 2020
+ * abis_om2000: Force TRX NM state to UNLOCKED once TRX is up
+ * osmobsc-usermanual: Add Chapter with AoIP message flow examples
+ * OM2K: Permit transition from WAIT_CONF_RES -> WAIT_ENABLE_ACCEPT
+ * OM2K: Skip the entire CON MO if there are no connection groups
+ * Add a bts_model->bts_init() and trx_init() call-back function
+ * abis_om2000: make om2k_mo_name() an exported function
+ * [cosmetic] abis_om2000: Re-format to use longer lines
+ * abis_om2000: Mark om2k_mo_fsm_start() as static
+ * abis_om2000: keep OM2K FSMs around, don't terminate
+ * om2000: Add "show bts 0 om2k-mo" command
+ * abis_om2000: Handle DP object in get_om2k_mo()
+ * smscb: Avoid scheduler array overflow
+ * smscb: Fix adding of SMSCB messages when no message with lower period exists
+ * select_best_cipher(): Prefer A5/1 over A5/2
+ * BS-11: Send proprietary MRPCI message after assignment + HO complete
+ * hide the "smscb-command" vty command; people should use osmo-cbc
+ * CBSP: document rate counters and their mapping to basic/extended CBCH
+
+ [ Philipp Maier ]
+ * bsc_main: use higher default loglevels.
+ * lchan_rtp_fsm: fix out_state_mask
+ * vty: check with is_ipaccess_bts() before using IPACC
+ * lchan_rtp_fsm: make _fsm_timer_cb and _fsm_cleanup static
+ * lchan_rtp_fsm: use E1 endpoints if the BTS is not ipaccess type
+ * e1: encode line number as trunk number in MGCP endpoint
+ * gsm_04_08_rr: block EMERGENCY SETUP when EMERGENCY CALLS are denied
+ * abis_rsl.c: make sure emergency calls are rejected early
+ * lchan_fsm: merge lchan_mr_config()
+ * lchan_fsm: make internal functions static.
+ * lchan_fsm: make rsl mode-modify working again
+ * abis_rsl: prioritize emergency calls over regular calls
+ * abis_rsl.c: flush channel request queue on RSL bootstrap
+ * abis_rsl: inform user when expired channel requests get tossed
+ * abis_rsl: fix memleak in rach dos reduction function
+ * bsc_vty: improve manual activation of lchans (debug / labtest)
+ * bsc_vty: fix manual channel activation
+ * bsc_vty: fix wrong else-if statement
+ * gsm_08_08: fix unreachable code in parse_powercap()
+ * osmo_bsc_bssap: actually check for lchan
+ * bts: add repeated acch mode flags + vty config
+ * abis_rsl: parse cm3 and indicate ACCH repetition cap to BTS
+ * bsc_vty: mark repeated ACCH value of 1.9% to 2.7% BER as default
+ * bsc_vty: fix acch_repetition ber threshold strings
+ * abis_rsl: check if emergency calling is disabled before premption
+ * bsc_vty: mark repeat rxqual 4 (BER >= 1.6) as default
+ * bts.adoc: describe ACCH repetition settings
+
+ [ Vadim Yanitskiy ]
+ * VTY: fix writing of custom timer values to a configuration file
+ * vty: fix: restore removed DNAT category as deprecated
+ * doc/manuals: remove deprecated DNAT from the VTY reference
+ * rest_octets: cosmetic: fix alignment in gprs_cell_options
+ * vty: 'gprs 11bit_rach_support_for_egprs': drop redundant check
+ * vty: 'gprs 11bit_rach_support_for_egprs': clarify error message
+ * vty: clarify EGPRS Packet Channel Request message support
+ * A-bis: fix logging level mismatch in abis_nm_rcvmsg_fom()
+ * bsc_subscr_find_or_create_by_{imsi,tmsi}(): fix NULL pointer dereference
+ * doc/manuals: regenerate the VTY reference file
+ * gsm_data: cosmetic: mark argument of is_*_bts() as const
+ * bts_unknown: fix: properly initialize the feature vector
+ * bts_sysmobts: fix: properly zero-initialize the feature vector
+ * bsc_bts_alloc_register(): fix possible NULL-pointer dereference
+ * handover_test: use 'unknown' BTS type instead of 'sysmobts'
+ * abis_nm: cosmetic: add curly braces to complex 'if' statements
+ * abis_nm: cosmetic: use sizeof() for printing buffer size
+ * abis_nm: fix: properly truncate feature vector reported by BTS
+ * abis_nm: fix ARFCN list encoding in Set Channel Attributes
+ * abis_nm: fix length indicator in Set Channel Attributes
+ * fix crashes due to OSMO_ASSERT(conn->lchan)
+ * abis_rsl: Mobile Allocation IE in CHANnel ACTIVation shall be empty
+ * system_information: publicly declare generate_cell_chan_list()
+ * system_information: constify bitvec in freq. list encoding API
+ * generate_ma_for_ts(): constify per cell/timeslot ARFCN bit-vectors
+ * gsm_04_08_rr: fix hopping parameters in RR Assignment Command
+ * doc/examples: remove deprecated 'dyn_ts_allow_tch_f'
+ * vty: ensure that all warning messages are prefixed with '%%'
+ * vty: fix missing comma in a warning message
+ * vty: introduce and use GPRS_CHECK_ENABLED() macro
+ * vty: allow enabling freq. hopping regardless of the feature vector
+ * debian/control: change maintainer to the Osmocom team / mailing list
+ * vty: fix copy-pasted 'no gprs control-ack-type-rach' description
+ * fix bsc_sapi_n_reject(): dlci is unsigned, use uint8_t
+ * bsc_subscr_conn_fsm: fix a memleak in rll_ind_cb()
+ * bsc_subscr_conn_fsm: use proper cause values in SAPI N REJECT
+ * abis_rsl: fix IAR Rest Octets in rsl_send_imm_ass_rej()
+ * vty: add a command to clear hopping ARFCN list
+ * gsm_04_08_rr: fix hopping parameters in RR Handover Command
+ * SI Type 4: fix missing CBCH Mobile Allocation IE
+ * generate_ma_for_ts(): use OSMO_BYTES_FOR_BITS() macro
+ * generate_ma_for_ts(): fix: properly encode ARFCN 0 (corner case)
+ * SI Type 4: prevent potential buffer overflow
+ * vty: propagate result of gsm_bts_set_system_infos()
+ * fix bootstrap_rsl(): check result of gsm_bts_trx_set_system_infos()
+ * abis_om2000: check result of gsm_bts_trx_set_system_infos()
+ * vty: clarify NM state owner printed by 'show trx N' command
+ * abis_nm: fix erroneous use of LOGPC() instead of LOGP()
+ * abis_nm: fix msgb memleak in _abis_nm_sendmsg()
+ * abis_nm: LOGPFOH()/DEBUGPFOH(): remove redundant context info
+ * abis_nm: abis_nm_get_ts(): use LOGPFOH() instead of generic LOGP()
+ * abis_nm: use btstype2str() in abis_nm_rcvmsg_manuf()
+ * abis_nm: use DEBUGPFOH() in abis_nm_rx_sw_act_req()
+ * abis_nm: use LOGPFOH()/DEBUGPFOH() in parse_attr_resp_info_unreported()
+ * abis_nm: improve logging message in abis_nm_get_attr()
+ * generate_ma_for_ts(): simplify MA bit-mask computation
+ * vty: fix unreacheable code / wrong check in lchan_act_all_trx()
+ * RSL/BSSAP: fix: properly convert between RSL Link ID and DLCI
+ * vty: add attributes to VTY commands indicating when they apply
+ * bts: move rate counter / stat item definitions from *.h to *.c
+ * main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
+ * vty: add reminder messages about the radio link timeout
+ * gsm_08_08: fix NULL pointer dereference in bsc_cm_update()
+ * NM FSMs: fix DISABLED_NOTINSTALLED -> DISABLED_NOTINSTALLED
+ * vty: add new attribute for vendor-specific commands
+ * abis_rsl: turn rsl_msgb_alloc() a macro and move it to header
+ * power_control: add new structures and default parameters
+ * power_control: add encoding/init API to 'struct gsm_bts_model'
+ * power_control: send default parameters from bootstrap_rsl()
+ * power_control: add encoder for ip.access nanoBTS and OsmoBTS
+ * power_control: make use of MS/BS parameters in RSL messages
+ * power_control: add VTY commands for per-BTS configuration
+ * power_control: add VTY command for re-sending default parameters
+ * power_control: add VTY command to set static / maximum BS Power
+ * power_control: reflect MS/BS Power difference in the VTY prompt
+ * vty: cosmetic: make all 'struct cmd_node' definitions static
+ * power_control: encoding of H_REQAVE and H_REQT for ip.access
+ * power_control: fix swapped lower/upper RxQual threshold values
+ * power_control: enable dynamic MS power control for osmo-bts
+ * power_control: vty: some commands are not vendor specific
+ * vty: fix NULL-pointer dereference in cfg_bts_rep_dl_facch()
+ * power_control: vty: do not print 'no (rxlev-avg|rxqual-avg)'
+ * vty: join UL/DL SACCH repetition commands together
+ * vty: fix wrong attributes for UL/DL ACCH repetition commands
+ * ericsson_rbs2000: fix unreachable code in inp_sig_cb()
+ * power_control: add documentation on available configuration params
+ * manuals: fix a duplicate line in the description of DTXu
+ * manuals: fix a typo: s/DTS/DTX/
+ * power_control: cosmetic changes and fixes to the documentation
+ * power_control: add increase / reduce step size recommendations
+ * vty: use 'const' for *nsvc in config_write_bts_gprs()
+ * vty: fix writing empty IP address for unconfigured NSVCs
+ * power_control: fix: properly initialize per-lchan BS power
+ * vty: fix 'codec-list' command: check all given arguments first
+ * gsm_data: return early if MS Power class remains the same
+ * assignment_fsm: assert the result of conn_get_bts()
+ * power_control: check BTS model in cfg_power_ctrl_avg_osmo_ewma()
+ * power_control: enable Uplink DPC by default if format is known
+ * power_control: make P_CON_INTERVAL parameter configurable
+ * abis_nm: enrich debug messages with contextual info
+
+ [ Pau Espin Pedrol ]
+ * bssap: Avoid logging error if no optional Global Call Ref IE received
+ * bsc: Allow setting negative nominal tx power through VTY
+ * Avoid selecting channels from administratively locked trx
+ * bsc_main: Improve log line on Lost E1 link
+ * bsc_main: Use LOG_TRX in log line
+ * ipaccess_sign_link_up: Log sign_link type
+ * Use OSMO_FD_* instead of deprecated BSC_FD_*
+ * gsm_data.h: Drop duplicated include stdint.h
+ * Fix trailing whitespace in several files
+ * ipaccess_nanobts: Log ipaccess_sign_link_down event
+ * Move struct gsm_bts: gsm_data.* => bts.*
+ * bts: Drop duplicated function to get trx by number
+ * Move struct gsm_bts_trx: gsm-data.* => bts_trx.*
+ * Move gsm_bts_{trx_}set_system_infos APIs to bts{_trx}.*
+ * Move acc_ramp_init inside gsm_bts_alloc
+ * configure.ac: Fix trailing whitespace
+ * doc: Copy {bsc,bts}.adoc from osmo-gsm-manuals
+ * {bts,bsc}.adoc: Drop deprecated OsmoNITB references
+ * rename files acc_ramp.* -> acc.c*
+ * Introduce support for ACC subset rotation
+ * Introduce support for ACC ramping during whole BTS life cycle
+ * Support setting rt-prio and cpu-affinity mask through VTY
+ * Change default SCTP conn NULL->127.0.0.1 to localhost->localhost
+ * ctrl: Fix CTRL TRAP for {msc.X,msc_)connection_status not sent
+ * acc_test: Print allowed ACC from t2 and t3
+ * acc: Fix ACC rotate barring highest ACCs too quickly during wraparound
+ * tests: acc_test: Test more rotating scenarios
+ * Allow storing IPv6 address strings in BSSAP structs
+ * vty: Hide show running-config ACC ramping params if not enabled
+ * Fail on invalid IP addresses passed to IPACC MDCX
+ * Fix creating MGCP proxy socket if MGW listens on an IPv6 address
+ * lchan_rtp_fsm: Deferr IPACC MDCX after BTS side MGCP MDCX
+ * oml: Fix premature Opstart to Radio Carrier
+ * bssap: Use new DTAP DLCI helper fields from libosmocore
+ * abis_nm: Log Rx Change Administrative State ACK
+ * abis_nm: Log no state change detected
+ * abis_nm: Remove duplicated log line
+ * cosmetic: tests/ctrl_test_runner.py: Fix trailing whitespace
+ * ipa oml: tx OPSTART after unlocking, not before
+ * contrib/jenkins: Enable parallel make in make distcheck
+ * Set all NM OML objects to Locked by default
+ * Introduce NM BTS Site Manager FSM
+ * Introduce NM BTS FSM
+ * Introduce NM BaseBand Transceiver FSM
+ * Introduce NM RadioCarrier FSM
+ * Introduce Radio Channel FSM
+ * OML: Stay compatible with older osmo-bts versions
+ * abis_nm: Log Opstart NACK with error loglevel
+ * ipa: Fix use of null pointer in log macro
+ * nm_channel_fsm: Fix innocuous transition not permitted log error
+ * main: generate coredump and exit upon SIGABRT received
+ * ipaccess-proxy: generate coredump and exit upon SIGABRT received
+ * Store GPRS MOs directly under BTS SiteMgr object
+ * nm_bts_sm_fsm: Fix peer_has_no_avstate_offline not applied for nanobts
+ * Introduce NM GPRS NSE FSM
+ * Introduce NM GPRS CELL FSM
+ * abis_nm: Simplify param passing to abis_nm_rx_get_attr_resp()
+ * Handle BTS/BBTRANSC Get Attributes (Ack) in NM FSMs
+ * Fix typo in function nanobts_attr_nsvc_get
+ * oml: Delay configuring NSVC until BTS features are negotiated
+ * gsm_lchan_name: assert on NULL lchan
+ * Use rest_octets functionalities from libosmocore
+ * gitignore: Ignore *~
+ * doc: handover: Fix malformed table
+ * Introduce Neighbor Resolution Service
+ * cosmetic: doc: wrap line too long
+ * cosmetic: bts-sysmo: Fix whitespace indentation
+ * Allow configuring SI13 CCN_ACTIVE bit from VTY, enable by default on osmo-bts
+ * Introduce VTY cmd to configure Alpha in SI13
+ * Move bts_ident_key to neighbor_ident.c
+ * Fix neigh resolution service on local neighbours
+ * SI13: Enable sending GPRS Cell Options Extension Information on GPRS-only BTS
+ * tests: Explicitly drop category from log
+ * tests: Replace deprecated API log_set_print_filename
+
+ [ Oliver Smith ]
+ * tests/Makefile.am: allow running only one VTY test
+ * osmo-bsc/bsc_vty: set default gprs cell bvci to 2
+ * osmo-bsc/bsc_vty: fail on get_amr_from_arg error
+ * osmo-bsc-minimal.cfg: fix codec-list
+ * main: exit on mutually exclusive codecs settings
+ * abis_nm.c: rx_fail_evt_rep: fix sd.bts
+ * abis_nm: move fail report parsing to extra func
+ * VTY: add show bts failure report
+ * VTY: let all descriptions end in \n
+ * VTY: regenerate bsc_vty_reference.xml
+ * timers: T->X: 23002, 23004, 23005, 23006
+ * contrib: import RPM spec
+ * contrib: integrate RPM spec
+ * Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
+ * lchan_fsm, lchan_rtp_fsm: make all timers configurable
+ * contrib/jenkins: don't build osmo-gsm-manuals
+ * configure.ac: set -std=gnu11
+
+ [ Neels Hofmeyr ]
+ * manual: add "Multiple Instances" section, akin to other manuals
+ * manual: add SCCP/M3UA section from common chapters
+ * manual: add SCCPlite section
+ * manual: fix config example typo 'msc-addr'
+ * manuals: update bsc_vty_reference.xml
+ * deprecate 'msc' / 'ip.access rtp-base <port>'
+ * cosmetic: put comment back at proper place in bsc_vty.c
+ * drop IMSI filter and libfilter completely
+ * drop CC 'local-prefix' feature
+ * code cleanup: absorb complete_layer3() into bsc_compl_l3()
+ * drop all BSC originated USSD notification features
+ * doc/examples: remove deprecated ussd text config
+ * drop MSC types "local" vs "normal"
+ * flatten: move network->bsc_data->* to network->*
+ * CTRL: determine MSC connection status from RESET-ACK, not AS_ACTIVE
+ * create only one SCCP user per SCCP instance
+ * cosmetic: tweak rc type of is_cm_service_for_emerg()
+ * is_cm_service_for_emerg(): return false, not 0
+ * gsm0408_test.c: drop test_mi_functionality()
+ * refactor bsc_find_msc()'s round-robin
+ * add osmo-bsc --vty-ref-xml: dump VTY ref XML to stdout
+ * manuals: generate vty reference xml at build time
+ * osmo-bsc main: exit on cmdline option error
+ * remove extract_sub(), add bsc_subscr_find_or_create_by_mi()
+ * use osmo_mobile_identity API everywhere
+ * tweak log category for Compl L3 error
+ * MSC pooling: make NRI mappings VTY configurable
+ * MSC pooling: implement NAS node selection by NRI from TMSI
+ * MSC pooling: LU: ignore TMSI NRI from a different PLMN
+ * MSC pooling: add 'no allow-attach' for MSC off-loading
+ * mscpool: add user manual chapter
+ * vty: add 'mscpool roundrobin next' for ttcn3 tests
+ * log: add MSC number to bsc_subscr_conn_fsm id
+ * debug: log about matching Paging Response to earlier Paging
+ * MSC pooling: add rate counters
+ * merge files: absorb osmo_bsc_vty.c into bsc_vty.c
+ * vty: hide 'mscpool roundrobin next'
+ * fix segfault introduced by recent segfault fix
+ * create ASP+AS only once per cs7 instance
+ * si2quater: fix budget calculation for multiple EARFCNs
+ * SI2quater: allow storing 48 EARFCNs
+ * RR Release Cell selection IE: fix repeated EARFCNs encoding
+ * RR Channel Release: pass Cause code from BSSMAP Clear to the BTS
+ * propagate RSL error cause codes to RR Channel Release cause
+ * fix CBSP server: actually open the srv_link
+ * handover_test.c: add test case 29: TCH/F -> TCH/H
+ * handover_test.c: typo s/more/less in test 19 description
+ * hodec2: fix logging of requirements flags
+ * hodec2 congestion: consider only congested pchan types
+ * hodec2: do not keep candidates with zero requirements met
+ * hodec2: log requirements of best candidate, log rxlev in dBm
+ * cosmetic: bscon: use GSM48_RR_CAUSE_NORMAL instead of 0
+ * gscon timeout: use proper cause code for lchan release
+ * debug log: add RR Release cause code to the log
+ * info, error log: show MSC nr for new conn
+ * mscpool: fix refcount leak for unusual case of no bts
+ * CBSP: on RESET, also clear the etws_timer, and stop ETWS PN broadcast
+ * CBSP: log CBSP RESET on NOTICE
+ * CBSP: rewrite the CBSP link setup and 'cbc' VTY section
+ * CBSP: add local bind to client mode
+ * cosmetic: use local var in handover_start_intra_bsc()
+ * cosmetic: dissolve error-goto with single source in handover_start()
+ * ho counters: count invalid target cell as 'error', not 'no_channel'
+ * fix 'handover:*' counters: add missing / move increments
+ * fix 'handover:*' counters: remove bogus increments
+ * fix HO inter-BSC-IN target bts for counters
+ * add {BTS,BSC}_CTR_INTER_BSC_HO_OUT_FAILED for RR HO Failure
+ * bssap: do not send a Clear Request after a Clear Command
+ * handover_fsm: signal Clear from gscon, for proper HO result counts
+ * handover: fix detection for ambiguous HO neighbor ident
+ * CBSP: fix link startup when enabled in config file
+ * drop some unused members and function decls
+ * CBSP: adjust manual to reflect new 'cbc' VTY config
+ * CBSP VTY: re-add legacy cbc config for backwards compat
+ * dissolve bsc_grace_paging_request()
+ * drop bsc_subscr.lac
+ * drop unused Tdef for 992427
+ * add timer.vty
+ * clean up timer definitions: introduce groups, move some T to X
+ * tests: add missing *.vty to EXTRA_DIST
+ * log MSC nr for opening new A conn
+ * remove unused signature gsm48_handle_paging_resp()
+ * gscon_bssmap_clear(): guard against NULL msc
+ * compl l3: separate paging handling from bsc_scan_bts_msg()
+ * compl l3: parse Mobile Identity once
+ * compl l3: cosmetics around Create Layer 3
+ * compl l3: allocate conn in gsm_08_08.c, not gsm_04_08_rr.c
+ * compl l3: populate conn's bsc subscr from MI
+ * compl l3: move Paging Response handling out of bsc_find_msc()
+ * compl l3: move all message parsing out of bsc_find_msc()
+ * refactor paging: introduce bsc_paging_params
+ * refactor paging: introduce bsc_paging_start()
+ * refactor paging: add bsc_subscr to bsc_paging_params
+ * LCS: add paging reason, return in paging_request_stop()
+ * introduce osmo_use_count for bsc_subscr
+ * bsc_subscr_name: print both IMSI and TMSI
+ * cosmetic: fix naming of GSCON_EV_A_CONN_REQ -> GSCON_EV_MO_COMPL_L3
+ * LCS: implement re-use of existing A-interface conn
+ * LCS: SCCP next conn id: prepare Lb-interface
+ * LCS: implement the bulk of Location Services
+ * LCS: allow RSL EST IND during GSCON_ST_ACTIVE
+ * remove unused osmo_bsc_reset.h
+ * add doc/location_services_ta.msc
+ * BSSMAP RESET: generalize a_reset FSM
+ * BSSMAP RESET: move cancel-paging call to osmo_bsc_sigtran_reset()
+ * BSSMAP RESET: move RESET-ACK into reset fsm
+ * BSSMAP RESET: tweak logging
+ * BSSMAP RESET: also accept conn cfm events during ST_DISC
+ * LCS: disable Lb interface by default, add vty 'smlc' / 'enable'
+ * add smlc.vty test
+ * LCS: add proper BSSMAP-LE RESET re-using new generalized reset FSM
+ * LCS: Lb startup: no need to re-use existing SCCP user
+ * drop features 'core-location-area-code' and 'core-cell-identity'
+ * manual: describe LCS and Lb interface
+ * fix missing RR release when there is no MSC
+ * add fixme comment for OS#3833
+ * minor code dup: smlc_set_cs7_instance()
+ * handover vty doc: explain rxqual values
+ * handover_test: fix comment
+ * handover_test prep: move arfcn into create_bts()
+ * handover_test prep: move generate_si() into create_bts()
+ * handover_test prep: allow arbitrary timeslots in create_bts() code
+ * handover_test prep: allow configuring several trx in create_bts()
+ * handover_test prep: rename 'create-bts' to 'create-n-bts'
+ * handover_test cosmetic: eliminate bts array and bts_num from main()
+ * handover_test: allow arbitrary timeslot config and multiple TRX
+ * handover test: fix dyn ts: set pchan_is on act / rel
+ * handover test: add 'expect-ts-use' to clarify tests
+ * handover_test: add 'set-ts-use'
+ * handover_test: change 'meas-rep' params to bts-trx-ts-ss nr
+ * handover_test: drop secondary array of lchans
+ * handover test: add test 30: de-congest TCH/F by moving to dyn TS TCH/H
+ * handover_test: add test 31: TCH/H: re-use dyn TS
+ * fix TCH/H allocation: use half occupied dyn TS instead of switching more dyn TS
+ * hodec 2: prep: common pick_better_lchan_to_move() function
+ * hodec 2: favor moving dyn TS
+ * handover test 30: play through filling up all lchans
+ * handover_test: add test 32: half used TCH/H on dyn TS
+ * fix AMR start-mode auto: reflect proper ICMI in MultiRate Config IE
+ * AMR start-mode: send proper smod bits
+ * AMR start-mode auto: send smod bits as zero
+ * ctrl_test_runner.py: remove per-test startup delay
+ * hodec 2: prefer moving TCH/H from half-used dyn TS
+ * handover_test: implement as VTY shell
+ * handover_test: log sending ho complete
+ * handover_test: send ho detection
+ * handover_test: adjust logging for reproducability
+ * handover_test: set a fake osmo_mgcpc_ep_ci pointer
+ * handover_test: rename test scripts from numbers to names
+ * handover_test: gitignore
+ * tweak handover_tests.sh
+ * handover_test: cosmetic cfg simplifications in 5 tests
+ * handover_test: fix test_congestion.ho_vty meas-rep
+ * handover_test.c: properly release lchans in set-ts-use
+ * hodec2: cosmetic: add dBm unit to rxval logging
+ * hodec2: code dup / cosmetics
+ * handover_test: add 'meas-rep repeat N'
+ * handover_test: add test_congestion_favor_best_target_rxlev.ho_vty
+ * handover_test: add AMR TCH/H->/F tests, showing a bug
+ * handover_test: show ineffective intra-cell choice
+ * handover_test: add test_congestion_intra_vs_inter_cell.ho_vty
+ * hodec2: clarify current and target rxlev per candidate
+ * hodec2: cosmetic: clarify afs_bias, simplify pick_better_lchan_to_move()
+ * hodec2: fix candidate choices in congestion check
+ * handover_test: include ack-chan in expect-chan
+ * handover_test: safeguard against unhandled chan req
+ * handover_test: saner chan act handling
+ * handover_test: saner ho request handling
+ * handover_test: vty echo
+ * handover_test: show a bug: add test_congestion_no_oscillation.ho_vty
+ * hodec2: cosmetic: clarify ho_candidate.{current,target}
+ * hodec2: reduce check_requirements() args
+ * hodec2: add ho_candidate.{current,target}.free_tch
+ * handover_test: add lchan wildcards to meas-rep cmd
+ * handover_test: show bug: add test_balance_congestion_tchf_tchh.ho_vty
+ * hodec2: fix congestion oscillation bug
+ * handover_test: add test_balance_congestion_by_percentage.ho_vty
+ * hodec2: to balance congestion, use overload percent
+ * handover_test: enhance test_balance_congestion_by_percentage.ho_vty
+ * handover_tests.sh: update stdout/stderr only on capital -U arg
+ * fix for test_dyn_ts_favor_moving_half_used_tch_h.ho_vty
+ * cosmetic: in a ho test, use '*' instead of pdch
+ * show bug: add test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty
+ * fixate test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty
+ * hodec2: fix congestion resolution on dyn TS
+ * add test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty
+ * lchan_avail(): omit logging for handover decision 2
+ * show bug: add test_dyn_ts_balance_congestion.ho_vty
+ * hodec2: fix congestion balancing on dyn TS
+ * show bug: add test_dyn_ts_amr_tch_{f,h}_to_{h,f}_congestion_assignment_2.ho_vty
+ * hodec2: fix intra-cell congestion balancing with source lchan on dyn TS
+ * lchan assignment when RTP is set up: don't break on Mode Modify
+ * rename lchan->rqd_ta to last_ta
+ * lchan activation: indicate whether TA is known
+
+ [ Keith ]
+ * Meas Tools: Avoid OSMO_ASSERT due to uninitialised logging.
+ * Meas Tools: Avoid unusable terminal in case of error on osmo_sock_init
+ * Meas Tools, Avoid compiler format warnings
+ * Disallow changing the type of an existing BTS from the vty
+
+ [ Eric ]
+ * configure.ac: fix libtool issue with clang and sanitizer
+
+ [ Sylvain Munaut ]
+ * om2k: Add definitions for the TG object
+ * om2k: Allow TG and MCTR to be manipulated via VTY
+ * om2k: Allow the CON configuration request to be triggered via VTY
+ * om2k: Add VTY command to allow TX of arbitrary message for testing
+ * om2k: Don't use slashes in FSM IDs and use dashes instead
+ * om2k: Fix TS channel config payload for non-superchannel case
+ * om2k: Fix type of msg_type in abis_om2k_tx_simple
+ * om2k: Acknowledge the HW Infos Reports
+ * om2k: Acknowledge the unknown MCTR messages we get from time to time
+ * om2k: Properly name message 0x0136, found to be MCTR Statistics Report
+ * om2k: Use the "from config" TS config to setup OM objects
+ * om2k: Dispatch TS_EV_OML_READY to TS FSM only when it's actually ready
+ * om2k: Fix the frequency specifier for TX/RX/TS conf requests
+ * bts_ericsson_rbs2000: Init all the TRX, not just C0
+ * bts_ericsson_rbs2000: Whitelist the E1d input driver
+ * om2k: Add option to limit OML version during negotiation
+ * om2k: Rename MCTR config request constants for consistency
+ * om2k: Add support for MCTR configuration
+ * om2k: Properly update the 'fake' 12.21 states using OM2000 status
+ * om2k: Wait for OM TRX links to stabilize before trying to bring up TRX
+ * chan_alloc: Don't re-invent trx_is_usable and use existing helper
+ * gsm_data: Update trx_is_usable for ericsson BTS
+ * om2k: Fix invalid use of linked list when building hopping freq list
+ * bts_nokia_site: Fake 12.21 OM objet state as "OK" when boot is done
+
+ [ Alexander Chemeris ]
+ * ctrs: Correctly count load total for dynamic timeslots.
+ * stats: Report per channel type load to statsd counters.
+ * stats: Remove dots from the end of stats descriptions.
+ * Fix indent whitespace and log message.
+ * stats: Fix stat group index for BTS stats.
+ * Fix a comment for the handle_unitdata_from_msc() function.
+ * chan_alloc: Add comments for the *_chan_load() functions.
+ * osmo_bsc_sigtran: Fix a SSCP-> SCCP typo in a comment
+ * stats: Add a stats gauge for the MSC links count.
+ * stats: report a number of configured BTS to a stats gauge.
+ * stats: Add counters for received BSSMAP messages.
+ * stats: Export connected OML/RSL links count per BTS.
+ * a_reset: Rename SIGTRAN connection to BSSMAP MSC assocation in log messages
+ * stats: Fix Rx DTAP error stat description
+ * bsc_subscr_conn_fsm: Fix a typo in the comment life->live
+ * lchan: Allow transition from BORKEN state to WAIT_RF_RELEASE_ACK
+ * timeslot_fsm: Allow PDCH_ACT_ACK in BORKEN state.
+ * stats: Only dereference a connection pointer after checking for NULL.
+ * handover_test: Properly allocate MSC data struct.
+ * stats: Add counters for Tx BSSMAP messages.
+ * stats: Rename BSSMAP Rx message counters to match Tx ones.
+ * bsc_vty: Coding style fix - brackets around a complex if/else
+ * log: Fix "Paging request failed" logging level
+ * log: Adjust "new SIGTRAN connection" logging level
+ * bssmap: Ignore repeated BSSMAP RESET ACK messages.
+ * log: Demote "CHAN RQD: reason" to INFO
+ * log: Demote "SAPI=%u ESTABLISH CONFIRM" message from ERROR to DEBUG.
+ * borken: Recover from more TS borken states.
+ * stats: Add counters and gauges for BORKEN lchans/TS
+ * stats: Add a BTS/BSC counter PAGING_NO_ACTIVE_PAGING.
+ * stats: Correctly count lchans under BORKEN TS.
+ * bssap: Handle BSSMAP CONFUSION message.
+ * Fix crash in bsc_patch_mm_info()
+ * bsc_patch: Don't even parse MM INFO if TZ patching is not enabled.
+ * bsc_subscr_conn_fsm: Fix crash in gscon_forget_lchan()
+ * stats: Count paging requests flushed due to MSC Reset.
+ * Return 0 from gsm0408_rcvmsg() if SCCP link is already closed.
+ * paging: Remove obsolete comment.
+ * chan_alloc: Fix typo in a comment.
+ * timeslot_fsm: Name TS FSM instances on allocation.
+
+ [ Daniel Willmann ]
+ * osmo-bsc: Use designated initializer in bts_stat_desc
+ * gsm_data.h: Remove period at end of counter description
+ * abis_rsl: Count successful channel requests
+ * Count assignment rates per BTS as well
+ * Remove punctuation in counter description
+ * Count handover per BTS as well as per BSC
+ * Count intra-cell and intra-bsc handover separately
+ * Add bts counters to count BTS events where we don't have a bts
+ * configure.ac: Require python3 for ext_tests
+
+ [ Alexander Couzens ]
+ * gsm_data: always set spare bits in channel description
+ * gsm 04.08: encode the LTE neighbors measurement bandwindth in Channel Release
+ * gsm 04.08: correct calculate the Cell Selection Indicator after release of all TCH and SDCCH
+ * osmo-bsc: fix a crash when receiving a RACH LOAD IND with 0
+ * abis_nm: abis_nm_perform_test: fix a potential null deref
+ * pcu_sock: use tn as variable name to improve readability
+ * pcuif_proto: protocol 9: add missing fields
+ * oml: encode IPv6 NSVC using the new OML attribute NM_ATT_OSMO_NS_LINK_CFG
+ * pcuif_proto: version 10: add frequency hopping parameters
+ * pcuif_proto: version 10: add support for IPv6 NSVCs
+ * bsc_vty: cfg_bts_gprs_nsvc_rip: add missing breaks
+ * bsc_vty: parse the return code to make coverity happy
+ * bts_ipaccess_nanobts: check if msgb_alloc fails
+ * sysmobts: expect feature IPV6_NSVC
+ * Introduce NM GPRS NSVC FSM
+
+ [ Michael Iedema ]
+ * stats: Add granularity to SDCCH/TCH/LU activity.
+ * stats: Add granularity to chan:rf_fail stat.
+ * cosmetic: shorten deref chains where possible
+ * stats: add SIGN/SPEECH assignment subcategories
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 18:43:04 +0100
+
osmo-bsc (1.6.0) unstable; urgency=medium
[ Philipp Maier ]
diff --git a/debian/control b/debian/control
index d2ecea0ad..a135119ee 100644
--- a/debian/control
+++ b/debian/control
@@ -12,13 +12,12 @@ Build-Depends: debhelper (>=9),
python3-minimal,
libcdk5-dev,
libtalloc-dev,
- libosmocore-dev (>= 1.4.0),
- libosmo-sccp-dev (>= 0.10.0),
- libosmo-sigtran-dev (>= 0.10.0),
- libosmo-abis-dev (>= 0.6.0),
- libosmo-netif-dev (>= 0.6.0),
- libosmo-mgcp-client-dev (>= 1.6.0),
- osmo-gsm-manuals-dev
+ libosmocore-dev (>= 1.5.0),
+ libosmo-sigtran-dev (>= 1.4.0),
+ libosmo-abis-dev (>= 1.1.0),
+ libosmo-netif-dev (>= 1.1.0),
+ libosmo-mgcp-client-dev (>= 1.8.0),
+ osmo-gsm-manuals-dev (>= 1.1.0)
Standards-Version: 3.9.8
Vcs-Git: git://git.osmocom.org/osmo-bsc.git
Vcs-Browser: https://git.osmocom.org/osmo-bsc/
@@ -28,6 +27,7 @@ Package: osmo-bsc
Architecture: any
Multi-Arch: foreign
Depends: ${misc:Depends}, ${shlibs:Depends}
+Recommends: osmo-mgw
Description: OsmoBSC: Osmocom's Base Station Controller for 2G circuit-switched mobile networks
Package: osmo-bsc-dbg
diff --git a/doc/Makefile.am b/doc/Makefile.am
index d3a04c62f..08558e5b5 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -13,6 +13,7 @@ msc: \
$(builddir)/handover-inter-bsc-out.png \
$(builddir)/handover-inter-bsc-in.png \
$(builddir)/mgw-endpoint.png \
+ $(builddir)/location_services_ta.png \
$(NULL)
dot: \
diff --git a/doc/assignment-fsm.dot b/doc/assignment-fsm.dot
index c2181535b..4eb8d9805 100644
--- a/doc/assignment-fsm.dot
+++ b/doc/assignment-fsm.dot
@@ -12,6 +12,7 @@ labelloc=t; label="Assignment FSM"
gscon2 [label="conn FSM",shape=box3d]
lchan [label="lchan FSM\n(new lchan)",shape=box3d]
old_lchan [label="old lchan",shape=box3d]
+ lchan2 [label="lchan FSM",shape=box3d]
bssap [label="osmo_bsc_bssap.c",shape=box]
@@ -22,8 +23,7 @@ labelloc=t; label="Assignment FSM"
bssap -> gscon [label="GSCON_EV_ASSIGNMENT_START\ndata=struct assignment_request",style=dotted]
gscon -> WAIT_LCHAN_ACTIVE [label="assignment_fsm_start()",style=dotted]
- gscon -> WAIT_LCHAN_ESTABLISHED [label="assignment_fsm_start()\n(mode modify)",style=dotted]
- WAIT_LCHAN_ACTIVE -> lchan [label="lchan_activate()\nFOR_ASSIGNMENT",style=dotted]
+ WAIT_LCHAN_ACTIVE -> lchan [label="lchan_activate()\nFOR_ASSIGNMENT",style=dotted]
lchan -> WAIT_LCHAN_ACTIVE [label="ASSIGNMENT_EV_\nLCHAN_\nACTIVE,ERROR",style=dotted]
lchan -> WAIT_LCHAN_ESTABLISHED [label="ASSIGNMENT_EV_\nLCHAN_\nESTABLISHED,ERROR",style=dotted]
@@ -40,4 +40,10 @@ labelloc=t; label="Assignment FSM"
WAIT_MGW_ENDPOINT_TO_MSC -> gscon2 [label="gscon_connect_\nmgw_to_msc()",style=dotted]
gscon2 -> WAIT_MGW_ENDPOINT_TO_MSC [label="ASSIGNMENT_EV_\nMSC_MGW_OK",style=dotted]
terminate -> gscon2 [label="GSCON_EV_\nASSIGNMENT_END",style=dotted]
+
+ WAIT_LCHAN_ACTIVE -> WAIT_LCHAN_MODIFIED [label="assignment_fsm_start()\n(mode modify)"]
+ WAIT_LCHAN_MODIFIED -> lchan2 [label="lchan_mode_modify()\nMODIFY_FOR_ASSIGNMENT",style=dotted]
+ lchan2 -> WAIT_LCHAN_MODIFIED [label="ASSIGNMENT_EV_\nLCHAN_\nMODIFIED,ERROR",style=dotted]
+ WAIT_LCHAN_MODIFIED -> WAIT_MGW_ENDPOINT_TO_MSC [label="needs\nvoice\nstream"]
+ WAIT_LCHAN_MODIFIED -> terminate [label="no change\nin voice\nstream"]
}
diff --git a/doc/assignment.msc b/doc/assignment.msc
index fae088f23..5c3bbfbb5 100644
--- a/doc/assignment.msc
+++ b/doc/assignment.msc
@@ -30,7 +30,7 @@ msc {
|||;
lchan abox lchan [label="UNUSED"];
ass box ass [label="conn->assignment.new_lchan = lchan_select_by_chan_mode()"];
- lchan <- ass [label="lchan_activate(FOR_ASSIGNMENT)"];
+ lchan <- ass [label="lchan_activate(ACTIVATE_FOR_ASSIGNMENT)"];
lchan abox lchan [label="WAIT_TS_READY"];
lchan rbox lchan [label="most details omitted, see lchan_fsm and lchan_rtp_fsm diagrams"];
...;
diff --git a/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg b/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg
index b8cd78db0..2258495d0 100644
--- a/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg
@@ -2,7 +2,7 @@ network
network country code 901
mobile network code 70
bts 0
- type sysmobts
+ type osmo-bts
band GSM-1800
location_area_code 23
ipa unit-id 1800 0
diff --git a/doc/examples/osmo-bsc/osmo-bsc.cfg b/doc/examples/osmo-bsc/osmo-bsc.cfg
index 828875d9e..807bd19b7 100644
--- a/doc/examples/osmo-bsc/osmo-bsc.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc.cfg
@@ -19,7 +19,7 @@ network
handover1 maximum distance 9999
periodic location update 30
bts 0
- type sysmobts
+ type osmo-bts
band DCS1800
cell_identity 6969
location_area_code 1
diff --git a/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg b/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg
index 0ecb5fc52..335e23dc5 100644
--- a/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg
@@ -18,7 +18,7 @@ network
handover1 maximum distance 9999
periodic location update 30
bts 0
- type sysmobts
+ type osmo-bts
band DCS1800
cell_identity 0
location_area_code 1
diff --git a/doc/handover-inter-bsc-in-fsm.dot b/doc/handover-inter-bsc-in-fsm.dot
index b52a16d6c..0c6af850e 100644
--- a/doc/handover-inter-bsc-in-fsm.dot
+++ b/doc/handover-inter-bsc-in-fsm.dot
@@ -18,7 +18,10 @@ labelloc=t; label="Handover FSM: Inter-BSC Incoming"
gscon -> HO_ST_WAIT_LCHAN_ACTIVE [label="handover_start_inter_bsc_in()",style=dotted]
HO_ST_WAIT_LCHAN_ACTIVE -> lchan [label="lchan_activate()\nFOR_HANDOVER",style=dotted]
lchan -> HO_ST_WAIT_LCHAN_ACTIVE [label="HO_EV_\nLCHAN_ACTIVE,\n_ERROR",style=dotted,constraint=false]
- HO_ST_WAIT_LCHAN_ACTIVE -> HO_ST_WAIT_RR_HO_DETECT
+ HO_ST_WAIT_LCHAN_ACTIVE -> HO_ST_WAIT_RR_HO_DETECT [label="SCCPlite\nor no voice"]
+
+ HO_ST_WAIT_LCHAN_ACTIVE -> WAIT_MGW_ENDPOINT_TO_MSC [label="AoIP\nhas voice"]
+ WAIT_MGW_ENDPOINT_TO_MSC -> HO_ST_WAIT_RR_HO_DETECT
HO_ST_WAIT_RR_HO_DETECT -> msc2 [label="BSSMAP\nHandover\nAccept\nwith\nRR Handover\nCommand",style=dotted]
msc2 -> old_bsc2 -> old_lchan [label="RR Handover\nCommand",style=dotted]
@@ -31,9 +34,7 @@ labelloc=t; label="Handover FSM: Inter-BSC Incoming"
WAIT_RR_HO_COMPLETE -> WAIT_LCHAN_ESTABLISHED
lchan -> WAIT_LCHAN_ESTABLISHED [label="HO_EV_LCHAN_\nESTABLISHED",style=dotted]
- WAIT_LCHAN_ESTABLISHED -> terminate [label="non-TCH"]
- WAIT_LCHAN_ESTABLISHED -> WAIT_MGW_ENDPOINT_TO_MSC
- WAIT_MGW_ENDPOINT_TO_MSC -> terminate [label="handover_end()"]
+ WAIT_LCHAN_ESTABLISHED -> terminate
terminate -> msc2 [label="BSSMAP Handover\nComplete\n/ Failure",style=dotted,constraint=false]
err [label="on error",shape=box,style=dashed]
diff --git a/doc/handover-inter-bsc-in.msc b/doc/handover-inter-bsc-in.msc
index 9534f908a..fa3d75e87 100644
--- a/doc/handover-inter-bsc-in.msc
+++ b/doc/handover-inter-bsc-in.msc
@@ -14,7 +14,7 @@ msc {
ho abox ho [label="allocate\nHO_ST_NOT_STARTED"];
ho box ho [label="lchan_select_by_chan_mode()"];
ho abox ho [label="HO_ST_WAIT_\nLCHAN_ACTIVE"];
- lchan <- ho [label="lchan_activate(FOR_HANDOVER)"];
+ lchan <- ho [label="lchan_activate(ACTIVATE_FOR_HANDOVER)"];
lchan rbox lchan [label="(most details omitted, see lchan_fsm diagrams)"];
...;
diff --git a/doc/handover-intra-bsc-fsm.dot b/doc/handover-intra-bsc-fsm.dot
index 7cb0d3cfe..39f309729 100644
--- a/doc/handover-intra-bsc-fsm.dot
+++ b/doc/handover-intra-bsc-fsm.dot
@@ -12,7 +12,7 @@ labelloc=t; label="Handover FSM: Intra-BSC"
invisible -> old_lchan [style=invisible,arrowhead=none]
intra -> WAIT_LCHAN_ACTIVE [label="handover_start()",style=dotted]
- WAIT_LCHAN_ACTIVE -> lchan [label="lchan_activate(FOR_HANDOVER)",style=dotted]
+ WAIT_LCHAN_ACTIVE -> lchan [label="lchan_activate(ACTIVATE_FOR_HANDOVER)",style=dotted]
lchan -> WAIT_LCHAN_ACTIVE [label="HO_EV_\nLCHAN_\nACTIVE,ERROR",style=dotted,constraint=false]
WAIT_LCHAN_ACTIVE -> WAIT_RR_HO_DETECT
WAIT_RR_HO_DETECT -> old_lchan [label="RR Handover\nCommand",style=dotted,constraint=false]
@@ -25,6 +25,4 @@ labelloc=t; label="Handover FSM: Intra-BSC"
lchan -> WAIT_LCHAN_ESTABLISHED [label="HO_EV_LCHAN_\nESTABLISHED",style=dotted]
WAIT_LCHAN_ESTABLISHED -> terminate [label="non-TCH"]
- WAIT_LCHAN_ESTABLISHED -> WAIT_MGW_ENDPOINT_TO_MSC
- WAIT_MGW_ENDPOINT_TO_MSC -> terminate [label="handover_end()"]
}
diff --git a/doc/handover.msc b/doc/handover.msc
index 1a2580a06..9734429ab 100644
--- a/doc/handover.msc
+++ b/doc/handover.msc
@@ -31,7 +31,7 @@ msc {
ho box ho [label="lchan_select_by_type()"];
ho abox ho [label="HO_ST_WAIT_\nLCHAN_ACTIVE"];
- lchan <- ho [label="lchan_activate(FOR_HANDOVER)"];
+ lchan <- ho [label="lchan_activate(ACTIVATE_FOR_HANDOVER)"];
lchan rbox lchan [label="(most details omitted, see lchan_fsm diagrams)"];
...;
diff --git a/doc/lchan.msc b/doc/lchan.msc
index af9a59b52..b35c31046 100644
--- a/doc/lchan.msc
+++ b/doc/lchan.msc
@@ -34,11 +34,11 @@ msc {
...;
ts -> lchan [label="LCHAN_EV_TS_READY"];
lchan abox lchan [label="LCHAN_ST_\nWAIT_ACTIV_ACK"];
- --- [label="IF FOR_MS_CHANNEL_REQUEST"];
+ --- [label="IF ACTIVATE_FOR_MS_CHANNEL_REQUEST"];
ms <= lchan [label="RSL Chan Activ (RSL_ACT_INTRA_IMM_ASS)"];
- --- [label="ELSE: FOR_ASSIGNMENT"];
+ --- [label="ELSE: ACTIVATE_FOR_ASSIGNMENT"];
ms <= lchan [label="RSL Chan Activ (RSL_ACT_INTRA_NORM_ASS)"];
- --- [label="ELSE: FOR_HANDOVER"];
+ --- [label="ELSE: ACTIVATE_FOR_HANDOVER"];
ms <= lchan [label="RSL Chan Activ (RSL_ACT_INTER_ASYNC)"];
--- [label="END"];
...;
@@ -47,12 +47,12 @@ msc {
ms => lchan [label="RSL Chan Activ ACK"];
lchan box lchan [label="lchan_fsm_post_activ_ack()"];
- --- [label="IF FOR_MS_CHANNEL_REQUEST"];
+ --- [label="IF ACTIVATE_FOR_MS_CHANNEL_REQUEST"];
ms <= lchan [label="RR Immediate Assignment"];
- --- [label="ELSE: FOR_ASSIGNMENT"];
+ --- [label="ELSE: ACTIVATE_FOR_ASSIGNMENT"];
lchan rbox lchan [label="dispatch\nASSIGNMENT_EV_\nLCHAN_ACTIVE\n(see Assignment FSM diagrams)"];
ms <= lchan [label="RR Assignment Command"];
- --- [label="ELSE: FOR_HANDOVER"];
+ --- [label="ELSE: ACTIVATE_FOR_HANDOVER"];
lchan rbox lchan [label="dispatch\nHO_EV_LCHAN_ACTIVE\n(see Handover FSM diagrams)"];
--- [label="END"];
@@ -107,12 +107,12 @@ msc {
ms => lchan [label="RLL Establish Ind"];
lchan abox lchan [label="LCHAN_ST_\nESTABLISHED"];
lchan box lchan [label="lchan_on_fully_established()"];
- --- [label="IF FOR_MS_CHANNEL_REQUEST"];
+ --- [label="IF ACTIVATE_FOR_MS_CHANNEL_REQUEST"];
ms note lchan [label="No action required. The MS will have sent an L3 message in the RLL
Establish Ind and is then free to dispatch DTAP."];
- --- [label="ELSE: FOR_ASSIGNMENT"];
+ --- [label="ELSE: ACTIVATE_FOR_ASSIGNMENT"];
lchan rbox lchan [label="dispatch\nASSIGNMENT_EV_\nLCHAN_ESTABLISHED\n(see Assignment FSM diagrams)"];
- --- [label="ELSE: FOR_HANDOVER"];
+ --- [label="ELSE: ACTIVATE_FOR_HANDOVER"];
lchan rbox lchan [label="dispatch\nHO_EV_LCHAN_ESTABLISHED\n(see Handover FSM diagrams)"];
--- [label="END"];
...;
@@ -202,11 +202,11 @@ msc {
ms rbox mgwep [label="On any error"];
|||;
- --- [label="IF FOR_MS_CHANNEL_REQUEST"];
+ --- [label="IF ACTIVATE_FOR_MS_CHANNEL_REQUEST"];
ms <= lchan [label="RR Immediate Assign Reject"];
- --- [label="ELSE: FOR_ASSIGNMENT"];
+ --- [label="ELSE: ACTIVATE_FOR_ASSIGNMENT"];
lchan rbox lchan [label="dispatch\nASSIGNMENT_EV_\nLCHAN_ERROR\n(see Assignment FSM diagrams)"];
- --- [label="ELSE: FOR_HANDOVER"];
+ --- [label="ELSE: ACTIVATE_FOR_HANDOVER"];
lchan rbox lchan [label="dispatch\nHO_EV_LCHAN_ERROR\n(see Handover FSM diagrams)"];
--- [label="END"];
|||;
diff --git a/doc/location_services_ta.msc b/doc/location_services_ta.msc
new file mode 100644
index 000000000..e6bef38ef
--- /dev/null
+++ b/doc/location_services_ta.msc
@@ -0,0 +1,49 @@
+msc {
+ hscale="2";
+
+ ms[label="MS/BTS"],bsc[label="BSC"],smlc[label="SMLC"],__msc[label="MSC"];
+
+ ||| [label="Location Services (LCS): Perform Location Request using TA"];
+
+ |||;
+ --- [label="MS in DEDICATED MODE (currently active)"];
+
+ ms =>> bsc [label="earlier Measurement Report provides Timing Advance"];
+
+ bsc <<= __msc [label="PERFORM LOCATION REQUEST\n3GPP TS 48.008 3.2.1.71"];
+
+ bsc =>> smlc [label="BSSMAP-LE PERFORM LOCATION REQUEST\n3GPP TS 49.031 9.1\nwith BSSLAP APDU = TA Layer3\n3GPP TS 48.071 4.2.8"];
+
+ smlc rbox smlc [label="SMLC uses TA included in TA Layer3"];
+
+ bsc <<= smlc [label="BSSMAP-LE PERFORM LOCATION RESPONSE\n3GPP TS 49.031 9.2"];
+
+ bsc =>> __msc [label="PERFORM LOCATION RESPONSE\n3GPP TS 48.008 3.2.1.72"];
+
+ ...;
+ ...;
+ --- [label="MS in IDLE MODE (not connected)"];
+
+ bsc <<= __msc [label="PERFORM LOCATION REQUEST\n3GPP TS 48.008 3.2.1.71"];
+
+ bsc =>> smlc [label="BSSMAP-LE PERFORM LOCATION REQUEST\n3GPP TS 49.031 9.1"];
+
+ smlc rbox smlc [label="SMLC needs TA information,\nasks BSC via BSSLAP"];
+
+ bsc <<= smlc [label="BSSMAP-LE CONNECTION ORIENTED INFORMATION\n3GPP TS 49.031 9.12\nwith BSSLAP APDU = TA Request\n3GPP TS 48.071 4.2.1"];
+
+
+ ms <<= bsc [label="RR Paging Request\n3GPP TS 48.018 9.1.22-24"];
+
+ ms =>> bsc [label="RSL CHANNEL REQUIRED\n3GPP TS 48.058 8.5.3\nincludes Access Delay (9.3.17) == Timing Advance"];
+
+ ms =>> bsc [label="RR Paging Response\n3GPP TS 48.018 9.1.25"];
+
+ ms <<= bsc [label="RF CHANNEL RELEASE\n3GPP TS 48.058 8.4.14"];
+
+ bsc =>> smlc [label="BSSMAP-LE CONNECTION ORIENTED INFORMATION\n3GPP TS 49.031 9.12\nwith BSSLAP APDU = TA Response\n3GPP TS 48.071 4.2.2"];
+
+ bsc <<= smlc [label="BSSMAP-LE PERFORM LOCATION RESPONSE\n3GPP TS 49.031 9.2"];
+
+ bsc =>> __msc [label="PERFORM LOCATION RESPONSE\n3GPP TS 48.008 3.2.1.72"];
+}
diff --git a/doc/manuals/aoip-mgw-options-docinfo.xml b/doc/manuals/aoip-mgw-options-docinfo.xml
index 080959443..4cec92fa7 100644
--- a/doc/manuals/aoip-mgw-options-docinfo.xml
+++ b/doc/manuals/aoip-mgw-options-docinfo.xml
@@ -7,6 +7,14 @@
Initial version of the proposal for internal discussion.
</revremark>
</revision>
+ <revision>
+ <revnumber>1.0</revnumber>
+ <date>November 2020</date>
+ <authorinitials>Harald Welte</authorinitials>
+ <revremark>
+ Update with changes on what was actually implemented in recent years; change from future to past tense.
+ </revremark>
+ </revision>
</revhistory>
<authorgroup>
@@ -24,7 +32,7 @@
</authorgroup>
<copyright>
- <year>2017</year>
+ <year>2017-2020</year>
<holder>sysmocom - s.f.m.c. GmbH</holder>
</copyright>
diff --git a/doc/manuals/aoip-mgw-options.adoc b/doc/manuals/aoip-mgw-options.adoc
index 124c03fda..62163ccc4 100644
--- a/doc/manuals/aoip-mgw-options.adoc
+++ b/doc/manuals/aoip-mgw-options.adoc
@@ -5,8 +5,8 @@
This document serves as a paper to illustrate the different
configurations of OsmoBSC in terms of integration with BTSs and MSCs.
-The document should accompany us in the 2017 development cycle which
-includes the _death of the NITB_, i.e. the move away from OsmoNITB to
+The document was created ahead of the 2017 development cycle which
+included the _death of the NITB_, i.e. the move away from OsmoNITB to
having OsmoBSC in all configurations, whether with a
proprietary/external MSC or with OsmoMSC.
@@ -24,8 +24,13 @@ such as
=== Classic GSM RAN with E1 based Abis and E1 A
-This configuration was actually never supported by OpenBSC, as E1 BTS
-support was so far for NITB only, but not for OsmoBSC.
+This is how GSM was originally specified when it was introduced: E1/T1
+circuits on all interfaces, no IP anywhere.
+
+This configuration was actually never supported by OpenBSC, as E1
+support was always only on the Abis side (OpenBSC, OsmoNITB and today OsmoBSC).
+
+We never supported A interface over E1. It could be done if there was a need.
[mscgen]
----
@@ -53,11 +58,11 @@ Release 7 of 3GPP included an official specification on how an
interoperable A-over-IP (AoIP) interface shall look like.
As more modern MSCs at operators tend to favor implementing 3GPP AoIP
-rather than the proprietary SCCPlite based A interface, it becomes
+rather than the proprietary SCCPlite based A interface, it became
necessary for OsmoBSC to support this.
At the same time, for compatibility reasons, the classic SCCPlite
-support shall be kept, if possible with reasonable effort.
+support is kept in OsmoBSC as a configuration option.
[mscgen]
----
@@ -65,11 +70,16 @@ include::{srcdir}/mgw/osmo-bsc-new-mgw.msc[]
----
-=== OsmoBSC 2017+: 3GPP AoIP + Abis/E1
+=== OsmoBSC 2020+: 3GPP AoIP + Abis/E1
+
+Since OsmoNITB was deprecated in 2017, and OsmoBSC only supported Abis/IP,
+we temporarily lost the ability to use classic E1 based BTSs. In 2020,
+we re-introduced and re-tested the support of Abis/E1.
-Since OsmoNITB will soon be deprecated, we will use OsmoBSC in all
-Osmocom GSM ntework setups, requiring the support for classic E1/T1
-based BTSs from OsmoBSC.
+Fro the control plane of Abis (RSL, OML) the E1 support via libosmo-abis
+never really ceased to exist. But for the user plane, E1 support had to be
+introduced to osmo-mgw, and osmo-bsc needed to be taught how to configure
+E1 endpoints at the MGW. The related call flow for such setups looks like this:
[mscgen]
----
diff --git a/doc/manuals/chapters/aoip-flows.adoc b/doc/manuals/chapters/aoip-flows.adoc
new file mode 100644
index 000000000..dd9f809c3
--- /dev/null
+++ b/doc/manuals/chapters/aoip-flows.adoc
@@ -0,0 +1,107 @@
+== AoIP message flow examples
+
+The flow diagrams / ladder diagrams of this section are intended to
+provide some examples on how AoIP procedures work. We hope they will be
+useful in understanding the interface better and aid in debugging any
+related issues.
+
+=== AoIP interface bring-up
+
+This Figure shows the exchange of messages of a BSC when it is
+establishing its AoIP interface from scratch, for example because it has
+just been started up. We assume the BSC/CN has already been fully
+brought up, so no SCTP/M3U*A bring-up between MSC and STP is dieplayed.
+
+The diagram shows only one possible scenario.
+
+Depending on the MSC implementation, in between the BSC and the MSC
+there may be either
+
+* a dedicated STP (or multiple replicated STPs)
+* no dedicated STP, as the functionality is implemented inside the MSC
+* an entire SS7 network between BSC and MSC, with multiple STP, SGW,
+ elements switching messages from the BSCs to the MSCs.
+
+The configuration details that need to be known to the BSC at start-up
+time are:
+
+* at SCTP level
+** remote IP addresses to which it should establish a SCTP association
+** SCTP port number for M3UA at the STP
+* at M3UA level
+** routing key (0 for none)
+** local BSC-side SS7 point code
+** remote MSC-side SS7 point code
+
+There possibly may be more configuration details, such as
+
+* multiple local and/or remote IP addresses for SCTP multi-homing
+* a fixed local (BSC side) IP address and/or SCTP port (default:
+ dynamic/random)
+
+.AoIP interface bring-up between BSC and MSC
+[mscgen]
+----
+include::../message-sequences/a_interface_bringup.msc[]
+----
+
+For the purpose of clairty, SCTP-level acknowledgement chunks are not
+shown. Those are automatically generated by the receiver for every
+DATA chunk received in order to confirm its reception and to allow the
+transmitter to re-transmit in case of packet loss.
+
+==== SCTP multi-homing
+
+If SCTP multi-homing is used, the additional IP addresses are typically
+exchanged via additional information elements in the INIT/INIT_ACK
+chunks at connection establishment. They may also change at a later
+point.
+
+==== MSC pooling
+
+If there is MSC pooling configured, there is typically still only one
+M3UA ASP / SCTP association. The different MSCs are addressed on the
+SCCP point-code level. It's the STPs job to route the messages based
+on point codes to the respective MSC.
+
+The BSC will try to establish BSSAP to each of the MSCs in the pool,
+using a separate BSSAP reset procedure to each of the pool members
+point code.
+
+See the Chapter _MSC Pooling_ in the OsmoBSC user manual for
+configuration examples of this situation.
+
+
+=== MO call establishment on AoIP with user plane
+
+The following figure shows a simplified version of the messages between
+MS, OsmoBTS, OsmoBSC, OsmoMGW@BSC, MSC[-Server] and MSC-MGW in during
+the establishment and release of a MO voice call. Particular focus
+is given on messages related to the establishment of the RTP based
+user plane.
+
+The fact whether or not the RAN or the CN use media gateways, how they
+control their respective media gateway, and whether there are multiple
+media gateways for load distribution is a private implementation detail
+of either RAN or CN. Either side does not need to know the
+internal structure of the other side, since the RTP endpoint parameters
+are signaled for each call individually over the A interface.
+
+The signaling between the BSC-colocated MGW and OsmoBSC is IETF MGCP
+(Media Gateway Control Protocol).
+
+The signaling between the MSC[-Server] and the MGW is internal to the
+CN. It is typically based on MEGACO/H.248.
+
+As only the BSC and the MSC exchange 3GPP specified signaling messages,
+there is no direct interaction between the RAN and the CN side MGW.
+They only exchange RTP and associated RTCP.
+
+In many real deployments, OsmoMGW will have a different IP address on
+the BTS/Abis facing interface than on the MSC/A facing interface. As
+a simplification, this has been omitted in the figure.
+
+[mscgen]
+----
+include::../message-sequences/mo_call-bsc-msc-mgw-aoip.msc[]
+----
diff --git a/doc/manuals/chapters/bsc.adoc b/doc/manuals/chapters/bsc.adoc
index 0d3ded5c4..38f267c7b 100644
--- a/doc/manuals/chapters/bsc.adoc
+++ b/doc/manuals/chapters/bsc.adoc
@@ -99,13 +99,11 @@ detected during cell selection.
Uplink DTX is possible on any TRX, and serves primarily two uses:
-possible on any TRX, and serves primarily two uses:
-
. reducing the MS battery consumption by transmitting at a lower duty cycle
. reducing the uplink interference caused in surrounding cells that
re-use the same ARFCN.
-DTS for both uplink and downlink is implemented in the BTS. Not all BTS
+DTX for both uplink and downlink is implemented in the BTS. Not all BTS
models support it.
The Osmocom BSC component can instruct the BTS to enable or disable
diff --git a/doc/manuals/chapters/bts.adoc b/doc/manuals/chapters/bts.adoc
index 6e7a308c7..4662c1a0c 100644
--- a/doc/manuals/chapters/bts.adoc
+++ b/doc/manuals/chapters/bts.adoc
@@ -105,7 +105,7 @@ like this:
----
network
bts 0
- type sysmobts
+ type osmo-bts
band DCS1800
description The new BTS in Baikonur
location_area_code 2342
@@ -225,7 +225,7 @@ this cell at all.
==== `gprs cell bvci <2-65535>`
Configures the 'BSSGP Virtual Circuit Identifier'. It must be unique
-between all BGSGP connections to one SGSN.
+between all BSSGP connections to one SGSN.
NOTE: It is up to the system administrator to ensure all PCUs are
allocated an unique bvci. OsmoBSC will not ensure this policy.
@@ -272,10 +272,11 @@ specification for the detailed meaning of those timers.
=== Dynamic Timeslot Configuration (TCH / PDCH)
-A dynamic timeslot is in principle a voice timeslot (TCH) that is used to serve
-GPRS data (PDCH) when no voice call is active on it. This enhances GPRS
-bandwidth while no voice calls are active, which is dynamically scaled down as
-voice calls need to be served. This is a tremendous improvement in service over
+A dynamic timeslot is in principle a timeslot that is used to serve GPRS data
+(PDCH), but that can be switched to be used either for voice (TCH) or signalling
+(SDCCH8) when all other static timeslots are already in use. This enhances GPRS
+bandwidth while there is no CS load, and is dynamically scaled down as CS
+services need to be served. This is a tremendous improvement in service over
statically assigning a fixed number of timeslots for voice and data.
The causality is as follows: to establish a voice call, the
@@ -295,7 +296,7 @@ all BTS models support dynamic channels.
.Dynamic timeslot support by various BTS models
[cols="50%,25%,25%"]
|===
-| |`TCH/F_TCH/H_PDCH` |`TCH/F_PDCH`
+| |`TCH/F_TCH/H_SDCCH8_PDCH` |`TCH/F_PDCH`
|ip.access nanoBTS |- |supported
|Ericsson RBS |supported |-
|sysmoBTS using _osmo-bts-sysmo_ |supported |supported
@@ -310,11 +311,11 @@ non-standard RSL messages used for these timeslot kinds.
NOTE: Same as for dedicated PDCH timeslots, you need to enable GPRS and operate
a PCU, SGSN and GGSN to provide the actual data service.
-==== Osmocom Style Dynamic Timeslots (TCH/F_TCH/H_PDCH)
+==== Osmocom Style Dynamic Timeslots (TCH/F_TCH/H_SDCCH8_PDCH)
-Timeslots of the `TCH/F_TCH/H_PDCH` type dynamically switch between TCH/F,
-TCH/H and PDCH, depending on the channel kind requested by the MSC. The RSL
-messaging for `TCH/F_TCH/H_PDCH` timeslots is compatible with Ericsson RBS.
+Timeslots of the `TCH/F_TCH/H_SDCCH8_PDCH` type dynamically switch between TCH/F,
+TCH/H, SDCCH8 and PDCH, depending on the channel kind requested by the MSC. The RSL
+messaging for `TCH/F_TCH/H_SDCCH8_PDCH` timeslots is compatible with Ericsson RBS.
BTS models supporting this timeslot kind are shown in <<dyn_ts_compat>>.
@@ -361,15 +362,15 @@ network
timeslot 1
phys_chan_config SDCCH8
timeslot 2
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config TCH/F_TCH/H_SDCCH8_PDCH
timeslot 3
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config TCH/F_TCH/H_SDCCH8_PDCH
timeslot 4
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config TCH/F_TCH/H_SDCCH8_PDCH
timeslot 5
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config TCH/F_TCH/H_SDCCH8_PDCH
timeslot 6
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config TCH/F_TCH/H_SDCCH8_PDCH
timeslot 7
phys_chan_config PDCH
----
@@ -469,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:
@@ -502,14 +515,85 @@ 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
+
+osmo-bts supports repetition of FACCH, uplink SACCH and downlink SACCH as
+described in _3GPP TS 44.006_ <<3gpp-ts-44.006>>. When the feature is enabled
+it is applied dynamically, depending on the rf signal quality and MS
+capabilities. FACCH/SACCH repetition (or ACCH repetition) repeats the channel
+block transmission two times. This allows the transceiver to combine the symbols
+from two separate transmissions, which increases the probability that even a
+weak signal can be decoded.
+
+Enabling ACCH repetition is especially recommended when using the AMR speech
+codec. AMR already provides a forward error correction that is superior to
+the forward error correction used with FACCH or SACCH. ACCH repetition is a
+good way to even out this imbalance.
+
+The VTY configuration allows to enable repetition for all three channel types
+separately. For FACCH the operator has the option to restrict the repetition
+to LAPDM command frames only. Alternatively it is also possible to allow all
+LAPDM frame types for repetition. The following example shows a typical
+configuration where ACCH repetition is fully enabled.
+
+.Example typical configuration of ACCH repetition parameters at VTY BTS node
+----
+OsmoBSC(config-net-bts)# repeat dl-facch all
+OsmoBSC(config-net-bts)# repeat ul-sacch
+OsmoBSC(config-net-bts)# repeat dl-sacch
+OsmoBSC(config-net-bts)# repeat rxqual 4
+----
+
+It should be noted that unless the repetition is enabled explicitly, the
+repetition is turned off by default. If no threshold (see <<acch_rep_thr>>) is
+set, the default value 4 (BER >= 1.6%) will be used. The following example shows
+a minimal configuration where the repetition is only activated for FACCH LAPDM
+command frames.
+
+.Example minimal configuration of ACCH repetition parameters at VTY BTS node
+----
+OsmoBSC(config-net-bts)# repeat dl-facch command
+----
+
+Since it is not worthwhile to apply any repetition when the signal conditions
+are good enough to ensure a reliable transmission in one round, the operator
+has the option to set a threshold based on RXQUAL/BER at which the repetition
+is switched on. The threshold mechanism implements a hysteresis to prevent
+bouncing between repetition on and repetition off. Only when the signal quality
+is increased again by two rxqual levels, the repetition is turned off again. It
+is even possible to permanently enable repetition, regardless of the signal
+quality.
+
+[[acch_rep_thr]]
+.ACCH repetition thresholds
+[options="header",cols="20%,40%,40%"]
+|===
+|rxqual |enable threshold |disable threshold
+|0 |(repetition always on) |(repetition always on)
+|1 |asciimath:[BER >= 0.2%] |asciimath:[BER = 0%]
+|2 |asciimath:[BER >= 0.4%] |asciimath:[BER = 0%]
+|3 |asciimath:[BER >= 0.8%] |asciimath:[BER <= 0.2%]
+|4 |asciimath:[BER >= 1.6%] |asciimath:[BER <= 0.4%]
+|5 |asciimath:[BER >= 3.2%] |asciimath:[BER <= 0.8%]
+|6 |asciimath:[BER >= 6.4%] |asciimath:[BER <= 1.6%]
+|7 |asciimath:[BER >= 12.8%] |asciimath:[BER <= 3.2%]
+|===
+
+NOTE: osmo-bsc only sets the ACCH repetition parameters via RSL. Whether ACCH
+repetition can be used depends on the BTS model and osmo-bts version. To
+find out if a BTS supports ACCH repetition (BTS_FEAT_ACCH_REP), the VTY
+command `show bts` can be used.
==== RACH Parameter Configuration
diff --git a/doc/manuals/chapters/control.adoc b/doc/manuals/chapters/control.adoc
index 6b1609ae5..09e828e9e 100644
--- a/doc/manuals/chapters/control.adoc
+++ b/doc/manuals/chapters/control.adoc
@@ -27,10 +27,12 @@ TRX-specific commands are additionally prefixed with TRX number e. g.
|inform-msc-v1|WO|Yes|Arbitrary value| See <<infomsc>> for details.
|rf_locked|RW|No|"0","1"|See <<rfl>> for details.
|number-of-bts|RO|No|"<num>"|Get number of configured BTS.
+|apply-config-file|WO|No|"<filename>"|Apply VTY config file snippet from file.
+|write-config-file|WO|No|"overwrite", "<filename>"|Write running configuration to file.
|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.
@@ -38,6 +40,37 @@ TRX-specific commands are additionally prefixed with TRX number e. g.
|bts.N.rf_state|RO|No|"<oper>,<admin>,<pol>"|See <<rfs>> for details.
|bts.N.trx.M.arfcn|RW|No|"<arfcn>"|Set/Get ARFCN (value between (0, 1023)).
|bts.N.trx.M.max-power-reduction|RW|No|"<mpr>"|See <<mpr>> for details.
+|[bts.N.]handover.active|RW|No|"0","1","default"|Enable/disable handover.
+|[bts.N.]handover.algorithm|RW|No|"1","2","default"|Choose algorithm for handover decision (hodec1 or hodec2).
+|[bts.N.]handover1.window.rxlev.averaging|RW|No|<1-10>,"default"|How many RxLev measurements to use for averaging.
+|[bts.N.]handover1.window.rxqual.averaging|RW|No|<1-10>,"default"|How many RxQual measurements to use for averaging.
+|[bts.N.]handover1.window.rxlev.neighbor.averaging|RW|No|<1-10>,"default"|How many Neighbor RxLev measurements to use for averaging.
+|[bts.N.]handover1.power.budget.interval|RW|No|<1-99>,"default"|How often to check for a better cell (SACCH frames).
+|[bts.N.]handover1.power.budget.hysteresis|RW|No|<0-999>,"default"|How many dB stronger must a neighbor be to become a HO candidate.
+|[bts.N.]handover1.maximum.distance|RW|No|<0-9999>,"default"|Maximum Timing-Advance value (i.e. MS distance) before triggering HO.
+|[bts.N.]handover2.window.rxlev.averaging|RW|No|<1-10>,"default"|How many RxLev measurements to use for averaging.
+|[bts.N.]handover2.window.rxqual.averaging|RW|No|<1-10>,"default"|How many RxQual measurements to use for averaging.
+|[bts.N.]handover2.window.rxlev.neighbor.averaging|RW|No|<1-10>,"default"|window rxlev neighbor averaging.
+|[bts.N.]handover2.power.budget.interval|RW|No|<1-99>,"default"|How many dB stronger must a neighbor be to become a HO candidate.
+|[bts.N.]handover2.power.budget.hysteresis|RW|No|<0-999>,"default"|How many dB stronger must a neighbor be to become a HO candidate.
+|[bts.N.]handover2.maximum.distance|RW|No|<0-9999>,"default"|Maximum Timing-Advance value (i.e. MS distance) before triggering HO.
+|[bts.N.]handover2.assignment|RW|No|"0","1","default"|Enable or disable in-call channel re-assignment within the same cell.
+|[bts.N.]handover2.tdma-measurement|RW|No|"full","subset","default"|Define measurement set of TDMA frames.
+|[bts.N.]handover2.min.rxlev|RW|No|<-110--50>,"default"|How weak may RxLev of an MS become before triggering HO.
+|[bts.N.]handover2.min.rxqual|RW|No|<0-7>,"default"|How bad may RxQual of an MS become before triggering HO.
+|[bts.N.]handover2.afs-bias.rxlev|RW|No|<0-20>,"default"|RxLev improvement bias for AFS over other codecs.
+|[bts.N.]handover2.afs-bias.rxqual|RW|No|<0-7>,"default"|RxQual improvement bias for AFS over other codecs.
+|[bts.N.]handover2.min-free-slots.tch-f|RW|No|<0-9999>,"default"|Minimum free TCH/F timeslots before cell is considered congested.
+|[bts.N.]handover2.min-free-slots.tch-h|RW|No|<0-9999>,"default"|Minimum free TCH/H timeslots before cell is considered congested.
+|[bts.N.]handover2.max-handovers|RW|No|<1-9999>,"default"|Maximum number of concurrent handovers allowed per cell.
+|[bts.N.]handover2.penalty-time.max-distance|RW|No|<0-99999>,"default"|ime to suspend handover for a subscriber after leaving this cell due to exceeding max distance.
+|[bts.N.]handover2.penalty-time.failed-ho|RW|No|<0-99999>,"default"|Time to suspend handover for a subscriber after a failed handover into this cell.
+|[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]]
@@ -90,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 d9805f78c..f56725977 100644
--- a/doc/manuals/chapters/handover.adoc
+++ b/doc/manuals/chapters/handover.adoc
@@ -1,4 +1,5 @@
-== Handover
+[[cs_handover]]
+== CS Handover
Handover is the process of moving a continuously used channel (lchan) from one
cell to another. Usually, that is an ongoing call, so that phones are able to
@@ -144,6 +145,7 @@ more than one neighbor per given ARFCN+BSIC, these values can be re-used any
number of times across a network, and even between cells managed by one and the
same BSC.
+[[config_neigh]]
=== Configuring Neighbors
The most important step to enable handover in OsmoBSC is to configure each cell
@@ -509,9 +511,11 @@ periodical congestion check attempts to distribute MS to less loaded neighbor
cells. Every time, the one MS that will suffer the least RXLEV loss while still
reducing congestion will be instructed to move first.
-If a cell and its neighbors are all loaded past their `min-free-slots`
-settings, the algorithmic aim is equal load: a load-based handover will never
-cause the target cell to be more congested than the source cell.
+If a cell and its neighbors are all loaded past their `min-free-slots` settings,
+the algorithmic aim is to improve the percentage of load above the
+`min-free-slots` setting: a load-based handover always requires the target cell
+to have a lower load percentage after handover than the source cell had before
+handover.
The min-free-slots setting is a tradeoff between immediate voice service
availability and optimal reception levels. A sane choice could be:
@@ -634,6 +638,7 @@ following properties:
* EARFCN (EUTRAN Absolute Radio Channel Number) on which the cell
broadcasts
* Reselection thresholds towards E-UTRAN cells:
+
[width="30%"]
|====
| 0 | 0 dB
@@ -642,10 +647,12 @@ following properties:
| 3 | 6 dB
| ... | ...
| 31 | 62 dB
-|=====
+|====
+
* Priority of E-UTRAN frequency: 0 = lowest priority, ..., 7 = highest priority
* QRXLEVMIN parameter: Minimum required RX level in the UTRAN FDD cell
(dBm), see 3GPP TS 25.304.
+
[width="30%"]
|====
| 0 | -140 dBm
@@ -654,6 +661,7 @@ following properties:
| ... | ...
| 31 | -78 dBm
|====
+
* Measurement bandwidth in MHz, see 3GPP TS 44.018 and 3GPP TS 44.060.
This field specifies the minimum value of the channel bandwidth of all
valid E-UTRAN cells on the specified EARFCN. It is defined by the
@@ -664,6 +672,7 @@ following properties:
station supporting wideband RSRQ measurements shall measure over the
indicated number of resource blocks. The field is coded according to
the following table:
+
[width="30%"]
|====
| 0 | N_RB = 6
@@ -687,3 +696,123 @@ network
4G neighbor cells can be removed using the same command, just replacing
`add` with `del`.
+
+[[ps_handover]]
+== PS Handover
+
+Packet Switch Handover is mostly managed by the packet switching nodes such as
+PCU, SGSN and GGSN. However, the PCU encounters a similar difficulty to that
+explained in <<cs_handover>>: that is, it must find a way to translate
+ARFCN+BSIC identifiers provided by the MS into the sort of identifiers
+understood by the Core Network (SGSN). These identifiers are in this case
+composed of RAC+CI (instead LAC+CI), or more specifically, CGI+RAC (named as
+"Packet Switched CGI" or "CGI-PS" from now on for convenience).
+
+Hence, it feels natural extending the <<config_neigh>> storage in the BSC to
+also provide Packet Switched related identifiers in order to provide the PCU the
+required information to do the translations, instead of duplicating the whole
+neighbor information in two different network nodes.
+
+This can be done, similarly to already presented CS related commands in
+<<config_neigh>>, by using the `neighbor cgi-ps` VTY command, which allows for
+specifying the extra identifier (RAC) required by PS related translations:
+
+.Example: configuring PS neighbors within the local BSS in osmo-bsc.cfg, identified by CGI-PS (CGI+RAC)
+----
+network
+ bts 0
+neighbor cgi-ps <MCC> <MNC> <LAC> <RAC> <CI> arfcn <0-1023> bsic (<0-63>|any)
+----
+
+This information should already be present by default if one uses the `local BTS
+number` to identify the network, as long as that BTS is properly configured to
+have GPRS enabled and the RAC code is set by `gprs routing area <0-255>`
+command:
+
+.Example: configuring PS neighbors within the local BSS in osmo-bsc.cfg, identified by local BTS number
+----
+network
+ bts 0
+ gprs mode gprs
+ gprs routing area 45
+ neighbor bts 1
+ ...
+ bts 1
+ gprs mode egprs
+ gprs routing area 48
+ neighbor bts 0
+ ...
+----
+
+This PS information is solely used by the Neighbor Resolution Service, aimed at
+nodes other than BSC itself, and described below.
+
+=== Neighbor Address Resolution Service
+
+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 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
+needs to be reachable by all PCU under the BSC. This way the user can still
+constrain the regular CTRL port (which may contains lots of configuration
+related commands) listening in a more constrained address (eg. localhost).
+
+By default, this interface is disabled, and it is enabled by configuring the
+desired IP address to bind to (and optionally a different port other than
+well-known `4248`):
+
+.Example: Enable and configure Neighbor Resolution Service CTRL interface
+----
+network
+ neighbor-resolution bind 127.0.0.1 5000
+----
+
+osmo-pcu will then be able to connect to the BSC and query for resolution during
+eg. NACC requests from MS.
+
+The relevant commands are::
+
+* neighbor_resolve_cgi_ps_from_lac_ci:
+----
+GET neighbor_resolve_cgi_ps_from_lac_ci.<src_lac>.<src_cell_id>.<dst_arfcn>.<dst_bsic>
+GET_REPLY <MCC>-<MNC>-<LAC>-<RAC>-<CI>
+----
+
+Where the `src_lac` and `src_ci` are provided by BTS/BSC over `PCUIF` interface
+during startup (`INFO IND`), and `dst_arfcn` and `dst_bsic` are those of the
+neighbor the PCU is willing to request the CGI-PS information. Hence, from the
+`src` params the BSC is able to look up the BTS and afterwards apply the
+translation of `dst` parameters based on the neighbor information available for
+that BTS.
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
new file mode 100644
index 000000000..f5f94f6a4
--- /dev/null
+++ b/doc/manuals/chapters/power_control.adoc
@@ -0,0 +1,634 @@
+== Power control
+
+The objective of power control is to regulate the transmit power of the MS (Uplink)
+as well as the BTS (Downlink) in order to achieve the optimal reception conditions,
+i.e. a desired signal strength and a desired signal quality.
+
+There are two advantages of power control:
+
+- reduction of the average power consumption (especially in the MS), and
+- reduction of the co-channel interference for adjacent channel users.
+
+Power control can be performed either by the BSC, or by the BTS autonomously.
+OsmoBSC currently lacks the power control logic, so it cannot act as the regulating
+entity, however it's capable to instruct a BTS that supports autonomous power
+control to perform the power regulation. This is achieved by including vendor-
+specific IEs with power control parameters in the channel activation messages
+on the A-bis/RSL interface.
+
+=== Power control parameters
+
+Unfortunately, 3GPP specifications do not specify the exact list of power control
+parameters and their encoding on the A-bis/RSL interface, so it's up to a BTS/BSC
+vendor what to send and in which format. Furthermore, there is no public
+documentation on which parameters are accepted by particular BTS models.
+
+3GPP TS 44.008 nonetheless defines a minimal set of parameters for a general power
+control algorithm. OsmoBSC allows to configure these parameters via the VTY
+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. 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
+BTS vendor/model requires _each_ RSL CHANnel ACTIVation message to contain the full
+set of parameters, then changing them in the BSC at run-time would affect all newly
+established logical channels immediately. The existing connections would continue
+to use parameters which were in use during the time of channel activation.
+
+For both ip.access nanoBTS and OsmoBTS, the configured parameters are being sent
+only once when the A-bis/RSL link is established. In all subsequent RSL messages,
+the MS/BS Power Parameters IE will be sent empty. Therefore, changing most of
+dynamic power control parameters at run-time would affect neither the existing
+nor newly established logical channels.
+
+It's still possible to "push" a modified set of MS/BS power control parameters to a
+BTS that accepts the default parameters at startup without triggering the A-bis/RSL
+link re-establishment and thus interrupting the service. The following command
+triggers resending of both MS/BS power control parameters:
+
+----
+# Resending from the 'enable' node:
+OsmoBSC# bts 0 resend-power-control-defaults
+
+# Resending from any configuration node (note prefix 'do'):
+OsmoBSC(config-ms-power-ctrl)# do bts 0 resend-power-control-defaults
+----
+
+NOTE: The above statement mostly applies to parameters for dynamic power control
+mode (see below). Switching between power control modes, as well as changing
+static/maximum power values, does not necessarily require resending of parameters.
+
+=== Power control configuration
+
+Two identical groups of parameters are available for both MS (Uplink) and BS
+(Downlink) power control. This chapter is aimed to put some light on them.
+
+All parameters can be set via the VTY interface, currently within the scope of
+a BTS node. This means that all transceivers will "inherit" the same configuration.
+
+----
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)# ?
+...
+ bs-power-control BS (Downlink) power control parameters
+ ms-power-control MS (Uplink) power control parameters
+...
+----
+
+Either of these commands would lead to a separate node:
+
+----
+OsmoBSC(config-net-bts)# ms-power-control
+OsmoBSC(config-ms-power-ctrl)# list with-flags
+...
+ . l. mode (static|dyn-bts) [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) 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
+BTS vendors/models may ignore or even reject it. Flag `l` indicates that changing
+a given parameter at run-time would affect only the new connections.
+
+==== Power control mode
+
+Three power control modes exist:
+
+----
+OsmoBSC(config-ms-power-ctrl)# mode ?
+ static Instruct the MS/BTS to use a static power level <1>
+ dyn-bts Power control to be performed dynamically by the BTS itself <2>
+OsmoBSC(config-net-bts)# no (bs-power-control|ms-power-control) <3>
+----
+<1> Send RSL MS/BS Power IE alone indicating a static power level to the BTS.
+<2> Send both RSL MS/BS Power IE and vendor-specific MS/BS Power Parameters IE.
+<3> Do not send any power control IEs in RSL CHANnel ACTIVation messages.
+
+By default, `static` mode is used for BS power control, while `dyn-bts` mode is
+automatically enabled for MS power control if vendor-specific format of the power
+control parameters (see above) is implemented for particular BTS model. Otherwise
+`static` mode is used too. Changing the mode at run-time would not affect already
+established connections, only the new ones (check flag `l`).
+
+For BS power control, there is an additional parameter:
+
+----
+OsmoBSC(config-bs-power-ctrl)# bs-power ?
+ static Fixed BS Power reduction value (for static mode)
+ dyn-max Maximum BS Power reduction value (for dynamic mode)
+----
+
+that allows to configure the maximum BS power reduction value in `dyn-bts` mode,
+and a fixed power reduction value in `static` mode. In the later case, no
+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
+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. 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)
+----
+
+3GPP TS 45.008 briefly mentions 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 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
+to the current transmit power level in order to raise or lower it.
+
+.Example: Power change step size
+----
+network
+ bts 0
+ bs-power-control
+ mode dyn-bts <1>
+ bs-power dyn-max 12 <2>
+ step-size inc 6 red 4 <3>
+ ms-power-control
+ mode dyn-bts <1>
+ step-size inc 4 red 2 <4>
+----
+<1> Both MS and BS power control is to be performed by the BTS autonomously.
+<2> The BTS is allowed to reduce the power on Downlink up to 12 dB.
+<3> On Downlink, BS power can be increased by 6 dB or reduced by 4 dB at once.
+<4> On Uplink, MS power can be increased by 4 dB or reduced by 2 dB at once.
+
+NOTE: In the context of BS power control, terms 'increase' and 'decrease' have the
+same meaning as in the context of MS power control: to make the output power stronger
+or weaker respectively. Even despite the BS power loop in fact controls the attenuation.
+
+TIP: It's recommended to pick the values in a way that the increase step is greater than
+the reduce step. This way the system would be able to react on signal degradation
+quickly, while a good signal would not trigger radical power reduction.
+
+Both parameters are mentioned in 3GPP TS 45.008, table A.1:
+
+- Pow_Incr_Step_Size (range 2, 4 or 6 dB),
+- Pow_Red_Step_Size (range 2 or 4 dB).
+
+==== RxLev and RxQual thresholds
+
+The general idea of power control is to maintain the signal level (RxLev) and quality
+(RxQual) within the target ranges. Each of these ranges can be defined as a pair of
+the lowest and the highest acceptable values called thresholds.
+
+The process of RxLev / RxQual threshold comparison is described in 3GPP TS 45.008,
+section A.3.2.1. All parameters involved in the process can be found in table
+A.1 with the recommended default values.
+
+.Example: RxLev and RxQual threshold configuration
+----
+network
+ bts 0
+ bs-power-control
+ mode dyn-bts <1>
+ rxlev-thresh lower 32 upper 38 <2>
+ rxqual-thresh lower 3 upper 0 <3>
+----
+<1> BS power control is to be performed by the BTS autonomously.
+<2> RxLev is to be maintained in range 32 .. 38 (-78 .. -72 dBm).
+<3> RxQual is to be maintained in range 3 .. 0 (less is better).
+
+NOTE: For both RxLev and RxQual thresholds, the lower and upper values are included
+in the tolerance window. In the example above, RxQual=3 would not trigger the
+control loop to increase BS power, as well as RxLev=38 (-72 dBm) would not trigger
+power reduction.
+
+TIP: It's recommended to harmonize the increase step size with the RxLev threshold
+window in a way that the former is less or equal than/to the later. For example,
+if the RxLev threshold is 32 .. 36 (-78 .. -74 dBm), then the window size is 4 dB,
+and thus the increase step should be less or equal (e.g. 2 or 4 dB).
+
+In 3GPP TS 45.008, lower and upper RxLev thresholds are referred as `L_RXLEV_XX_P`
+and `U_RXLEV_XX_P`, while the RxQual thresholds are referred as `L_RXQUAL_XX_P` and
+`U_RXQUAL_XX_P`, where the `XX` is either `DL` (Downlink) or `UL` (Uplink).
+
+The process of threshold comparison actually involves more than just upper and lower
+values for RxLev and RxQual. The received "raw" measurements are being averaged and
+stored in a circular buffer, so the power change is triggered only if Pn averages out
+of Nn averages exceed the corresponding thresholds.
+
+.Example: RxLev and RxQual threshold comparators
+----
+network
+ bts 0
+ bs-power-control
+ mode dyn-bts <1>
+ 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> 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
+
+3GPP 45.008, section A.3.1 requires that the measurement values reported by both
+an MS and the BTS are being pre-processed before appearing on the input of the
+corresponding power control loops in any of the following ways:
+
+- Unweighted average;
+- Weighted average, with the weightings determined by O\&M;
+- Modified median calculation, with exceptionally high and low values
+ (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, RxQual and C/I).
+
+----
+OsmoBSC(config-bs-power-ctrl)# rxlev-avg algo ?
+ unweighted Un-weighted average
+ weighted Weighted average
+ mod-median Modified median calculation
+ osmo-ewma Exponentially Weighted Moving Average (EWMA)
+OsmoBSC(config-bs-power-ctrl)# rxqual-avg algo ?
+ unweighted Un-weighted average
+ weighted Weighted average
+ mod-median Modified median calculation
+ osmo-ewma Exponentially Weighted Moving Average (EWMA)
+----
+
+OsmoBTS features a non-standard Osmocom specific EWMA (Exponentially Weighted Moving
+Average) based pre-processing. Other BTS models may support additional non-standard
+methods too, the corresponding VTY options can be added on request.
+
+Among with the averaging methods, 3GPP 45.008 also defines two pre-processing
+parameters in section A.3.1:
+
+- Hreqave - defines the period over which an average is produced, in terms of the
+ number of SACCH blocks containing measurement results, i.e. the number of
+ measurements contributing to each averaged measurement;
+
+- Hreqt - is the number of averaged results that are maintained.
+
+By default, OsmoBSC would not send any pre-processing parameters, so the BTS may
+apply its default pre-processing algorithm with default parameters, or may not
+apply any pre-processing at all - this is up to the vendor. The pre-processing
+parameters need to be configured explicitly as shown in the example below.
+
+.Example: Explicit pre-processing configuration
+----
+network
+ bts 0
+ bs-power-control
+ mode dyn-bts <1>
+ rxlev-avg algo unweighted <2>
+ rxlev-avg params hreqave 4 hreqt 6 <3>
+ rxqual-avg algo osmo-ewma beta 50 <4>
+ rxqual-avg params hreqave 2 hreqt 3 <5>
+ ms-power-control
+ mode dyn-bts <1>
+ rxlev-avg algo unweighted <2>
+ 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>
+// OsmoBSC(config-net-bts-trx)# max_power_red <0-100>
+
+=== BCCH carrier power reduction operation
+
+According to 3GPP TS 45.008, section 7.1, the BCCH carrier (sometimes called C0) of
+a BTS shall maintain continuous Downlink transmission at full power in order to
+stay "visible" to the mobile stations. Because of that, early versions of this 3GPP
+document prohibited BS power reduction on C0. However, a new feature was introduced
+in version 13.0.0 (2015-11) - "BCCH carrier power reduction operation".
+
+This is a special mode of operation, in which the variation of RF power level for
+some timeslots is relaxed for the purpose of energy saving. In other words, the
+output power on some timeslots, except the timeslot(s) carrying BCCH/CCCH, can be
+lower than the full power. In this case the maximum allowed difference is 6 dB.
+
+Of course, energy saving comes at a price and has impacts to the network KPI. In
+particular, it does negatively affect cell reselection performance and does increase
+handover failure and call drop rates. This is why BCCH carrier power reduction
+operation mode is not enabled by default. More information on potential impact
+and the simulation results can be found in 3GPP TR 45.926.
+
+==== Supported BTS models
+
+At the time of writing this manual, the only BTS model that can be instructed to
+enter or leave the BCCH power reduction mode is osmo-bts-trx. Support for other
+BTS vendors/models may be added in the future.
+
+TIP: If you're using OsmoBTS, make sure that it reports feature #021 "BCCH carrier
+power reduction mode" in the feature vector. This can be checked by issuing
+`show bts` command in OsmoBSC's VTY interface.
+
+==== Interworking with static and dynamic power control
+
+The key difference between BCCH carrier power reduction and the BS power control
+is that the former affects *inactive* timeslots (or sub-channels), so only dummy
+bursts are affected. The later depends on the Downlink measurement reports sent
+by the MS, and thus applies to *active* channels only. However, both features
+are interconnected: the maximum BCCH carrier power reduction value constrains
+the BS Power value that can be used for dynamic or static BS power control.
+
+BS power control on the BCCH carrier will not be enabled unless the BTS is in BCCH
+carrier power reduction mode of operation. Once it is, the BS power reduction
+value in either of `dyn-bts` or `static` modes would be constrained by currently
+applied BCCH power reduction value, and thus would never exceed the maximum of 6 dB.
+
+For example, consider a BTS with BS power control configured to use _dynamic_ mode
+and the maximum power reduction of 16 dB. Once this BTS is switched into the BCCH
+carrier power reduction mode with the maximum attenuation of 4 dB, the maximum
+power reduction value for the BS power loop on the C0 carrier would be 4 dB.
+
+Moreover, according to 3GPP TS 45.008, between a timeslot used for BCCH/CCCH and
+the timeslot preceding it, the difference in output power actually transmitted by
+the BTS shall not exceed 3 dB. This means that on some timeslots the power
+reduction value can be constrained even further.
+
+==== Managing BCCH carrier power reduction
+
+The BCCH carrier power reduction can be controlled via the CTRL and VTY interfaces.
+There is currently no logic in OsmoBSC for automatic activation and deactivation
+of this mode, so it's up to the network operator (or an external monitoring suite)
+when and depending on which factors to toggle it. Setting a value greater than
+zero enables the BCCH power reduction mode; setting zero disables it completely.
+
+.Example: Activating BCCH carrier power reduction via the VTY
+----
+OsmoBSC> enable
+OsmoBSC# bts 0 <1> c0-power-reduction ?
+ <0-6> Power reduction value (in dB, even numbers only)
+OsmoBSC# bts 0 <1> c0-power-reduction 4 <2>
+----
+<1> BTS number for which to activate BCCH carrier power reduction
+<2> Maximum BCCH carrier power reduction (in 2 dB steps, 4 dB in this example)
+
+.Example: Activating BCCH carrier power reduction via the CTRL
+----
+$ osmo_ctrl.py \
+ --host 127.0.0.1 <1> -p 4249 \
+ --set "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 (in 2 dB steps, 4 dB in this example)
+
+Once activated, it's possible to introspect the current maximum reduction value:
+
+.Example: Checking BCCH carrier power reduction state via the VTY
+----
+OsmoBSC> enable
+OsmoBSC# show bts 0 <1>
+BTS 0 is of osmo-bts type in band DCS1800, has CI 0 LAC 1, BSIC 63 (NCC=7, BCC=7) and 2 TRX
+ Description: (null)
+ ARFCNs: 751 753
+ BCCH carrier power reduction (maximum): 4 dB <2>
+ ...
+----
+<1> BTS number for which to show BCCH carrier power reduction state
+<2> Maximum BCCH carrier power reduction currently applied
+
+.Example: Checking BCCH carrier power reduction state via the CTRL
+----
+$ osmo_ctrl.py \
+ --host 127.0.0.1 <1> -p 4249 \
+ --get "bts.0.c0-power-reduction"
+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/qos-example.adoc b/doc/manuals/chapters/qos-example.adoc
new file mode 100644
index 000000000..2f88b935c
--- /dev/null
+++ b/doc/manuals/chapters/qos-example.adoc
@@ -0,0 +1,46 @@
+==== Full example of QoS for osmo-bsc downlink QoS
+
+In the below example we will show the full set of configuration required
+for both DSCP and PCP differentiation of downlink Abis traffic by osmo-bsc.
+
+What we want to achieve in this example is the following configuration:
+
+.DSCP and PCP assignments for osmo-bsc downlink traffic in this example
+[options="header",width="30%",cols="2,1,1"]
+|===
+|Traffic |DSCP|PCP
+|A-bis RSL | 56| 7
+|A-bis RTP | 46| 6
+|A-bis OML | 34| 5
+|===
+
+. configure the osmocom program to set the DSCP value
+. configure an egrees QoS map to map from priority to PCP
+
+.Example Step 1: add related VTY configuration to `osmo-bsc.cfg`
+----
+...
+e1_input
+ ipa ip-dscp oml 34
+ ipa socket-priority oml 5
+ ipa ip-dscp rsl 56
+ ipa socket-priority rsl 7
+...
+----
+
+.Example Step 2: egress QoS map to map from socket priority to PCP values
+----
+$ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 0:0 5:5 6:6 7:7 <2>
+----
+<1> make sure to specify your specific VLAN interface name here instead of `eth0.9`.
+<2> create a egress QoS map that maps the priority value 1:1 to the PCP. We also include the
+ mapping 6:6 from the osmo-mgw example (see <<userman-osmomgw>>) here.
+
+NOTE:: The settings of the `ip` command are volatile and only active until
+the next reboot (or the network device or VLAN is removed). Please refer to
+the documentation of your specific Linux distribution in order to find out how
+to make such settings persistent by means of an `ifup` hook whenever the interface
+comes up. For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local
+script` (when using `network-scripts` and not NetworkManager). For Debian or Ubuntu,
+this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d`
+script.
diff --git a/doc/manuals/chapters/running.adoc b/doc/manuals/chapters/running.adoc
index ae45afcee..8e1c8e94a 100644
--- a/doc/manuals/chapters/running.adoc
+++ b/doc/manuals/chapters/running.adoc
@@ -35,8 +35,6 @@ arguments:
Set the global log level for logging to stderr. This has mostly
been deprecated by VTY based logging configuration, see
<<logging>> for more information.
-*-l, --local='IP'*::
- Specify the local IP address of the OsmoBSC-MGCP
*-r, --rf-ctl 'RFCTL'*::
Offer a Unix domain socket for RF control at the path/filename
'RFCTL' in the file system.
@@ -139,4 +137,24 @@ Here is an example configuration for a remote MGW:
msc 0
mgw remote-ip 10.9.8.7
mgw remote-port 2427
+ mgw reset-endpoint rtpbridge/* <1>
----
+<1> The 'reset-endpoint' setting instructs the OsmoBSC to send a wildcarded
+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
+
+Enable the Lb interface. OsmoBSC will then use the default point-codes to
+establish a connection to the SMLC.
+
+----
+smlc
+ enable
+----
+
+More detailed configuration is described in <<smlc-config>>.
diff --git a/doc/manuals/chapters/smlc.adoc b/doc/manuals/chapters/smlc.adoc
new file mode 100644
index 000000000..8bf33014d
--- /dev/null
+++ b/doc/manuals/chapters/smlc.adoc
@@ -0,0 +1,90 @@
+== Location Services: Lb interface to SMLC
+
+OsmoBSC and OsmoSMLC support positioning by Timing-Advance (TA), since October
+2020.
+
+A Perform Location Request is initiated by the MSC via BSSMAP on the
+A-interface, for a specific subscriber. The request is typically passed on via
+BSSMAP-LE on the Lb-interface to the SMLC. If required, the SMLC may request the
+subscriber's Timing Advance (a.k.a. Access Delay) from the BSC via BSSLAP
+(encapsulated BSSLAP APDU in a BSSMAP-LE Connection Oriented Information
+message). The SMLC may combine several location and velocity estimate methods to
+form a GAD PDU containing the resulting geographic location information. In
+TA-based positioning, the Timing-Advance information from the BSC is combined
+with the preconfigured latitude and longitude of the serving cell to form a
+location estimate. This is returned to the BSC via the Lb-interface, and in turn
+to the MSC via the A-interface.
+
+[mscgen]
+----
+include::{srcdir}/message-sequences/location_services_ta.msc[]
+----
+
+Location Services (LCS) are described in 3GPP TS 43.059 <<3gpp-ts-43-059>>.
+Messages for LCS on the A-interface (BSSMAP, between BSC and MSC) are described
+in 3GPP TS 48.008 <<3gpp-ts-48-008>>, on the Lb-interface (BSSMAP-LE between BSC
+and SMLC) in 3GPP TS 49.031 <<3gpp-ts-49-031>>. The resulting geographic
+location and possibly velocity information is encoded in GAD, described in 3GPP
+TS 23.032 <<3gpp-ts-23-032>>.
+
+[[smlc-config]]
+=== Configure Lb-interface
+
+All Lb-interface related configuration is found in the `smlc` section of
+OsmoBSC's configuration.
+
+By default, the Lb-interface is disabled in OsmoBSC. It is started by `enable`.
+
+----
+smlc
+ enable
+----
+
+On the Lb-interface, OsmoBSC always uses SSN "BSC (BSSMAP-LE)" (SSN code 250)
+and contacts the remote SMLC on SSN "SMLC (BSSMAP-LE)" (SSN code 252).
+
+The point-codes are configurable, and default to OsmoBSC's local point-code
+0.23.3 (187), and remote SMLC point-code 0.23.6 (190).
+
+Typically, multiple BSCs connect to the same SMLC, so that each BSC needs to
+have a distinct point-code, while the SMLC has a single, fixed point-code.
+
+To configure a different remote SMLC point-code, first configure an arbitrarily
+named SCCP address in the `cs7` address book, and then apply that to the
+`smlc-addr` configuration:
+
+----
+cs7 instance 0
+ sccp-address my-smlc
+ point-code 0.42.6
+smlc
+ enable
+ smlc-addr my-smlc
+----
+
+For the BSC side, it suffices to configure a point-code in the `cs7` section,
+so that the BSC typically uses the same point-code on A and Lb interfaces. In
+this example, the BSC has point-code `1.2.3` on the Lb interface:
+
+----
+cs7 instance 0
+ point-code 1.2.3
+smlc
+ enable
+----
+
+It is also possible to configure a distinct BSC's point-code on Lb, using the
+`bsc-addr` configuration. In the following example, the BSC uses point-code
+`0.42.3` only on the Lb interface, while the A interface remains unchanged:
+
+----
+cs7 instance 0
+ sccp-address my-bsc-on-lb
+ point-code 0.42.3
+smlc
+ enable
+ bsc-addr my-bsc-on-lb
+----
+
+The geographic locations of individual cells are configured at the SMLC. See
+for example OsmoSMLC's user manual <<userman-osmosmlc>>.
diff --git a/doc/manuals/chapters/smscb.adoc b/doc/manuals/chapters/smscb.adoc
index 011aec413..6ac5370e6 100644
--- a/doc/manuals/chapters/smscb.adoc
+++ b/doc/manuals/chapters/smscb.adoc
@@ -102,3 +102,29 @@ OsmoBSC(config-cbc)# end
----
For more details on the available configuration commands, please check the OsmoBSC VTY Reference.
+
+=== Counters
+
+OsmoBSC has two Cell Broadcast related rate counter groups for each BTS:
+
+* the basic CBCH ("cell broadcast channel 0")
+* the extended CBCH ("cell broadcast channel 1")
+
+See below example for a situation where no CBS messages were received yet from the CBC,
+and hence only NULL messages have been sent on both basic and extended CBCH:
+
+.Example: CBCH related rate counters on a BTS that didn't receive any CBS messages
+----
+cell broadcast channel 1:
+ cbch:rcvd_queued: 0 (0/s 0/m 0/h 0/d) Received + queued CBCH messages (Abis)
+ cbch:rcvd_dropped: 0 (0/s 0/m 0/h 0/d) Received + dropped CBCH messages (Abis)
+ cbch:sent_single: 0 (0/s 0/m 0/h 0/d) Sent single CBCH messages (Um)
+ cbch:sent_default: 0 (0/s 0/m 0/h 0/d) Sent default CBCH messages (Um)
+ cbch:sent_null: 31366 (0/s 45/m 2677/h 30588/d) Sent NULL CBCH messages (Um)
+cell broadcast channel 0:
+ cbch:rcvd_queued: 0 (0/s 0/m 0/h 0/d) Received + queued CBCH messages (Abis)
+ cbch:rcvd_dropped: 0 (0/s 0/m 0/h 0/d) Received + dropped CBCH messages (Abis)
+ cbch:sent_single: 0 (0/s 0/m 0/h 0/d) Sent single CBCH messages (Um)
+ cbch:sent_default: 0 (0/s 0/m 0/h 0/d) Sent default CBCH messages (Um)
+ cbch:sent_null: 31366 (1/s 46/m 2676/h 30588/d) Sent NULL CBCH messages (Um)
+----
diff --git a/doc/manuals/message-sequences/a_interface_bringup.msc b/doc/manuals/message-sequences/a_interface_bringup.msc
new file mode 100644
index 000000000..165262167
--- /dev/null
+++ b/doc/manuals/message-sequences/a_interface_bringup.msc
@@ -0,0 +1,31 @@
+msc {
+ hscale=2;
+ bsc[label="BSC"], stp[label="STP"], _msc[label="MSC"];
+
+ |||;
+ ||| [label="We assume the MSC is already well connected to the STP and MGW(CN)"];
+
+ --- [label="SCTP/IP level establishment"];
+ bsc =>> stp [label="SCTP INIT"];
+ bsc <<= stp [label="SCTP INIT_ACK"];
+ bsc =>> stp [label="SCTP COOKIE_ECHO"];
+ bsc <<= stp [label="SCTP COOKIE_ACK"];
+ ||| [label="SCTP payload can now be carried over the link"];
+ |||;
+ |||;
+ --- [label="M3UA level establishment (over SCTP)"];
+ bsc =>> stp [label="M3UA ASPUP"];
+ bsc <<= stp [label="M3UA ASPUP_ACK"];
+ bsc =>> stp [label="M3UA ASPAC (routing context)"];
+ bsc <<= stp [label="M3UA ASPAC_ACK (routing context)"];
+ bsc <<= stp [label="M3UA NTFY (AS-ACTIVE)"];
+ ||| [label="M3UA payload can now be carried over the link"];
+ |||;
+ --- [label="BSSMAP level establishment (over SCCP/M3UA/SCTP)"];
+ bsc =>> _msc [label="BSSMAP RESET (OPC=BSC, DPC=MSC)"];
+ bsc <<= _msc [label="BSSMAP RESET ACK (OPC=MSC, DPC=BSC)"];
+ ||| [label="BSSMAP connection-oriented data can now be exchanged"];
+ |||;
+ --- [label="Repeat BSSMAP level establishment (to other MSCs in the pool)"];
+ |||;
+}
diff --git a/doc/manuals/message-sequences/location_services_ta.msc b/doc/manuals/message-sequences/location_services_ta.msc
new file mode 100644
index 000000000..e6bef38ef
--- /dev/null
+++ b/doc/manuals/message-sequences/location_services_ta.msc
@@ -0,0 +1,49 @@
+msc {
+ hscale="2";
+
+ ms[label="MS/BTS"],bsc[label="BSC"],smlc[label="SMLC"],__msc[label="MSC"];
+
+ ||| [label="Location Services (LCS): Perform Location Request using TA"];
+
+ |||;
+ --- [label="MS in DEDICATED MODE (currently active)"];
+
+ ms =>> bsc [label="earlier Measurement Report provides Timing Advance"];
+
+ bsc <<= __msc [label="PERFORM LOCATION REQUEST\n3GPP TS 48.008 3.2.1.71"];
+
+ bsc =>> smlc [label="BSSMAP-LE PERFORM LOCATION REQUEST\n3GPP TS 49.031 9.1\nwith BSSLAP APDU = TA Layer3\n3GPP TS 48.071 4.2.8"];
+
+ smlc rbox smlc [label="SMLC uses TA included in TA Layer3"];
+
+ bsc <<= smlc [label="BSSMAP-LE PERFORM LOCATION RESPONSE\n3GPP TS 49.031 9.2"];
+
+ bsc =>> __msc [label="PERFORM LOCATION RESPONSE\n3GPP TS 48.008 3.2.1.72"];
+
+ ...;
+ ...;
+ --- [label="MS in IDLE MODE (not connected)"];
+
+ bsc <<= __msc [label="PERFORM LOCATION REQUEST\n3GPP TS 48.008 3.2.1.71"];
+
+ bsc =>> smlc [label="BSSMAP-LE PERFORM LOCATION REQUEST\n3GPP TS 49.031 9.1"];
+
+ smlc rbox smlc [label="SMLC needs TA information,\nasks BSC via BSSLAP"];
+
+ bsc <<= smlc [label="BSSMAP-LE CONNECTION ORIENTED INFORMATION\n3GPP TS 49.031 9.12\nwith BSSLAP APDU = TA Request\n3GPP TS 48.071 4.2.1"];
+
+
+ ms <<= bsc [label="RR Paging Request\n3GPP TS 48.018 9.1.22-24"];
+
+ ms =>> bsc [label="RSL CHANNEL REQUIRED\n3GPP TS 48.058 8.5.3\nincludes Access Delay (9.3.17) == Timing Advance"];
+
+ ms =>> bsc [label="RR Paging Response\n3GPP TS 48.018 9.1.25"];
+
+ ms <<= bsc [label="RF CHANNEL RELEASE\n3GPP TS 48.058 8.4.14"];
+
+ bsc =>> smlc [label="BSSMAP-LE CONNECTION ORIENTED INFORMATION\n3GPP TS 49.031 9.12\nwith BSSLAP APDU = TA Response\n3GPP TS 48.071 4.2.2"];
+
+ bsc <<= smlc [label="BSSMAP-LE PERFORM LOCATION RESPONSE\n3GPP TS 49.031 9.2"];
+
+ bsc =>> __msc [label="PERFORM LOCATION RESPONSE\n3GPP TS 48.008 3.2.1.72"];
+}
diff --git a/doc/manuals/message-sequences/mo_call-bsc-msc-mgw-aoip.msc b/doc/manuals/message-sequences/mo_call-bsc-msc-mgw-aoip.msc
new file mode 100644
index 000000000..55fd6cd2f
--- /dev/null
+++ b/doc/manuals/message-sequences/mo_call-bsc-msc-mgw-aoip.msc
@@ -0,0 +1,75 @@
+# MO-Call with OsmoBTS + OsmoBSC with true 3GPP AoIP
+msc {
+ hscale=2;
+ ms [label="MS"], bts [label="OsmoBTS\n1.1.1.1"], bsc[label="OsmoBSC"], mgcp[label="OsmoMGW@BSC\n3.3.3.3"], m_sc[label="MSC"], mgw_m[label="MGW@MSC\n5.5.5.5"];
+
+ ms box m_sc [label="We assume a SDCCH is already established"];
+ ...;
+
+ ms -> m_sc [label="DTAP CC SETUP"];
+ ms <- m_sc [label="DTAP CC CALL PROCEEDING"];
+
+ m_sc -> mgw_m [label="Request allocation of local RTP port"];
+ m_sc <- mgw_m [label="Respond with RTP port the MGW bound to (5.5.5.5:4000)"];
+ bsc <- m_sc [label="BSSAP ASSIGN REQ (3GPP AoIP, CN RTP IP/Port 5.5.5.5:4000)"];
+ bts <- bsc [label="RSL CHAN ACT (TCH)"];
+ bts -> bsc [label="RSL CHAN ACT ACK"];
+ ms <-> bsc [label="RR Assignment of TCH"];
+ ...;
+
+ # connect BTS RTP with BSC-MGW RTP
+ bts <- bsc [label="IPA CRCX"];
+ bts box bts [label="Bind to BTS-local RTP Port (1.1.1.1:1000)"];
+ bts -> bsc [label="IPA CRCX ACK (1.1.1.1:1000)"];
+ bsc -> mgcp [label="MGCP CRCX rtpbridge/2@mgw (1.1.1.1:1000)"];
+ mgcp box mgcp [label="Bind to MGW-local RTP Port (3.3.3.3:2000)\nConnect to 1.1.1.1:1000"];
+ bsc <- mgcp [label="MGCP CRCX rtpbridge/2@mgw OK (3.3.3.3:2000)"];
+ bts <- bsc [label="IPA MDCX (3.3.3.3:2000)"];
+ bts box bts [label="Connect RTP socket to remote (MGW) RTP Port"];
+ bts -> bsc [label="IPA MDCX ACK"];
+ bsc -> mgcp [label="MGCP MDCX rtpbridge/2@mgw (optional)"];
+ bsc <- mgcp [label="MGCP MDCX rtpbridge/2@mgw OK (optional)"];
+ ...;
+
+ mgcp <- bsc [label="MGCP CRCX rtpbridge/2@mgw (5.5.5.5:4000)"];
+ mgcp box mgcp [label="Bind to MGW-local RTP Port (3.3.3.3:3000)\nConnect to 5.5.5.5:4000"];
+ mgcp -> bsc [label="MGCP CRCX rtpbridge/2@mgw OK (3.3.3.3:3000)"];
+ ...;
+
+ bsc -> m_sc [label="BSSAP ASSIGN CMPL (3GPP AoIP 3.3.3.3:3000)"];
+ m_sc -> mgw_m [label="Request MGW to connect RTP to remote endpoint 3.3.3.3:3000"];
+ mgw_m box mgw_m [label="Connect RTP to 3.3.3.3:3000"];
+ ...;
+
+ mgcp <=> mgw_m [label="RTP Audio 3.3.3.3:3000 5.5.5.5:4000"];
+ bts <=> mgcp [label="RTP Audio 1.1.1.1:1000 3.3.3.3:2000"];
+ ms <=> bts [label="Um Audio (bidirectional)"];
+ ms <-> m_sc [label="DTAP CC ALERTING"];
+ ...;
+
+ ms <- m_sc [label="DTAP CC CONNECT"];
+ ms -> m_sc [label="DTAP CC CONNECT ACK"];
+ --- [label="Voice Call in Progress"];
+ ms <- m_sc [label="DTAP CC DISCONNET"];
+ ms <- m_sc [label="DTAP CC RELEASE"];
+ ms <- m_sc [label="DTAP CC RELEASE COMPL"];
+ ...;
+ bsc <- m_sc [label="BSSMAP CLEAR CMD"];
+ m_sc -> mgw_m [label="Request release RTP port/termination"];
+ mgw_m box mgw_m [label="Release RTP port 5.5.5.5:4000"];
+ bsc -> m_sc [label="BSSMAP CLEAR COMPL"];
+ bsc <- m_sc [label="SCCP RLSD"];
+ bsc -> m_sc [label="SCCP RLC"];
+ ...;
+ mgcp <- bsc [label="MGCP DLCX rtpbridge/2@mgw"];
+ mgcp box mgcp [label="Release MSC-facing local RTP port (3.3.3.3:3000)"];
+ mgcp -> bsc [label="MGCP DLCX rtpbridge/2@mgw OK"];
+
+ mgcp <- bsc [label="MGCP DLCX rtpbridge/2@mgw"];
+ mgcp box mgcp [label="Release BTS-facing local RTP port (3.3.3.3:2000)"];
+ mgcp -> bsc [label="MGCP DLCX rtpbridge/2@mgw OK"];
+
+ bts <- bsc [label="IPA DLCX"];
+ bts box bts [label="Release BTS-local RTP port (1.1.1.1:1000)"];
+ bts -> bsc [label="IPA DLCX OK"];
+}
diff --git a/doc/manuals/mgw/osmo-bsc-new-mgw-e1.msc b/doc/manuals/mgw/osmo-bsc-new-mgw-e1.msc
index 04b114fff..676948cdd 100644
--- a/doc/manuals/mgw/osmo-bsc-new-mgw-e1.msc
+++ b/doc/manuals/mgw/osmo-bsc-new-mgw-e1.msc
@@ -1,8 +1,8 @@
# MO-Call with E1 BTS + OsmoBSC with true 3GPP AoIP (planned
-# osmo-bsc_mgcp has to be extended to true MGW functionality!
+# osmo-mgw has to be extended to true MGW functionality!
msc {
hscale=2;
- ms [label="MS"], bts [label="E1 BTS"], bsc[label="OsmoBSC"], mgcp[label="osmo-bsc_mgcp"], m_sc[label="MSC"];
+ ms [label="MS"], bts [label="E1 BTS"], bsc[label="OsmoBSC"], mgcp[label="osmo-mgw"], m_sc[label="MSC"];
ms box m_sc [label="We assume a SDCCH is already established"];
...;
diff --git a/doc/manuals/mgw/osmo-bsc-new-mgw.msc b/doc/manuals/mgw/osmo-bsc-new-mgw.msc
index e618bb721..d6450bd22 100644
--- a/doc/manuals/mgw/osmo-bsc-new-mgw.msc
+++ b/doc/manuals/mgw/osmo-bsc-new-mgw.msc
@@ -1,7 +1,7 @@
-# MO-Call with OsmoBTS + OsmoBSC with true 3GPP AoIP (planned)
+# MO-Call with OsmoBTS + OsmoBSC with true 3GPP AoIP
msc {
hscale=2;
- ms [label="MS"], bts [label="OsmoBTS"], bsc[label="OsmoBSC"], mgcp[label="osmo-bsc_mgcp"], m_sc[label="MSC"];
+ ms [label="MS"], bts [label="OsmoBTS"], bsc[label="OsmoBSC"], mgcp[label="OsmoMGW"], m_sc[label="MSC"];
ms box m_sc [label="We assume a SDCCH is already established"];
...;
diff --git a/doc/manuals/osmobsc-usermanual.adoc b/doc/manuals/osmobsc-usermanual.adoc
index a34d3d422..0afa0158c 100644
--- a/doc/manuals/osmobsc-usermanual.adoc
+++ b/doc/manuals/osmobsc-usermanual.adoc
@@ -24,12 +24,22 @@ include::{srcdir}/chapters/bts-examples.adoc[]
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[]
+
include::./common/chapters/counters-overview.adoc[]
include::{srcdir}/chapters/counters.adoc[]
@@ -44,6 +54,8 @@ include::{srcdir}/chapters/osmux_bsc.adoc[]
include::./common/chapters/vty_cpu_sched.adoc[]
+include::{srcdir}/chapters/aoip-flows.adoc[]
+
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[]
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index 1ee96ed50..3ddad45db 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -2,18 +2,21 @@ noinst_HEADERS = \
a_reset.h \
abis_nm.h \
abis_om2000.h \
+ abis_osmo.h \
abis_rsl.h \
acc.h \
- arfcn_range_encode.h \
assignment_fsm.h \
bsc_rll.h \
+ bsc_stats.h \
bsc_subscriber.h \
bsc_subscr_conn_fsm.h \
bss.h \
bts.h \
+ bts_sm.h \
bts_trx.h \
bts_ipaccess_nanobts_omlattr.h \
chan_alloc.h \
+ chan_counts.h \
codec_pref.h \
ctrl.h \
debug.h \
@@ -22,19 +25,24 @@ noinst_HEADERS = \
gsm_data.h \
handover.h \
handover_cfg.h \
+ handover_ctrl.h \
handover_decision.h \
handover_decision_2.h \
handover_fsm.h \
handover_vty.h \
ipaccess.h \
+ lb.h \
lchan_fsm.h \
lchan_rtp_fsm.h \
lchan_select.h \
+ lcs_loc_req.h \
+ lcs_ta_req.h \
meas_feed.h \
meas_rep.h \
misdn.h \
neighbor_ident.h \
network_listen.h \
+ nm_common_fsm.h \
openbscdefines.h \
osmo_bsc.h \
osmo_bsc_grace.h \
@@ -45,7 +53,7 @@ noinst_HEADERS = \
paging.h \
pcu_if.h \
pcuif_proto.h \
- rest_octets.h \
+ bssmap_reset.h \
rs232.h \
signal.h \
system_information.h \
@@ -55,4 +63,5 @@ noinst_HEADERS = \
penalty_timers.h \
osmo_bsc_lcls.h \
smscb.h \
+ power_control.h \
$(NULL)
diff --git a/include/osmocom/bsc/a_reset.h b/include/osmocom/bsc/a_reset.h
index 46a392ff0..dd44ea5c1 100644
--- a/include/osmocom/bsc/a_reset.h
+++ b/include/osmocom/bsc/a_reset.h
@@ -23,7 +23,7 @@
struct bsc_msc_data;
/* Create and start state machine which handles the reset/reset-ack procedure */
-void a_reset_alloc(struct bsc_msc_data *msc, const char *name, void *cb);
+void a_reset_alloc(struct bsc_msc_data *msc, const char *name);
/* Confirm that we successfully received a reset acknowledge message */
void a_reset_ack_confirm(struct bsc_msc_data *msc);
diff --git a/include/osmocom/bsc/abis_nm.h b/include/osmocom/bsc/abis_nm.h
index 3a0cb0e3c..bfafa63ed 100644
--- a/include/osmocom/bsc/abis_nm.h
+++ b/include/osmocom/bsc/abis_nm.h
@@ -178,4 +178,7 @@ int abis_nm_update_max_power_red(struct gsm_bts_trx *trx);
struct gsm_bts_trx_ts *abis_nm_get_ts(const struct msgb *oml_msg);
+#define LOGPFOH(ss, lvl, foh, fmt, args ...) LOGP(ss, lvl, "%s: " fmt, abis_nm_dump_foh(foh), ## args)
+#define DEBUGPFOH(ss, foh, fmt, args ...) LOGPFOH(ss, LOGL_DEBUG, foh, fmt, ## args)
+
#endif /* _NM_H */
diff --git a/include/osmocom/bsc/abis_om2000.h b/include/osmocom/bsc/abis_om2000.h
index 0d48c0c2f..a61601552 100644
--- a/include/osmocom/bsc/abis_om2000.h
+++ b/include/osmocom/bsc/abis_om2000.h
@@ -43,6 +43,17 @@ enum om2k_mo_state {
OM2K_MO_S_DISABLED,
};
+enum om2k_sync_src {
+ OM2K_SYNC_SRC_INTERNAL = 0x00,
+ OM2K_SYNC_SRC_EXTERNAL = 0x01,
+};
+
+enum om2k_rx_diversity {
+ OM2K_RX_DIVERSITY_B = 0x01,
+ OM2K_RX_DIVERSITY_A = 0x02,
+ OM2K_RX_DIVERSITY_AB = 0x03,
+};
+
/* on-wire format for IS conn group */
struct om2k_is_conn_grp {
uint16_t icp1;
@@ -121,13 +132,33 @@ int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx);
int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx);
int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts);
-struct osmo_fsm_inst *om2k_bts_fsm_start(struct gsm_bts *bts);
+enum om2k_bts_state {
+ OM2K_BTS_S_INIT,
+ OM2K_BTS_S_WAIT_CF,
+ OM2K_BTS_S_WAIT_IS,
+ OM2K_BTS_S_WAIT_CON,
+ OM2K_BTS_S_WAIT_TF,
+ OM2K_BTS_S_WAIT_MCTR,
+ OM2K_BTS_S_WAIT_TRX_LAPD,
+ OM2K_BTS_S_WAIT_TRX,
+ OM2K_BTS_S_DONE,
+ OM2K_BTS_S_ERROR,
+};
+
void abis_om2k_bts_init(struct gsm_bts *bts);
+void om2k_bts_fsm_start(struct gsm_bts *bts);
+void om2k_bts_fsm_reset(struct gsm_bts *bts);
+
void abis_om2k_trx_init(struct gsm_bts_trx *trx);
+void om2k_trx_fsm_start(struct gsm_bts_trx *trx);
+void om2k_trx_fsm_reset(struct gsm_bts_trx *trx);
int abis_om2k_vty_init(void);
struct vty;
void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts);
+void abis_om2k_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx);
+
+const char *abis_om2k_mo_name(const struct abis_om2k_mo *mo);
#endif /* OPENBCS_ABIS_OM2K_H */
diff --git a/include/osmocom/bsc/osmo_bsc_reset.h b/include/osmocom/bsc/abis_osmo.h
index fb66df03d..97871ace0 100644
--- a/include/osmocom/bsc/osmo_bsc_reset.h
+++ b/include/osmocom/bsc/abis_osmo.h
@@ -1,8 +1,9 @@
-/* (C) 2017 by sysmocom s.f.m.c. GmbH
+/* GSM Network Management messages on the A-bis interface
+ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
- * Author: Philipp Maier
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
@@ -18,17 +19,15 @@
*
*/
-/* Create and start state machine which handles the reset/reset-ack procedure */
-void start_reset_fsm(struct bsc_msc_data *msc);
+#pragma once
+
+#include <stdint.h>
-/* Confirm that we successfully received a reset acknowledge message */
-void reset_ack_confirm(struct bsc_msc_data *msc);
+#include <osmocom/core/msgb.h>
-/* Report a failed connection */
-void report_conn_fail(struct bsc_msc_data *msc);
+#include <osmocom/gsm/protocol/gsm_04_08.h>
-/* Report a successful connection */
-void report_conn_success(struct bsc_msc_data *msc);
+struct gsm_bts;
-/* Check if we have a connection to a specified msc */
-bool sccp_conn_ready(struct bsc_msc_data *msc);
+int abis_osmo_rcvmsg(struct msgb *msg);
+int abis_osmo_sendmsg(struct gsm_bts *bts, struct msgb *msg);
diff --git a/include/osmocom/bsc/abis_rsl.h b/include/osmocom/bsc/abis_rsl.h
index 964e282b5..dcae348f7 100644
--- a/include/osmocom/bsc/abis_rsl.h
+++ b/include/osmocom/bsc/abis_rsl.h
@@ -35,8 +35,17 @@ struct gsm_bts_trx_ts;
#define GSM48_LEN2PLEN(a) (((a) << 2) | 1)
+#define RSL_ALLOC_SIZE 1024
+#define RSL_ALLOC_HEADROOM 128
+
+#define rsl_msgb_alloc(args...) \
+ msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, \
+ __FILE__ ":" OSMO_STRINGIFY_VAL(__LINE__))
+
const char *ip_to_a(uint32_t ip);
+struct e1inp_sign_link *rsl_chan_link(const struct gsm_lchan *lchan);
+
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len);
int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type,
const uint8_t *data, int len);
@@ -73,8 +82,6 @@ int abis_rsl_rcvmsg(struct msgb *msg);
int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id,
enum rsl_rel_mode release_mode);
-int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *broken);
-
/* to be provided by external code */
int rsl_deact_sacch(struct gsm_lchan *lchan);
diff --git a/include/osmocom/bsc/arfcn_range_encode.h b/include/osmocom/bsc/arfcn_range_encode.h
deleted file mode 100644
index 7ec710c33..000000000
--- a/include/osmocom/bsc/arfcn_range_encode.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef ARFCN_RANGE_ENCODE_H
-#define ARFCN_RANGE_ENCODE_H
-
-#include <stdint.h>
-
-enum gsm48_range {
- ARFCN_RANGE_INVALID = -1,
- ARFCN_RANGE_128 = 127,
- ARFCN_RANGE_256 = 255,
- ARFCN_RANGE_512 = 511,
- ARFCN_RANGE_1024 = 1023,
-};
-
-#define RANGE_ENC_MAX_ARFCNS 29
-
-int range_enc_determine_range(const int *arfcns, int size, int *f0_out);
-int range_enc_arfcns(enum gsm48_range rng, const int *arfcns, int sze, int *out, int idx);
-int range_enc_find_index(enum gsm48_range rng, const int *arfcns, int size);
-int range_enc_filter_arfcns(int *arfcns, const int sze, const int f0, int *f0_included);
-
-int range_enc_range128(uint8_t *chan_list, int f0, int *w);
-int range_enc_range256(uint8_t *chan_list, int f0, int *w);
-int range_enc_range512(uint8_t *chan_list, int f0, int *w);
-int range_enc_range1024(uint8_t *chan_list, int f0, int f0_incl, int *w);
-
-#endif
diff --git a/include/osmocom/bsc/assignment_fsm.h b/include/osmocom/bsc/assignment_fsm.h
index 156da42fa..6c429e418 100644
--- a/include/osmocom/bsc/assignment_fsm.h
+++ b/include/osmocom/bsc/assignment_fsm.h
@@ -17,18 +17,20 @@
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,
ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE,
ASSIGNMENT_ST_WAIT_LCHAN_ESTABLISHED,
ASSIGNMENT_ST_WAIT_MGW_ENDPOINT_TO_MSC,
+ ASSIGNMENT_ST_WAIT_LCHAN_MODIFIED,
};
enum assignment_fsm_event {
ASSIGNMENT_EV_LCHAN_ACTIVE,
ASSIGNMENT_EV_LCHAN_ESTABLISHED,
+ ASSIGNMENT_EV_LCHAN_MODIFIED,
ASSIGNMENT_EV_LCHAN_ERROR,
ASSIGNMENT_EV_MSC_MGW_OK,
ASSIGNMENT_EV_MSC_MGW_FAIL,
@@ -39,6 +41,11 @@ enum assignment_fsm_event {
void assignment_fsm_init();
+int reassignment_request_to_lchan(enum assign_for assign_for, struct gsm_lchan *lchan, struct gsm_lchan *to_lchan,
+ int tsc_set, int tsc);
+int reassignment_request_to_chan_type(enum assign_for assign_for, struct gsm_lchan *lchan,
+ enum gsm_chan_t new_lchan_type);
+
void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts *bts,
struct assignment_request *req);
void assignment_reset(struct gsm_subscriber_connection *conn);
diff --git a/include/osmocom/bsc/bsc_msc_data.h b/include/osmocom/bsc/bsc_msc_data.h
index 5699b776e..e5f48d1d1 100644
--- a/include/osmocom/bsc/bsc_msc_data.h
+++ b/include/osmocom/bsc/bsc_msc_data.h
@@ -128,8 +128,6 @@ struct bsc_msc_data {
/* Connection data */
struct osmo_plmn_id core_plmn;
- int core_lac;
- int core_ci;
/* audio codecs */
struct gsm48_multi_rate_conf amr_conf;
@@ -171,7 +169,7 @@ struct bsc_msc_data {
/* Pointer to the osmo-fsm that controls the
* BSSMAP RESET procedure */
- struct osmo_fsm_inst *reset_fsm;
+ struct bssmap_reset *bssmap_reset;
} a;
uint32_t x_osmo_ign;
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/bsc_subscr_conn_fsm.h b/include/osmocom/bsc/bsc_subscr_conn_fsm.h
index 142d535c2..d7deb06a8 100644
--- a/include/osmocom/bsc/bsc_subscr_conn_fsm.h
+++ b/include/osmocom/bsc/bsc_subscr_conn_fsm.h
@@ -2,6 +2,7 @@
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/core/fsm.h>
+#include "osmocom/bsc/gsm_data.h"
#define BSUB_USE_CONN "conn"
@@ -48,11 +49,6 @@ enum gscon_fsm_event {
GSCON_EV_LCS_LOC_REQ_END,
};
-struct gscon_clear_cmd_data {
- enum gsm0808_cause cause_0808;
- bool is_csfb;
-};
-
struct gsm_subscriber_connection;
struct gsm_network;
struct msgb;
@@ -93,3 +89,12 @@ void gscon_forget_mgw_endpoint_ci(struct gsm_subscriber_connection *conn, struct
bool gscon_is_aoip(struct gsm_subscriber_connection *conn);
bool gscon_is_sccplite(struct gsm_subscriber_connection *conn);
+
+
+static inline const struct osmo_plmn_id *gscon_last_eutran_plmn(const struct gsm_subscriber_connection *conn)
+{
+ return (conn && conn->fast_return.allowed &&
+ conn->fast_return.last_eutran_plmn_valid) ?
+ &conn->fast_return.last_eutran_plmn :
+ NULL;
+}
diff --git a/include/osmocom/bsc/bss.h b/include/osmocom/bsc/bss.h
index b8945db50..199f98145 100644
--- a/include/osmocom/bsc/bss.h
+++ b/include/osmocom/bsc/bss.h
@@ -16,5 +16,5 @@ extern int bts_model_bs11_init(void);
extern int bts_model_rbs2k_init(void);
extern int bts_model_nanobts_init(void);
extern int bts_model_nokia_site_init(void);
-extern int bts_model_sysmobts_init(void);
+extern int bts_model_osmobts_init(void);
#endif
diff --git a/include/osmocom/bsc/bssmap_reset.h b/include/osmocom/bsc/bssmap_reset.h
new file mode 100644
index 000000000..fcd850b8b
--- /dev/null
+++ b/include/osmocom/bsc/bssmap_reset.h
@@ -0,0 +1,31 @@
+/* Manage RESET and disconnection detection on BSSMAP and BSSMAP-LE */
+#pragma once
+
+enum bssmap_reset_fsm_event {
+ BSSMAP_RESET_EV_RX_RESET,
+ BSSMAP_RESET_EV_RX_RESET_ACK,
+ BSSMAP_RESET_EV_CONN_CFM_SUCCESS,
+ BSSMAP_RESET_EV_CONN_CFM_FAILURE,
+};
+
+struct bssmap_reset_cfg {
+ int conn_cfm_failure_threshold;
+ struct {
+ void (*tx_reset)(void *data);
+ void (*tx_reset_ack)(void *data);
+ void (*link_up)(void *data);
+ void (*link_lost)(void *data);
+ } ops;
+ void *data;
+};
+
+struct bssmap_reset {
+ struct osmo_fsm_inst *fi;
+ struct bssmap_reset_cfg cfg;
+ int conn_cfm_failures;
+};
+
+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 7f369042e..922753c9e 100644
--- a/include/osmocom/bsc/bts.h
+++ b/include/osmocom/bsc/bts.h
@@ -14,12 +14,31 @@
#include "osmocom/bsc/gsm_data.h"
#include "osmocom/bsc/bts_trx.h"
+#include "osmocom/bsc/bts_sm.h"
+#include "osmocom/bsc/abis_om2000.h"
enum bts_counter_id {
BTS_CTR_CHREQ_TOTAL,
+ BTS_CTR_CHREQ_ATTEMPTED_EMERG,
+ BTS_CTR_CHREQ_ATTEMPTED_CALL,
+ BTS_CTR_CHREQ_ATTEMPTED_LOCATION_UPD,
+ BTS_CTR_CHREQ_ATTEMPTED_PAG,
+ BTS_CTR_CHREQ_ATTEMPTED_PDCH,
+ 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,
+ BTS_CTR_CHAN_RF_FAIL_TCH,
+ BTS_CTR_CHAN_RF_FAIL_SDCCH,
BTS_CTR_CHAN_RLL_ERR,
BTS_CTR_BTS_OML_FAIL,
BTS_CTR_BTS_RSL_FAIL,
@@ -35,6 +54,8 @@ enum bts_counter_id {
BTS_CTR_PAGING_NO_ACTIVE_PAGING,
BTS_CTR_PAGING_MSC_FLUSH,
BTS_CTR_CHAN_ACT_TOTAL,
+ BTS_CTR_CHAN_ACT_SDCCH,
+ BTS_CTR_CHAN_ACT_TCH,
BTS_CTR_CHAN_ACT_NACK,
BTS_CTR_RSL_UNKNOWN,
BTS_CTR_RSL_IPA_NACK,
@@ -50,6 +71,7 @@ enum bts_counter_id {
BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK,
BTS_CTR_LCHAN_BORKEN_EV_VTY,
BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN,
+ BTS_CTR_LCHAN_BORKEN_EV_TS_ERROR,
BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RR_CHAN_MODE_MODIFY_ACK,
BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RSL_CHAN_MODE_MODIFY_ACK,
BTS_CTR_TS_BORKEN_FROM_NOT_INITIALIZED,
@@ -64,12 +86,30 @@ enum bts_counter_id {
BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK,
BTS_CTR_TS_BORKEN_EV_TEARDOWN,
BTS_CTR_ASSIGNMENT_ATTEMPTED,
+ BTS_CTR_ASSIGNMENT_ATTEMPTED_SIGN,
+ BTS_CTR_ASSIGNMENT_ATTEMPTED_SPEECH,
BTS_CTR_ASSIGNMENT_COMPLETED,
+ BTS_CTR_ASSIGNMENT_COMPLETED_SIGN,
+ BTS_CTR_ASSIGNMENT_COMPLETED_SPEECH,
BTS_CTR_ASSIGNMENT_STOPPED,
+ BTS_CTR_ASSIGNMENT_STOPPED_SIGN,
+ BTS_CTR_ASSIGNMENT_STOPPED_SPEECH,
BTS_CTR_ASSIGNMENT_NO_CHANNEL,
+ BTS_CTR_ASSIGNMENT_NO_CHANNEL_SIGN,
+ BTS_CTR_ASSIGNMENT_NO_CHANNEL_SPEECH,
BTS_CTR_ASSIGNMENT_TIMEOUT,
+ BTS_CTR_ASSIGNMENT_TIMEOUT_SIGN,
+ BTS_CTR_ASSIGNMENT_TIMEOUT_SPEECH,
BTS_CTR_ASSIGNMENT_FAILED,
+ BTS_CTR_ASSIGNMENT_FAILED_SIGN,
+ BTS_CTR_ASSIGNMENT_FAILED_SPEECH,
BTS_CTR_ASSIGNMENT_ERROR,
+ BTS_CTR_ASSIGNMENT_ERROR_SIGN,
+ BTS_CTR_ASSIGNMENT_ERROR_SPEECH,
+ BTS_CTR_LOCATION_UPDATE_ACCEPT,
+ BTS_CTR_LOCATION_UPDATE_REJECT,
+ BTS_CTR_LOCATION_UPDATE_DETACH,
+ BTS_CTR_LOCATION_UPDATE_UNKNOWN,
BTS_CTR_HANDOVER_ATTEMPTED,
BTS_CTR_HANDOVER_COMPLETED,
BTS_CTR_HANDOVER_STOPPED,
@@ -91,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,
@@ -104,123 +151,49 @@ enum bts_counter_id {
BTS_CTR_INTER_BSC_HO_IN_FAILED,
BTS_CTR_INTER_BSC_HO_IN_TIMEOUT,
BTS_CTR_INTER_BSC_HO_IN_ERROR,
+ BTS_CTR_SRVCC_ATTEMPTED,
+ BTS_CTR_SRVCC_COMPLETED,
+ BTS_CTR_SRVCC_STOPPED,
+ BTS_CTR_SRVCC_NO_CHANNEL,
+ 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,
};
-static const struct rate_ctr_desc bts_ctr_description[] = {
- [BTS_CTR_CHREQ_TOTAL] = {"chreq:total", "Received channel requests"},
- [BTS_CTR_CHREQ_SUCCESSFUL] = {"chreq:successful", "Successful channel requests (immediate assign sent)"},
- [BTS_CTR_CHREQ_NO_CHANNEL] = {"chreq:no_channel", "Sent to MS no channel available"},
- [BTS_CTR_CHAN_RF_FAIL] = {"chan:rf_fail", "Received a RF failure indication from BTS"},
- [BTS_CTR_CHAN_RLL_ERR] = {"chan:rll_err", "Received a RLL failure with T200 cause from BTS"},
- [BTS_CTR_BTS_OML_FAIL] = {"oml_fail", "Received a TEI down on a OML link"},
- [BTS_CTR_BTS_RSL_FAIL] = {"rsl_fail", "Received a TEI down on a OML link"},
- [BTS_CTR_CODEC_AMR_F] = {"codec:amr_f", "Count the usage of AMR/F codec by channel mode requested"},
- [BTS_CTR_CODEC_AMR_H] = {"codec:amr_h", "Count the usage of AMR/H codec by channel mode requested"},
- [BTS_CTR_CODEC_EFR] = {"codec:efr", "Count the usage of EFR codec by channel mode requested"},
- [BTS_CTR_CODEC_V1_FR] = {"codec:fr", "Count the usage of FR codec by channel mode requested"},
- [BTS_CTR_CODEC_V1_HR] = {"codec:hr", "Count the usage of HR codec by channel mode requested"},
-
- [BTS_CTR_PAGING_ATTEMPTED] = {"paging:attempted", "Paging attempts for a subscriber"},
- [BTS_CTR_PAGING_ALREADY] = {"paging:already", "Paging attempts ignored as subscriber was already being paged"},
- [BTS_CTR_PAGING_RESPONDED] = {"paging:responded", "Paging attempts with successful paging response"},
- [BTS_CTR_PAGING_EXPIRED] = {"paging:expired", "Paging Request expired because of timeout T3113"},
- [BTS_CTR_PAGING_NO_ACTIVE_PAGING] = {"paging:no_active_paging", "Paging response without an active paging request (arrived after paging expiration?)"},
- [BTS_CTR_PAGING_MSC_FLUSH] = {"paging:msc_flush", "Paging flushed due to MSC Reset BSSMAP message"},
- [BTS_CTR_CHAN_ACT_TOTAL] = {"chan_act:total", "Total number of Channel Activations"},
- [BTS_CTR_CHAN_ACT_NACK] = {"chan_act:nack", "Number of Channel Activations that the BTS NACKed"},
- [BTS_CTR_RSL_UNKNOWN] = {"rsl:unknown", "Number of unknown/unsupported RSL messages received from BTS"},
- [BTS_CTR_RSL_IPA_NACK] = {"rsl:ipa_nack", "Number of IPA (RTP/dyn-PDCH) related NACKs received from BTS"},
- [BTS_CTR_RSL_DELETE_IND] = {"rsl:delete_ind", "Number of RSL DELETE INDICATION (DL CCCH overload)"},
- [BTS_CTR_MODE_MODIFY_NACK] = {"chan:mode_modify_nack", "Number of Channel Mode Modify NACKs received from BTS"},
-
- /* lchan/TS BORKEN state counters */
- [BTS_CTR_LCHAN_BORKEN_FROM_UNUSED] = {"lchan_borken:from_state:unused", "Transitions from lchan UNUSED state to BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_FROM_WAIT_ACTIV_ACK] = {"lchan_borken:from_state:wait_activ_ack", "Transitions from lchan WAIT_ACTIV_ACK state to BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RF_RELEASE_ACK] = {"lchan_borken:from_state:wait_rf_release_ack", "Transitions from lchan WAIT_RF_RELEASE_ACK state to BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_FROM_BORKEN] = {"lchan_borken:from_state:borken", "Transitions from lchan BORKEN state to BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RR_CHAN_MODE_MODIFY_ACK] = {"lchan_borken:from_state:wait_rr_chan_mode_modify_ack", "Transitions from lchan WAIT_RR_CHAN_MODE_MODIFY_ACK state to BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RSL_CHAN_MODE_MODIFY_ACK] = {"lchan_borken:from_state:wait_rsl_chan_mode_modify_ack", "Transitions from lchan RSL_CHAN_MODE_MODIFY_ACK state to BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_FROM_UNKNOWN] = {"lchan_borken:from_state:unknown", "Transitions from an unknown lchan state to BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK] = {"lchan_borken:event:chan_activ_ack", "CHAN_ACTIV_ACK received in the lchan BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK] = {"lchan_borken:event:chan_activ_nack", "CHAN_ACTIV_NACK received in the lchan BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK] = {"lchan_borken:event:rf_chan_rel_ack", "RF_CHAN_REL_ACK received in the lchan BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_EV_VTY] = {"lchan_borken:event:vty", "VTY commands received in the lchan BORKEN state"},
- [BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN] = {"lchan_borken:event:teardown", "lchan in a BORKEN state is shutting down (BTS disconnected?)"},
- [BTS_CTR_TS_BORKEN_FROM_NOT_INITIALIZED] = {"ts_borken:from_state:not_initialized", "Transitions from TS NOT_INITIALIZED state to BORKEN state"},
- [BTS_CTR_TS_BORKEN_FROM_UNUSED] = {"ts_borken:from_state:unused", "Transitions from TS UNUSED state to BORKEN state"},
- [BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_ACT] = {"ts_borken:from_state:wait_pdch_act", "Transitions from TS WAIT_PDCH_ACT state to BORKEN state"},
- [BTS_CTR_TS_BORKEN_FROM_PDCH] = {"ts_borken:from_state:pdch", "Transitions from TS PDCH state to BORKEN state"},
- [BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_DEACT] = {"ts_borken:from_state:wait_pdch_deact", "Transitions from TS WAIT_PDCH_DEACT state to BORKEN state"},
- [BTS_CTR_TS_BORKEN_FROM_IN_USE] = {"ts_borken:from_state:in_use", "Transitions from TS IN_USE state to BORKEN state"},
- [BTS_CTR_TS_BORKEN_FROM_BORKEN] = {"ts_borken:from_state:borken", "Transitions from TS BORKEN state to BORKEN state"},
- [BTS_CTR_TS_BORKEN_FROM_UNKNOWN] = {"ts_borken:from_state:unknown", "Transitions from an unknown TS state to BORKEN state"},
- [BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK] = {"ts_borken:event:pdch_act_ack_nack", "PDCH_ACT_ACK/NACK received in the TS BORKEN state"},
- [BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK] = {"ts_borken:event:pdch_deact_ack_nack", "PDCH_DEACT_ACK/NACK received in the TS BORKEN state"},
- [BTS_CTR_TS_BORKEN_EV_TEARDOWN] = {"ts_borken:event:teardown", "TS in a BORKEN state is shutting down (BTS disconnected?)"},
- [BTS_CTR_ASSIGNMENT_ATTEMPTED] = {"assignment:attempted", "Assignment attempts"},
- [BTS_CTR_ASSIGNMENT_COMPLETED] = {"assignment:completed", "Assignment completed"},
- [BTS_CTR_ASSIGNMENT_STOPPED] = {"assignment:stopped", "Connection ended during Assignment"},
- [BTS_CTR_ASSIGNMENT_NO_CHANNEL] = {"assignment:no_channel", "Failure to allocate lchan for Assignment"},
- [BTS_CTR_ASSIGNMENT_TIMEOUT] = {"assignment:timeout", "Assignment timed out"},
- [BTS_CTR_ASSIGNMENT_FAILED] = {"assignment:failed", "Received Assignment Failure message"},
- [BTS_CTR_ASSIGNMENT_ERROR] = {"assignment:error", "Assignment failed for other reason"},
-
- [BTS_CTR_HANDOVER_ATTEMPTED] = {"handover:attempted", "Intra-BSC handover attempts"},
- [BTS_CTR_HANDOVER_COMPLETED] = {"handover:completed", "Intra-BSC handover completed"},
- [BTS_CTR_HANDOVER_STOPPED] = {"handover:stopped", "Connection ended during HO"},
- [BTS_CTR_HANDOVER_NO_CHANNEL] = {"handover:no_channel", "Failure to allocate lchan for HO"},
- [BTS_CTR_HANDOVER_TIMEOUT] = {"handover:timeout", "Handover timed out"},
- [BTS_CTR_HANDOVER_FAILED] = {"handover:failed", "Received Handover Fail messages"},
- [BTS_CTR_HANDOVER_ERROR] = {"handover:error", "Re-assignment failed for other reason"},
-
- [BTS_CTR_INTRA_CELL_HO_ATTEMPTED] = {"intra_cell_ho:attempted", "Intra-Cell handover attempts"},
- [BTS_CTR_INTRA_CELL_HO_COMPLETED] = {"intra_cell_ho:completed", "Intra-Cell handover completed"},
- [BTS_CTR_INTRA_CELL_HO_STOPPED] = {"intra_cell_ho:stopped", "Connection ended during HO"},
- [BTS_CTR_INTRA_CELL_HO_NO_CHANNEL] = {"intra_cell_ho:no_channel", "Failure to allocate lchan for HO"},
- [BTS_CTR_INTRA_CELL_HO_TIMEOUT] = {"intra_cell_ho:timeout", "Handover timed out"},
- [BTS_CTR_INTRA_CELL_HO_FAILED] = {"intra_cell_ho:failed", "Received Handover Fail messages"},
- [BTS_CTR_INTRA_CELL_HO_ERROR] = {"intra_cell_ho:error", "Re-assignment failed for other reason"},
-
- [BTS_CTR_INTRA_BSC_HO_ATTEMPTED] = {"intra_bsc_ho:attempted", "Intra-BSC handover attempts"},
- [BTS_CTR_INTRA_BSC_HO_COMPLETED] = {"intra_bsc_ho:completed", "Intra-BSC handover completed"},
- [BTS_CTR_INTRA_BSC_HO_STOPPED] = {"intra_bsc_ho:stopped", "Connection ended during HO"},
- [BTS_CTR_INTRA_BSC_HO_NO_CHANNEL] = {"intra_bsc_ho:no_channel", "Failure to allocate lchan for HO"},
- [BTS_CTR_INTRA_BSC_HO_TIMEOUT] = {"intra_bsc_ho:timeout", "Handover timed out"},
- [BTS_CTR_INTRA_BSC_HO_FAILED] = {"intra_bsc_ho:failed", "Received Handover Fail messages"},
- [BTS_CTR_INTRA_BSC_HO_ERROR] = {"intra_bsc_ho:error", "Re-assignment failed for other reason"},
-
- [BTS_CTR_INTER_BSC_HO_OUT_ATTEMPTED] = {"interbsc_ho_out:attempted",
- "Attempts to handover to remote BSS"},
- [BTS_CTR_INTER_BSC_HO_OUT_COMPLETED] = {"interbsc_ho_out:completed",
- "Handover to remote BSS completed"},
- [BTS_CTR_INTER_BSC_HO_OUT_STOPPED] = {"interbsc_ho_out:stopped", "Connection ended during HO"},
- [BTS_CTR_INTER_BSC_HO_OUT_TIMEOUT] = {"interbsc_ho_out:timeout", "Handover timed out"},
- [BTS_CTR_INTER_BSC_HO_OUT_FAILED] = {"interbsc_ho_out:failed", "Received Handover Fail message"},
- [BTS_CTR_INTER_BSC_HO_OUT_ERROR] = {"interbsc_ho_out:error",
- "Handover to remote BSS failed for other reason"},
-
- [BTS_CTR_INTER_BSC_HO_IN_ATTEMPTED] = {"interbsc_ho_in:attempted",
- "Attempts to handover from remote BSS"},
- [BTS_CTR_INTER_BSC_HO_IN_COMPLETED] = {"interbsc_ho_in:completed",
- "Handover from remote BSS completed"},
- [BTS_CTR_INTER_BSC_HO_IN_STOPPED] = {"interbsc_ho_in:stopped", "Connection ended during HO"},
- [BTS_CTR_INTER_BSC_HO_IN_NO_CHANNEL] = {"interbsc_ho_in:no_channel",
- "Failure to allocate lchan for HO"},
- [BTS_CTR_INTER_BSC_HO_IN_TIMEOUT] = {"interbsc_ho_in:timeout", "Handover from remote BSS timed out"},
- [BTS_CTR_INTER_BSC_HO_IN_FAILED] = {"interbsc_ho_in:failed", "Received Handover Fail message"},
- [BTS_CTR_INTER_BSC_HO_IN_ERROR] = {"interbsc_ho_in:error",
- "Handover from remote BSS failed for other reason"},
-};
-
-static const struct rate_ctr_group_desc bts_ctrg_desc = {
- "bts",
- "base transceiver station",
- OSMO_STATS_CLASS_GLOBAL,
- ARRAY_SIZE(bts_ctr_description),
- bts_ctr_description,
-};
+extern const struct rate_ctr_desc bts_ctr_description[];
+extern const struct rate_ctr_group_desc bts_ctrg_desc;
enum {
+ BTS_STAT_UPTIME_SECONDS,
BTS_STAT_CHAN_LOAD_AVERAGE,
BTS_STAT_CHAN_CCCH_SDCCH4_USED,
BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL,
@@ -236,8 +209,8 @@ enum {
BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL,
BTS_STAT_CHAN_SDCCH8_CBCH_USED,
BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL,
- BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_USED,
- BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_TOTAL,
+ BTS_STAT_CHAN_OSMO_DYN_USED,
+ BTS_STAT_CHAN_OSMO_DYN_TOTAL,
BTS_STAT_T3122,
BTS_STAT_RACH_BUSY,
BTS_STAT_RACH_ACCESS,
@@ -245,63 +218,12 @@ enum {
BTS_STAT_RSL_CONNECTED,
BTS_STAT_LCHAN_BORKEN,
BTS_STAT_TS_BORKEN,
+ BTS_STAT_NUM_TRX_RSL_CONNECTED,
+ BTS_STAT_NUM_TRX_TOTAL,
};
-static const struct osmo_stat_item_desc bts_stat_desc[] = {
- [BTS_STAT_CHAN_LOAD_AVERAGE] = { "chanloadavg", "Channel load average", "%", 16, 0 },
- [BTS_STAT_CHAN_CCCH_SDCCH4_USED] = { "chan_ccch_sdcch4:used",
- "Number of CCCH+SDCCH4 channels used", "", 16, 0 },
- [BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL] = { "chan_ccch_sdcch4:total",
- "Number of CCCH+SDCCH4 channels total", "", 16, 0 },
- [BTS_STAT_CHAN_TCH_F_USED] = { "chan_tch_f:used",
- "Number of TCH/F channels used", "", 16, 0 },
- [BTS_STAT_CHAN_TCH_F_TOTAL] = { "chan_tch_f:total",
- "Number of TCH/F channels total", "", 16, 0 },
- [BTS_STAT_CHAN_TCH_H_USED] = { "chan_tch_h:used",
- "Number of TCH/H channels used", "", 16, 0 },
- [BTS_STAT_CHAN_TCH_H_TOTAL] = { "chan_tch_h:total",
- "Number of TCH/H channels total", "", 16, 0 },
- [BTS_STAT_CHAN_SDCCH8_USED] = { "chan_sdcch8:used",
- "Number of SDCCH8 channels used", "", 16, 0 },
- [BTS_STAT_CHAN_SDCCH8_TOTAL] = { "chan_sdcch8:total",
- "Number of SDCCH8 channels total", "", 16, 0 },
- [BTS_STAT_CHAN_TCH_F_PDCH_USED] = { "chan_tch_f_pdch:used",
- "Number of TCH/F_PDCH channels used", "", 16, 0 },
- [BTS_STAT_CHAN_TCH_F_PDCH_TOTAL] = { "chan_tch_f_pdch:total",
- "Number of TCH/F_PDCH channels total", "", 16, 0 },
- [BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED] = { "chan_ccch_sdcch4_cbch:used",
- "Number of CCCH+SDCCH4+CBCH channels used", "", 16, 0 },
- [BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL] = { "chan_ccch_sdcch4_cbch:total",
- "Number of CCCH+SDCCH4+CBCH channels total", "", 16, 0 },
- [BTS_STAT_CHAN_SDCCH8_CBCH_USED] = { "chan_sdcch8_cbch:used",
- "Number of SDCCH8+CBCH channels used", "", 16, 0 },
- [BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL] = { "chan_sdcch8_cbch:total",
- "Number of SDCCH8+CBCH channels total", "", 16, 0 },
- [BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_USED] = { "chan_tch_f_tch_h_pdch:used",
- "Number of TCH/F_TCH/H_PDCH channels used", "", 16, 0 },
- [BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_TOTAL] = { "chan_tch_f_tch_h_pdch:total",
- "Number of TCH/F_TCH/H_PDCH channels total", "", 16, 0 },
- [BTS_STAT_T3122] = { "T3122", "T3122 IMMEDIATE ASSIGNMENT REJECT wait indicator",
- "s", 16, GSM_T3122_DEFAULT },
- [BTS_STAT_RACH_BUSY] = { "rach_busy",
- "RACH slots with signal above threshold", "%", 16, 0 },
- [BTS_STAT_RACH_ACCESS] = { "rach_access",
- "RACH slots with access bursts in them", "%", 16, 0 },
- [BTS_STAT_OML_CONNECTED] = { "oml_connected", "Number of OML links connected", "", 16, 0 },
- [BTS_STAT_RSL_CONNECTED] = { "rsl_connected", "Number of RSL links connected", "", 16, 0 },
- [BTS_STAT_LCHAN_BORKEN] = { "lchan_borken",
- "Number of lchans in the BORKEN state", "", 16, 0 },
- [BTS_STAT_TS_BORKEN] = { "ts_borken",
- "Number of timeslots in the BORKEN state", "", 16, 0 },
-};
-
-static const struct osmo_stat_item_group_desc bts_statg_desc = {
- .group_name_prefix = "bts",
- .group_description = "base transceiver station",
- .class_id = OSMO_STATS_CLASS_GLOBAL,
- .num_items = ARRAY_SIZE(bts_stat_desc),
- .item_desc = bts_stat_desc,
-};
+extern const struct osmo_stat_item_desc bts_stat_desc[];
+extern const struct osmo_stat_item_group_desc bts_statg_desc;
enum gsm_bts_type {
GSM_BTS_TYPE_UNKNOWN,
@@ -341,12 +263,27 @@ struct gsm_bts_model {
const char *name;
bool started;
+ /* start the model itself */
int (*start)(struct gsm_network *net);
+
+ /* initialize a single BTS for this model */
+ int (*bts_init)(struct gsm_bts *bts);
+
+ /* initialize a single TRX for this model */
+ int (*trx_init)(struct gsm_bts_trx *trx);
+
int (*oml_rcvmsg)(struct msgb *msg);
char * (*oml_status)(const struct gsm_bts *bts);
void (*e1line_bind_ops)(struct e1inp_line *line);
+ /* (Optional) function for encoding MS/BS Power Control paramaters */
+ int (*power_ctrl_enc_rsl_params)(struct msgb *msg, const struct gsm_power_ctrl_params *cp);
+ /* (Optional) function for sending default MS/BS Power Control paramaters */
+ int (*power_ctrl_send_def_params)(const struct gsm_bts_trx *trx);
+ /* (Optional) function for toggling BCCH carrier power reduction operation */
+ int (*power_ctrl_set_c0_power_red)(const struct gsm_bts *bts, const uint8_t red);
+
void (*config_write_bts)(struct vty *vty, struct gsm_bts *bts);
void (*config_write_trx)(struct vty *vty, struct gsm_bts_trx *trx);
void (*config_write_ts)(struct vty *vty, struct gsm_bts_trx_ts *ts);
@@ -361,6 +298,13 @@ struct gsm_bts_model {
uint8_t _features_data[MAX_BTS_FEATURES/8];
};
+struct gsm_gprs_cell {
+ struct gsm_abis_mo mo;
+ uint16_t bvci;
+ uint8_t timer[11];
+ struct gprs_rlc_cfg rlc_cfg;
+};
+
/* One BTS */
struct gsm_bts {
/* list header in net->bts_list */
@@ -394,6 +338,8 @@ struct gsm_bts {
/* Connected PCU version (if any) */
char pcu_version[MAX_VERSION_LENGTH];
+ /* PCU sign_link, over OML line: */
+ struct e1inp_sign_link *osmo_link;
/* maximum Tx power that the MS is permitted to use in this cell */
int ms_max_power;
@@ -425,9 +371,7 @@ struct gsm_bts {
/* CCCH is on C0 */
struct gsm_bts_trx *c0;
- struct {
- struct gsm_abis_mo mo;
- } site_mgr;
+ struct gsm_bts_sm *site_mgr; /* backpointer */
/* bitmask of all SI that are present/valid in si_buf */
uint32_t si_valid;
@@ -461,6 +405,7 @@ struct gsm_bts {
struct gsm_envabtse envabtse[4];
} bs11;
struct {
+ struct osmo_fsm_inst *bts_fi;
struct {
struct om2k_mo om2k_mo;
struct gsm_abis_mo mo;
@@ -493,6 +438,7 @@ struct gsm_bts {
uint16_t limit;
uint16_t active;
} om2k_version[16];
+ enum om2k_sync_src sync_src;
} rbs2000;
struct {
uint8_t bts_type;
@@ -509,22 +455,18 @@ struct gsm_bts {
/* Not entirely sure how ip.access specific this is */
struct {
enum bts_gprs_mode mode;
- struct {
- struct gsm_abis_mo mo;
- uint16_t nsei;
- uint8_t timer[7];
- } nse;
- struct {
- struct gsm_abis_mo mo;
- uint16_t bvci;
- uint8_t timer[11];
- struct gprs_rlc_cfg rlc_cfg;
- } cell;
- struct gsm_bts_gprs_nsvc nsvc[2];
+ struct gsm_gprs_cell cell;
uint8_t rac;
uint8_t net_ctrl_ord;
bool ctrl_ack_type_use_block;
bool egprs_pkt_chan_request;
+ struct {
+ bool active; /* CCN_ACTIVE */
+ bool forced_vty; /* set by VTY ? */
+ } ccn; /* TS 44.060 sec 8.8.2 */
+ struct {
+ uint8_t alpha; /* ALPHA*10, units of 0.1, range <0-10> */
+ } pwr_ctrl; /* TS 44.060 Table 12.9.1 */
} gprs;
/* threshold (in percent) when BTS shall send CCCH LOAD IND */
@@ -553,13 +495,21 @@ struct gsm_bts {
* rather than starting from TRX0 and go upwards? */
int chan_alloc_reverse;
+ /* When true, interference measurements from the BTS are used in the channel allocator to favor lchans with less
+ * interference reported in RSL Resource Indication. */
+ bool chan_alloc_avoid_interf;
+
+ /* When true (default), TCH can be allocated to serve
+ * non-voicecall-related signalling services when SDCCHs are exhausted */
+ bool chan_alloc_allow_tch_for_signalling;
+
enum neigh_list_manual_mode neigh_list_manual_mode;
/* parameters from which we build SYSTEM INFORMATION */
struct {
struct gsm48_rach_control rach_control;
uint8_t ncc_permitted;
struct gsm48_cell_sel_par cell_sel_par;
- struct gsm48_si_selection_params cell_ro_sel_par; /* rest octet */
+ struct osmo_gsm48_si_selection_params cell_ro_sel_par; /* rest octet */
struct gsm48_cell_options cell_options;
struct gsm48_control_channel_descr chan_desc;
struct bitvec neigh_list;
@@ -567,6 +517,7 @@ struct gsm_bts {
struct bitvec si5_neigh_list;
struct osmo_earfcn_si2q si2quater_neigh_list;
size_t uarfcn_length; /* index for uarfcn and scramble lists */
+ size_t cell_chan_num; /* number of channels in Cell Allocation */
struct {
/* bitmask large enough for all possible ARFCN's */
uint8_t neigh_list[1024/8];
@@ -613,10 +564,13 @@ struct gsm_bts {
struct handover_cfg *ho;
- /* A list of struct gsm_bts_ref, indicating neighbors of this BTS.
- * When the si_common neigh_list is in automatic mode, it is populated from this list as well as
- * gsm_network->neighbor_bss_cells. */
- struct llist_head local_neighbors;
+ /* Local and remote neighbor configuration: a list of neighbors as written in the VTY config, not resolved to
+ * actual cells. Entries may point at non-existing BTS numbers, or yet unconfigured ARFCN+BSIC. The point of
+ * this list is to keep the config as the user entered it: a) to write it back exactly as entered, and b) to
+ * allow adding neighbor cells that will only be configured further down in the config file.
+ * An actual neighbor cell object (local or remote-BSS) is resolved "at runtime" whenever a neighbor is being
+ * looked up. */
+ struct llist_head neighbors;
/* BTS-specific overrides for timer values from struct gsm_network. */
uint8_t T3122; /* ASSIGNMENT REJECT wait indication */
@@ -635,6 +589,43 @@ struct gsm_bts {
struct llist_head oml_fail_rep;
struct llist_head chan_rqd_queue;
+
+ /* 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;
+ struct gsm_power_ctrl_params bs_power_ctrl;
+
+ /* Maximum BCCH carrier power reduction */
+ uint8_t c0_max_power_red_db;
+
+ /* Interference Measurement Parameters, as read from VTY */
+ struct gsm_interf_meas_params interf_meas_params_cfg;
+ /* Interference Measurement Parameters, as last sent via OML */
+ struct gsm_interf_meas_params interf_meas_params_used;
+
+ /* We will ignore CHAN RQD with access delay greater than rach_max_delay */
+ uint8_t rach_max_delay;
+
+ /* 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])
@@ -654,7 +645,7 @@ static inline int is_ipaccess_bts(const struct gsm_bts *bts)
return 0;
}
-static inline int is_sysmobts_v2(const struct gsm_bts *bts)
+static inline int is_osmobts(const struct gsm_bts *bts)
{
switch (bts->type) {
case GSM_BTS_TYPE_OSMOBTS:
@@ -725,12 +716,14 @@ static inline const struct osmo_location_area_id *bts_lai(struct gsm_bts *bts)
return &lai;
}
-struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num);
+struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, struct gsm_bts_sm *bts_sm, uint8_t bts_num);
char *gsm_bts_name(const struct gsm_bts *bts);
bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai);
bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id);
+void gsm_bts_cell_id(struct gsm0808_cell_id *cell_id, const struct gsm_bts *bts);
+void gsm_bts_cell_id_list(struct gsm0808_cell_id_list2 *cell_id_list, const struct gsm_bts *bts);
int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor);
int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor);
@@ -744,12 +737,20 @@ struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num);
int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode);
+#define BTS_STORE_UPTIME_INTERVAL 10 /* in seconds */
+void bts_store_uptime(struct gsm_bts *bts);
+
unsigned long long bts_uptime(const struct gsm_bts *bts);
char *get_model_oml_status(const struct gsm_bts *bts);
/* reset the state of all MO in the BTS */
void gsm_bts_mo_reset(struct gsm_bts *bts);
+static inline bool gsm_bts_features_negotiated(struct gsm_bts *bts)
+{
+ return bts->mo.get_attr_rep_received || bts->mo.nm_state.operational == NM_OPSTATE_ENABLED;
+}
+
/* dependency handling */
void bts_depend_mark(struct gsm_bts *bts, int dep);
void bts_depend_clear(struct gsm_bts *bts, int dep);
@@ -761,10 +762,12 @@ 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);
+
+void gsm_bts_stats_reset(struct gsm_bts *bts);
+
int gsm_bts_model_register(struct gsm_bts_model *model);
struct gsm_bts_model *bts_model_find(enum gsm_bts_type type);
diff --git a/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h b/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h
index bc7860b2d..687f16a23 100644
--- a/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h
+++ b/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h
@@ -24,9 +24,13 @@
#include <stdint.h>
#include <osmocom/core/msgb.h>
+struct gsm_bts_sm;
+struct gsm_bts;
+struct gsm_bts_trx;
+
struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts);
-struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts);
+struct msgb *nanobts_attr_nse_get(struct gsm_bts_sm *bts_sm);
struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts);
-struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts);
+struct msgb *nanobts_attr_nsvc_get(struct gsm_bts *bts);
struct msgb *nanobts_attr_radio_get(struct gsm_bts *bts,
struct gsm_bts_trx *trx);
diff --git a/include/osmocom/bsc/bts_sm.h b/include/osmocom/bsc/bts_sm.h
new file mode 100644
index 000000000..7ed7aba0a
--- /dev/null
+++ b/include/osmocom/bsc/bts_sm.h
@@ -0,0 +1,79 @@
+/* BTS Site Manager */
+
+/* (C) 2020 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@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/>.
+ *
+ */
+
+#pragma once
+
+#include <unistd.h>
+#include <stdint.h>
+
+#include "osmocom/bsc/gsm_data.h"
+
+struct gsm_bts;
+
+struct gsm_gprs_nse {
+ struct gsm_abis_mo mo;
+ uint16_t nsei;
+ uint8_t timer[7];
+};
+
+struct gsm_gprs_nsvc {
+ struct gsm_bts *bts;
+ /* data read via VTY config file, to configure the BTS
+ * via OML from BSC */
+ int id;
+ uint16_t nsvci;
+ uint16_t local_port; /* on the BTS */
+ struct osmo_sockaddr remote;
+ struct gsm_abis_mo mo;
+};
+
+
+/* BTS Site Manager */
+struct gsm_bts_sm {
+ struct gsm_bts *bts[1]; /* only one bts supported so far */
+ struct gsm_abis_mo mo;
+ /* nanoBTS and old versions of osmo-bts behaves this way due to
+ broken FSMs not following TS 12.21: they never do
+ Dependency->Offline transition, but they should be OPSTARTed
+ nevertheless during Dependnecy state to work. This field is
+ used by all dependent NM objects. */
+ bool peer_has_no_avstate_offline;
+ struct {
+ struct gsm_gprs_nse nse;
+ struct gsm_gprs_nsvc nsvc[2];
+ } gprs;
+};
+
+static inline struct gsm_bts *gsm_bts_sm_get_bts(struct gsm_bts_sm *site_mgr) {
+ return site_mgr->bts[0];
+}
+
+struct gsm_bts_sm *gsm_bts_sm_alloc(struct gsm_network *net, uint8_t bts_num);
+
+void gsm_bts_sm_mo_reset(struct gsm_bts_sm *bts_sm);
+
+static inline struct gsm_gprs_nsvc *gsm_bts_sm_nsvc_num(struct gsm_bts_sm *bts_sm, uint8_t nsvc_num)
+{
+ if (nsvc_num >= ARRAY_SIZE(bts_sm->gprs.nsvc))
+ return NULL;
+ return &bts_sm->gprs.nsvc[nsvc_num];
+}
diff --git a/include/osmocom/bsc/bts_trx.h b/include/osmocom/bsc/bts_trx.h
index 3a635ba28..c8df9d9c4 100644
--- a/include/osmocom/bsc/bts_trx.h
+++ b/include/osmocom/bsc/bts_trx.h
@@ -13,11 +13,16 @@
#include <osmocom/abis/e1_input.h>
#include "osmocom/bsc/gsm_data.h"
+#include "osmocom/bsc/abis_om2000.h"
struct gsm_bts;
#define TRX_NR_TS 8
+struct gsm_bts_bb_trx {
+ struct gsm_abis_mo mo;
+};
+
/* One TRX in a BTS */
struct gsm_bts_trx {
/* list header in bts->trx_list */
@@ -26,12 +31,10 @@ struct gsm_bts_trx {
struct gsm_bts *bts;
/* number of this TRX in the BTS */
uint8_t nr;
- /* human readable name / description */
- char *description;
/* how do we talk RSL with this TRX? */
struct gsm_e1_subslot rsl_e1_link;
- uint8_t rsl_tei;
- struct e1inp_sign_link *rsl_link;
+ uint8_t rsl_tei_primary;
+ struct e1inp_sign_link *rsl_link_primary;
/* Timeout for initiating the RSL connection. */
struct osmo_timer_list rsl_connect_timeout;
@@ -41,9 +44,7 @@ struct gsm_bts_trx {
struct gsm_abis_mo mo;
struct tlv_parsed nm_attr;
- struct {
- struct gsm_abis_mo mo;
- } bb_transc;
+ struct gsm_bts_bb_trx bb_transc;
uint16_t arfcn;
int nominal_power; /* in dBm */
@@ -64,6 +65,7 @@ struct gsm_bts_trx {
struct rxlev_stats rxlev_stat;
} ipaccess;
struct {
+ struct osmo_fsm_inst *trx_fi;
struct {
struct om2k_mo om2k_mo;
} trxc;
@@ -73,11 +75,16 @@ struct gsm_bts_trx {
struct {
struct om2k_mo om2k_mo;
} tx;
+ enum om2k_rx_diversity rx_diversity;
} rbs2000;
};
struct gsm_bts_trx_ts ts[TRX_NR_TS];
};
+static inline struct gsm_bts_trx *gsm_bts_bb_trx_get_trx(struct gsm_bts_bb_trx *bb_transc) {
+ return (struct gsm_bts_trx *)container_of(bb_transc, struct gsm_bts_trx, bb_transc);
+}
+
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts);
char *gsm_trx_name(const struct gsm_bts_trx *trx);
@@ -88,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 0380b74c5..a3cad68b7 100644
--- a/include/osmocom/bsc/debug.h
+++ b/include/osmocom/bsc/debug.h
@@ -28,6 +28,8 @@ enum {
DAS,
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 91dcbe35a..1f50ef982 100644
--- a/include/osmocom/bsc/gsm_04_08_rr.h
+++ b/include/osmocom/bsc/gsm_04_08_rr.h
@@ -21,7 +21,8 @@ int send_siemens_mrpci(struct gsm_lchan *lchan,
uint8_t *classmark2_lv);
int gsm48_send_rr_classmark_enquiry(struct gsm_lchan *lchan);
int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
-int gsm48_multirate_config(uint8_t *lv, const struct gsm48_multi_rate_conf *mr_conf,
+int gsm48_multirate_config(struct msgb *msg,
+ const struct gsm48_multi_rate_conf *mr_conf,
const struct amr_mode *modes, unsigned int num_modes);
struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref);
int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
@@ -32,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 691ab5af4..238b2f78a 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -18,10 +18,11 @@
#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>
-#include <osmocom/bsc/rest_octets.h>
+#include <osmocom/gsm/gsm48_rest_octets.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/gsm/gsm_utils.h>
@@ -31,11 +32,8 @@
#include <osmocom/abis/e1_input.h>
#include <osmocom/bsc/meas_rep.h>
#include <osmocom/bsc/acc.h>
-#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/osmux.h>
-#include <osmocom/sigtran/sccp_sap.h>
-
#define GSM_T3122_DEFAULT 10
struct mgcp_client_conf;
@@ -54,6 +52,7 @@ struct bsc_subscr;
struct gprs_ra_id;
struct handover;
struct osmo_sccp_instance;
+struct smlc_config;
#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3]
@@ -107,16 +106,53 @@ enum channel_rate {
CH_RATE_FULL,
};
+enum channel_rate chan_t_to_chan_rate(enum gsm_chan_t chan_t);
+
+enum lchan_csd_mode {
+ LCHAN_CSD_M_NT,
+ LCHAN_CSD_M_T_1200_75,
+ LCHAN_CSD_M_T_600,
+ LCHAN_CSD_M_T_1200,
+ LCHAN_CSD_M_T_2400,
+ LCHAN_CSD_M_T_9600,
+ LCHAN_CSD_M_T_14400,
+ LCHAN_CSD_M_T_29000,
+ LCHAN_CSD_M_T_32000,
+};
+
struct channel_mode_and_rate {
enum gsm48_chan_mode chan_mode;
enum channel_rate chan_rate;
uint16_t s15_s0;
+ /* only used for GSM48_CMODE_DATA_* */
+ enum lchan_csd_mode csd_mode;
+};
+
+enum assign_for {
+ ASSIGN_FOR_NONE,
+ ASSIGN_FOR_BSSMAP_REQ,
+ ASSIGN_FOR_CONGESTION_RESOLUTION,
+ ASSIGN_FOR_VTY,
+};
+
+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. */
struct assignment_request {
+ enum assign_for assign_for;
+
bool aoip;
uint16_t msc_assigned_cic;
@@ -128,15 +164,37 @@ struct assignment_request {
/* Rate/codec setting in preference order (need at least 1 !) */
int n_ch_mode_rate;
- struct channel_mode_and_rate ch_mode_rate[3];
+ struct channel_mode_and_rate ch_mode_rate_list[3];
+
+ /* An assignment request usually requests to assign any available lchan, to match above requirements. This may
+ * also choose to just keep the current lchan and merely modify it as appropriate. In these cases, keep
+ * target_lchan == NULL.
+ * In some situations, an assignment to a specific target lchan is requested (congestion resolution, VAMOS
+ * multiplexing, user request via VTY). In these situations, select a target lchan beforehand and point
+ * target_lchan to it. */
+ struct gsm_lchan *target_lchan;
+
+ /* 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
* currently used lchan and gscon unmodified until the outcome of an Assignment is known. If the Assignment fails, this
* state is simply discarded, and the gscon carries on with the original lchan remaining unchanged. */
struct assignment_fsm_data {
+ /* The request as made by the caller, see GSCON_EV_ASSIGNMENT_START or reassignment_request_to_lchan() /
+ * reassignment_request_to_chan_type().
+ * conn->assignment.req is treated immutable: remains unchanged throughout the Assignment. The mutable fields
+ * are below: choices and automatic adjustments are stored in conn->assignment.*, not conn->assignment.req.
+ */
struct assignment_request req;
+
bool requires_voice_stream;
+ struct channel_mode_and_rate selected_ch_mode_rate;
struct osmo_fsm_inst *fi;
struct gsm_lchan *new_lchan;
@@ -175,10 +233,16 @@ extern const struct value_string handover_scope_names[];
inline static const char *handover_scope_name(enum handover_scope val)
{ return get_value_string(handover_scope_names, val); }
+/* Cell ARFCN + BSIC. */
+struct cell_ab {
+ uint16_t arfcn;
+ uint8_t bsic;
+};
+
struct handover_out_req {
enum hodec_id from_hodec_id;
struct gsm_lchan *old_lchan;
- struct neighbor_ident_key target_nik;
+ struct cell_ab target_cell_ab;
enum gsm_chan_t new_lchan_type; /*< leave GSM_LCHAN_NONE to use same as old_lchan */
};
@@ -186,6 +250,8 @@ struct handover_in_req {
struct gsm0808_channel_type ct;
struct gsm0808_speech_codec_list scl;
struct gsm0808_encrypt_info ei;
+ bool kc128_present;
+ uint8_t kc128[16];
struct gsm_classmark classmark;
/* chosen_encr_alg reflects the encoded value as in RSL_ENC_ALG_A5(a5_numer):
* chosen_encr_alg == 1 means A5/0 i.e. no encryption, chosen_encr_alg == 4 means A5/3.
@@ -198,6 +264,8 @@ struct handover_in_req {
uint16_t msc_assigned_cic;
char msc_assigned_rtp_addr[INET6_ADDRSTRLEN];
uint16_t msc_assigned_rtp_port;
+ bool last_eutran_plmn_valid;
+ struct osmo_plmn_id last_eutran_plmn;
};
struct handover {
@@ -206,7 +274,11 @@ struct handover {
enum hodec_id from_hodec_id;
enum handover_scope scope;
enum gsm_chan_t new_lchan_type;
- struct neighbor_ident_key target_cell;
+ struct cell_ab target_cell_ab;
+
+ /* For inter-BSC handover, this may reflect more than one Cell ID. Must also be set for intra-BSC handover,
+ * because it is used as key for penalty timers (e.g. in handover decision 2). */
+ struct gsm0808_cell_id_list2 target_cell_ids;
uint8_t ho_ref;
struct gsm_bts *new_bts;
@@ -249,7 +321,7 @@ struct gsm_subscriber_connection {
struct {
int failures;
- struct penalty_timers *penalty_timers;
+ struct llist_head penalty_timers;
} hodec2;
/* "Codec List (MSC Preferred)" as received by the BSSAP Assignment Request. 3GPP 48.008
@@ -323,6 +395,15 @@ struct gsm_subscriber_connection {
enum subscr_sccp_state state;
} lb;
} lcs;
+
+ struct gsm48_classmark3 cm3;
+ bool cm3_valid;
+
+ struct {
+ bool allowed; /* Is fast return to LTE allowed once the conn is released? */
+ bool last_eutran_plmn_valid; /* Is information stored in field below available? */
+ struct osmo_plmn_id last_eutran_plmn;
+ } fast_return;
};
@@ -347,6 +428,11 @@ enum gsm_chreq_reason_t {
GSM_CHREQ_REASON_PDCH,
};
+static inline bool gsm_chreq_reason_is_voicecall(enum gsm_chreq_reason_t reason)
+{
+ return reason == GSM_CHREQ_REASON_EMERG || reason == GSM_CHREQ_REASON_CALL;
+}
+
/* lchans 0..3 are SDCCH in combined channel configuration,
use 4 as magic number for BCCH hack - see osmo-bts-../oml.c:opstart_compl() */
#define CCCH_LCHAN 4
@@ -394,6 +480,14 @@ struct gsm_abis_mo {
struct gsm_nm_state nm_state;
struct tlv_parsed *nm_attr;
struct gsm_bts *bts;
+ struct osmo_fsm_inst *fi;
+ bool opstart_sent;
+ bool adm_unlock_sent;
+ bool get_attr_sent;
+ bool get_attr_rep_received;
+ bool set_attr_sent;
+ bool set_attr_ack_received;
+ bool force_rf_lock;
};
/* Ericsson OM2000 Managed Object */
@@ -461,18 +555,6 @@ struct amr_multirate_conf {
};
/* /BTS ONLY */
-enum lchan_csd_mode {
- LCHAN_CSD_M_NT,
- LCHAN_CSD_M_T_1200_75,
- LCHAN_CSD_M_T_600,
- LCHAN_CSD_M_T_1200,
- LCHAN_CSD_M_T_2400,
- LCHAN_CSD_M_T_9600,
- LCHAN_CSD_M_T_14400,
- LCHAN_CSD_M_T_29000,
- LCHAN_CSD_M_T_32000,
-};
-
/* State of the SAPIs in the lchan */
enum lchan_sapi_state {
LCHAN_SAPI_S_NONE,
@@ -488,6 +570,8 @@ struct gsm_encr {
uint8_t alg_id;
uint8_t key_len;
uint8_t key[MAX_A5_KEY_LEN];
+ bool kc128_present;
+ uint8_t kc128[16];
};
#define LOGPLCHAN(lchan, ss, level, fmt, args...) \
@@ -498,69 +582,119 @@ struct gsm_encr {
bsc_subscr_name(lchan && lchan->conn ? lchan->conn->bsub : NULL), \
## args)
-/* Iterate lchans that have an FSM allocated based based on explicit pchan kind
- * (GSM_PCHAN_* constant).
- * Remark: PDCH related lchans are not handled in BSC but in PCU, so trying to
- * iterate through GSM_PCHAN_PDCH is considered a void loop.
+/* Iterate at most N lchans of the given timeslot.
+ * usage:
+ * struct gsm_lchan *lchan;
+ * struct gsm_bts_trx_ts *ts = get_some_timeslot();
+ * ts_for_n_lchans(lchan, ts, 3) {
+ * LOG_LCHAN(lchan, LOGL_DEBUG, "hello world\n");
+ * }
*/
-#define ts_as_pchan_for_each_lchan(lchan, ts, as_pchan) \
+#define ts_for_n_lchans(lchan, ts, N) \
for (lchan = (ts)->lchan; \
((lchan - (ts)->lchan) < ARRAY_SIZE((ts)->lchan)) \
&& lchan->fi \
- && lchan->nr < pchan_subslots(as_pchan); \
+ && ((lchan - (ts)->lchan) < (N)); \
lchan++)
-/* Iterate lchans that have an FSM allocated based on current PCHAN
- * mode set in \ref ts.
- * usage:
- * struct gsm_lchan *lchan;
- * struct gsm_bts_trx_ts *ts = get_some_timeslot();
- * ts_for_each_lchan(lchan, ts) {
- * LOGPLCHAN(DMAIN, LOGL_DEBUG, "hello world\n");
- * }
- */
-#define ts_for_each_lchan(lchan, ts) ts_as_pchan_for_each_lchan(lchan, ts, (ts)->pchan_is)
-
-/* Iterate over all possible lchans available that have an FSM allocated based
- * on PCHAN \ref ts (dynamic) configuration.
- * Iterate all lchan instances set up by this \ref ts type, including those
- * lchans currently disabled or in process of being enabled (e.g. due to dynamic
- * timeslot in switchover). Compare ts_for_each_lchan(), which iterates only the
- * enabled lchans.
- * For example, it is useful in case dynamic timeslot \ref ts is in process of
- * switching from configuration PDCH (no lchans) to TCH_F (1 lchan), where
- * pchan_is is still set to PDCH but \ref ts may contain already an \ref lchan
- * of type TCH_F which initiated the request to switch the \ts configuration.
- */
-#define ts_for_each_potential_lchan(lchan, ts) ts_as_pchan_for_each_lchan(lchan, ts, (ts)->pchan_on_init)
-
-enum lchan_activate_mode {
- FOR_NONE,
- FOR_MS_CHANNEL_REQUEST,
- FOR_ASSIGNMENT,
- FOR_HANDOVER,
- FOR_VTY,
+enum lchan_activate_for {
+ ACTIVATE_FOR_NONE,
+ ACTIVATE_FOR_MS_CHANNEL_REQUEST,
+ ACTIVATE_FOR_ASSIGNMENT,
+ ACTIVATE_FOR_HANDOVER,
+ ACTIVATE_FOR_VTY,
+ ACTIVATE_FOR_MODE_MODIFY_RTP,
};
extern const struct value_string lchan_activate_mode_names[];
-static inline const char *lchan_activate_mode_name(enum lchan_activate_mode activ_for)
+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_mode activ_for;
+ 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;
- /* This always is for a specific lchan, so its lchan->type indicates full or half rate.
- * When a dyn TS was selected, the lchan->type has been set to the desired rate. */
- enum gsm48_chan_mode chan_mode;
+ struct channel_mode_and_rate ch_mode_rate;
struct gsm_encr encr;
- /* AMR config */
- uint16_t s15_s0;
bool requires_voice_stream;
bool wait_before_switching_rtp; /*< true = requires LCHAN_EV_READY_TO_SWITCH_RTP */
uint16_t msc_assigned_cic;
/* During intra-BSC handover, we keep the MGW endpoint intact and just re-route to the new lchan. This
* activate_info is for the new lchan, the re_use_mgw_endpoint_from_lchan points at the old lchan. */
struct gsm_lchan *re_use_mgw_endpoint_from_lchan;
+ bool ta_known;
+ uint8_t ta;
+
+ /* 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 {
+ MODIFY_FOR_NONE,
+ MODIFY_FOR_ASSIGNMENT,
+ MODIFY_FOR_VTY,
+};
+
+extern const struct value_string lchan_modify_for_names[];
+static inline const char *lchan_modify_for_name(enum lchan_modify_for modify_for)
+{ return get_value_string(lchan_modify_for_names, modify_for); }
+
+struct lchan_modify_info {
+ enum lchan_modify_for modify_for;
+ struct channel_mode_and_rate ch_mode_rate;
+ bool requires_voice_stream;
+ uint16_t msc_assigned_cic;
+
+ /* 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;
+};
+
+#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 {
@@ -577,7 +711,15 @@ struct gsm_lchan {
struct osmo_mgcpc_ep_ci *mgw_endpoint_ci_bts;
struct {
+ /* The request as made by the caller, see lchan_activate().
+ * lchan->activate.info is treated immutable: remains unchanged throughout the Activation.
+ * The mutable versions are below: some values need automatic adjustments, in which case they are copied
+ * from immutable lchan->activate.info.* to lchan->activate.*, for example lchan->activate.ch_mode_rate
+ * is initially a copy of lchan->activate.info.ch_mode_rate, and is possibly adjusted afterwards. */
struct lchan_activate_info info;
+
+ struct channel_mode_and_rate ch_mode_rate;
+ struct gsm48_multi_rate_conf mr_conf_filtered;
bool activ_ack; /*< true as soon as RSL Chan Activ Ack is received */
bool immediate_assignment_sent;
/*! This flag ensures that when an lchan activation has succeeded, and we have already
@@ -585,14 +727,37 @@ 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;
struct {
+ /* The request as made by the caller, see lchan_mode_modify().
+ * lchan->modify.info is treated immutable: remains unchanged throughout the Mode Modify.
+ * The mutable versions are below: some values need automatic adjustments, in which case they are copied
+ * from immutable lchan->modify.info.* to lchan->modify.*, for example lchan->modify.ch_mode_rate
+ * is initially a copy of lchan->modify.info.ch_mode_rate, and is possibly adjusted afterwards. */
+ struct lchan_modify_info info;
+
+ 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;
+
+ struct {
/* If an event to release the lchan comes in while still waiting for responses, just mark this
* flag, so that the lchan will gracefully release at the next sensible junction. */
bool requested;
bool do_rr_release;
enum gsm48_rr_cause rr_cause;
+ bool last_eutran_plmn_valid;
+ struct osmo_plmn_id last_eutran_plmn;
/* There is an RSL error cause of value 0, so we need a separate flag. */
bool in_error;
@@ -602,30 +767,16 @@ struct gsm_lchan {
/* If a release event is being handled, ignore other ricocheting release events until that
* release handling has concluded. */
bool in_release_handler;
-
- /* is this release at the end of a CSFB call? */
- bool is_csfb;
} release;
/* The logical channel type */
enum gsm_chan_t type;
- /* RSL channel mode */
- enum rsl_cmod_spd rsl_cmode;
- /* If TCH, traffic channel mode */
- enum gsm48_chan_mode tch_mode;
- enum lchan_csd_mode csd_mode;
/* Power levels for MS and BTS */
- uint8_t bs_power;
+ uint8_t bs_power_db;
uint8_t ms_power;
/* Encryption information */
struct gsm_encr encr;
- /* AMR bits */
- uint8_t mr_ms_lv[7];
- uint8_t mr_bts_lv[7];
- /* AMR bits were based on these rate bits: */
- uint16_t s15_s0;
-
/* Established data link layer services */
uint8_t sapis[8];
@@ -647,7 +798,9 @@ struct gsm_lchan {
} ass_compl;
} abis_ip;
- uint8_t rqd_ta;
+ /* At first, the Timing Advance from the initial Channel Request. Later, the Timing Advance value received from
+ * the most recent Measurement Report. */
+ uint8_t last_ta;
/* table of neighbor cell measurements */
struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS];
@@ -664,12 +817,35 @@ struct gsm_lchan {
struct gsm_subscriber_connection *conn;
- /* Depending on the preferences that where submitted together with
- * the assignment and the current channel load, the BSC has to select
- * one of the offered codec/rates. The final selection by the BSC is
- * stored here and is used when sending the assignment complete or
- * when performing a handover procedure. */
- struct channel_mode_and_rate ch_mode_rate;
+ /* After the Channel Activation ACK or RSL Mode Modify ACK is received, this reflects the actually used
+ * channel_mode_and_rate. */
+ struct channel_mode_and_rate current_ch_mode_rate;
+ struct gsm48_multi_rate_conf current_mr_conf;
+
+ /* Circuit-Switched TSC Set in use, or -1 if no specific TSC Set was requested. The valid range is 1-4 as
+ * described in the spec 3GPP TS 45.002. */
+ int tsc_set;
+ /* Training Sequence Code in use. The valid range is 0-7 as described in the spec 3GPP TS 45.002. */
+ uint8_t tsc;
+
+ struct {
+ /* Whether this lchan represents a secondary "shadow" lchan to multiplex a second MS onto a primary
+ * "normal" lchan */
+ bool is_secondary;
+
+ /* Whether this lchan is activated/modified into a mode that allows VAMOS multiplexing at this moment */
+ bool enabled;
+ } vamos;
+
+ /* dBm value of interference level as reported in the most recent Resource Indication, if any for this lchan. Or
+ * INTERF_DBM_UNKNOWN if this lchan was not included in the most recent Resource Indication.
+ * The range is typically -115 to -85 dBm, here stored 1:1 as a signed integer, to ease comparison. */
+ int16_t interf_dbm;
+ /* 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 */
@@ -685,7 +861,7 @@ struct gsm_bts_trx_ts {
* vty after OML activation. Gets written on vty 'write file'. */
enum gsm_phys_chan_config pchan_from_config;
/* When the timeslot OML is established, pchan_from_config is copied here. This is the pchan
- * currently in effect; for dynamic ts, this is the dyn kind (GSM_PCHAN_TCH_F_TCH_H_PDCH or
+ * currently in effect; for dynamic ts, this is the dyn kind (GSM_PCHAN_OSMO_DYN or
* GSM_PCHAN_TCH_F_PDCH) and does not show the pchan type currently active. */
enum gsm_phys_chan_config pchan_on_init;
/* This is the *actual* pchan type currently active. For dynamic timeslots, this reflects either
@@ -730,6 +906,15 @@ struct gsm_bts_trx_ts {
} rbs2000;
};
+ /* Maximum BCCH carrier power reduction */
+ uint8_t c0_max_power_red_db;
+
+ /* Maximum number of lchans that could become usable, for example by switching a dynamic timeslot's type or by
+ * enabling VAMOS secondary lchans. This does include the maximum count of possible VAMOS secondary lchans. */
+ uint8_t max_lchans_possible;
+ /* Currently usable lchans, according to the current pchan mode (for dynamic timeslots, this may change).
+ * Does not include count of secondary VAMOS lchans. */
+ uint8_t max_primary_lchans;
struct gsm_lchan lchan[TS_MAX_LCHAN];
};
@@ -759,17 +944,6 @@ struct gsm_envabtse {
struct gsm_abis_mo mo;
};
-struct gsm_bts_gprs_nsvc {
- struct gsm_bts *bts;
- /* data read via VTY config file, to configure the BTS
- * via OML from BSC */
- int id;
- uint16_t nsvci;
- uint16_t local_port; /* on the BTS */
- struct osmo_sockaddr remote;
- struct gsm_abis_mo mo;
-};
-
enum gprs_rlc_par {
RLC_T3142,
RLC_T3169,
@@ -843,12 +1017,6 @@ struct load_counter {
unsigned int used;
};
-/* Useful to track N-N relations between BTS, for example neighbors. */
-struct gsm_bts_ref {
- struct llist_head entry;
- struct gsm_bts *bts;
-};
-
/* A single Page of a SMSCB message */
struct bts_smscb_page {
/* SMSCB message we're part of */
@@ -934,10 +1102,11 @@ const char *gsm_lchant_name(enum gsm_chan_t c);
const char *gsm_chreq_name(enum gsm_chreq_reason_t c);
char *gsm_ts_name(const struct gsm_bts_trx_ts *ts);
char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts);
-char *gsm_lchan_name_compute(const struct gsm_lchan *lchan);
+void lchan_update_name(struct gsm_lchan *lchan);
static inline char *gsm_lchan_name(const struct gsm_lchan *lchan)
{
+ OSMO_ASSERT(lchan);
return lchan->name;
}
@@ -952,170 +1121,33 @@ void *
gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class,
const struct abis_om_obj_inst *obj_inst);
-uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
- uint8_t ts_nr, uint8_t lchan_nr);
-uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan);
-uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan,
- enum gsm_phys_chan_config as_pchan);
+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);
-void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
- const struct gsm_lchan *lchan);
-void gsm48_lchan2chan_desc_as_configured(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan);
+int gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
+ const struct gsm_lchan *lchan,
+ uint8_t tsc, bool allow_osmo_cbits);
+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);
-enum gsm_phys_chan_config ts_pchan(struct gsm_bts_trx_ts *ts);
uint8_t pchan_subslots(enum gsm_phys_chan_config pchan);
+uint8_t pchan_subslots_vamos(enum gsm_phys_chan_config pchan);
bool ts_is_tch(struct gsm_bts_trx_ts *ts);
+struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos);
+struct gsm_lchan *gsm_lchan_primary_to_vamos(const struct gsm_lchan *lchan_primary);
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_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_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 */
@@ -1124,12 +1156,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 */
@@ -1181,7 +1207,7 @@ struct gsm_network {
/* control interface */
struct ctrl_handle *ctrl;
- /* Allow or disallow TCH/F on dynamic TCH/F_TCH/H_PDCH; OS#1778 */
+ /* Allow or disallow TCH/F on dynamic TCH/F_TCH/H_SDCCH8_PDCH; OS#1778 */
bool dyn_ts_allow_tch_f;
/* all active subscriber connections. */
@@ -1205,14 +1231,28 @@ struct gsm_network {
/* Timer for periodic channel load measurements to maintain each BTS's T3122. */
struct osmo_timer_list t3122_chan_load_timer;
+ /* Timer to write each BTS's uptime counter state to the stats system. */
+ 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;
- /* Remote BSS Cell Identifier Lists */
- struct neighbor_ident_list *neighbor_bss_cells;
+ /* Remote BSS resolution sevice (CTRL iface) */
+ struct {
+ char *addr;
+ uint16_t port;
+ struct ctrl_handle *handle;
+ } neigh_ctrl;
/* Don't refuse to start with mutually exclusive codec settings */
bool allow_unusable_timeslots;
@@ -1220,23 +1260,12 @@ struct gsm_network {
uint8_t nri_bitlen;
struct osmo_nri_ranges *null_nri_ranges;
- struct {
- uint32_t cs7_instance;
- bool cs7_instance_valid;
- struct osmo_sccp_instance *sccp;
- struct osmo_sccp_user *sccp_user;
-
- struct osmo_sccp_addr bsc_addr;
- char *bsc_addr_name;
-
- struct osmo_sccp_addr smlc_addr;
- char *smlc_addr_name;
-
- /*! True after either side has sent a BSSMAP-LE RESET-ACK */
- bool ready;
+ struct smlc_config *smlc;
- struct rate_ctr_group *ctrs;
- } 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 {
@@ -1285,4 +1314,108 @@ enum gsm48_rr_cause bsc_gsm48_rr_cause_from_rsl_cause(uint8_t c);
int bsc_sccp_inst_next_conn_id(struct osmo_sccp_instance *sccp);
+/* MS/BS Power related measurement averaging algo */
+enum gsm_power_ctrl_meas_avg_algo {
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE = 0x00,
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED = 0x01,
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED = 0x02,
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN = 0x03,
+ /* EWMA is an Osmocom specific algo */
+ GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA = 0x04,
+};
+
+/* MS/BS Power related measurement parameters */
+struct gsm_power_ctrl_meas_params {
+ /* 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 */
+
+ /* Threshold Comparators for lower (decreasing) direction */
+ uint8_t lower_cmp_p; /* P1 for RxLev, P3 for RxQual */
+ uint8_t lower_cmp_n; /* N1 for RxLev, N3 for RxQual */
+ /* Threshold Comparators for upper (increasing) direction */
+ uint8_t upper_cmp_p; /* P2 for RxLev, P4 for RxQual */
+ uint8_t upper_cmp_n; /* N2 for RxLev, N4 for RxQual */
+
+ /* Hreqave and Hreqt (see 3GPP TS 45.008, Annex A) */
+ uint8_t h_reqave;
+ uint8_t h_reqt;
+
+ /* AVG algorithm and its specific parameters */
+ enum gsm_power_ctrl_meas_avg_algo algo;
+ union {
+ /* Exponentially Weighted Moving Average */
+ struct {
+ /* Smoothing factor: higher the value - less smoothing */
+ uint8_t alpha; /* 1 .. 99 (in %) */
+ } ewma;
+ };
+};
+
+enum gsm_power_ctrl_dir {
+ GSM_PWR_CTRL_DIR_UL, /* MS Power Control */
+ GSM_PWR_CTRL_DIR_DL, /* BS Power Control */
+};
+
+enum gsm_power_ctrl_mode {
+ /* Do not send MS/BS Power Control IEs */
+ GSM_PWR_CTRL_MODE_NONE = 0,
+ /* Send MS/BS Power IE only (with target level) */
+ 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 */
+struct gsm_power_ctrl_params {
+ /* Power Control direction: Uplink or Downlink */
+ enum gsm_power_ctrl_dir dir;
+ /* Power Control mode to be used by the BTS */
+ enum gsm_power_ctrl_mode mode;
+
+ /* BS Power reduction value / maximum (in dB) */
+ uint8_t bs_power_val_db; /* for static mode */
+ uint8_t bs_power_max_db; /* for dynamic mode */
+
+ /* Power change step size (dynamic mode only) */
+ uint8_t inc_step_size_db; /* increasing direction */
+ uint8_t red_step_size_db; /* reducing direction */
+
+ /* Minimum interval between power level changes */
+ uint8_t ctrl_interval; /* 1 step is 2 SACCH periods */
+
+ /* 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 {
+ /* Intave: Interference Averaging period (see 3GPP TS 45.008, table A.1) */
+ uint8_t avg_period; /* number of SACCH multiframes, 1 .. 31 */
+ /* Interference level Boundaries (see 3GPP TS 52.021, section 9.4.25) */
+ uint8_t bounds_dbm[6]; /* -x dBm values for boundaries 0 .. X5 */
+};
+
+extern const struct gsm_interf_meas_params interf_meas_params_def;
+
+enum rsl_cmod_spd chan_mode_to_rsl_cmod_spd(enum gsm48_chan_mode chan_mode);
+
#endif /* _GSM_DATA_H */
diff --git a/include/osmocom/bsc/handover.h b/include/osmocom/bsc/handover.h
index d22ac8696..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;
@@ -82,12 +82,11 @@ enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection
void bsc_tx_bssmap_ho_failure(struct gsm_subscriber_connection *conn);
int find_handover_target_cell(struct gsm_bts **local_target_cell_p,
- const struct gsm0808_cell_id_list2 **remote_target_cell_p,
- struct gsm_subscriber_connection *conn, const struct neighbor_ident_key *search_for,
+ struct gsm0808_cell_id_list2 *remote_target_cells,
+ struct gsm_subscriber_connection *conn,
+ const struct cell_ab *search_for,
bool log_errors);
-struct neighbor_ident_key *bts_ident_key(const struct gsm_bts *bts);
-
void handover_parse_inter_bsc_mt(struct gsm_subscriber_connection *conn,
struct msgb *ho_request_msg);
diff --git a/include/osmocom/bsc/handover_cfg.h b/include/osmocom/bsc/handover_cfg.h
index f174aad77..2bd26812e 100644
--- a/include/osmocom/bsc/handover_cfg.h
+++ b/include/osmocom/bsc/handover_cfg.h
@@ -38,22 +38,10 @@ static inline int bool2i(bool arg)
return arg? 1 : 0;
}
-static inline bool a2tdma(const char *arg)
-{
- if (!strcmp(arg, "full"))
- return true;
- return false;
-}
-
-static inline const char *tdma2a(bool val)
-{
- return val? "full" : "subset";
-}
-
/* The HO_CFG_ONE_MEMBER macro gets redefined, depending on whether to define struct members,
* function declarations or definitions... It is of the format
* HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL,
- * VTY_CMD, VTY_CMD_ARG, VTY_ARG_EVAL,
+ * VTY_CMD_PREFIX, VTY_CMD, VTY_CMD_ARG, VTY_ARG_EVAL,
* VTY_WRITE_FMT, VTY_WRITE_CONV,
* VTY_DOC)
* Then using HO_CFG_ALL_MEMBERS can save a lot of code dup in defining API declaration, API
@@ -188,10 +176,13 @@ static inline const char *tdma2a(bool val)
"Disable in-call assignment\n" \
"Enable in-call assignment\n") \
\
- HO_CFG_ONE_MEMBER(bool, hodec2_full_tdma, subset, \
- "handover2 ", "tdma-measurement", "full|subset", a2tdma, "%s", tdma2a, \
+ HO_CFG_ONE_MEMBER(enum tdma_meas_set, hodec2_tdma_meas_set, subset, \
+ "handover2 ", "tdma-measurement", "auto|full|subset", \
+ tdma_meas_set_from_str, "%s", tdma_meas_set_name, \
HO_CFG_STR_HANDOVER2 \
"Define measurement set of TDMA frames\n" \
+ "Use full set when DTX is not in use, use subset when DTX is in use," \
+ " as indicated by each Measurement Report\n" \
"Full set of 102/104 TDMA frames\n" \
"Sub set of 4 TDMA frames (SACCH)\n") \
\
@@ -206,8 +197,11 @@ static inline const char *tdma2a(bool val)
"handover2 ", "min rxqual", "<0-7>", atoi, "%d", as_is, \
HO_CFG_STR_HANDOVER2 \
HO_CFG_STR_MIN \
- "How bad may RxQual of an MS become before triggering HO\n" \
- "minimum RxQual\n") \
+ "How bad may RxQual of an MS become before triggering HO," \
+ " where 0 is the best quality (bit error rate < 0.2%) and" \
+ " 7 is the worst quality (bit error rate > 12.8%)," \
+ " see 3GPP TS 45.008 8.2.4.\n" \
+ "worst acceptable RxQual\n") \
\
HO_CFG_ONE_MEMBER(int, hodec2_afs_bias_rxlev, 0, \
"handover2 ", "afs-bias rxlev", "<0-20>", atoi, "%d", as_is, \
@@ -267,6 +261,13 @@ static inline const char *tdma2a(bool val)
" see also 'handover2 retries'\n" \
"Seconds\n") \
\
+ HO_CFG_ONE_MEMBER(int, hodec2_penalty_low_rxqual_as, 60, \
+ "handover2 ", "penalty-time low-rxqual-assignment", "<0-99999>", atoi, "%d", as_is, \
+ HO_CFG_STR_HANDOVER2 \
+ HO_CFG_STR_PENALTY_TIME \
+ "Time to suspend re-assignment after an lchan was re-assigned because of low RxQual\n" \
+ "Seconds\n") \
+ \
HO_CFG_ONE_MEMBER(int, hodec2_retries, 0, \
"handover2 ", "retries", "<0-9>", atoi, "%d", as_is, \
HO_CFG_STR_HANDOVER2 \
diff --git a/include/osmocom/bsc/handover_ctrl.h b/include/osmocom/bsc/handover_ctrl.h
new file mode 100644
index 000000000..c0bd70c19
--- /dev/null
+++ b/include/osmocom/bsc/handover_ctrl.h
@@ -0,0 +1,3 @@
+#pragma once
+
+int bsc_ho_ctrl_cmds_install(void *ctx);
diff --git a/include/osmocom/bsc/handover_fsm.h b/include/osmocom/bsc/handover_fsm.h
index 1628d8fd9..052e09d79 100644
--- a/include/osmocom/bsc/handover_fsm.h
+++ b/include/osmocom/bsc/handover_fsm.h
@@ -57,7 +57,7 @@ struct handover_rr_detect_data {
void handover_fsm_init();
-void handover_request(struct handover_out_req *req);
+int handover_request(struct handover_out_req *req);
void handover_start(struct handover_out_req *req);
void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
struct msgb *ho_request_msg);
diff --git a/include/osmocom/bsc/lb.h b/include/osmocom/bsc/lb.h
index e04df7d28..dbea0b0e1 100644
--- a/include/osmocom/bsc/lb.h
+++ b/include/osmocom/bsc/lb.h
@@ -1,7 +1,10 @@
/* Location Services (LCS): low level Lb/SCCP handling in OsmoBSC, API */
#pragma once
+#include <stdbool.h>
+#include <stdint.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/sigtran/sccp_sap.h>
struct bssap_le_pdu;
struct gsm_subscriber_connection;
@@ -31,9 +34,30 @@ enum {
SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_ABORT,
};
+struct smlc_config {
+ bool enable;
+
+ uint32_t cs7_instance;
+ bool cs7_instance_valid;
+ struct osmo_sccp_instance *sccp;
+ struct osmo_sccp_user *sccp_user;
+
+ struct osmo_sccp_addr bsc_addr;
+ char *bsc_addr_name;
+
+ struct osmo_sccp_addr smlc_addr;
+ char *smlc_addr_name;
+
+ /*! Lb link is ready when bssmap_reset_is_conn_ready(bssmap_reset) returns true. */
+ struct bssmap_reset *bssmap_reset;
+
+ struct rate_ctr_group *ctrs;
+};
+
extern const struct rate_ctr_desc smlc_ctr_description[];
extern const struct rate_ctr_group_desc smlc_ctrg_desc;
int lb_init();
+int lb_start_or_stop();
int lb_send(struct gsm_subscriber_connection *conn, const struct bssap_le_pdu *bssap_le);
void lb_close_conn(struct gsm_subscriber_connection *conn);
diff --git a/include/osmocom/bsc/lchan_fsm.h b/include/osmocom/bsc/lchan_fsm.h
index 9fe7db107..eb8312e03 100644
--- a/include/osmocom/bsc/lchan_fsm.h
+++ b/include/osmocom/bsc/lchan_fsm.h
@@ -6,11 +6,18 @@
/* This macro automatically includes a final \n, if omitted. */
#define LOG_LCHAN(lchan, level, fmt, args...) do { \
- if (lchan->fi) \
- LOGPFSML(lchan->fi, level, "(type=%s) " fmt, gsm_lchant_name(lchan->type), ## args); \
+ if ((lchan)->fi) \
+ 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)
enum lchan_fsm_state {
LCHAN_ST_UNUSED,
@@ -53,17 +60,19 @@ void lchan_fsm_init();
void lchan_fsm_alloc(struct gsm_lchan *lchan);
void lchan_release(struct gsm_lchan *lchan, bool do_rr_release,
- bool err, enum gsm48_rr_cause cause_rr);
+ bool err, enum gsm48_rr_cause cause_rr,
+ const struct osmo_plmn_id *last_eutran_plmn);
void lchan_activate(struct gsm_lchan *lchan, struct lchan_activate_info *info);
void lchan_ready_to_switch_rtp(struct gsm_lchan *lchan);
+void lchan_mode_modify(struct gsm_lchan *lchan, struct lchan_modify_info *info);
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);
@@ -73,6 +82,5 @@ bool lchan_may_receive_data(struct gsm_lchan *lchan);
void lchan_forget_conn(struct gsm_lchan *lchan);
-void lchan_set_last_error(struct gsm_lchan *lchan, const char *fmt, ...);
-
void lchan_fsm_skip_error(struct gsm_lchan *lchan);
+void lchan_fsm_update_id(struct gsm_lchan *lchan);
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/lchan_select.h b/include/osmocom/bsc/lchan_select.h
index 41e7015cf..3bb0d1ce6 100644
--- a/include/osmocom/bsc/lchan_select.h
+++ b/include/osmocom/bsc/lchan_select.h
@@ -2,6 +2,8 @@
#pragma once
struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type);
+enum gsm_chan_t chan_mode_to_chan_type(enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate);
struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts,
enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate);
-struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type);
+struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type, bool log);
+void lchan_select_set_type(struct gsm_lchan *lchan, enum gsm_chan_t type);
diff --git a/include/osmocom/bsc/lcs_loc_req.h b/include/osmocom/bsc/lcs_loc_req.h
index babb4324d..86540f278 100644
--- a/include/osmocom/bsc/lcs_loc_req.h
+++ b/include/osmocom/bsc/lcs_loc_req.h
@@ -4,11 +4,11 @@
#include <osmocom/gsm/bssmap_le.h>
#define LOG_LCS_LOC_REQ(LOC_REQ, level, fmt, args...) do { \
- if (LOC_REQ) \
- LOGPFSML((LOC_REQ)->fi, level, fmt, ## args); \
- else \
- LOGP(DLCS, level, "LCS Perf Loc Req: " fmt, ## args); \
- } while(0)
+ if (LOC_REQ) \
+ LOGPFSML((LOC_REQ)->fi, level, fmt, ## args); \
+ else \
+ LOGP(DLCS, level, "LCS Perf Loc Req: " fmt, ## args); \
+ } 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/meas_rep.h b/include/osmocom/bsc/meas_rep.h
index b0c03f0bb..402a888b1 100644
--- a/include/osmocom/bsc/meas_rep.h
+++ b/include/osmocom/bsc/meas_rep.h
@@ -38,7 +38,7 @@ struct gsm_meas_rep {
struct gsm_meas_rep_unidir ul;
struct gsm_meas_rep_unidir dl;
- uint8_t bs_power;
+ uint8_t bs_power_db;
/* according to 3GPP TS 48.058 § MS Timing Offset [-63; 192] */
int16_t ms_timing_offset;
struct {
@@ -51,14 +51,38 @@ struct gsm_meas_rep {
struct gsm_meas_rep_cell cell[6];
};
+enum tdma_meas_field {
+ TDMA_MEAS_FIELD_RXLEV = 0,
+ TDMA_MEAS_FIELD_RXQUAL = 1,
+};
+
+enum tdma_meas_dir {
+ TDMA_MEAS_DIR_UL = 0,
+ TDMA_MEAS_DIR_DL = 1,
+};
+
+/* (function choose_meas_rep_field() depends on FULL and SUB being 0 and 1, but doesn't care about AUTO's value) */
+enum tdma_meas_set {
+ TDMA_MEAS_SET_FULL = 0,
+ TDMA_MEAS_SET_SUB = 1,
+ TDMA_MEAS_SET_AUTO,
+};
+
+extern const struct value_string tdma_meas_set_names[];
+static inline const char *tdma_meas_set_name(enum tdma_meas_set val)
+{ return get_value_string(tdma_meas_set_names, val); }
+static inline enum tdma_meas_set tdma_meas_set_from_str(const char *name)
+{ return get_string_value(tdma_meas_set_names, name); }
+
/* obtain an average over the last 'num' fields in the meas reps */
int get_meas_rep_avg(const struct gsm_lchan *lchan,
- enum meas_rep_field field, unsigned int num);
+ enum tdma_meas_field field, enum tdma_meas_dir dir, enum tdma_meas_set set,
+ unsigned int num);
/* Check if N out of M last values for FIELD are >= bd */
int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan,
- enum meas_rep_field field,
- unsigned int n, unsigned int m, int be);
+ enum tdma_meas_field field, enum tdma_meas_dir dir, enum tdma_meas_set set,
+ unsigned int n, unsigned int m, int be);
unsigned int calc_initial_idx(unsigned int array_size,
unsigned int meas_rep_idx,
diff --git a/include/osmocom/bsc/neighbor_ident.h b/include/osmocom/bsc/neighbor_ident.h
index aa3827635..c6a2c4237 100644
--- a/include/osmocom/bsc/neighbor_ident.h
+++ b/include/osmocom/bsc/neighbor_ident.h
@@ -5,56 +5,99 @@
#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
+#include <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/bsc/gsm_data.h>
struct vty;
struct gsm_network;
struct gsm_bts;
-struct neighbor_ident_list;
struct gsm0808_cell_id_list2;
#define NEIGHBOR_IDENT_KEY_ANY_BTS -1
#define BSIC_ANY 0xff
-struct neighbor_ident_key {
- int from_bts; /*< BTS nr 0..255 or NEIGHBOR_IDENT_KEY_ANY_BTS */
- uint16_t arfcn;
- uint8_t bsic;
+enum neighbor_type {
+ NEIGHBOR_TYPE_UNSET = 0,
+ NEIGHBOR_TYPE_BTS_NR = 1,
+ NEIGHBOR_TYPE_CELL_ID = 2,
};
-const char *neighbor_ident_key_name(const struct neighbor_ident_key *ni_key);
+/* One line of VTY neighbor configuration as entered by the user.
+ * One of three variants:
+ *
+ * - just the local-BSS neighbor BTS nr:
+ * neighbor bts 123
+ *
+ * - a neighbor cell identifier *without* ARFCN+BSIC:
+ * neighbor (lac|lac-ci|cgi|cgi-ps) 1 2 3...
+ * This is an elaborate / BTS-nr-agnostic way of indicating a local-BSS neighbor cell.
+ *
+ * - a neighbor cell identifier *with* ARFCN+BSIC:
+ * neighbor (lac|lac-ci|cgi|cgi-ps) 1 2 3... arfcn 456 bsic (23|any)
+ * This can either be
+ * - a remote-BSS neighbor cell, or
+ * - a super elaborate way of indicating a local-BSS neighbor, if this cell id exists in the local BSS.
+ */
+struct neighbor {
+ struct llist_head entry;
+
+ enum neighbor_type type;
+ union {
+ uint8_t bts_nr;
+ struct {
+ struct gsm0808_cell_id id;
+ bool ab_present;
+ struct cell_ab ab;
+ } cell_id;
+ };
+};
+
+int resolve_local_neighbor(struct gsm_bts **local_neighbor_p, const struct gsm_bts *from_bts,
+ const struct neighbor *neighbor);
+int resolve_remote_neighbors(struct gsm_bts *from_bts, const struct cell_ab *target_ab);
+
+int cell_ab_to_str_buf(char *buf, size_t buflen, const struct cell_ab *cell);
+char *cell_ab_to_str_c(void *ctx, const struct cell_ab *cell);
-struct neighbor_ident_list *neighbor_ident_init(void *talloc_ctx);
-void neighbor_ident_free(struct neighbor_ident_list *nil);
+bool cell_ab_match(const struct cell_ab *entry, const struct cell_ab *search_for, bool exact_match);
+bool cell_ab_valid(const struct cell_ab *cell);
-bool neighbor_ident_key_match(const struct neighbor_ident_key *entry,
- const struct neighbor_ident_key *search_for,
- bool exact_match);
+int neighbor_to_str_buf(char *buf, size_t buflen, const struct neighbor *n);
+char *neighbor_to_str_c(void *ctx, const struct neighbor *n);
+bool neighbor_same(const struct neighbor *a, const struct neighbor *b, bool check_cell_ab);
-int neighbor_ident_add(struct neighbor_ident_list *nil, const struct neighbor_ident_key *key,
- const struct gsm0808_cell_id_list2 *val);
-const struct gsm0808_cell_id_list2 *neighbor_ident_get(const struct neighbor_ident_list *nil,
- const struct neighbor_ident_key *key);
-bool neighbor_ident_del(struct neighbor_ident_list *nil, const struct neighbor_ident_key *key);
-void neighbor_ident_clear(struct neighbor_ident_list *nil);
+void bts_cell_ab(struct cell_ab *arfcn_bsic, const struct gsm_bts *bts);
-void neighbor_ident_iter(const struct neighbor_ident_list *nil,
- bool (* iter_cb )(const struct neighbor_ident_key *key,
- const struct gsm0808_cell_id_list2 *val,
- void *cb_data),
- void *cb_data);
+int resolve_neighbors(struct gsm_bts **local_neighbor_p, struct gsm0808_cell_id_list2 *remote_neighbors,
+ struct gsm_bts *from_bts, const struct cell_ab *target_ab, bool log_errors);
-void neighbor_ident_vty_init(struct gsm_network *net, struct neighbor_ident_list *nil);
-void neighbor_ident_vty_write(struct vty *vty, const char *indent, struct gsm_bts *bts);
+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);
-bool neighbor_ident_bts_entry_exists(uint8_t from_bts);
+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);
-#define NEIGHBOR_IDENT_VTY_KEY_PARAMS "arfcn <0-1023> bsic (<0-63>|any)"
-#define NEIGHBOR_IDENT_VTY_KEY_DOC \
+int neighbors_check_cfg();
+
+#define CELL_AB_VTY_PARAMS "arfcn <0-1023> bsic (<0-63>|any)"
+#define CELL_AB_VTY_DOC \
"ARFCN of neighbor cell\n" "ARFCN value\n" \
"BSIC of neighbor cell\n" "BSIC value\n" \
"for all BSICs / use any BSIC in this ARFCN\n"
-bool neighbor_ident_vty_parse_key_params(struct vty *vty, const char **argv,
- struct neighbor_ident_key *key);
-bool neighbor_ident_bts_parse_key_params(struct vty *vty, struct gsm_bts *bts, const char **argv,
- struct neighbor_ident_key *key);
+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);
+
+enum neighbor_ctrl_node {
+ CTRL_NODE_NEIGH = _LAST_CTRL_NODE,
+ _LAST_CTRL_NODE_NEIGHBOR
+};
diff --git a/include/osmocom/bsc/nm_common_fsm.h b/include/osmocom/bsc/nm_common_fsm.h
new file mode 100644
index 000000000..89ec863aa
--- /dev/null
+++ b/include/osmocom/bsc/nm_common_fsm.h
@@ -0,0 +1,113 @@
+/* Header for all NM FSM. Following 3GPP TS 12.21 Figure 2/GSM 12.21:
+ GSM 12.21 Objects' Operational state and availability status behaviour during initialization */
+
+/* (C) 2020 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@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/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/utils.h>
+
+/* Common */
+enum nm_fsm_events {
+ NM_EV_SW_ACT_REP,
+ NM_EV_STATE_CHG_REP,
+ NM_EV_GET_ATTR_REP,
+ NM_EV_SET_ATTR_ACK,
+ NM_EV_OPSTART_ACK,
+ NM_EV_OPSTART_NACK,
+ NM_EV_OML_DOWN,
+ NM_EV_FORCE_LOCK, /* Only supported by RadioCarrier so far */
+ NM_EV_FEATURE_NEGOTIATED, /* Sent by BTS to NSVC MO */
+};
+extern const struct value_string nm_fsm_event_names[];
+
+/* BTS SiteManager */
+enum nm_bts_sm_op_fsm_states {
+ NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED,
+ NM_BTS_SM_ST_OP_DISABLED_DEPENDENCY,
+ NM_BTS_SM_ST_OP_DISABLED_OFFLINE,
+ NM_BTS_SM_ST_OP_ENABLED,
+};
+extern struct osmo_fsm nm_bts_sm_fsm;
+
+/* BTS */
+enum nm_bts_op_fsm_states {
+ NM_BTS_ST_OP_DISABLED_NOTINSTALLED,
+ NM_BTS_ST_OP_DISABLED_DEPENDENCY,
+ NM_BTS_ST_OP_DISABLED_OFFLINE,
+ NM_BTS_ST_OP_ENABLED,
+};
+extern struct osmo_fsm nm_bts_fsm;
+
+/* BaseBand Transceiver */
+enum nm_bb_transc_op_fsm_states {
+ NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED,
+ NM_BB_TRANSC_ST_OP_DISABLED_DEPENDENCY,
+ NM_BB_TRANSC_ST_OP_DISABLED_OFFLINE,
+ NM_BB_TRANSC_ST_OP_ENABLED,
+};
+extern struct osmo_fsm nm_bb_transc_fsm;
+
+/* Radio Carrier */
+enum nm_rcarrier_op_fsm_states {
+ NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED,
+ NM_RCARRIER_ST_OP_DISABLED_DEPENDENCY,
+ NM_RCARRIER_ST_OP_DISABLED_OFFLINE,
+ NM_RCARRIER_ST_OP_ENABLED,
+};
+extern struct osmo_fsm nm_rcarrier_fsm;
+
+/* Radio Channel */
+enum nm_chan_op_fsm_states {
+ NM_CHAN_ST_OP_DISABLED_NOTINSTALLED,
+ NM_CHAN_ST_OP_DISABLED_DEPENDENCY,
+ NM_CHAN_ST_OP_DISABLED_OFFLINE,
+ NM_CHAN_ST_OP_ENABLED,
+};
+extern struct osmo_fsm nm_chan_fsm;
+
+/* GPRS NSE */
+enum nm_gprs_op_nse_states {
+ NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED,
+ NM_GPRS_NSE_ST_OP_DISABLED_DEPENDENCY,
+ NM_GPRS_NSE_ST_OP_DISABLED_OFFLINE,
+ NM_GPRS_NSE_ST_OP_ENABLED,
+};
+extern struct osmo_fsm nm_gprs_nse_fsm;
+
+/* GPRS Cell */
+enum nm_gprs_op_cell_states {
+ NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED,
+ NM_GPRS_CELL_ST_OP_DISABLED_DEPENDENCY,
+ NM_GPRS_CELL_ST_OP_DISABLED_OFFLINE,
+ NM_GPRS_CELL_ST_OP_ENABLED,
+};
+extern struct osmo_fsm nm_gprs_cell_fsm;
+
+/* GPRS NSVC */
+enum nm_gprs_op_nsvc_fsm_states {
+ NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED,
+ NM_GPRS_NSVC_ST_OP_DISABLED_DEPENDENCY,
+ NM_GPRS_NSVC_ST_OP_DISABLED_OFFLINE,
+ NM_GPRS_NSVC_ST_OP_ENABLED,
+};
+extern struct osmo_fsm nm_gprs_nsvc_fsm;
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/osmo_bsc_sigtran.h b/include/osmocom/bsc/osmo_bsc_sigtran.h
index faaceb220..df37cf44f 100644
--- a/include/osmocom/bsc/osmo_bsc_sigtran.h
+++ b/include/osmocom/bsc/osmo_bsc_sigtran.h
@@ -38,7 +38,9 @@ int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *m
int osmo_bsc_sigtran_init(struct llist_head *mscs);
/* Close all open sigtran connections and channels */
-void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc);
+void osmo_bsc_sigtran_reset(struct bsc_msc_data *msc);
+
+void osmo_bsc_sigtran_tx_reset(const struct bsc_msc_data *msc);
/* Send reset-ack to MSC */
void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc);
diff --git a/include/osmocom/bsc/pcuif_proto.h b/include/osmocom/bsc/pcuif_proto.h
index 8f7260280..4921fc38d 100644
--- a/include/osmocom/bsc/pcuif_proto.h
+++ b/include/osmocom/bsc/pcuif_proto.h
@@ -21,8 +21,14 @@
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
+#define PCU_IF_MSG_INTERF_IND 0x53 /* interference report */
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
#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 */
@@ -208,6 +214,46 @@ struct gsm_pcu_if_susp_req {
uint8_t cause;
} __attribute__ ((packed));
+/* Interference measurements on PDCH timeslots */
+struct gsm_pcu_if_interf_ind {
+ uint8_t trx_nr;
+ uint8_t spare[3];
+ uint32_t fn;
+ uint8_t interf[8];
+} __attribute__ ((packed));
+
+/* Contains messages transmitted BSC<->PCU, potentially forwarded by BTS via IPA/PCU */
+struct gsm_pcu_if_container {
+ uint8_t msg_type;
+ uint8_t spare;
+ uint16_t length; /* network byte order */
+ 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 */
@@ -228,6 +274,8 @@ struct gsm_pcu_if {
struct gsm_pcu_if_time_ind time_ind;
struct gsm_pcu_if_pag_req pag_req;
struct gsm_pcu_if_app_info_req app_info_req;
+ struct gsm_pcu_if_interf_ind interf_ind;
+ struct gsm_pcu_if_container container;
} u;
} __attribute__ ((packed));
diff --git a/include/osmocom/bsc/penalty_timers.h b/include/osmocom/bsc/penalty_timers.h
index f5d17786c..d662b3c31 100644
--- a/include/osmocom/bsc/penalty_timers.h
+++ b/include/osmocom/bsc/penalty_timers.h
@@ -2,40 +2,23 @@
* initially used by handover algorithm 2 to keep per-BTS timers for each subscriber connection. */
#pragma once
-/* Opaque struct to manage penalty timers */
-struct penalty_timers;
+#include <osmocom/gsm/gsm0808_utils.h>
-/* Initialize a list of penalty timers.
- * param ctx: talloc context to allocate in.
- * returns an empty struct penalty_timers. */
-struct penalty_timers *penalty_timers_init(void *ctx);
+struct penalty_timer {
+ struct llist_head entry;
-/* Add a penalty timer for an arbitrary object.
- * Note: the ownership of for_object remains with the caller; it is handled as a mere void* value, so
- * invalid pointers can be handled without problems, while common sense dictates that invalidated
- * pointers (freed objects) should probably be removed from this list. More importantly, the pointer must
- * match any pointers used to query penalty timers, so for_object should reference some global/singleton
- * object that tends to stay around longer than the penalty timers.
- * param pt: penalty timers list as from penalty_timers_init().
- * param for_object: arbitrary pointer reference to store a penalty timer for (passing NULL is possible,
- * but note that penalty_timers_clear() will clear all timers if given for_object=NULL).
- * param timeout: penalty time in seconds. */
-void penalty_timers_add(struct penalty_timers *pt, const void *for_object, int timeout);
+ struct gsm0808_cell_id for_target_cell;
+ unsigned int timeout;
+};
-/* Return the amount of penalty time remaining for an object.
- * param pt: penalty timers list as from penalty_timers_init().
- * param for_object: arbitrary pointer reference to query penalty timers for.
- * returns seconds remaining until all penalty time has expired. */
-unsigned int penalty_timers_remaining(struct penalty_timers *pt, const void *for_object);
+void penalty_timers_add(void *ctx, struct llist_head *penalty_timers,
+ const struct gsm0808_cell_id *for_target_cell, int timeout);
+void penalty_timers_add_list(void *ctx, struct llist_head *penalty_timers,
+ const struct gsm0808_cell_id_list2 *for_target_cells, int timeout);
-/* Clear penalty timers for one or all objects.
- * param pt: penalty timers list as from penalty_timers_init().
- * param for_object: arbitrary pointer reference to clear penalty time for,
- * or NULL to clear all timers. */
-void penalty_timers_clear(struct penalty_timers *pt, const void *for_object);
+unsigned int penalty_timers_remaining(struct llist_head *penalty_timers,
+ const struct gsm0808_cell_id *for_target_cell);
+unsigned int penalty_timers_remaining_list(struct llist_head *penalty_timers,
+ const struct gsm0808_cell_id_list2 *for_target_cells);
-/* Free a struct as returned from penalty_timers_init().
- * Clear all timers from the list, deallocate the list and set the pointer to NULL.
- * param pt: pointer-to-pointer which references a struct penalty_timers as returned by
- * penalty_timers_init(); *pt_p will be set to NULL. */
-void penalty_timers_free(struct penalty_timers **pt_p);
+void penalty_timers_clear(struct llist_head *penalty_timers, const struct gsm0808_cell_id *for_target_cell);
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/rest_octets.h b/include/osmocom/bsc/rest_octets.h
deleted file mode 100644
index 7df66f732..000000000
--- a/include/osmocom/bsc/rest_octets.h
+++ /dev/null
@@ -1,121 +0,0 @@
-#ifndef _REST_OCTETS_H
-#define _REST_OCTETS_H
-
-#include <stdbool.h>
-#include <osmocom/gsm/sysinfo.h>
-
-struct gsm_bts;
-
-/* generate SI1 rest octets */
-int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net);
-int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts);
-int rest_octets_si2ter(uint8_t *data);
-int rest_octets_si2bis(uint8_t *data);
-int rest_octets_si6(uint8_t *data, bool is1800_net);
-
-struct gsm48_si_selection_params {
- uint16_t penalty_time:5,
- temp_offs:3,
- cell_resel_off:6,
- cbq:1,
- present:1;
-};
-
-struct gsm48_si_power_offset {
- uint8_t power_offset:2,
- present:1;
-};
-
-struct gsm48_si3_gprs_ind {
- uint8_t si13_position:1,
- ra_colour:3,
- present:1;
-};
-
-struct gsm48_lsa_params {
- uint32_t prio_thr:3,
- lsa_offset:3,
- mcc:12,
- mnc:12;
- unsigned int present;
-};
-
-struct gsm48_si_ro_info {
- struct gsm48_si_selection_params selection_params;
- struct gsm48_si_power_offset power_offset;
- bool si2ter_indicator;
- bool early_cm_ctrl;
- struct {
- uint8_t where:3,
- present:1;
- } scheduling;
- struct gsm48_si3_gprs_ind gprs_ind;
- /* SI 3 specific */
- bool early_cm_restrict_3g;
- bool si2quater_indicator;
- /* SI 4 specific */
- struct gsm48_lsa_params lsa_params;
- uint16_t cell_id;
- uint8_t break_ind; /* do we have SI7 + SI8 ? */
-};
-
-
-/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */
-int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3);
-
-/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
-int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len);
-
-/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */
-enum gprs_nmo {
- GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */
- GPRS_NMO_II = 1, /* all paging on CCCH */
- GPRS_NMO_III = 2, /* no paging coordination */
-};
-
-/* TS 04.60 12.24 */
-struct gprs_cell_options {
- enum gprs_nmo nmo;
- /* T3168: wait for packet uplink assignment message */
- uint32_t t3168; /* in milliseconds */
- /* T3192: wait for release of the TBF after reception of the final block */
- uint32_t t3192; /* in milliseconds */
- uint32_t drx_timer_max;/* in seconds */
- uint32_t bs_cv_max;
- bool ctrl_ack_type_use_block; /* use PACKET CONTROL ACKNOWLEDGMENT */
-
- uint8_t ext_info_present;
- struct {
- uint8_t egprs_supported;
- uint8_t use_egprs_p_ch_req;
- uint8_t bep_period;
- uint8_t pfc_supported;
- uint8_t dtm_supported;
- uint8_t bss_paging_coordination;
- } ext_info;
-};
-
-/* TS 04.60 Table 12.9.2 */
-struct gprs_power_ctrl_pars {
- uint8_t alpha;
- uint8_t t_avg_w;
- uint8_t t_avg_t;
- uint8_t pc_meas_chan;
- uint8_t n_avg_i;
-};
-
-struct gsm48_si13_info {
- struct gprs_cell_options cell_opts;
- struct gprs_power_ctrl_pars pwr_ctrl_pars;
- uint8_t bcch_change_mark;
- uint8_t si_change_field;
- uint8_t rac;
- uint8_t spgc_ccch_sup;
- uint8_t net_ctrl_ord;
- uint8_t prio_acc_thr;
-};
-
-/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
-int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13);
-
-#endif /* _REST_OCTETS_H */
diff --git a/include/osmocom/bsc/signal.h b/include/osmocom/bsc/signal.h
index c7d7fe129..084c30dd6 100644
--- a/include/osmocom/bsc/signal.h
+++ b/include/osmocom/bsc/signal.h
@@ -65,6 +65,7 @@ enum signal_nm {
S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */
S_NM_IPACC_NACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_NACK happened */
S_NM_IPACC_ACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_ACK happened */
+ S_NM_IPACC_SET_ATTR_ACK,/* GSM 12.21 nanoBTS extensions NM_MT_IPACC_SET_ATTR_ACK happened */
S_NM_IPACC_RESTART_ACK, /* nanoBTS has send a restart ack */
S_NM_IPACC_RESTART_NACK,/* nanoBTS has send a restart ack */
S_NM_TEST_REP, /* GSM 12.21 Test Report */
@@ -72,8 +73,11 @@ enum signal_nm {
S_NM_STATECHG_ADM, /* Administrative State changed */
S_NM_OM2K_CONF_RES, /* OM2K Configuration Result */
S_NM_OPSTART_ACK, /* Received OPSTART ACK, arg is struct msgb *oml_msg */
+ S_NM_OPSTART_NACK, /* Received OPSTART NACK, arg is struct msgb *oml_msg */
S_NM_GET_ATTR_REP, /* Received Get Attributes Response, arg is struct msgb *oml_msg */
S_NM_SET_RADIO_ATTR_ACK, /* Received Set Radio Carrier Attributes Ack, arg is struct msgb *oml_msg */
+ S_NM_SET_CHAN_ATTR_ACK, /* Received Set Radio Channel Attributes Ack, arg is struct msgb *oml_msg */
+ S_NM_SET_BTS_ATTR_ACK, /* Received Set BTS Attributes Ack, arg is struct msgb *oml_msg */
};
/* SS_LCHAN signals */
@@ -130,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/system_information.h b/include/osmocom/bsc/system_information.h
index 35892d9d6..08d34f8bf 100644
--- a/include/osmocom/bsc/system_information.h
+++ b/include/osmocom/bsc/system_information.h
@@ -3,16 +3,17 @@
#include <osmocom/gsm/sysinfo.h>
-#include <osmocom/bsc/arfcn_range_encode.h>
+#include <osmocom/gsm/gsm48_arfcn_range_encode.h>
struct gsm_bts;
+int generate_cell_chan_alloc(struct gsm_bts *bts);
int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts);
int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type type);
size_t si2q_earfcn_count(const struct osmo_earfcn_si2q *e);
unsigned range1024_p(unsigned n);
unsigned range512_q(unsigned m);
-int range_encode(enum gsm48_range r, int *arfcns, int arfcns_used, int *w,
+int range_encode(enum osmo_gsm48_range r, int *arfcns, int arfcns_used, int *w,
int f0, uint8_t *chan_list);
uint8_t si2q_num(struct gsm_bts *bts);
int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio,
diff --git a/include/osmocom/bsc/timeslot_fsm.h b/include/osmocom/bsc/timeslot_fsm.h
index d02e156df..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,12 +42,13 @@ 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);
bool ts_is_lchan_waiting_for_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config *target_pchan);
bool ts_is_pchan_switching(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config *target_pchan);
-bool ts_usable_as_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config as_pchan);
+bool ts_usable_as_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config as_pchan, bool allow_pchan_switch);
+
+void ts_set_pchan_is(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan_is);
diff --git a/include/osmocom/bsc/vty.h b/include/osmocom/bsc/vty.h
index 29c7f52c6..70b973db3 100644
--- a/include/osmocom/bsc/vty.h
+++ b/include/osmocom/bsc/vty.h
@@ -6,6 +6,15 @@
#include <osmocom/vty/command.h>
struct gsm_network;
+struct gsm_bts;
+struct gsm_bts_trx;
+struct gsm_bts_trx_ts;
+struct gsm_nm_state;
+struct pchan_load;
+struct gsm_lchan;
+struct bsc_subscr;
+struct gsm_e1_subslot;
+struct e1inp_sign_link;
struct vty;
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
@@ -14,12 +23,11 @@ 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,
OML_NODE,
- NAT_NODE,
- NAT_BSC_NODE,
MSC_NODE,
OM2K_NODE,
OM2K_CON_GROUP_NODE,
@@ -28,12 +36,63 @@ enum bsc_vty_node {
CBC_SERVER_NODE,
CBC_CLIENT_NODE,
SMLC_NODE,
+ POWER_CTRL_NODE,
};
struct log_info;
int bsc_vty_init(struct gsm_network *network);
int bsc_vty_init_extra(void);
+void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms);
+int dummy_config_write(struct vty *v);
+void dump_pchan_load_vty(struct vty *vty, char *prefix, const struct pchan_load *pl);
+void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub);
struct gsm_network *gsmnet_from_vty(struct vty *vty);
+int bts_vty_init(void);
+void bts_dump_vty(struct vty *vty, struct gsm_bts *bts);
+void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx, bool print_rsl, bool show_connected);
+void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts);
+void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan);
+void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan);
+
+int bts_trx_vty_init(void);
+void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx);
+void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link,
+ const char *prefix);
+void e1isl_dump_vty_tcp(struct vty *vty, const struct e1inp_sign_link *e1l);
+void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l);
+void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line,
+ const char *ts, const char *ss);
+
+enum bsc_vty_cmd_attr {
+ BSC_VTY_ATTR_RESTART_ABIS_OML_LINK = 0,
+ BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK,
+ BSC_VTY_ATTR_NEW_LCHAN,
+ BSC_VTY_ATTR_VENDOR_SPECIFIC,
+ /* NOTE: up to 32 entries */
+};
+
+#define BTS_NR_STR "BTS Number\n"
+#define TRX_NR_STR "TRX Number\n"
+#define TS_NR_STR "Timeslot Number\n"
+#define SS_NR_STR "Sub-slot Number\n"
+#define LCHAN_NR_STR "Logical Channel Number\n"
+#define BTS_TRX_STR BTS_NR_STR TRX_NR_STR
+#define BTS_TRX_TS_STR BTS_TRX_STR TS_NR_STR
+#define BTS_TRX_TS_LCHAN_STR BTS_TRX_TS_STR LCHAN_NR_STR
+#define BTS_NR_TRX_TS_STR2 \
+ "BTS for manual command\n" BTS_NR_STR \
+ "TRX for manual command\n" TRX_NR_STR \
+ "Timeslot for manual command\n" TS_NR_STR
+#define BTS_NR_TRX_TS_SS_STR2 \
+ BTS_NR_TRX_TS_STR2 \
+ "Sub-slot for manual command\n" SS_NR_STR
+
+#define TSC_ARGS_OPT "[tsc] [<1-4>] [<0-7>]"
+#define TSC_ARGS_DOC \
+ "Provide specific TSC Set and Training Sequence Code\n" \
+ "TSC Set\n" \
+ "Training Sequence Code\n"
+
#endif
diff --git a/src/ipaccess/Makefile.am b/src/ipaccess/Makefile.am
index 717a6a187..9a9fff69b 100644
--- a/src/ipaccess/Makefile.am
+++ b/src/ipaccess/Makefile.am
@@ -46,14 +46,7 @@ ipaccess_config_SOURCES = \
# FIXME: resolve the bogus dependencies patched around here:
ipaccess_config_LDADD = \
- $(top_builddir)/src/osmo-bsc/bts.o \
- $(top_builddir)/src/osmo-bsc/bts_trx.o \
- $(top_builddir)/src/osmo-bsc/abis_nm.o \
- $(top_builddir)/src/osmo-bsc/acc.o \
- $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts.o \
- $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \
- $(top_builddir)/src/osmo-bsc/gsm_data.o \
- $(top_builddir)/src/osmo-bsc/net_init.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(OSMO_LIBS) \
$(NULL)
@@ -63,10 +56,6 @@ ipaccess_proxy_SOURCES = \
$(NULL)
ipaccess_proxy_LDADD = \
- $(top_builddir)/src/osmo-bsc/abis_nm.o \
- $(top_builddir)/src/osmo-bsc/acc.o \
- $(top_builddir)/src/osmo-bsc/bts.o \
- $(top_builddir)/src/osmo-bsc/bts_trx.o \
- $(top_builddir)/src/osmo-bsc/gsm_data.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(OSMO_LIBS) \
$(NULL)
diff --git a/src/ipaccess/abisip-find.c b/src/ipaccess/abisip-find.c
index 11b2851ae..4bba8a315 100644
--- a/src/ipaccess/abisip-find.c
+++ b/src/ipaccess/abisip-find.c
@@ -406,7 +406,7 @@ static int bfd_cb(struct osmo_fd *bfd, unsigned int flags)
if (flags & OSMO_FD_READ)
return read_response(bfd->fd);
if (flags & OSMO_FD_WRITE) {
- bfd->when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(bfd);
return bcast_find(bfd->fd);
}
return 0;
@@ -418,7 +418,7 @@ static void timer_cb(void *_data)
{
struct osmo_fd *bfd = _data;
- bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(bfd);
base_stations_bump(false);
@@ -446,13 +446,12 @@ int main(int argc, char **argv)
else if (cmdline_opts.send_interval >= cmdline_opts.list_view_timeout)
fprintf(stdout, "\nWARNING: the --timeout should be larger than --interval.\n\n");
- bfd.cb = bfd_cb;
- bfd.when = OSMO_FD_READ | OSMO_FD_WRITE;
- bfd.fd = udp_sock(cmdline_opts.ifname, cmdline_opts.bind_ip);
- if (bfd.fd < 0) {
+ rc = udp_sock(cmdline_opts.ifname, cmdline_opts.bind_ip);
+ if (rc < 0) {
perror("Cannot create local socket for broadcast udp");
exit(1);
}
+ osmo_fd_setup(&bfd, rc, OSMO_FD_READ | OSMO_FD_WRITE, bfd_cb, NULL, 0);
rc = osmo_fd_register(&bfd);
if (rc < 0) {
diff --git a/src/ipaccess/ipaccess-config.c b/src/ipaccess/ipaccess-config.c
index c9264d723..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;
@@ -144,21 +142,22 @@ static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
}
line->ops = &ipaccess_e1inp_line_ops;
+ sign_ts = e1inp_line_ipa_oml_ts(line);
+ rsl_ts = e1inp_line_ipa_rsl_ts(line, 0);
+
/* create E1 timeslots for signalling and TRAU frames */
- e1inp_ts_config_sign(&line->ts[1-1], line);
- e1inp_ts_config_sign(&line->ts[2-1], line);
+ e1inp_ts_config_sign(sign_ts, line);
+ e1inp_ts_config_sign(rsl_ts, line);
- /* create signalling links for TS1 */
- sign_ts = &line->ts[1-1];
- rsl_ts = &line->ts[2-1];
+ /* create signalling links for TRX0 */
oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
- bts->c0, 0xff, 0);
+ bts->c0, IPAC_PROTO_OML, 0);
rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL,
- bts->c0, 0, 0);
+ bts->c0, IPAC_PROTO_RSL, 0);
/* create back-links from bts/trx */
bts->oml_link = oml_link;
- bts->c0->rsl_link = rsl_link;
+ bts->c0->rsl_link_primary = rsl_link;
/* default port at BTS for incoming connections is 3006 */
if (sin->sin_port == 0)
@@ -502,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" },
@@ -517,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)
@@ -926,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,
},
};
@@ -1121,7 +1122,7 @@ int main(int argc, char **argv)
}
bts->oml_link->ts->sign.delay = 10;
- bts->c0->rsl_link->ts->sign.delay = 10;
+ bts->c0->rsl_link_primary->ts->sign.delay = 10;
while (1) {
rc = osmo_select_main(0);
if (rc < 0)
@@ -1130,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 d5dd8d4d5..7ede28396 100644
--- a/src/ipaccess/ipaccess-proxy.c
+++ b/src/ipaccess/ipaccess-proxy.c
@@ -283,7 +283,7 @@ static int handle_udp_read(struct osmo_fd *bfd)
if (other_conn) {
/* enqueue the message for TX on the respective FD */
msgb_enqueue(&other_conn->tx_queue, msg);
- other_conn->fd.when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(&other_conn->fd);
} else
msgb_free(msg);
@@ -293,7 +293,7 @@ static int handle_udp_read(struct osmo_fd *bfd)
static int handle_udp_write(struct osmo_fd *bfd)
{
/* not implemented yet */
- bfd->when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(bfd);
return -EIO;
}
@@ -840,7 +840,7 @@ static int handle_tcp_read(struct osmo_fd *bfd)
/* enqueue packet towards BSC */
msgb_enqueue(&bsc_conn->tx_queue, msg);
/* mark respective filedescriptor as 'we want to write' */
- bsc_conn->fd.when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(&bsc_conn->fd);
} else {
logp_ipbc_uid(DLINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
LOGPC(DLINP, LOGL_INFO, "Dropping packet from %s, "
@@ -869,7 +869,7 @@ static int handle_tcp_write(struct osmo_fd *bfd)
/* get the next msg for this timeslot */
if (llist_empty(&ipc->tx_queue)) {
- bfd->when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(bfd);
return 0;
}
lh = ipc->tx_queue.next;
@@ -936,11 +936,7 @@ static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
}
bfd = &ipc->fd;
- bfd->fd = ret;
- bfd->data = ipc;
- bfd->priv_nr = listen_bfd->priv_nr;
- bfd->cb = proxy_ipaccess_fd_cb;
- bfd->when = OSMO_FD_READ;
+ osmo_fd_setup(bfd, ret, OSMO_FD_READ, proxy_ipaccess_fd_cb, ipc, listen_bfd->priv_nr);
ret = osmo_fd_register(bfd);
if (ret < 0) {
LOGP(DLINP, LOGL_ERROR, "could not register FD\n");
@@ -1016,20 +1012,17 @@ static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, v
ipc->bts_conn = data;
- bfd = &ipc->fd;
- bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- bfd->cb = ipaccess_fd_cb;
- bfd->when = OSMO_FD_READ | OSMO_FD_WRITE;
- bfd->data = ipc;
- bfd->priv_nr = priv_nr;
-
- if (bfd->fd < 0) {
+ ret = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (ret < 0) {
LOGP(DLINP, LOGL_ERROR, "Could not create socket: %s\n",
strerror(errno));
talloc_free(ipc);
return NULL;
}
+ bfd = &ipc->fd;
+ osmo_fd_setup(bfd, ret, OSMO_FD_READ | OSMO_FD_WRITE, ipaccess_fd_cb, ipc, priv_nr);
+
ret = setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (ret < 0) {
LOGP(DLINP, LOGL_ERROR, "Could not set socket option\n");
@@ -1093,14 +1086,22 @@ static int ipaccess_proxy_setup(void)
return ret;
}
-static void signal_handler(int signal)
+static void signal_handler(int signum)
{
- fprintf(stdout, "signal %u received\n", signal);
+ fprintf(stdout, "signal %u received\n", signum);
- switch (signal) {
+ switch (signum) {
case SIGABRT:
- /* in case of abort, we want to obtain a talloc report
- * and then return to the caller, who will abort the process */
+ /* in case of abort, we want to obtain a talloc report and
+ * then run default SIGABRT handler, who will generate coredump
+ * and abort the process. abort() should do this for us after we
+ * return, but program wouldn't exit if an external SIGABRT is
+ * received.
+ */
+ talloc_report_full(tall_bsc_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
case SIGUSR1:
talloc_report_full(tall_bsc_ctx, stderr);
break;
@@ -1252,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 8d109fd08..583fb79d9 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -21,19 +21,17 @@ AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
-bin_PROGRAMS = \
- osmo-bsc \
- $(NULL)
+noinst_LTLIBRARIES = libbsc.la
-osmo_bsc_SOURCES = \
+libbsc_la_SOURCES = \
a_reset.c \
abis_nm.c \
abis_nm_vty.c \
abis_om2000.c \
abis_om2000_vty.c \
+ abis_osmo.c \
abis_rsl.c \
acc.c \
- arfcn_range_encode.c \
assignment_fsm.c \
bsc_ctrl_commands.c \
bsc_ctrl_lookup.c \
@@ -41,6 +39,7 @@ osmo_bsc_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 \
@@ -52,14 +51,19 @@ osmo_bsc_SOURCES = \
bts_ipaccess_nanobts_omlattr.c \
bts_nokia_site.c \
bts_siemens_bs11.c \
- bts_sysmobts.c \
+ bts_sm.c \
+ bts_osmobts.c \
bts_unknown.c \
+ bts_vty.c \
+ bts_trx_vty.c \
chan_alloc.c \
+ chan_counts.c \
codec_pref.c \
e1_config.c \
gsm_04_08_rr.c \
gsm_data.c \
handover_cfg.c \
+ handover_ctrl.c \
handover_decision.c \
handover_decision_2.c \
handover_fsm.c \
@@ -75,29 +79,60 @@ osmo_bsc_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 \
+ nm_bts_sm_fsm.c \
+ nm_bts_fsm.c \
+ nm_gprs_cell_fsm.c \
+ nm_gprs_nse_fsm.c \
+ nm_gprs_nsvc_fsm.c \
+ nm_channel_fsm.c \
+ nm_rcarrier_fsm.c \
gsm_08_08.c \
osmo_bsc_bssap.c \
osmo_bsc_ctrl.c \
osmo_bsc_filter.c \
osmo_bsc_grace.c \
osmo_bsc_lcls.c \
- osmo_bsc_main.c \
osmo_bsc_mgcp.c \
osmo_bsc_msc.c \
osmo_bsc_sigtran.c \
paging.c \
pcu_sock.c \
penalty_timers.c \
- rest_octets.c \
+ bssmap_reset.c \
system_information.c \
timeslot_fsm.c \
smscb.c \
cbch_scheduler.c \
cbsp_link.c \
+ power_control.c \
+ $(NULL)
+
+libbsc_la_LIBADD = \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ $(LIBOSMOCTRL_LIBS) \
+ $(LIBOSMONETIF_LIBS) \
+ $(COVERAGE_LDFLAGS) \
+ $(LIBOSMOABIS_LIBS) \
+ $(LIBOSMOSIGTRAN_LIBS) \
+ $(LIBOSMOMGCPCLIENT_LIBS) \
+ $(NULL)
+
+bin_PROGRAMS = \
+ osmo-bsc \
+ $(NULL)
+
+osmo_bsc_SOURCES = \
+ osmo_bsc_main.c \
$(NULL)
osmo_bsc_LDADD = \
+ libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
diff --git a/src/osmo-bsc/a_reset.c b/src/osmo-bsc/a_reset.c
index 9446d1311..d23ffa799 100644
--- a/src/osmo-bsc/a_reset.c
+++ b/src/osmo-bsc/a_reset.c
@@ -18,183 +18,68 @@
*
*/
-#include <osmocom/core/logging.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/fsm.h>
#include <osmocom/core/signal.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
+#include <osmocom/bsc/signal.h>
+
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
-#include <osmocom/bsc/signal.h>
-
-#define RESET_RESEND_INTERVAL 2 /* sec */
-#define RESET_RESEND_TIMER_NO 4 /* See also 3GPP TS 48.008 Chapter 3.1.4.1.3.1 */
-#define BAD_CONNECTION_THRESOLD 3 /* connection failures */
-
-/* Reset context data (callbacks, state machine etc...) */
-struct reset_ctx {
- /* Connection failure counter. When this counter
- * reaches a certain threshold, the reset procedure
- * will be triggered */
- int conn_loss_counter;
-
- /* Callback function to be called when a connection
- * failure is detected and a rest must occur */
- void (*cb)(void *priv);
-
- /* Privated data for the callback function */
- void *priv;
-};
-
-enum reset_fsm_states {
- ST_DISC, /* Disconnected from remote end */
- ST_CONN, /* We have a confirmed connection */
-};
-
-enum reset_fsm_evt {
- EV_RESET_ACK, /* got reset acknowlegement from remote end */
- EV_N_DISCONNECT, /* lost a connection */
- EV_N_CONNECT, /* made a successful connection */
-};
-
-static const struct value_string fsm_event_names[] = {
- OSMO_VALUE_STRING(EV_RESET_ACK),
- OSMO_VALUE_STRING(EV_N_DISCONNECT),
- OSMO_VALUE_STRING(EV_N_CONNECT),
- {0, NULL}
-};
-
-/* Disconnected state event handler */
-static void fsm_disc_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv;
- OSMO_ASSERT(reset_ctx);
-
- reset_ctx->conn_loss_counter = 0;
- osmo_fsm_inst_state_chg(fi, ST_CONN, 0, 0);
-}
+#include <osmocom/bsc/bssmap_reset.h>
+#include <osmocom/bsc/bsc_stats.h>
-/* Called when entering Disconnected state */
-static void fsm_disc_onenter_cb(struct osmo_fsm_inst *fi, uint32_t prev_state)
+static void a_reset_tx_reset(void *data)
{
- struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv;
- struct bsc_msc_data *msc = reset_ctx->priv;
-
- LOGPFSML(fi, LOGL_NOTICE, "BSSMAP MSC assocation is down, reconnecting...\n");
- if (prev_state != ST_DISC) {
- osmo_stat_item_dec(msc->msc_statg->items[MSC_STAT_MSC_LINKS_ACTIVE], 1);
- osmo_signal_dispatch(SS_MSC, S_MSC_LOST, msc);
- }
+ struct bsc_msc_data *msc = data;
+ osmo_bsc_sigtran_tx_reset(msc);
}
-/* Connected state event handler */
-static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+static void a_reset_tx_reset_ack(void *data)
{
- struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv;
- OSMO_ASSERT(reset_ctx);
-
- switch (event) {
- case EV_N_DISCONNECT:
- if (reset_ctx->conn_loss_counter >= BAD_CONNECTION_THRESOLD)
- osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO);
- else
- reset_ctx->conn_loss_counter++;
- break;
- case EV_N_CONNECT:
- reset_ctx->conn_loss_counter = 0;
- break;
- case EV_RESET_ACK:
- LOGPFSML(fi, LOGL_INFO, "Received a duplicated BSSMAP RESET ACK, ignoring\n");
- break;
- }
+ struct bsc_msc_data *msc = data;
+ osmo_bsc_sigtran_tx_reset_ack(msc);
}
-/* Called when entering Connected state */
-static void fsm_conn_onenter_cb(struct osmo_fsm_inst *fi, uint32_t prev_state)
+static void a_reset_link_up(void *data)
{
- struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv;
- struct bsc_msc_data *msc = reset_ctx->priv;
-
- LOGPFSML(fi, LOGL_NOTICE, "BSSMAP MSC assocation is up.\n");
- if (prev_state != ST_CONN) {
- osmo_stat_item_inc(msc->msc_statg->items[MSC_STAT_MSC_LINKS_ACTIVE], 1);
- osmo_signal_dispatch(SS_MSC, S_MSC_CONNECTED, msc);
- }
+ 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);
}
-/* Timer callback to retransmit the reset signal */
-static int fsm_reset_ack_timeout_cb(struct osmo_fsm_inst *fi)
+static void a_reset_link_lost(void *data)
{
- struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv;
- OSMO_ASSERT(reset_ctx);
-
- LOGPFSML(fi, LOGL_NOTICE, "(re)sending BSSMAP RESET message...\n");
-
- reset_ctx->cb(reset_ctx->priv);
-
- osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO);
- return 0;
+ 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);
}
-static struct osmo_fsm_state reset_fsm_states[] = {
- [ST_DISC] = {
- .in_event_mask = (1 << EV_RESET_ACK),
- .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN),
- .name = "DISC",
- .action = fsm_disc_cb,
- .onenter = fsm_disc_onenter_cb,
- },
- [ST_CONN] = {
- .in_event_mask = (1 << EV_N_DISCONNECT) | (1 << EV_N_CONNECT) | (1 << EV_RESET_ACK),
- .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN),
- .name = "CONN",
- .action = fsm_conn_cb,
- .onenter = fsm_conn_onenter_cb,
- },
-};
-
-/* State machine definition */
-static struct osmo_fsm fsm = {
- .name = "A-RESET",
- .states = reset_fsm_states,
- .num_states = ARRAY_SIZE(reset_fsm_states),
- .log_subsys = DMSC,
- .timer_cb = fsm_reset_ack_timeout_cb,
- .event_names = fsm_event_names,
-};
-
/* Create and start state machine which handles the reset/reset-ack procedure */
-void a_reset_alloc(struct bsc_msc_data *msc, const char *name, void *cb)
+void a_reset_alloc(struct bsc_msc_data *msc, const char *name)
{
- struct reset_ctx *reset_ctx;
- struct osmo_fsm_inst *reset_fsm;
-
- OSMO_ASSERT(msc);
- OSMO_ASSERT(name);
- OSMO_ASSERT(cb);
+ struct bssmap_reset_cfg cfg = {
+ .conn_cfm_failure_threshold = 3,
+ .ops = {
+ .tx_reset = a_reset_tx_reset,
+ .tx_reset_ack = a_reset_tx_reset_ack,
+ .link_up = a_reset_link_up,
+ .link_lost = a_reset_link_lost,
+ },
+ .data = msc,
+ };
/* There must not be any double allocation! */
- OSMO_ASSERT(msc->a.reset_fsm == NULL);
-
- /* Allocate and configure a new fsm instance */
- reset_ctx = talloc_zero(msc, struct reset_ctx);
- OSMO_ASSERT(reset_ctx);
- reset_ctx->priv = msc;
- reset_ctx->cb = cb;
- reset_ctx->conn_loss_counter = 0;
- reset_fsm = osmo_fsm_inst_alloc(&fsm, msc, reset_ctx, LOGL_DEBUG, name);
- OSMO_ASSERT(reset_fsm);
- msc->a.reset_fsm = reset_fsm;
-
- /* Immediately (1ms) kick off reset sending mechanism */
- osmo_fsm_inst_state_chg_ms(reset_fsm, ST_DISC, 1, RESET_RESEND_TIMER_NO);
-
- /* Count the new MSC link */
- osmo_stat_item_inc(msc->msc_statg->items[MSC_STAT_MSC_LINKS_TOTAL], 1);
+ if (msc->a.bssmap_reset) {
+ LOGP(DMSC, LOGL_ERROR, "(msc%d) will not allocate a second reset FSM for this MSC\n", msc->nr);
+ return;
+ }
+
+ 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 */
@@ -203,10 +88,10 @@ void a_reset_ack_confirm(struct bsc_msc_data *msc)
if (!msc)
return;
- if (!msc->a.reset_fsm)
+ if (!msc->a.bssmap_reset)
return;
- osmo_fsm_inst_dispatch(msc->a.reset_fsm, EV_RESET_ACK, NULL);
+ osmo_fsm_inst_dispatch(msc->a.bssmap_reset->fi, BSSMAP_RESET_EV_RX_RESET_ACK, NULL);
}
/* Report a failed connection */
@@ -215,10 +100,10 @@ void a_reset_conn_fail(struct bsc_msc_data *msc)
if (!msc)
return;
- if (!msc->a.reset_fsm)
+ if (!msc->a.bssmap_reset)
return;
- osmo_fsm_inst_dispatch(msc->a.reset_fsm, EV_N_DISCONNECT, NULL);
+ osmo_fsm_inst_dispatch(msc->a.bssmap_reset->fi, BSSMAP_RESET_EV_CONN_CFM_FAILURE, NULL);
}
/* Report a successful connection */
@@ -227,10 +112,10 @@ void a_reset_conn_success(struct bsc_msc_data *msc)
if (!msc)
return;
- if (!msc->a.reset_fsm)
+ if (!msc->a.bssmap_reset)
return;
- osmo_fsm_inst_dispatch(msc->a.reset_fsm, EV_N_CONNECT, NULL);
+ osmo_fsm_inst_dispatch(msc->a.bssmap_reset->fi, BSSMAP_RESET_EV_CONN_CFM_SUCCESS, NULL);
}
/* Check if we have a connection to a specified msc */
@@ -239,16 +124,8 @@ bool a_reset_conn_ready(struct bsc_msc_data *msc)
if (!msc)
return false;
- if (!msc->a.reset_fsm)
+ if (!msc->a.bssmap_reset)
return false;
- if (msc->a.reset_fsm->state == ST_CONN)
- return true;
-
- return false;
-}
-
-static __attribute__((constructor)) void a_reset_fsm_init()
-{
- OSMO_ASSERT(osmo_fsm_register(&fsm) == 0);
+ return bssmap_reset_is_conn_ready(msc->a.bssmap_reset);
}
diff --git a/src/osmo-bsc/abis_nm.c b/src/osmo-bsc/abis_nm.c
index 0bcb81c98..7c0439413 100644
--- a/src/osmo-bsc/abis_nm.c
+++ b/src/osmo-bsc/abis_nm.c
@@ -49,14 +49,18 @@
#include <osmocom/abis/e1_input.h>
#include <osmocom/bsc/chan_alloc.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/nm_common_fsm.h>
#include <osmocom/gsm/bts_features.h>
#define OM_ALLOC_SIZE 1024
#define OM_HEADROOM_SIZE 128
#define IPACC_SEGMENT_SIZE 245
-#define LOGPFOH(ss, lvl, foh, fmt, args ...) LOGP(ss, lvl, "%s: " fmt, abis_nm_dump_foh(foh), ## args)
-#define DEBUGPFOH(ss, foh, fmt, args ...) LOGPFOH(ss, LOGL_DEBUG, foh, fmt, ## args)
+#define LOGPMO(mo, ss, lvl, fmt, args ...) \
+ LOGP(ss, lvl, "OC=%s(%02x) INST=(%02x,%02x,%02x): " fmt, \
+ get_value_string(abis_nm_obj_class_names, (mo)->obj_class), \
+ (mo)->obj_class, (mo)->obj_inst.bts_nr, (mo)->obj_inst.trx_nr, \
+ (mo)->obj_inst.ts_nr, ## args)
int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len)
{
@@ -315,7 +319,7 @@ static inline void log_oml_fail_rep(const struct gsm_bts *bts, const char *type,
enum abis_nm_pcause_type pcause = p_val[0];
enum abis_mm_event_causes cause = osmo_load16be(p_val + 1);
- LOGP(DNM, LOGL_ERROR, "BTS %u: Failure Event Report: ", bts->nr);
+ LOGPMO(&bts->mo, DNM, LOGL_ERROR, "Failure Event Report: ");
if (type)
LOGPC(DNM, LOGL_ERROR, "Type=%s, ", type);
if (severity)
@@ -345,10 +349,10 @@ static inline void handle_manufact_report(struct gsm_bts *bts, const uint8_t *p_
switch (cause) {
case OSMO_EVT_PCU_VERS:
if (text) {
- LOGP(DNM, LOGL_NOTICE, "BTS %u reported connected PCU version %s\n", bts->nr, text);
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Reported connected PCU version %s\n", text);
osmo_strlcpy(bts->pcu_version, text, sizeof(bts->pcu_version));
} else {
- LOGP(DNM, LOGL_ERROR, "BTS %u reported PCU disconnection.\n", bts->nr);
+ LOGPMO(&bts->mo, DNM, LOGL_ERROR, "Reported PCU disconnection.\n");
bts->pcu_version[0] = '\0';
}
break;
@@ -525,10 +529,10 @@ static inline bool handle_attr(const struct gsm_bts *bts, enum bts_attribute id,
{
switch (id) {
case BTS_TYPE_VARIANT:
- LOGP(DNM, LOGL_NOTICE, "BTS%u reported variant: %s\n", bts->nr, val);
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Reported variant: %s\n", val);
break;
case BTS_SUB_MODEL:
- LOGP(DNM, LOGL_NOTICE, "BTS%u reported submodel: %s\n", bts->nr, val);
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Reported submodel: %s\n", val);
break;
default:
return false;
@@ -576,27 +580,34 @@ static int parse_attr_resp_info_attr(struct gsm_bts *bts, const struct gsm_bts_t
/* log potential BTS feature vector overflow */
if (len > sizeof(bts->_features_data)) {
- LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: feature vector is truncated "
- "(from %u to %zu bytes)\n", bts->nr, len, sizeof(bts->_features_data));
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Get Attributes Response: feature vector is truncated "
+ "(from %u to %zu bytes)\n", len, sizeof(bts->_features_data));
len = sizeof(bts->_features_data);
}
/* check that max. expected BTS attribute is above given feature vector length */
if (len > OSMO_BYTES_FOR_BITS(_NUM_BTS_FEAT)) {
- LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: reported unexpectedly long (%u bytes) "
- "feature vector - most likely it was compiled against newer BSC headers. "
- "Consider upgrading your BSC to later version.\n",
- bts->nr, len);
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Get Attributes Response: reported unexpectedly long (%u bytes) "
+ "feature vector - most likely it was compiled against newer BSC headers. "
+ "Consider upgrading your BSC to later version.\n", len);
}
memcpy(bts->_features_data, TLVP_VAL(tp, NM_ATT_MANUF_ID), len);
- for (i = 0; i < _NUM_BTS_FEAT; i++) {
- if (osmo_bts_has_feature(&bts->features, i) != osmo_bts_has_feature(&bts->model->features, i)) {
- LOGP(DNM, LOGL_NOTICE, "BTS%u feature '%s' reported via OML does not match statically "
- "set feature: %u != %u. Please fix.\n", bts->nr,
- get_value_string(osmo_bts_features_descs, i),
- osmo_bts_has_feature(&bts->features, i), osmo_bts_has_feature(&bts->model->features, i));
+ /* Check each BTS feature in the reported vector */
+ for (i = 0; i < len * 8; i++) {
+ bool Frep = osmo_bts_has_feature(&bts->features, i);
+ if (i >= _NUM_BTS_FEAT && Frep) {
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Get Attributes Response: "
+ "unknown feature 0x%02x. Consider upgrading osmo-bsc.\n", i);
+ continue;
+ }
+
+ bool Fexp = osmo_bts_has_feature(&bts->model->features, i);
+ if (!Frep && Fexp) {
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Get Attributes Response: "
+ "reported feature '%s' is not supported, while we thought it is.\n",
+ osmo_bts_features_name(i));
}
}
}
@@ -671,15 +682,19 @@ static int parse_attr_resp_info(struct gsm_bts *bts, const struct gsm_bts_trx *t
}
/* Handle 3GPP TS 52.021 §8.11.3 Get Attribute Response */
-static int abis_nm_rx_get_attr_resp(struct msgb *mb, const struct gsm_bts_trx *trx)
+static int abis_nm_rx_get_attr_resp(struct msgb *mb)
{
struct abis_om_hdr *oh = msgb_l2(mb);
struct abis_om_fom_hdr *foh = msgb_l3(mb);
struct e1inp_sign_link *sign_link = mb->dst;
- struct gsm_bts *bts = trx ? trx->bts : sign_link->trx->bts;
+ struct gsm_bts *bts = sign_link->trx->bts;
+ const struct gsm_bts_trx *trx;
struct tlv_parsed tp;
int rc;
+ trx = foh->obj_class == NM_OC_BASEB_TRANSC ?
+ gsm_bts_trx_num(bts, foh->obj_inst.trx_nr) : NULL;
+
DEBUGPFOH(DNM, foh, "Get Attributes Response\n");
abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
@@ -826,6 +841,14 @@ static int abis_nm_rx_opstart_ack(struct msgb *mb)
return 0;
}
+static int abis_nm_rx_opstart_nack(struct msgb *mb)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Opstart NACK\n");
+ osmo_signal_dispatch(SS_NM, S_NM_OPSTART_NACK, mb);
+ return 0;
+}
+
static int abis_nm_rx_set_radio_attr_ack(struct msgb *mb)
{
struct abis_om_fom_hdr *foh = msgb_l3(mb);
@@ -834,6 +857,22 @@ static int abis_nm_rx_set_radio_attr_ack(struct msgb *mb)
return 0;
}
+static int abis_nm_rx_set_channel_attr_ack(struct msgb *mb)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ DEBUGPFOH(DNM, foh, "Set Channel Attributes ACK\n");
+ osmo_signal_dispatch(SS_NM, S_NM_SET_CHAN_ATTR_ACK, mb);
+ return 0;
+}
+
+static int abis_nm_rx_set_bts_attr_ack(struct msgb *mb)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ DEBUGPFOH(DNM, foh, "Set BTS Attributes ACK\n");
+ osmo_signal_dispatch(SS_NM, S_NM_SET_BTS_ATTR_ACK, mb);
+ return 0;
+}
+
bool all_trx_rsl_connected_unlocked(const struct gsm_bts *bts)
{
const struct gsm_bts_trx *trx;
@@ -845,16 +884,16 @@ bool all_trx_rsl_connected_unlocked(const struct gsm_bts *bts)
if (bts->gprs.cell.mo.nm_state.administrative == NM_STATE_LOCKED)
return false;
- if (bts->gprs.nse.mo.nm_state.administrative == NM_STATE_LOCKED)
+ if (bts->site_mgr->gprs.nse.mo.nm_state.administrative == NM_STATE_LOCKED)
return false;
- if (bts->gprs.nsvc[0].mo.nm_state.administrative == NM_STATE_LOCKED &&
- bts->gprs.nsvc[1].mo.nm_state.administrative == NM_STATE_LOCKED)
+ if (bts->site_mgr->gprs.nsvc[0].mo.nm_state.administrative == NM_STATE_LOCKED &&
+ bts->site_mgr->gprs.nsvc[1].mo.nm_state.administrative == NM_STATE_LOCKED)
return false;
}
llist_for_each_entry(trx, &bts->trx_list, list) {
- if (!trx->rsl_link)
+ if (!trx->rsl_link_primary)
return false;
if (!trx_is_usable(trx))
@@ -947,10 +986,13 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
ret = abis_nm_rx_lmt_event(mb);
break;
case NM_MT_OPSTART_ACK:
- abis_nm_rx_opstart_ack(mb);
+ ret = abis_nm_rx_opstart_ack(mb);
+ break;
+ case NM_MT_OPSTART_NACK:
+ ret = abis_nm_rx_opstart_nack(mb);
break;
case NM_MT_SET_CHAN_ATTR_ACK:
- DEBUGPFOH(DNM, foh, "Set Channel Attributes ACK\n");
+ abis_nm_rx_set_channel_attr_ack(mb);
break;
case NM_MT_SET_RADIO_ATTR_ACK:
abis_nm_rx_set_radio_attr_ack(mb);
@@ -967,10 +1009,10 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_NACK, NULL);
break;
case NM_MT_SET_BTS_ATTR_ACK:
- DEBUGPFOH(DNM, foh, "Set BTS Attribute ACK\n");
+ abis_nm_rx_set_bts_attr_ack(mb);
break;
case NM_MT_GET_ATTR_RESP:
- ret = abis_nm_rx_get_attr_resp(mb, gsm_bts_trx_num(bts, (foh)->obj_inst.trx_nr));
+ ret = abis_nm_rx_get_attr_resp(mb);
break;
case NM_MT_ESTABLISH_TEI_ACK:
case NM_MT_CONN_TERR_SIGN_ACK:
@@ -999,17 +1041,17 @@ static int abis_nm_rcvmsg_manuf(struct msgb *mb)
{
int rc;
struct e1inp_sign_link *sign_link = mb->dst;
- int bts_type = sign_link->trx->bts->type;
+ struct gsm_bts *bts = sign_link->trx->bts;
- switch (bts_type) {
+ switch (bts->type) {
case GSM_BTS_TYPE_NANOBTS:
case GSM_BTS_TYPE_OSMOBTS:
rc = abis_nm_rx_ipacc(mb);
- abis_nm_queue_send_next(sign_link->trx->bts);
+ abis_nm_queue_send_next(bts);
break;
default:
- LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this "
- "BTS type (%s)\n", btstype2str(bts_type));
+ LOGPMO(&bts->mo, DNM, LOGL_ERROR, "don't know how to parse OML for this "
+ "BTS type (%s)\n", btstype2str(bts->type));
rc = 0;
break;
}
@@ -1154,7 +1196,7 @@ static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg)
msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
sw->file_version);
} else {
- LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n");
+ LOGPMO(&sw->bts->mo, DNM, LOGL_ERROR, "Please implement this for the BTS.\n");
}
}
@@ -1252,7 +1294,7 @@ static int sw_load_segment(struct abis_nm_sw *sw)
break;
}
default:
- LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n");
+ LOGPMO(&sw->bts->mo, DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n");
/* FIXME: Other BTS types */
return -1;
}
@@ -1309,18 +1351,19 @@ struct sdp_firmware {
static int parse_sdp_header(struct abis_nm_sw *sw)
{
+ const struct gsm_abis_mo *mo = &sw->bts->mo;
struct sdp_firmware firmware_header;
int rc;
struct stat stat;
rc = read(sw->fd, &firmware_header, sizeof(firmware_header));
if (rc != sizeof(firmware_header)) {
- LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n");
+ LOGPMO(mo, DNM, LOGL_ERROR, "Could not read SDP file header.\n");
return -1;
}
if (strncmp(firmware_header.magic, " SDP", 4) != 0) {
- LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n");
+ LOGPMO(mo, DNM, LOGL_ERROR, "The magic number1 is wrong.\n");
return -1;
}
@@ -1328,28 +1371,27 @@ static int parse_sdp_header(struct abis_nm_sw *sw)
firmware_header.more_magic[1] != 0x02 ||
firmware_header.more_magic[2] != 0x00 ||
firmware_header.more_magic[3] != 0x00) {
- LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n");
+ LOGPMO(mo, DNM, LOGL_ERROR, "The more magic number is wrong.\n");
return -1;
}
if (fstat(sw->fd, &stat) == -1) {
- LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n");
+ LOGPMO(mo, DNM, LOGL_ERROR, "Could not stat the file.\n");
return -1;
}
if (ntohl(firmware_header.file_length) != stat.st_size) {
- LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n");
+ LOGPMO(mo, DNM, LOGL_ERROR, "The filesizes do not match.\n");
return -1;
}
/* go back to the start as we checked the whole filesize.. */
lseek(sw->fd, 0l, SEEK_SET);
- LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood."
- " There might be checksums in the file that are not"
- " verified and incomplete firmware might be flashed."
- " There is absolutely no WARRANTY that flashing will"
- " work.\n");
+ LOGPMO(mo, DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood. "
+ "There might be checksums in the file that are not verified and incomplete "
+ "firmware might be flashed. There is absolutely no WARRANTY that "
+ "flashing will work.\n");
return 0;
}
@@ -1591,7 +1633,7 @@ int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname,
struct abis_nm_sw *sw = &g_sw;
int rc;
- DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n", bts->nr, fname);
+ LOGPMO(&bts->mo, DNM, LOGL_DEBUG, "Software Load (file \"%s\")\n", fname);
if (sw->state != SW_STATE_NONE)
return -EBUSY;
@@ -1614,7 +1656,7 @@ int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname,
break;
case GSM_BTS_TYPE_UNKNOWN:
default:
- LOGP(DNM, LOGL_ERROR, "Software Load not properly implemented.\n");
+ LOGPMO(&bts->mo, DNM, LOGL_ERROR, "Software Load not properly implemented.\n");
return -1;
break;
}
@@ -1659,7 +1701,7 @@ int abis_nm_software_activate(struct gsm_bts *bts, const char *fname,
struct abis_nm_sw *sw = &g_sw;
int rc;
- DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n", bts->nr, fname);
+ LOGPMO(&bts->mo, DNM, LOGL_DEBUG, "Activating Software (file \"%s\")\n", fname);
if (sw->state != SW_STATE_NONE)
return -EBUSY;
@@ -1756,9 +1798,8 @@ int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts,
ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
- DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n",
- gsm_ts_name(ts),
- e1_port, e1_timeslot, e1_subslot);
+ LOGPMO(&ts->mo, DNM, LOGL_DEBUG, "CONNECT TERR TRAF E1=(%u,%u,%u)\n",
+ e1_port, e1_timeslot, e1_subslot);
return abis_nm_sendmsg(bts, msg);
}
@@ -1780,8 +1821,8 @@ int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uin
struct msgb *msg;
if (bts->type != GSM_BTS_TYPE_OSMOBTS && bts->type != GSM_BTS_TYPE_NANOBTS) {
- LOGP(DNM, LOGL_NOTICE, "Getting attributes from BTS%d type %s is not supported.\n",
- bts->nr, btstype2str(bts->type));
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Getting attributes from BTS "
+ "type %s is not supported.\n", btstype2str(bts->type));
return -EINVAL;
}
@@ -1803,7 +1844,7 @@ int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len)
struct msgb *msg = nm_msgb_alloc();
uint8_t *cur;
- LOG_BTS(bts, DNM, LOGL_DEBUG, "Set BTS Attr\n");
+ LOGPMO(&bts->mo, DNM, LOGL_DEBUG, "Set BTS Attr\n");
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff);
@@ -1851,7 +1892,7 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb,
switch (chan_comb) {
case NM_CHANC_TCHHalf:
case NM_CHANC_TCHHalf2:
- case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH:
+ case NM_CHANC_OSMO_DYN:
/* not supported */
*reason = "TCH/H is not supported.";
return -EINVAL;
@@ -1949,7 +1990,7 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb,
case NM_CHANC_TCHHalf:
case NM_CHANC_IPAC_TCHFull_TCHHalf:
case NM_CHANC_IPAC_TCHFull_PDCH:
- case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH:
+ case NM_CHANC_OSMO_DYN:
return 0;
default:
*reason = "TS1 must carry a CBCH, SDCCH or TCH.";
@@ -1981,7 +2022,7 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb,
return 0;
case NM_CHANC_IPAC_PDCH:
case NM_CHANC_IPAC_TCHFull_PDCH:
- case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH:
+ case NM_CHANC_OSMO_DYN:
if (ts->trx->nr == 0)
return 0;
else {
@@ -2898,6 +2939,9 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
signal.msg_type = foh->msg_type;
osmo_signal_dispatch(SS_NM, S_NM_IPACC_ACK, &signal);
break;
+ case NM_MT_IPACC_SET_ATTR_ACK:
+ osmo_signal_dispatch(SS_NM, S_NM_IPACC_SET_ATTR_ACK, msg);
+ break;
default:
break;
}
@@ -2970,32 +3014,29 @@ static void rsl_connect_timeout(void *data)
int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
uint32_t ip, uint16_t port, uint8_t stream)
{
+ struct msgb *attr;
struct in_addr ia;
- uint8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0,
- NM_ATT_IPACC_DST_IP_PORT, 0, 0,
- NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 };
-
- int attr_len = sizeof(attr);
int error;
osmo_timer_setup(&trx->rsl_connect_timeout, rsl_connect_timeout, trx);
- ia.s_addr = htonl(ip);
- attr[1] = stream;
- attr[3] = port >> 8;
- attr[4] = port & 0xff;
- memcpy(attr + 6, &ia.s_addr, sizeof(uint32_t));
+ attr = msgb_alloc(32, "RSL-connect-attr");
+ msgb_tv_put(attr, NM_ATT_IPACC_STREAM_ID, stream);
+ msgb_tv16_put(attr, NM_ATT_IPACC_DST_IP_PORT, port);
/* if ip == 0, we use the default IP */
- if (ip == 0)
- attr_len -= 5;
+ if (ip != 0) {
+ ia.s_addr = htonl(ip);
+ msgb_tv_fixed_put(attr, NM_ATT_IPACC_DST_IP, 4, (void*)&ia.s_addr);
+ }
LOG_TRX(trx, DNM, LOGL_INFO, "IPA RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n",
inet_ntoa(ia), port, stream);
error = abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT,
NM_OC_BASEB_TRANSC, trx->bts->bts_nr,
- trx->nr, 0xff, attr, attr_len);
+ trx->nr, 0xff, attr->data, attr->len);
+ msgb_free(attr);
if (error == 0)
osmo_timer_schedule(&trx->rsl_connect_timeout, 60, 0);
diff --git a/src/osmo-bsc/abis_nm_vty.c b/src/osmo-bsc/abis_nm_vty.c
index fe467fad1..bbd2d157f 100644
--- a/src/osmo-bsc/abis_nm_vty.c
+++ b/src/osmo-bsc/abis_nm_vty.c
@@ -54,11 +54,6 @@ struct oml_node_state {
uint8_t obj_inst[3];
};
-static int dummy_config_write(struct vty *v)
-{
- return CMD_SUCCESS;
-}
-
/* FIXME: auto-generate those strings from the value_string lists */
#define NM_OBJCLASS_VTY "(site-manager|bts|radio-carrier|baseband-transceiver|channel|adjc|handover|power-contorl|btse|rack|test|envabtse|bport|gprs-nse|gprs-cell|gprs-nsvc|siemenshw)"
#define NM_OBJCLASS_VTY_HELP "Site Manager Object\n" \
diff --git a/src/osmo-bsc/abis_om2000.c b/src/osmo-bsc/abis_om2000.c
index a3f689add..8ee6371c0 100644
--- a/src/osmo-bsc/abis_om2000.c
+++ b/src/osmo-bsc/abis_om2000.c
@@ -776,6 +776,9 @@ get_om2k_mo(struct gsm_bts *bts, const struct abis_om2k_mo *abis_mo)
struct gsm_bts_trx *trx;
switch (abis_mo->class) {
+ case OM2K_MO_CLS_DP:
+ mo = &bts->rbs2000.dp.om2k_mo;
+ break;
case OM2K_MO_CLS_CF:
mo = &bts->rbs2000.cf.om2k_mo;
break;
@@ -849,7 +852,7 @@ static int om2k_decode_msg(struct om2k_decoded_msg *odm, struct msgb *msg)
return abis_om2k_msg_tlv_parse(&odm->tp, o2h);
}
-static char *om2k_mo_name(const struct abis_om2k_mo *mo)
+const char *abis_om2k_mo_name(const struct abis_om2k_mo *mo)
{
static char mo_buf[64];
@@ -861,8 +864,7 @@ static char *om2k_mo_name(const struct abis_om2k_mo *mo)
}
/* resolve the gsm_nm_state data structure for a given MO */
-static struct gsm_nm_state *
-mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
+static struct gsm_nm_state *mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
{
struct gsm_bts_trx *trx;
struct gsm_nm_state *nm_state = NULL;
@@ -945,8 +947,7 @@ static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo)
return NULL;
}
-static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo,
- uint8_t mo_state)
+static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, uint8_t mo_state)
{
struct gsm_nm_state *nm_state = mo2nm_state(bts, mo);
struct gsm_nm_state new_state;
@@ -998,8 +999,7 @@ static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo,
nm_state->availability = new_state.availability;
}
-static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo,
- uint8_t op_state)
+static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t op_state)
{
struct gsm_nm_state *nm_state = mo2nm_state(bts, mo);
struct gsm_nm_state new_state;
@@ -1041,8 +1041,8 @@ static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg)
/* Route through per-TRX OML Link to the appropriate TRX */
trx = gsm_bts_trx_num(bts, o2h->mo.inst);
if (!trx) {
- LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to "
- "non-existing TRX\n", om2k_mo_name(&o2h->mo));
+ LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to non-existing TRX\n",
+ abis_om2k_mo_name(&o2h->mo));
return -ENODEV;
}
msg->dst = trx->oml_link;
@@ -1051,8 +1051,8 @@ static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg)
/* Route through per-TRX OML Link to the appropriate TRX */
trx = gsm_bts_trx_num(bts, o2h->mo.assoc_so);
if (!trx) {
- LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to "
- "non-existing TRX\n", om2k_mo_name(&o2h->mo));
+ LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to non-existing TRX\n",
+ abis_om2k_mo_name(&o2h->mo));
return -ENODEV;
}
msg->dst = trx->oml_link;
@@ -1066,8 +1066,7 @@ static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg)
return _abis_nm_sendmsg(msg);
}
-static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo,
- uint16_t msg_type)
+static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, uint16_t msg_type)
{
o2h->om.mdisc = ABIS_OM_MDISC_FOM;
o2h->om.placement = ABIS_OM_PLACEMENT_ONLY;
@@ -1085,8 +1084,7 @@ static int abis_om2k_cal_time_resp(struct gsm_bts *bts)
struct tm *tm;
o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &bts->rbs2000.cf.om2k_mo.addr,
- OM2K_MSGT_CAL_TIME_RESP);
+ fill_om2k_hdr(o2k, &bts->rbs2000.cf.om2k_mo.addr, OM2K_MSGT_CAL_TIME_RESP);
tm_t = time(NULL);
tm = localtime(&tm_t);
@@ -1102,8 +1100,7 @@ static int abis_om2k_cal_time_resp(struct gsm_bts *bts)
return abis_om2k_sendmsg(bts, msg);
}
-static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo,
- uint16_t msg_type)
+static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint16_t msg_type)
{
struct msgb *msg = om2k_msgb_alloc();
struct abis_om2k_hdr *o2k;
@@ -1111,8 +1108,7 @@ static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *m
o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
fill_om2k_hdr(o2k, mo, msg_type);
- DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo),
- get_value_string(om2k_msgcode_vals, msg_type));
+ DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, msg_type));
return abis_om2k_sendmsg(bts, msg);
}
@@ -1157,8 +1153,7 @@ int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ);
}
-int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo,
- uint8_t operational)
+int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t operational)
{
struct msgb *msg = om2k_msgb_alloc();
struct abis_om2k_hdr *o2k;
@@ -1168,7 +1163,7 @@ int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo,
msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational);
- DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo),
+ DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(mo),
get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO));
/* we update the state here... and send the signal at ACK */
@@ -1228,19 +1223,16 @@ int abis_om2k_tx_is_conf_req(struct gsm_bts *bts)
om2k_fill_is_conn_grp(&cg[i++], grp->icp1, grp->icp2, grp->ci);
o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &bts->rbs2000.is.om2k_mo.addr,
- OM2K_MSGT_IS_CONF_REQ);
+ fill_om2k_hdr(o2k, &bts->rbs2000.is.om2k_mo.addr, OM2K_MSGT_IS_CONF_REQ);
msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1);
msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1);
- msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST,
- num_grps * sizeof(*cg), (uint8_t *)cg);
+ msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, num_grps * sizeof(*cg), (uint8_t *)cg);
talloc_free(cg);
- DEBUGP(DNM, "Tx MO=%s %s\n",
- om2k_mo_name(&bts->rbs2000.is.om2k_mo.addr),
+ DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(&bts->rbs2000.is.om2k_mo.addr),
get_value_string(om2k_msgcode_vals, OM2K_MSGT_IS_CONF_REQ));
return abis_om2k_sendmsg(bts, msg);
@@ -1286,11 +1278,9 @@ int abis_om2k_tx_con_conf_req(struct gsm_bts *bts)
/* pre-pend the OM2K header */
o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr,
- OM2K_MSGT_CON_CONF_REQ);
+ fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr, OM2K_MSGT_CON_CONF_REQ);
- DEBUGP(DNM, "Tx MO=%s %s\n",
- om2k_mo_name(&bts->rbs2000.con.om2k_mo.addr),
+ DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(&bts->rbs2000.con.om2k_mo.addr),
get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ));
return abis_om2k_sendmsg(bts, msg);
@@ -1316,17 +1306,14 @@ int abis_om2k_tx_mctr_conf_req(struct gsm_bts *bts)
/* pre-pend the OM2K header */
o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &bts->rbs2000.mctr.om2k_mo.addr,
- OM2K_MSGT_MCTR_CONF_REQ);
- DEBUGP(DNM, "Tx MO=%s %s\n",
- om2k_mo_name(&bts->rbs2000.mctr.om2k_mo.addr),
+ fill_om2k_hdr(o2k, &bts->rbs2000.mctr.om2k_mo.addr, OM2K_MSGT_MCTR_CONF_REQ);
+ DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(&bts->rbs2000.mctr.om2k_mo.addr),
get_value_string(om2k_msgcode_vals, OM2K_MSGT_MCTR_CONF_REQ));
return abis_om2k_sendmsg(bts, msg);
}
-static void om2k_trx_to_mo(struct abis_om2k_mo *mo,
- const struct gsm_bts_trx *trx,
+static void om2k_trx_to_mo(struct abis_om2k_mo *mo, const struct gsm_bts_trx *trx,
enum abis_om2k_mo_cls cls)
{
mo->class = cls;
@@ -1335,8 +1322,7 @@ static void om2k_trx_to_mo(struct abis_om2k_mo *mo,
mo->assoc_so = 255;
}
-static void om2k_ts_to_mo(struct abis_om2k_mo *mo,
- const struct gsm_bts_trx_ts *ts)
+static void om2k_ts_to_mo(struct abis_om2k_mo *mo, const struct gsm_bts_trx_ts *ts)
{
mo->class = OM2K_MO_CLS_TS;
mo->bts = 0;
@@ -1358,7 +1344,7 @@ int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx)
/* OM2K_DEI_FREQ_SPEC_RX: Using trx_nr as "RX address" only works for single MCTR case */
msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, 0x8000 | ((uint16_t)trx->nr << 10));
- msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */
+ msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, trx->rbs2000.rx_diversity);
return abis_om2k_sendmsg(trx->bts, msg);
}
@@ -1400,16 +1386,13 @@ int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts)
struct abis_om2k_hdr *o2k;
o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &bts->rbs2000.tf.om2k_mo.addr,
- OM2K_MSGT_TF_CONF_REQ);
+ fill_om2k_hdr(o2k, &bts->rbs2000.tf.om2k_mo.addr, OM2K_MSGT_TF_CONF_REQ);
msgb_tv_put(msg, OM2K_DEI_TF_MODE, OM2K_TF_MODE_STANDALONE);
- msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, 0x00);
- msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET,
- sizeof(fs_offset_undef), fs_offset_undef);
+ msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, bts->rbs2000.sync_src);
+ msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET, sizeof(fs_offset_undef), fs_offset_undef);
- DEBUGP(DNM, "Tx MO=%s %s\n",
- om2k_mo_name(&bts->rbs2000.tf.om2k_mo.addr),
+ DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(&bts->rbs2000.tf.om2k_mo.addr),
get_value_string(om2k_msgcode_vals, OM2K_MSGT_TF_CONF_REQ));
return abis_om2k_sendmsg(bts, msg);
@@ -1428,7 +1411,7 @@ static uint8_t pchan2comb(enum gsm_phys_chan_config pchan)
case GSM_PCHAN_TCH_H:
case GSM_PCHAN_PDCH:
case GSM_PCHAN_TCH_F_PDCH:
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
return 8;
default:
return 0;
@@ -1438,11 +1421,9 @@ static uint8_t pchan2comb(enum gsm_phys_chan_config pchan)
static uint8_t ts2comb(struct gsm_bts_trx_ts *ts)
{
if (ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH) {
- LOGP(DNM, LOGL_ERROR, "%s pchan %s not intended for use"
- " with OM2000, use %s instead\n",
- gsm_ts_and_pchan_name(ts),
- gsm_pchan_name(GSM_PCHAN_TCH_F_PDCH),
- gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH));
+ LOGP(DNM, LOGL_ERROR, "%s pchan %s not intended for use with OM2000, use %s instead\n",
+ gsm_ts_and_pchan_name(ts), gsm_pchan_name(GSM_PCHAN_TCH_F_PDCH),
+ gsm_pchan_name(GSM_PCHAN_OSMO_DYN));
/* If we allowed initialization of TCH/F_PDCH, it would fail
* when we try to send the ip.access specific RSL PDCH Act
* message for it. Rather fail completely right now: */
@@ -1537,7 +1518,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn);
msgb_tv_put(msg, OM2K_DEI_MAIO, ts->hopping.maio);
msgb_tv_put(msg, OM2K_DEI_BSIC, ts->trx->bts->bsic);
- msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */
+ msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, ts->trx->rbs2000.rx_diversity);
msgb_tv16_put(msg, OM2K_DEI_FN_OFFSET, 0);
msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */
/* Optional: Interference Rejection Combining */
@@ -1606,7 +1587,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
}
DEBUGP(DNM, "Tx MO=%s %s\n",
- om2k_mo_name(&mo),
+ abis_om2k_mo_name(&mo),
get_value_string(om2k_msgcode_vals, OM2K_MSGT_TS_CONF_REQ));
return abis_om2k_sendmsg(ts->trx->bts, msg);
@@ -1620,7 +1601,9 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
#define S(x) (1 << (x))
enum om2k_event_name {
+ OM2K_MO_EVT_RESET,
OM2K_MO_EVT_START,
+ OM2K_MO_EVT_CHILD_TERM,
OM2K_MO_EVT_RX_CONN_COMPL,
OM2K_MO_EVT_RX_RESET_COMPL,
OM2K_MO_EVT_RX_START_REQ_ACCEPT,
@@ -1633,7 +1616,9 @@ enum om2k_event_name {
};
static const struct value_string om2k_event_names[] = {
+ { OM2K_MO_EVT_RESET, "RESET" },
{ OM2K_MO_EVT_START, "START" },
+ { OM2K_MO_EVT_CHILD_TERM, "CHILD-TERM" },
{ OM2K_MO_EVT_RX_CONN_COMPL, "RX-CONN-COMPL" },
{ OM2K_MO_EVT_RX_RESET_COMPL, "RX-RESET-COMPL" },
{ OM2K_MO_EVT_RX_START_REQ_ACCEPT, "RX-RESET-REQ-ACCEPT" },
@@ -1665,6 +1650,7 @@ struct om2k_mo_fsm_priv {
struct gsm_bts_trx *trx;
struct om2k_mo *mo;
uint8_t ts_nr;
+ uint32_t done_event;
};
static void om2k_mo_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -1676,20 +1662,17 @@ static void om2k_mo_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data
switch (omfp->mo->addr.class) {
case OM2K_MO_CLS_CF:
/* no Connect required, is always connected */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, OM2K_TIMEOUT, 0);
abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr);
break;
case OM2K_MO_CLS_TRXC:
/* no Connect required, start with Reset */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, OM2K_TIMEOUT, 0);
abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr);
break;
default:
/* start with Connect */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CONN_COMPL,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CONN_COMPL, OM2K_TIMEOUT, 0);
abis_om2k_tx_connect_cmd(omfp->trx->bts, &omfp->mo->addr);
break;
}
@@ -1703,14 +1686,12 @@ static void om2k_mo_st_wait_conn_compl(struct osmo_fsm_inst *fi, uint32_t event,
#if 0
case OM2K_MO_CLS_TF:
/* skip the reset, hope that helps */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, OM2K_TIMEOUT, 0);
abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr);
break;
#endif
default:
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, OM2K_TIMEOUT, 0);
abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr);
break;
}
@@ -1720,8 +1701,7 @@ static void om2k_mo_st_wait_res_compl(struct osmo_fsm_inst *fi, uint32_t event,
{
struct om2k_mo_fsm_priv *omfp = fi->priv;
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, OM2K_TIMEOUT, 0);
abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr);
}
@@ -1731,8 +1711,7 @@ static void om2k_mo_st_wait_start_accept(struct osmo_fsm_inst *fi, uint32_t even
switch (omd->msg_type) {
case OM2K_MSGT_START_REQ_ACK:
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_RES,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_RES, OM2K_TIMEOUT, 0);
break;
case OM2K_MSGT_START_REQ_REJ:
osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0);
@@ -1749,21 +1728,18 @@ static void om2k_mo_st_wait_start_res(struct osmo_fsm_inst *fi, uint32_t event,
case OM2K_MO_CLS_CF:
case OM2K_MO_CLS_TRXC:
/* Transition directly to Operational Info */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, OM2K_TIMEOUT, 0);
abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1);
return;
case OM2K_MO_CLS_DP:
/* Transition directory to WAIT_ENABLE_ACCEPT */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, OM2K_TIMEOUT, 0);
abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr);
return;
#if 0
case OM2K_MO_CLS_TF:
/* skip the config, hope that helps speeding things up */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, OM2K_TIMEOUT, 0);
abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr);
return;
#endif
@@ -1826,8 +1802,7 @@ static void om2k_mo_st_wait_cfg_res(struct osmo_fsm_inst *fi, uint32_t event, vo
return;
}
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, OM2K_TIMEOUT, 0);
abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr);
}
@@ -1841,11 +1816,9 @@ static void om2k_mo_st_wait_enable_accept(struct osmo_fsm_inst *fi, uint32_t eve
osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0);
break;
case OM2K_MSGT_ENABLE_REQ_ACK:
- if (omfp->mo->addr.class == OM2K_MO_CLS_IS &&
- omfp->trx->bts->rbs2000.use_superchannel)
+ if (omfp->mo->addr.class == OM2K_MO_CLS_IS && omfp->trx->bts->rbs2000.use_superchannel)
e1inp_ericsson_set_altc(omfp->trx->bts->oml_link->ts->line, 1);
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_RES,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_RES, OM2K_TIMEOUT, 0);
}
}
@@ -1855,8 +1828,7 @@ static void om2k_mo_st_wait_enable_res(struct osmo_fsm_inst *fi, uint32_t event,
//struct om2k_decoded_msg *omd = data;
/* TODO: check if state is actually enabled now? */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT,
- OM2K_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, OM2K_TIMEOUT, 0);
abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1);
}
@@ -1868,8 +1840,9 @@ static void om2k_mo_st_wait_opinfo_accept(struct osmo_fsm_inst *fi, uint32_t eve
static void om2k_mo_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct om2k_mo_fsm_priv *omfp = fi->priv;
- omfp->mo->fsm = NULL;
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+
+ if (fi->proc.parent)
+ osmo_fsm_inst_dispatch(fi->proc.parent, omfp->done_event, NULL);
}
static void om2k_mo_s_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
@@ -1880,11 +1853,24 @@ static void om2k_mo_s_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_stat
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
}
+static void om2k_mo_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case OM2K_MO_EVT_RESET:
+ osmo_fsm_inst_broadcast_children(fi, event, data);
+ osmo_fsm_inst_state_chg(fi, OM2K_ST_INIT, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
static const struct osmo_fsm_state om2k_is_states[] = {
[OM2K_ST_INIT] = {
.name = "INIT",
.in_event_mask = S(OM2K_MO_EVT_START),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR) |
S(OM2K_ST_WAIT_CONN_COMPL) |
S(OM2K_ST_WAIT_START_ACCEPT) |
@@ -1895,6 +1881,7 @@ static const struct osmo_fsm_state om2k_is_states[] = {
.name = "WAIT-CONN-COMPL",
.in_event_mask = S(OM2K_MO_EVT_RX_CONN_COMPL),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR) |
S(OM2K_ST_WAIT_START_ACCEPT) |
S(OM2K_ST_WAIT_RES_COMPL),
@@ -1904,6 +1891,7 @@ static const struct osmo_fsm_state om2k_is_states[] = {
.name = "WAIT-RES-COMPL",
.in_event_mask = S(OM2K_MO_EVT_RX_RESET_COMPL),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR) |
S(OM2K_ST_WAIT_START_ACCEPT),
.action = om2k_mo_st_wait_res_compl,
@@ -1912,6 +1900,7 @@ static const struct osmo_fsm_state om2k_is_states[] = {
.name = "WAIT-START-ACCEPT",
.in_event_mask = S(OM2K_MO_EVT_RX_START_REQ_ACCEPT),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR) |
S(OM2K_ST_WAIT_START_RES),
.action =om2k_mo_st_wait_start_accept,
@@ -1920,15 +1909,18 @@ static const struct osmo_fsm_state om2k_is_states[] = {
.name = "WAIT-START-RES",
.in_event_mask = S(OM2K_MO_EVT_RX_START_RES),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR) |
S(OM2K_ST_WAIT_CFG_ACCEPT) |
- S(OM2K_ST_WAIT_OPINFO_ACCEPT),
+ S(OM2K_ST_WAIT_OPINFO_ACCEPT) |
+ S(OM2K_ST_WAIT_ENABLE_ACCEPT),
.action = om2k_mo_st_wait_start_res,
},
[OM2K_ST_WAIT_CFG_ACCEPT] = {
.name = "WAIT-CFG-ACCEPT",
.in_event_mask = S(OM2K_MO_EVT_RX_CFG_REQ_ACCEPT),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR) |
S(OM2K_ST_WAIT_CFG_RES),
.action = om2k_mo_st_wait_cfg_accept,
@@ -1937,6 +1929,7 @@ static const struct osmo_fsm_state om2k_is_states[] = {
.name = "WAIT-CFG-RES",
.in_event_mask = S(OM2K_MO_EVT_RX_CFG_RES),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR) |
S(OM2K_ST_WAIT_ENABLE_ACCEPT),
.action = om2k_mo_st_wait_cfg_res,
@@ -1945,6 +1938,7 @@ static const struct osmo_fsm_state om2k_is_states[] = {
.name = "WAIT-ENABLE-ACCEPT",
.in_event_mask = S(OM2K_MO_EVT_RX_ENA_REQ_ACCEPT),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR) |
S(OM2K_ST_WAIT_ENABLE_RES),
.action = om2k_mo_st_wait_enable_accept,
@@ -1953,6 +1947,7 @@ static const struct osmo_fsm_state om2k_is_states[] = {
.name = "WAIT-ENABLE-RES",
.in_event_mask = S(OM2K_MO_EVT_RX_ENA_RES),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR) |
S(OM2K_ST_WAIT_OPINFO_ACCEPT),
.action = om2k_mo_st_wait_enable_res,
@@ -1961,19 +1956,20 @@ static const struct osmo_fsm_state om2k_is_states[] = {
.name = "WAIT-OPINFO-ACCEPT",
.in_event_mask = S(OM2K_MO_EVT_RX_OPINFO_ACC),
.out_state_mask = S(OM2K_ST_DONE) |
+ S(OM2K_ST_INIT) |
S(OM2K_ST_ERROR),
.action = om2k_mo_st_wait_opinfo_accept,
},
[OM2K_ST_DONE] = {
.name = "DONE",
.in_event_mask = 0,
- .out_state_mask = 0,
+ .out_state_mask = S(OM2K_ST_INIT),
.onenter = om2k_mo_s_done_onenter,
},
[OM2K_ST_ERROR] = {
.name = "ERROR",
.in_event_mask = 0,
- .out_state_mask = 0,
+ .out_state_mask = S(OM2K_ST_INIT),
.onenter = om2k_mo_s_error_onenter,
},
@@ -1990,13 +1986,14 @@ static struct osmo_fsm om2k_mo_fsm = {
.states = om2k_is_states,
.num_states = ARRAY_SIZE(om2k_is_states),
.log_subsys = DNM,
+ .allstate_event_mask = S(OM2K_MO_EVT_RESET),
+ .allstate_action = om2k_mo_allstate,
.event_names = om2k_event_names,
.timer_cb = om2k_mo_timer_cb,
};
-struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent,
- uint32_t term_event,
- struct gsm_bts_trx *trx, struct om2k_mo *mo)
+static struct osmo_fsm_inst *om2k_mo_fsm_alloc(struct osmo_fsm_inst *parent, uint32_t done_event,
+ struct gsm_bts_trx *trx, struct om2k_mo *mo)
{
struct osmo_fsm_inst *fi;
struct om2k_mo_fsm_priv *omfp;
@@ -2006,8 +2003,7 @@ struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent,
get_value_string(om2k_mo_class_short_vals, mo->addr.class),
mo->addr.bts, mo->addr.assoc_so, mo->addr.inst);
- fi = osmo_fsm_inst_alloc_child_id(&om2k_mo_fsm, parent,
- term_event, idbuf);
+ fi = osmo_fsm_inst_alloc_child_id(&om2k_mo_fsm, parent, OM2K_MO_EVT_CHILD_TERM, idbuf);
if (!fi)
return NULL;
@@ -2015,38 +2011,38 @@ struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent,
omfp = talloc_zero(fi, struct om2k_mo_fsm_priv);
omfp->mo = mo;
omfp->trx = trx;
+ omfp->done_event = done_event;
fi->priv = omfp;
- osmo_fsm_inst_dispatch(fi, OM2K_MO_EVT_START, NULL);
-
return fi;
}
+static void om2k_mo_fsm_start(struct om2k_mo *mo)
+{
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_START, NULL);
+}
+
int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo,
struct om2k_decoded_msg *odm)
{
switch (odm->msg_type) {
case OM2K_MSGT_CONNECT_COMPL:
case OM2K_MSGT_CONNECT_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_CONN_COMPL, odm);
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_CONN_COMPL, odm);
break;
case OM2K_MSGT_RESET_COMPL:
case OM2K_MSGT_RESET_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_RESET_COMPL, odm);
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_RESET_COMPL, odm);
break;
case OM2K_MSGT_START_REQ_ACK:
case OM2K_MSGT_START_REQ_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_START_REQ_ACCEPT, odm);
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_START_REQ_ACCEPT, odm);
break;
case OM2K_MSGT_START_RES:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_START_RES, odm);
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_START_RES, odm);
break;
case OM2K_MSGT_CON_CONF_REQ_ACK:
@@ -2056,8 +2052,7 @@ int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo,
case OM2K_MSGT_TF_CONF_REQ_ACK:
case OM2K_MSGT_TS_CONF_REQ_ACK:
case OM2K_MSGT_TX_CONF_REQ_ACK:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, odm);
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, odm);
break;
case OM2K_MSGT_CON_CONF_RES:
@@ -2067,24 +2062,20 @@ int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo,
case OM2K_MSGT_TF_CONF_RES:
case OM2K_MSGT_TS_CONF_RES:
case OM2K_MSGT_TX_CONF_RES:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_CFG_RES, odm);
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_CFG_RES, odm);
break;
case OM2K_MSGT_ENABLE_REQ_ACK:
case OM2K_MSGT_ENABLE_REQ_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, odm);
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, odm);
break;
case OM2K_MSGT_ENABLE_RES:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_ENA_RES, odm);
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_ENA_RES, odm);
break;
case OM2K_MSGT_OP_INFO_ACK:
case OM2K_MSGT_OP_INFO_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_OPINFO_ACC, odm);
+ osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_OPINFO_ACC, odm);
break;
default:
return -1;
@@ -2098,7 +2089,9 @@ int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo,
***********************************************************************/
enum om2k_trx_event {
- OM2K_TRX_EVT_START,
+ OM2K_TRX_EVT_RESET = OM2K_MO_EVT_RESET,
+ OM2K_TRX_EVT_START = OM2K_MO_EVT_START,
+ OM2K_TRX_EVT_CHILD_TERM = OM2K_MO_EVT_CHILD_TERM,
OM2K_TRX_EVT_TRXC_DONE,
OM2K_TRX_EVT_TX_DONE,
OM2K_TRX_EVT_RX_DONE,
@@ -2107,7 +2100,9 @@ enum om2k_trx_event {
};
static struct value_string om2k_trx_events[] = {
+ { OM2K_TRX_EVT_RESET, "RESET" },
{ OM2K_TRX_EVT_START, "START" },
+ { OM2K_TRX_EVT_CHILD_TERM, "CHILD-TERM" },
{ OM2K_TRX_EVT_TRXC_DONE, "TRXC-DONE" },
{ OM2K_TRX_EVT_TX_DONE, "TX-DONE" },
{ OM2K_TRX_EVT_RX_DONE, "RX-DONE" },
@@ -2130,6 +2125,7 @@ enum om2k_trx_state {
struct om2k_trx_fsm_priv {
struct gsm_bts_trx *trx;
uint8_t cur_ts_nr;
+ uint32_t done_event;
};
static void om2k_trx_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2137,10 +2133,8 @@ static void om2k_trx_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data
struct om2k_trx_fsm_priv *otfp = fi->priv;
/* First initialize TRXC */
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TRXC,
- TRX_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TRXC_DONE, otfp->trx,
- &otfp->trx->rbs2000.trxc.om2k_mo);
+ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TRXC, TRX_FSM_TIMEOUT, 0);
+ om2k_mo_fsm_start(&otfp->trx->rbs2000.trxc.om2k_mo);
}
static void om2k_trx_s_wait_trxc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2148,10 +2142,8 @@ static void om2k_trx_s_wait_trxc(struct osmo_fsm_inst *fi, uint32_t event, void
struct om2k_trx_fsm_priv *otfp = fi->priv;
/* Initialize TX after TRXC */
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TX,
- TRX_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TX_DONE, otfp->trx,
- &otfp->trx->rbs2000.tx.om2k_mo);
+ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TX, TRX_FSM_TIMEOUT, 0);
+ om2k_mo_fsm_start(&otfp->trx->rbs2000.tx.om2k_mo);
}
static void om2k_trx_s_wait_tx(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2159,10 +2151,8 @@ static void om2k_trx_s_wait_tx(struct osmo_fsm_inst *fi, uint32_t event, void *d
struct om2k_trx_fsm_priv *otfp = fi->priv;
/* Initialize RX after TX */
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_RX,
- TRX_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_RX_DONE, otfp->trx,
- &otfp->trx->rbs2000.rx.om2k_mo);
+ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_RX, TRX_FSM_TIMEOUT, 0);
+ om2k_mo_fsm_start(&otfp->trx->rbs2000.rx.om2k_mo);
}
static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2171,12 +2161,10 @@ static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *d
struct gsm_bts_trx_ts *ts;
/* Initialize Timeslots after TX */
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TS,
- TRX_FSM_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TS, TRX_FSM_TIMEOUT, 0);
otfp->cur_ts_nr = 0;
ts = &otfp->trx->ts[otfp->cur_ts_nr];
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx,
- &ts->rbs2000.om2k_mo);
+ om2k_mo_fsm_start(&ts->rbs2000.om2k_mo);
}
static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2192,8 +2180,7 @@ static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *d
if (++otfp->cur_ts_nr < 8) {
/* iterate to the next timeslot */
ts = &otfp->trx->ts[otfp->cur_ts_nr];
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx,
- &ts->rbs2000.om2k_mo);
+ om2k_mo_fsm_start(&ts->rbs2000.om2k_mo);
} else {
/* only after all 8 TS */
osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_SEND_SI, 0, 0);
@@ -2212,55 +2199,81 @@ static void om2k_trx_s_send_si(struct osmo_fsm_inst *fi, uint32_t prev_state)
static void om2k_trx_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ struct om2k_trx_fsm_priv *otfp = fi->priv;
+
+ /* See e1_config:bts_isdn_sign_link() / OS#4914 */
+ otfp->trx->mo.nm_state.administrative = NM_STATE_UNLOCKED;
+
+ if (fi->proc.parent)
+ osmo_fsm_inst_dispatch(fi->proc.parent, otfp->done_event, NULL);
+}
+
+static void om2k_trx_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case OM2K_TRX_EVT_RESET:
+ osmo_fsm_inst_broadcast_children(fi, event, data);
+ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_INIT, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
}
static const struct osmo_fsm_state om2k_trx_states[] = {
[OM2K_TRX_S_INIT] = {
.in_event_mask = S(OM2K_TRX_EVT_START),
- .out_state_mask = S(OM2K_TRX_S_WAIT_TRXC),
+ .out_state_mask = S(OM2K_TRX_S_WAIT_TRXC) |
+ S(OM2K_TRX_S_INIT),
.name = "INIT",
.action = om2k_trx_s_init,
},
[OM2K_TRX_S_WAIT_TRXC] = {
.in_event_mask = S(OM2K_TRX_EVT_TRXC_DONE),
.out_state_mask = S(OM2K_TRX_S_ERROR) |
- S(OM2K_TRX_S_WAIT_TX),
+ S(OM2K_TRX_S_WAIT_TX) |
+ S(OM2K_TRX_S_INIT),
.name = "WAIT-TRXC",
.action = om2k_trx_s_wait_trxc,
},
[OM2K_TRX_S_WAIT_TX] = {
.in_event_mask = S(OM2K_TRX_EVT_TX_DONE),
.out_state_mask = S(OM2K_TRX_S_ERROR) |
- S(OM2K_TRX_S_WAIT_RX),
+ S(OM2K_TRX_S_WAIT_RX) |
+ S(OM2K_TRX_S_INIT),
.name = "WAIT-TX",
.action = om2k_trx_s_wait_tx,
},
[OM2K_TRX_S_WAIT_RX] = {
.in_event_mask = S(OM2K_TRX_EVT_RX_DONE),
.out_state_mask = S(OM2K_TRX_S_ERROR) |
- S(OM2K_TRX_S_WAIT_TS),
+ S(OM2K_TRX_S_WAIT_TS) |
+ S(OM2K_TRX_S_INIT),
.name = "WAIT-RX",
.action = om2k_trx_s_wait_rx,
},
[OM2K_TRX_S_WAIT_TS] = {
.in_event_mask = S(OM2K_TRX_EVT_TS_DONE),
.out_state_mask = S(OM2K_TRX_S_ERROR) |
- S(OM2K_TRX_S_SEND_SI),
+ S(OM2K_TRX_S_SEND_SI) |
+ S(OM2K_TRX_S_INIT),
.name = "WAIT-TS",
.action = om2k_trx_s_wait_ts,
},
[OM2K_TRX_S_SEND_SI] = {
.out_state_mask = S(OM2K_TRX_S_ERROR) |
- S(OM2K_TRX_S_DONE),
+ S(OM2K_TRX_S_DONE) |
+ S(OM2K_TRX_S_INIT),
.name = "SEND-SI",
.onenter = om2k_trx_s_send_si,
},
[OM2K_TRX_S_DONE] = {
+ .out_state_mask = S(OM2K_TRX_S_INIT),
.name = "DONE",
.onenter = om2k_trx_s_done_onenter,
},
[OM2K_TRX_S_ERROR] = {
+ .out_state_mask = S(OM2K_TRX_S_INIT),
.name = "ERROR",
},
};
@@ -2276,41 +2289,68 @@ static struct osmo_fsm om2k_trx_fsm = {
.states = om2k_trx_states,
.num_states = ARRAY_SIZE(om2k_trx_states),
.log_subsys = DNM,
+ .allstate_event_mask = S(OM2K_TRX_EVT_RESET),
+ .allstate_action = om2k_trx_allstate,
.event_names = om2k_trx_events,
.timer_cb = om2k_trx_timer_cb,
};
-struct osmo_fsm_inst *om2k_trx_fsm_start(struct osmo_fsm_inst *parent,
- struct gsm_bts_trx *trx,
- uint32_t term_event)
+static struct osmo_fsm_inst *om2k_trx_fsm_alloc(struct osmo_fsm_inst *parent,
+ struct gsm_bts_trx *trx, uint32_t done_event)
{
struct osmo_fsm_inst *fi;
struct om2k_trx_fsm_priv *otfp;
char idbuf[32];
+ OSMO_ASSERT(!trx->rbs2000.trx_fi);
+
snprintf(idbuf, sizeof(idbuf), "%u-%u", trx->bts->nr, trx->nr);
- fi = osmo_fsm_inst_alloc_child_id(&om2k_trx_fsm, parent, term_event,
- idbuf);
+ fi = osmo_fsm_inst_alloc_child_id(&om2k_trx_fsm, parent, OM2K_MO_EVT_CHILD_TERM, idbuf);
if (!fi)
return NULL;
otfp = talloc_zero(fi, struct om2k_trx_fsm_priv);
otfp->trx = trx;
+ otfp->done_event = done_event;
fi->priv = otfp;
- osmo_fsm_inst_dispatch(fi, OM2K_TRX_EVT_START, NULL);
-
return fi;
}
+void om2k_trx_fsm_start(struct gsm_bts_trx *trx)
+{
+ struct osmo_fsm_inst *bts_fi = trx->bts->rbs2000.bts_fi;
+ OSMO_ASSERT(trx->rbs2000.trx_fi);
+
+ /* suppress if BTS is not yet brought up */
+ if (bts_fi->state == OM2K_BTS_S_DONE || bts_fi->state == OM2K_BTS_S_WAIT_TRX)
+ return;
+
+ osmo_fsm_inst_dispatch(trx->rbs2000.trx_fi, OM2K_TRX_EVT_START, NULL);
+}
+
+void om2k_trx_fsm_reset(struct gsm_bts_trx *trx)
+{
+ struct osmo_fsm_inst *bts_fi = trx->bts->rbs2000.bts_fi;
+ OSMO_ASSERT(trx->rbs2000.trx_fi);
+ OSMO_ASSERT(trx->rbs2000.trx_fi);
+
+ /* suppress if BTS is not yet brought up */
+ if (bts_fi->state == OM2K_BTS_S_DONE || bts_fi->state == OM2K_BTS_S_WAIT_TRX)
+ return;
+
+ osmo_fsm_inst_dispatch(trx->rbs2000.trx_fi, OM2K_TRX_EVT_RESET, NULL);
+}
/***********************************************************************
* OM2000 BTS Finite State Machine, initializes CF and all siblings
***********************************************************************/
enum om2k_bts_event {
- OM2K_BTS_EVT_START,
+ OM2K_BTS_EVT_RESET = OM2K_MO_EVT_RESET,
+ OM2K_BTS_EVT_START = OM2K_MO_EVT_START,
+ OM2K_BTS_EVT_CHILD_TERM = OM2K_MO_EVT_CHILD_TERM,
OM2K_BTS_EVT_CF_DONE,
OM2K_BTS_EVT_IS_DONE,
OM2K_BTS_EVT_CON_DONE,
@@ -2318,11 +2358,14 @@ enum om2k_bts_event {
OM2K_BTS_EVT_MCTR_DONE,
OM2K_BTS_EVT_TRX_LAPD_UP,
OM2K_BTS_EVT_TRX_DONE,
+ OM2K_BTS_EVT_TRX_TERM,
OM2K_BTS_EVT_STOP,
};
static const struct value_string om2k_bts_events[] = {
+ { OM2K_BTS_EVT_RESET, "RESET" },
{ OM2K_BTS_EVT_START, "START" },
+ { OM2K_BTS_EVT_CHILD_TERM, "CHILD-TERM" },
{ OM2K_BTS_EVT_CF_DONE, "CF-DONE" },
{ OM2K_BTS_EVT_IS_DONE, "IS-DONE" },
{ OM2K_BTS_EVT_CON_DONE, "CON-DONE" },
@@ -2334,19 +2377,6 @@ static const struct value_string om2k_bts_events[] = {
{ 0, NULL }
};
-enum om2k_bts_state {
- OM2K_BTS_S_INIT,
- OM2K_BTS_S_WAIT_CF,
- OM2K_BTS_S_WAIT_IS,
- OM2K_BTS_S_WAIT_CON,
- OM2K_BTS_S_WAIT_TF,
- OM2K_BTS_S_WAIT_MCTR,
- OM2K_BTS_S_WAIT_TRX_LAPD,
- OM2K_BTS_S_WAIT_TRX,
- OM2K_BTS_S_DONE,
- OM2K_BTS_S_ERROR,
-};
-
struct om2k_bts_fsm_priv {
struct gsm_bts *bts;
uint8_t next_trx_nr;
@@ -2358,10 +2388,8 @@ static void om2k_bts_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data
struct gsm_bts *bts = obfp->bts;
OSMO_ASSERT(event == OM2K_BTS_EVT_START);
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CF,
- BTS_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CF_DONE, bts->c0,
- &bts->rbs2000.cf.om2k_mo);
+ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CF, BTS_FSM_TIMEOUT, 0);
+ om2k_mo_fsm_start(&bts->rbs2000.cf.om2k_mo);
}
static void om2k_bts_s_wait_cf(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2372,8 +2400,7 @@ static void om2k_bts_s_wait_cf(struct osmo_fsm_inst *fi, uint32_t event, void *d
OSMO_ASSERT(event == OM2K_BTS_EVT_CF_DONE);
/* TF can take a long time to initialize, wait for 10min */
osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TF, 600, 0);
- om2k_mo_fsm_start(fi, OM2K_BTS_EVT_TF_DONE, bts->c0,
- &bts->rbs2000.tf.om2k_mo);
+ om2k_mo_fsm_start(&bts->rbs2000.tf.om2k_mo);
}
static void om2k_bts_s_wait_tf(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2383,10 +2410,14 @@ static void om2k_bts_s_wait_tf(struct osmo_fsm_inst *fi, uint32_t event, void *d
OSMO_ASSERT(event == OM2K_BTS_EVT_TF_DONE);
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CON,
- BTS_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CON_DONE, bts->c0,
- &bts->rbs2000.con.om2k_mo);
+ if (!llist_count(&bts->rbs2000.con.conn_groups)) {
+ /* skip CON object if we have no configuration for it */
+ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS, BTS_FSM_TIMEOUT, 0);
+ om2k_mo_fsm_start(&bts->rbs2000.is.om2k_mo);
+ } else {
+ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CON, BTS_FSM_TIMEOUT, 0);
+ om2k_mo_fsm_start(&bts->rbs2000.con.om2k_mo);
+ }
}
static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2396,10 +2427,8 @@ static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void *
OSMO_ASSERT(event == OM2K_BTS_EVT_CON_DONE);
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS,
- BTS_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_BTS_EVT_IS_DONE, bts->c0,
- &bts->rbs2000.is.om2k_mo);
+ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS, BTS_FSM_TIMEOUT, 0);
+ om2k_mo_fsm_start(&bts->rbs2000.is.om2k_mo);
}
static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2411,13 +2440,10 @@ static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *d
/* If we're running OML >= G12R13, start MCTR, else skip directly to TRX */
if (bts->rbs2000.om2k_version[0].active >= 0x0c0d) {
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_MCTR,
- BTS_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_BTS_EVT_MCTR_DONE, bts->c0,
- &bts->rbs2000.mctr.om2k_mo);
+ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_MCTR, BTS_FSM_TIMEOUT, 0);
+ om2k_mo_fsm_start(&bts->rbs2000.mctr.om2k_mo);
} else {
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD,
- TRX_LAPD_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD, TRX_LAPD_TIMEOUT, 0);
}
}
@@ -2425,8 +2451,7 @@ static void om2k_bts_s_wait_mctr(struct osmo_fsm_inst *fi, uint32_t event, void
{
OSMO_ASSERT(event == OM2K_BTS_EVT_MCTR_DONE);
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD,
- TRX_LAPD_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD, TRX_LAPD_TIMEOUT, 0);
}
static void om2k_bts_s_wait_trx_lapd(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2436,11 +2461,10 @@ static void om2k_bts_s_wait_trx_lapd(struct osmo_fsm_inst *fi, uint32_t event, v
OSMO_ASSERT(event == OM2K_BTS_EVT_TRX_LAPD_UP);
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX,
- BTS_FSM_TIMEOUT, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX, BTS_FSM_TIMEOUT, 0);
obfp->next_trx_nr = 0;
trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++);
- om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE);
+ om2k_trx_fsm_start(trx);
}
static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -2452,7 +2476,7 @@ static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void *
if (obfp->next_trx_nr < obfp->bts->num_trx) {
struct gsm_bts_trx *trx;
trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++);
- om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE);
+ om2k_trx_fsm_start(trx);
} else {
osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_DONE, 0, 0);
}
@@ -2460,34 +2484,50 @@ static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void *
static void om2k_bts_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+}
+
+static void om2k_bts_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case OM2K_BTS_EVT_RESET:
+ osmo_fsm_inst_broadcast_children(fi, event, data);
+ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_INIT, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
}
static const struct osmo_fsm_state om2k_bts_states[] = {
[OM2K_BTS_S_INIT] = {
.in_event_mask = S(OM2K_BTS_EVT_START),
- .out_state_mask = S(OM2K_BTS_S_WAIT_CF),
+ .out_state_mask = S(OM2K_BTS_S_WAIT_CF) |
+ S(OM2K_BTS_S_INIT),
.name = "INIT",
.action = om2k_bts_s_init,
},
[OM2K_BTS_S_WAIT_CF] = {
.in_event_mask = S(OM2K_BTS_EVT_CF_DONE),
.out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_WAIT_TF),
+ S(OM2K_BTS_S_WAIT_TF) |
+ S(OM2K_BTS_S_INIT),
.name = "WAIT-CF",
.action = om2k_bts_s_wait_cf,
},
[OM2K_BTS_S_WAIT_TF] = {
.in_event_mask = S(OM2K_BTS_EVT_TF_DONE),
.out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_WAIT_CON),
+ S(OM2K_BTS_S_WAIT_CON) |
+ S(OM2K_BTS_S_WAIT_IS) |
+ S(OM2K_BTS_S_INIT),
.name = "WAIT-TF",
.action = om2k_bts_s_wait_tf,
},
[OM2K_BTS_S_WAIT_CON] = {
.in_event_mask = S(OM2K_BTS_EVT_CON_DONE),
.out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_WAIT_IS),
+ S(OM2K_BTS_S_WAIT_IS) |
+ S(OM2K_BTS_S_INIT),
.name = "WAIT-CON",
.action = om2k_bts_s_wait_con,
},
@@ -2495,35 +2535,41 @@ static const struct osmo_fsm_state om2k_bts_states[] = {
.in_event_mask = S(OM2K_BTS_EVT_IS_DONE),
.out_state_mask = S(OM2K_BTS_S_ERROR) |
S(OM2K_BTS_S_WAIT_MCTR) |
- S(OM2K_BTS_S_WAIT_TRX_LAPD),
+ S(OM2K_BTS_S_WAIT_TRX_LAPD) |
+ S(OM2K_BTS_S_INIT),
.name = "WAIT-IS",
.action = om2k_bts_s_wait_is,
},
[OM2K_BTS_S_WAIT_MCTR] = {
.in_event_mask = S(OM2K_BTS_EVT_MCTR_DONE),
.out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_WAIT_TRX_LAPD),
+ S(OM2K_BTS_S_WAIT_TRX_LAPD) |
+ S(OM2K_BTS_S_INIT),
.name = "WAIT-MCTR",
.action = om2k_bts_s_wait_mctr,
},
[OM2K_BTS_S_WAIT_TRX_LAPD] = {
.in_event_mask = S(OM2K_BTS_EVT_TRX_LAPD_UP),
- .out_state_mask = S(OM2K_BTS_S_WAIT_TRX),
+ .out_state_mask = S(OM2K_BTS_S_WAIT_TRX) |
+ S(OM2K_BTS_S_INIT),
.name = "WAIT-TRX-LAPD",
.action = om2k_bts_s_wait_trx_lapd,
},
[OM2K_BTS_S_WAIT_TRX] = {
.in_event_mask = S(OM2K_BTS_EVT_TRX_DONE),
.out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_DONE),
+ S(OM2K_BTS_S_DONE) |
+ S(OM2K_BTS_S_INIT),
.name = "WAIT-TRX",
.action = om2k_bts_s_wait_trx,
},
[OM2K_BTS_S_DONE] = {
+ .out_state_mask = S(OM2K_BTS_S_INIT),
.name = "DONE",
.onenter = om2k_bts_s_done_onenter,
},
[OM2K_BTS_S_ERROR] = {
+ .out_state_mask = S(OM2K_BTS_S_INIT),
.name = "ERROR",
},
};
@@ -2546,31 +2592,44 @@ static struct osmo_fsm om2k_bts_fsm = {
.states = om2k_bts_states,
.num_states = ARRAY_SIZE(om2k_bts_states),
.log_subsys = DNM,
+ .allstate_event_mask = S(OM2K_BTS_EVT_RESET),
+ .allstate_action = om2k_bts_allstate,
.event_names = om2k_bts_events,
.timer_cb = om2k_bts_timer_cb,
};
-struct osmo_fsm_inst *
-om2k_bts_fsm_start(struct gsm_bts *bts)
+static struct osmo_fsm_inst *
+om2k_bts_fsm_alloc(struct gsm_bts *bts)
{
struct osmo_fsm_inst *fi;
struct om2k_bts_fsm_priv *obfp;
char idbuf[16];
+ OSMO_ASSERT(!bts->rbs2000.bts_fi);
+
snprintf(idbuf, sizeof(idbuf), "%u", bts->nr);
- fi = osmo_fsm_inst_alloc(&om2k_bts_fsm, bts, NULL,
- LOGL_DEBUG, idbuf);
+ fi = osmo_fsm_inst_alloc(&om2k_bts_fsm, bts, NULL, LOGL_DEBUG, idbuf);
if (!fi)
return NULL;
+
fi->priv = obfp = talloc_zero(fi, struct om2k_bts_fsm_priv);
obfp->bts = bts;
- osmo_fsm_inst_dispatch(fi, OM2K_BTS_EVT_START, NULL);
-
return fi;
}
+void om2k_bts_fsm_start(struct gsm_bts *bts)
+{
+ OSMO_ASSERT(bts->rbs2000.bts_fi);
+ osmo_fsm_inst_dispatch(bts->rbs2000.bts_fi, OM2K_BTS_EVT_START, NULL);
+}
+
+void om2k_bts_fsm_reset(struct gsm_bts *bts)
+{
+ OSMO_ASSERT(bts->rbs2000.bts_fi);
+ osmo_fsm_inst_dispatch(bts->rbs2000.bts_fi, OM2K_BTS_EVT_RESET, NULL);
+}
/***********************************************************************
* OM2000 Negotiation
@@ -2587,7 +2646,7 @@ static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2
msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data);
- DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo),
+ DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(mo),
get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK));
return abis_om2k_sendmsg(bts, msg);
@@ -2709,18 +2768,16 @@ static int om2k_rx_nack(struct msgb *msg)
uint16_t msg_type = ntohs(o2h->msg_type);
struct tlv_parsed tp;
- LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", om2k_mo_name(&o2h->mo),
+ LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", abis_om2k_mo_name(&o2h->mo),
get_value_string(om2k_msgcode_vals, msg_type));
abis_om2k_msg_tlv_parse(&tp, o2h);
if (TLVP_PRESENT(&tp, OM2K_DEI_REASON_CODE))
- LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x",
- *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE));
+ LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x", *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE));
if (TLVP_PRESENT(&tp, OM2K_DEI_RESULT_CODE))
LOGPC(DNM, LOGL_ERROR, ", Result %s",
- get_value_string(om2k_result_strings,
- *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE)));
+ get_value_string(om2k_result_strings, *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE)));
LOGPC(DNM, LOGL_ERROR, "\n");
return 0;
@@ -2734,8 +2791,7 @@ static int process_mo_state(struct gsm_bts *bts, struct om2k_decoded_msg *odm)
return -EIO;
mo_state = *TLVP_VAL(&odm->tp, OM2K_DEI_MO_STATE);
- LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n",
- om2k_mo_name(&odm->o2h.mo),
+ LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n", abis_om2k_mo_name(&odm->o2h.mo),
get_value_string(om2k_msgcode_vals, odm->msg_type),
get_value_string(om2k_mostate_vals, mo_state));
@@ -2743,10 +2799,8 @@ static int process_mo_state(struct gsm_bts *bts, struct om2k_decoded_msg *odm)
* not yield an enabled mo-state */
if (odm->msg_type == OM2K_MSGT_ENABLE_RES
&& mo_state != OM2K_MO_S_ENABLED) {
- LOGP(DNM, LOGL_ERROR,
- "Rx MO=%s %s Failed to enable MO State!\n",
- om2k_mo_name(&odm->o2h.mo),
- get_value_string(om2k_msgcode_vals, odm->msg_type));
+ LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s Failed to enable MO State!\n",
+ abis_om2k_mo_name(&odm->o2h.mo), get_value_string(om2k_msgcode_vals, odm->msg_type));
}
update_mo_state(bts, &odm->o2h.mo, mo_state);
@@ -2786,7 +2840,7 @@ static bool display_fault_bits(const uint8_t *vect, uint16_t len,
}
sprintf(string + strlen(string), ")\n");
- DEBUGP(DNM, "Rx MO=%s %s", om2k_mo_name(mo), string);
+ DEBUGP(DNM, "Rx MO=%s %s", abis_om2k_mo_name(mo), string);
return true;
}
@@ -2816,8 +2870,7 @@ static void display_fault_maps(const uint8_t *src, unsigned int src_len,
src++;
src_len--;
if (msg_code != OM2K_MSGT_FAULT_REP) {
- LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault report: invalid message code!\n",
- om2k_mo_name(mo));
+ LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault report: invalid message code!\n", abis_om2k_mo_name(mo));
return;
}
@@ -2831,21 +2884,18 @@ static void display_fault_maps(const uint8_t *src, unsigned int src_len,
/* Bail if an the maximum number of TLV fields
* have been parsed */
if (tlv_count >= 11) {
- LOGP(DNM, LOGL_ERROR,
- "Rx MO=%s Fault Report: too many tlv elements!\n",
- om2k_mo_name(mo));
+ LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault Report: too many tlv elements!\n",
+ abis_om2k_mo_name(mo));
return;
}
/* Parse TLV field */
- rc = tlv_parse_one(&tag, &tag_len, &val, &om2k_att_tlvdef,
- src + src_pos, src_len - src_pos);
+ rc = tlv_parse_one(&tag, &tag_len, &val, &om2k_att_tlvdef, src + src_pos, src_len - src_pos);
if (rc > 0)
src_pos += rc;
else {
- LOGP(DNM, LOGL_ERROR,
- "Rx MO=%s Fault Report: invalid tlv element!\n",
- om2k_mo_name(mo));
+ LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault Report: invalid tlv element!\n",
+ abis_om2k_mo_name(mo));
return;
}
@@ -2872,8 +2922,7 @@ static void display_fault_maps(const uint8_t *src, unsigned int src_len,
}
if (!faults_present) {
- DEBUGP(DNM, "Rx MO=%s Fault Report: All faults ceased!\n",
- om2k_mo_name(mo));
+ DEBUGP(DNM, "Rx MO=%s Fault Report: All faults ceased!\n", abis_om2k_mo_name(mo));
}
}
@@ -2890,28 +2939,24 @@ int abis_om2k_rcvmsg(struct msgb *msg)
/* Various consistency checks */
if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
- oh->placement);
+ LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", oh->placement);
if (oh->placement != ABIS_OM_PLACEMENT_FIRST)
return -EINVAL;
}
if (oh->sequence != 0) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n",
- oh->sequence);
+ LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", oh->sequence);
return -EINVAL;
}
msg->l3h = (unsigned char *)o2h + sizeof(*o2h);
if (oh->mdisc != ABIS_OM_MDISC_FOM) {
- LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n",
- oh->mdisc);
+ LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", oh->mdisc);
return -EINVAL;
}
- DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo),
- get_value_string(om2k_msgcode_vals, msg_type),
- osmo_hexdump(msg->l2h, msgb_l2len(msg)));
+ DEBUGP(DNM, "Rx MO=%s %s (%s)\n", abis_om2k_mo_name(&o2h->mo),
+ get_value_string(om2k_msgcode_vals, msg_type), osmo_hexdump(msg->l2h, msgb_l2len(msg)));
om2k_decode_msg(&odm, msg);
@@ -2996,14 +3041,12 @@ int abis_om2k_rcvmsg(struct msgb *msg)
mo = get_om2k_mo(bts, &o2h->mo);
if (!mo) {
LOGP(DNM, LOGL_ERROR, "Couldn't resolve MO for OM2K msg "
- "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type),
- msgb_hexdump(msg));
+ "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), msgb_hexdump(msg));
return 0;
}
if (!mo->fsm) {
LOGP(DNM, LOGL_ERROR, "MO object should not generate any message. fsm == NULL "
- "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type),
- msgb_hexdump(msg));
+ "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), msgb_hexdump(msg));
return 0;
}
@@ -3014,8 +3057,7 @@ int abis_om2k_rcvmsg(struct msgb *msg)
return rc;
}
-static void om2k_mo_init(struct om2k_mo *mo, uint8_t class,
- uint8_t bts_nr, uint8_t assoc_so, uint8_t inst)
+static void om2k_mo_init(struct om2k_mo *mo, uint8_t class, uint8_t bts_nr, uint8_t assoc_so, uint8_t inst)
{
mo->addr.class = class;
mo->addr.bts = bts_nr;
@@ -3027,21 +3069,28 @@ static void om2k_mo_init(struct om2k_mo *mo, uint8_t class,
void abis_om2k_trx_init(struct gsm_bts_trx *trx)
{
struct gsm_bts *bts = trx->bts;
+ struct osmo_fsm_inst *trx_fi;
unsigned int i;
OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000);
- om2k_mo_init(&trx->rbs2000.trxc.om2k_mo, OM2K_MO_CLS_TRXC,
- bts->nr, 255, trx->nr);
- om2k_mo_init(&trx->rbs2000.tx.om2k_mo, OM2K_MO_CLS_TX,
- bts->nr, 255, trx->nr);
- om2k_mo_init(&trx->rbs2000.rx.om2k_mo, OM2K_MO_CLS_RX,
- bts->nr, 255, trx->nr);
+ trx_fi = om2k_trx_fsm_alloc(trx->bts->rbs2000.bts_fi, trx, OM2K_BTS_EVT_TRX_DONE);
+ trx->rbs2000.trx_fi = trx_fi;
+ trx->rbs2000.rx_diversity = OM2K_RX_DIVERSITY_A;
+
+ om2k_mo_init(&trx->rbs2000.trxc.om2k_mo, OM2K_MO_CLS_TRXC, bts->nr, 255, trx->nr);
+ om2k_mo_fsm_alloc(trx_fi, OM2K_TRX_EVT_TRXC_DONE, trx, &trx->rbs2000.trxc.om2k_mo);
+
+ om2k_mo_init(&trx->rbs2000.tx.om2k_mo, OM2K_MO_CLS_TX, bts->nr, 255, trx->nr);
+ om2k_mo_fsm_alloc(trx_fi, OM2K_TRX_EVT_TX_DONE, trx, &trx->rbs2000.tx.om2k_mo);
+
+ om2k_mo_init(&trx->rbs2000.rx.om2k_mo, OM2K_MO_CLS_RX, bts->nr, 255, trx->nr);
+ om2k_mo_fsm_alloc(trx_fi, OM2K_TRX_EVT_RX_DONE, trx, &trx->rbs2000.rx.om2k_mo);
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
struct gsm_bts_trx_ts *ts = &trx->ts[i];
- om2k_mo_init(&ts->rbs2000.om2k_mo, OM2K_MO_CLS_TS,
- bts->nr, trx->nr, i);
+ om2k_mo_init(&ts->rbs2000.om2k_mo, OM2K_MO_CLS_TS, bts->nr, trx->nr, i);
+ om2k_mo_fsm_alloc(trx_fi, OM2K_TRX_EVT_TS_DONE, trx, &ts->rbs2000.om2k_mo);
OSMO_ASSERT(ts->fi);
}
}
@@ -3049,20 +3098,31 @@ void abis_om2k_trx_init(struct gsm_bts_trx *trx)
/* initialize the OM2K_MO members of gsm_bts */
void abis_om2k_bts_init(struct gsm_bts *bts)
{
+ struct osmo_fsm_inst *bts_fi;
+
OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000);
- om2k_mo_init(&bts->rbs2000.cf.om2k_mo, OM2K_MO_CLS_CF,
- bts->nr, 0xFF, 0);
- om2k_mo_init(&bts->rbs2000.is.om2k_mo, OM2K_MO_CLS_IS,
- bts->nr, 0xFF, 0);
- om2k_mo_init(&bts->rbs2000.con.om2k_mo, OM2K_MO_CLS_CON,
- bts->nr, 0xFF, 0);
- om2k_mo_init(&bts->rbs2000.dp.om2k_mo, OM2K_MO_CLS_DP,
- bts->nr, 0xFF, 0);
- om2k_mo_init(&bts->rbs2000.tf.om2k_mo, OM2K_MO_CLS_TF,
- bts->nr, 0xFF, 0);
- om2k_mo_init(&bts->rbs2000.mctr.om2k_mo, OM2K_MO_CLS_MCTR,
- bts->nr, 0xFF, 0); // FIXME: There can be multiple MCTRs ...
+ bts_fi = om2k_bts_fsm_alloc(bts);
+ bts->rbs2000.bts_fi = bts_fi;
+ bts->rbs2000.sync_src = OM2K_SYNC_SRC_INTERNAL;
+
+ om2k_mo_init(&bts->rbs2000.cf.om2k_mo, OM2K_MO_CLS_CF, bts->nr, 0xFF, 0);
+ om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_CF_DONE, bts->c0, &bts->rbs2000.cf.om2k_mo);
+
+ om2k_mo_init(&bts->rbs2000.is.om2k_mo, OM2K_MO_CLS_IS, bts->nr, 0xFF, 0);
+ om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_IS_DONE, bts->c0, &bts->rbs2000.is.om2k_mo);
+
+ om2k_mo_init(&bts->rbs2000.con.om2k_mo, OM2K_MO_CLS_CON, bts->nr, 0xFF, 0);
+ om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_CON_DONE, bts->c0, &bts->rbs2000.con.om2k_mo);
+
+ om2k_mo_init(&bts->rbs2000.dp.om2k_mo, OM2K_MO_CLS_DP, bts->nr, 0xFF, 0);
+
+ om2k_mo_init(&bts->rbs2000.tf.om2k_mo, OM2K_MO_CLS_TF, bts->nr, 0xFF, 0);
+ om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_TF_DONE, bts->c0, &bts->rbs2000.tf.om2k_mo);
+
+ om2k_mo_init(&bts->rbs2000.mctr.om2k_mo, OM2K_MO_CLS_MCTR, bts->nr, 0xFF, 0);
+ om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_MCTR_DONE, bts->c0, &bts->rbs2000.mctr.om2k_mo);
+ // FIXME: There can be multiple MCTRs ...
}
static __attribute__((constructor)) void abis_om2k_init(void)
diff --git a/src/osmo-bsc/abis_om2000_vty.c b/src/osmo-bsc/abis_om2000_vty.c
index 26e8488cb..76048071a 100644
--- a/src/osmo-bsc/abis_om2000_vty.c
+++ b/src/osmo-bsc/abis_om2000_vty.c
@@ -41,6 +41,8 @@
#include <osmocom/vty/logging.h>
#include <osmocom/vty/telnet_interface.h>
+#define X(x) (1 << x)
+
static struct cmd_node om2k_node = {
OM2K_NODE,
"%s(om2k)# ",
@@ -61,11 +63,6 @@ struct oml_node_state {
struct con_group *cg;
};
-static int dummy_config_write(struct vty *v)
-{
- return CMD_SUCCESS;
-}
-
/* FIXME: auto-generate those strings from the value_string lists */
#define OM2K_OBJCLASS_VTY "(trxc|tg|ts|tf|is|con|dp|mctr|cf|tx|rx)"
#define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \
@@ -79,6 +76,7 @@ static int dummy_config_write(struct vty *v)
"Central Function\n" \
"Transmitter\n" \
"Receiver\n"
+#define OM2K_VTY_HELP "Configure OM2K specific parameters\n"
DEFUN(om2k_class_inst, om2k_class_inst_cmd,
"bts <0-255> om2000 class " OM2K_OBJCLASS_VTY
@@ -343,10 +341,11 @@ static int con_group_del_path(struct con_group *cg, uint16_t ccp,
return -ENOENT;
}
-DEFUN(cfg_om2k_con_group, cfg_om2k_con_group_cmd,
- "con-connection-group <1-31>",
- "Configure a CON (Concentrator) Connection Group\n"
- "CON Connection Group Number\n")
+DEFUN_ATTR(cfg_om2k_con_group, cfg_om2k_con_group_cmd,
+ "con-connection-group <1-31>",
+ "Configure a CON (Concentrator) Connection Group\n"
+ "CON Connection Group Number\n",
+ CMD_ATTR_IMMEDIATE)
{
struct gsm_bts *bts = vty->index;
struct con_group *cg;
@@ -371,10 +370,11 @@ DEFUN(cfg_om2k_con_group, cfg_om2k_con_group_cmd,
return CMD_SUCCESS;
}
-DEFUN(del_om2k_con_group, del_om2k_con_group_cmd,
- "del-connection-group <1-31>",
- "Delete a CON (Concentrator) Connection Group\n"
- "CON Connection Group Number\n")
+DEFUN_ATTR(del_om2k_con_group, del_om2k_con_group_cmd,
+ "del-connection-group <1-31>",
+ "Delete a CON (Concentrator) Connection Group\n"
+ "CON Connection Group Number\n",
+ CMD_ATTR_IMMEDIATE)
{
struct gsm_bts *bts = vty->index;
int rc;
@@ -402,9 +402,10 @@ DEFUN(del_om2k_con_group, del_om2k_con_group_cmd,
"CON Connection Point\n" \
"Contiguity Index\n" \
-DEFUN(cfg_om2k_con_path_dec, cfg_om2k_con_path_dec_cmd,
- "con-path (add|del) <0-2047> <0-255> deconcentrated <0-63>",
- CON_PATH_HELP "De-concentrated in/outlet\n" "TEI Value\n")
+DEFUN_USRATTR(cfg_om2k_con_path_dec, cfg_om2k_con_path_dec_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "con-path (add|del) <0-2047> <0-255> deconcentrated <0-63>",
+ CON_PATH_HELP "De-concentrated in/outlet\n" "TEI Value\n")
{
struct con_group *cg = vty->index;
uint16_t ccp = atoi(argv[1]);
@@ -424,9 +425,10 @@ DEFUN(cfg_om2k_con_path_dec, cfg_om2k_con_path_dec_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_om2k_con_path_conc, cfg_om2k_con_path_conc_cmd,
- "con-path (add|del) <0-2047> <0-255> concentrated <1-16>",
- CON_PATH_HELP "Concentrated in/outlet\n" "Tag Number\n")
+DEFUN_USRATTR(cfg_om2k_con_path_conc, cfg_om2k_con_path_conc_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "con-path (add|del) <0-2047> <0-255> concentrated <1-16>",
+ CON_PATH_HELP "Concentrated in/outlet\n" "Tag Number\n")
{
struct con_group *cg = vty->index;
uint16_t ccp = atoi(argv[1]);
@@ -446,11 +448,12 @@ DEFUN(cfg_om2k_con_path_conc, cfg_om2k_con_path_conc_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_bts_alt_mode, cfg_bts_alt_mode_cmd,
- "abis-lower-transport (single-timeslot|super-channel)",
- "Configure thee Abis Lower Transport\n"
- "Single Timeslot (classic Abis)\n"
- "SuperChannel (Packet Abis)\n")
+DEFUN_USRATTR(cfg_bts_alt_mode, cfg_bts_alt_mode_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "abis-lower-transport (single-timeslot|super-channel)",
+ "Configure thee Abis Lower Transport\n"
+ "Single Timeslot (classic Abis)\n"
+ "SuperChannel (Packet Abis)\n")
{
struct gsm_bts *bts = vty->index;
@@ -468,15 +471,37 @@ DEFUN(cfg_bts_alt_mode, cfg_bts_alt_mode_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_bts_om2k_version_limit, cfg_bts_om2k_version_limit_cmd,
- "om2000 version-limit (oml|rsl) gen <0-99> rev <0-99>",
- "Configure OM2K specific parameters\n"
- "Configure optional maximum protocol version to negotiate\n"
- "Limit OML IWD version\n" "Limit RSL IWD version\n"
- "Generation limit\n"
- "Generation number to limit to (inclusive)\n"
- "Revision limit\n"
- "Revision number to limit to (inclusive)\n")
+DEFUN_USRATTR(cfg_bts_om2k_sync, cfg_bts_om2k_sync_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "om2000 sync-source (internal|external)",
+ OM2K_VTY_HELP
+ "TF Synchronization Source\n"
+ "Use Internal (E1)\n"
+ "USe External (GPS)\n")
+{
+ struct gsm_bts *bts = vty->index;
+ if (bts->type != GSM_BTS_TYPE_RBS2000) {
+ vty_out(vty, "%% Command only works for RBS2000%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (!strcmp(argv[0], "internal"))
+ bts->rbs2000.sync_src = OM2K_SYNC_SRC_INTERNAL;
+ else if (!strcmp(argv[0], "external"))
+ bts->rbs2000.sync_src = OM2K_SYNC_SRC_EXTERNAL;
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_om2k_version_limit, cfg_bts_om2k_version_limit_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "om2000 version-limit (oml|rsl) gen <0-99> rev <0-99>",
+ OM2K_VTY_HELP
+ "Configure optional maximum protocol version to negotiate\n"
+ "Limit OML IWD version\n" "Limit RSL IWD version\n"
+ "Generation limit\n"
+ "Generation number to limit to (inclusive)\n"
+ "Revision limit\n"
+ "Revision number to limit to (inclusive)\n")
{
struct gsm_bts *bts = vty->index;
int iwd;
@@ -502,11 +527,12 @@ DEFUN(cfg_bts_om2k_version_limit, cfg_bts_om2k_version_limit_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd,
- "is-connection-list (add|del) <0-2047> <0-2047> <0-255>",
- "Interface Switch Connection List\n"
- "Add to IS list\n" "Delete from IS list\n"
- "ICP1\n" "ICP2\n" "Contiguity Index\n")
+DEFUN_USRATTR(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "is-connection-list (add|del) <0-2047> <0-2047> <0-255>",
+ "Interface Switch Connection List\n"
+ "Add to IS list\n" "Delete from IS list\n"
+ "ICP1\n" "ICP2\n" "Contiguity Index\n")
{
struct gsm_bts *bts = vty->index;
uint16_t icp1 = atoi(argv[1]);
@@ -543,6 +569,33 @@ DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd,
return CMD_SUCCESS;
}
+DEFUN_USRATTR(cfg_trx_om2k_rx_diversity,
+ cfg_trx_om2k_rx_diversity_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "om2000 rx-diversity-mode (a|ab|b)",
+ OM2K_VTY_HELP
+ "RX Diversity\n"
+ "Antenna TX/RX (A)\n"
+ "Both Antennas\n"
+ "Antenna RX (B)\n")
+
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ if (trx->bts->type != GSM_BTS_TYPE_RBS2000) {
+ vty_out(vty, "%% Command only works for RBS2000%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[0], "a"))
+ trx->rbs2000.rx_diversity = OM2K_RX_DIVERSITY_A;
+ else if (!strcmp(argv[0], "ab"))
+ trx->rbs2000.rx_diversity = OM2K_RX_DIVERSITY_AB;
+ else if (!strcmp(argv[0], "b"))
+ trx->rbs2000.rx_diversity = OM2K_RX_DIVERSITY_B;
+ return CMD_SUCCESS;
+}
DEFUN(om2k_conf_req, om2k_conf_req_cmd,
"configuration-request",
@@ -622,6 +675,24 @@ static void dump_con_group(struct vty *vty, struct con_group *cg)
}
}
+static const struct value_string om2k_rx_diversity_names[4] = {
+ { OM2K_RX_DIVERSITY_A, "a" },
+ { OM2K_RX_DIVERSITY_AB, "ab" },
+ { OM2K_RX_DIVERSITY_B, "b" },
+ { 0, NULL }
+};
+
+static const char *rx_diversity2str(enum om2k_rx_diversity type)
+{
+ return get_value_string(om2k_rx_diversity_names, type);
+}
+
+void abis_om2k_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
+{
+ vty_out(vty, " om2000 rx-diversity-mode %s%s",
+ rx_diversity2str(trx->rbs2000.rx_diversity), VTY_NEWLINE);
+}
+
void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts)
{
struct is_conn_group *igrp;
@@ -647,10 +718,86 @@ void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts)
(bts->rbs2000.om2k_version[i].limit >> 8),
(bts->rbs2000.om2k_version[i].limit & 0xff),
VTY_NEWLINE);
+ vty_out(vty, " om2000 sync-source %s%s",
+ bts->rbs2000.sync_src != OM2K_SYNC_SRC_EXTERNAL
+ ? "internal" : "external",
+ VTY_NEWLINE);
+}
+
+static void vty_dump_om2k_mo(struct vty *vty, const struct om2k_mo *mo, const char *pfx)
+{
+ unsigned int pfx_len = strlen(pfx);
+ const char *mo_name = abis_om2k_mo_name(&mo->addr);
+ unsigned int pfx_mo_len = pfx_len + strlen(mo_name);
+ unsigned int pfx2_len;
+ char pfx2[23];
+ int i;
+
+ /* generate padding after MO class to align the state names in the same column */
+ if (pfx_mo_len > sizeof(pfx2)-1)
+ pfx2_len = 0;
+ else
+ pfx2_len = sizeof(pfx2)-1 - pfx_mo_len;
+ for (i = 0; i < pfx2_len; i++)
+ pfx2[i] = ' ';
+ pfx2[pfx2_len] = '\0';
+
+ vty_out(vty, "%s%s%s %s%s", pfx, mo_name, pfx2,
+ mo->fsm ? osmo_fsm_inst_state_name(mo->fsm) : "[NULL]",
+ VTY_NEWLINE);
+}
+
+DEFUN(show_om2k_mo, show_om2k_mo_cmd,
+ "show bts <0-255> om2k-mo",
+ SHOW_STR "Display information about a BTS\n"
+ "BTS number\n" "OM2000 Managed Object information\n")
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ int bts_nr = atoi(argv[0]);
+ struct gsm_bts *bts = gsm_bts_num(net, bts_nr);
+ struct gsm_bts_trx *trx;
+
+ if (!bts) {
+ vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bts->type != GSM_BTS_TYPE_RBS2000) {
+ vty_out(vty, "%% BTS is not using OM2000%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "BTS %3u OM2K-FSM state %s%s", bts->nr,
+ osmo_fsm_inst_state_name(bts->rbs2000.bts_fi), VTY_NEWLINE);
+ vty_dump_om2k_mo(vty, &bts->rbs2000.cf.om2k_mo, " ");
+ vty_dump_om2k_mo(vty, &bts->rbs2000.con.om2k_mo, " ");
+ vty_dump_om2k_mo(vty, &bts->rbs2000.is.om2k_mo, " ");
+ vty_dump_om2k_mo(vty, &bts->rbs2000.dp.om2k_mo, " ");
+ vty_dump_om2k_mo(vty, &bts->rbs2000.tf.om2k_mo, " ");
+ vty_dump_om2k_mo(vty, &bts->rbs2000.mctr.om2k_mo, " ");
+
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ int tn;
+
+ vty_out(vty, " TRX %u OM2K-FSM state %s%s", trx->nr,
+ osmo_fsm_inst_state_name(trx->rbs2000.trx_fi), VTY_NEWLINE);
+ vty_dump_om2k_mo(vty, &trx->rbs2000.trxc.om2k_mo, " ");
+ vty_dump_om2k_mo(vty, &trx->rbs2000.rx.om2k_mo, " ");
+ vty_dump_om2k_mo(vty, &trx->rbs2000.tx.om2k_mo, " ");
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ vty_dump_om2k_mo(vty, &ts->rbs2000.om2k_mo, " ");
+ }
+ }
+
+ return CMD_SUCCESS;
}
int abis_om2k_vty_init(void)
{
+ install_element_ve(&show_om2k_mo_cmd);
install_element(ENABLE_NODE, &om2k_class_inst_cmd);
install_element(ENABLE_NODE, &om2k_classnum_inst_cmd);
install_node(&om2k_node, dummy_config_write);
@@ -674,9 +821,12 @@ int abis_om2k_vty_init(void)
install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd);
install_element(BTS_NODE, &cfg_bts_alt_mode_cmd);
+ install_element(BTS_NODE, &cfg_bts_om2k_sync_cmd);
install_element(BTS_NODE, &cfg_bts_om2k_version_limit_cmd);
install_element(BTS_NODE, &cfg_om2k_con_group_cmd);
install_element(BTS_NODE, &del_om2k_con_group_cmd);
+ install_element(TRX_NODE, &cfg_trx_om2k_rx_diversity_cmd);
+
return 0;
}
diff --git a/src/osmo-bsc/abis_osmo.c b/src/osmo-bsc/abis_osmo.c
new file mode 100644
index 000000000..48774b4e8
--- /dev/null
+++ b/src/osmo-bsc/abis_osmo.c
@@ -0,0 +1,226 @@
+/* Osmocom specific protocols over Abis (IPA) */
+
+/* (C) 2021 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@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 <errno.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/protocol/ipaccess.h>
+#include <osmocom/gsm/ipa.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/bsc/abis_osmo.h>
+#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
+
+////////////////////////////////////////
+// OSMO ABIS extensions (PCU)
+///////////////////////////////////////
+#define PCUIF_HDR_SIZE ( sizeof(struct gsm_pcu_if) - sizeof(((struct gsm_pcu_if *)0)->u) )
+
+static struct msgb *abis_osmo_pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr, size_t extra_size)
+{
+ struct msgb *msg;
+ struct gsm_pcu_if *pcu_prim;
+ msg = msgb_alloc_headroom(OM_HEADROOM_SIZE + sizeof(struct gsm_pcu_if) + extra_size,
+ OM_HEADROOM_SIZE, "IPA/ABIS/OSMO");
+ /* Only header is filled, caller is responible for reserving + filling
+ * message type specific contents: */
+ msgb_put(msg, PCUIF_HDR_SIZE);
+ pcu_prim = (struct gsm_pcu_if *) msgb_data(msg);
+ pcu_prim->msg_type = msg_type;
+ pcu_prim->bts_nr = bts_nr;
+ return msg;
+}
+
+/* Send a OML NM Message from BSC to BTS */
+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);
+}
+
+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)
+{
+ int rc;
+ uint16_t data_length = osmo_load16be(&container->length);
+
+ if (container_len < sizeof(*container) + data_length) {
+ LOGP(DNM, LOGL_ERROR, "ABIS_OSMO_PCU CONTAINER message inside (%d) too short\n",
+ container->msg_type);
+ return -EINVAL;
+ }
+
+ LOGP(DNM, LOGL_INFO, "(bts=%d) Rx ABIS_OSMO_PCU CONTAINER msg type %u\n",
+ 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);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int rcvmsg_pcu(struct gsm_bts *bts, struct msgb *msg)
+{
+ struct gsm_pcu_if *pcu_prim;
+ int rc;
+
+ if (msgb_l2len(msg) < PCUIF_HDR_SIZE) {
+ LOGP(DNM, LOGL_ERROR, "ABIS_OSMO_PCU message too short\n");
+ return -EIO;
+ }
+
+ pcu_prim = msgb_l2(msg);
+ LOGP(DNM, LOGL_INFO, "(bts=%d) Rx ABIS_OSMO_PCU msg type %u\n",
+ pcu_prim->bts_nr, pcu_prim->msg_type);
+
+ switch (pcu_prim->msg_type) {
+ case PCU_IF_MSG_CONTAINER:
+ if (msgb_l2len(msg) < PCUIF_HDR_SIZE + sizeof(pcu_prim->u.container)) {
+ LOGP(DNM, LOGL_ERROR, "ABIS_OSMO_PCU CONTAINER message too short\n");
+ rc = -EINVAL;
+ } else {
+ rc = rcvmsg_pcu_container(bts, &pcu_prim->u.container, msgb_l2len(msg) - PCUIF_HDR_SIZE);
+ }
+ break;
+ default:
+ LOGP(DNM, LOGL_NOTICE, "(bts=%d) Rx ABIS_OSMO_PCU unexpected msg type %u!\n",
+ pcu_prim->bts_nr, pcu_prim->msg_type);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+////////////////////////////////////////
+// OSMO ABIS extensions (generic code)
+///////////////////////////////////////
+
+/* High-Level API */
+/* Entry-point where L2 OSMO from BTS enters the NM code */
+int abis_osmo_rcvmsg(struct msgb *msg)
+{
+ int rc;
+ struct e1inp_sign_link *link = msg->dst;
+ struct gsm_bts *bts = link->trx->bts;
+ uint8_t *osmo_type = msgb_l2(msg);
+ msg->l2h = osmo_type + 1;
+
+ switch (*osmo_type) {
+ case IPAC_PROTO_EXT_PCU:
+ rc = rcvmsg_pcu(bts, msg);
+ break;
+ default:
+ LOGP(DNM, LOGL_ERROR, "IPAC_PROTO_EXT 0x%x not supported!\n",
+ *osmo_type);
+ rc = -EINVAL;
+ }
+
+ msgb_free(msg);
+ return rc;
+}
+
+
+/* Send a OML NM Message from BSC to BTS */
+int abis_osmo_sendmsg(struct gsm_bts *bts, struct msgb *msg)
+{
+ msg->dst = bts->osmo_link;
+
+ msg->l2h = msg->data;
+
+ return abis_sendmsg(msg);
+
+}
diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c
index 858c683e1..52008a5ba 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -55,8 +55,8 @@
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/smscb.h>
#include <osmocom/bsc/bts.h>
-#define RSL_ALLOC_SIZE 1024
-#define RSL_ALLOC_HEADROOM 128
+#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)
@@ -72,26 +72,26 @@ static void count_codecs(struct gsm_bts *bts, struct gsm_lchan *lchan)
OSMO_ASSERT(bts);
if (lchan->type == GSM_LCHAN_TCH_H) {
- switch (lchan->tch_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(lchan->current_ch_mode_rate.chan_mode)) {
case GSM48_CMODE_SPEECH_AMR:
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_H]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CODEC_AMR_H));
break;
case GSM48_CMODE_SPEECH_V1:
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_V1_HR]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CODEC_V1_HR));
break;
default:
break;
}
} else if (lchan->type == GSM_LCHAN_TCH_F) {
- switch (lchan->tch_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(lchan->current_ch_mode_rate.chan_mode)) {
case GSM48_CMODE_SPEECH_AMR:
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_F]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CODEC_AMR_F));
break;
case GSM48_CMODE_SPEECH_V1:
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_V1_FR]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CODEC_V1_FR));
break;
case GSM48_CMODE_SPEECH_EFR:
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_EFR]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CODEC_EFR));
break;
default:
break;
@@ -146,12 +146,6 @@ static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
return lchan;
}
-static struct msgb *rsl_msgb_alloc(void)
-{
- return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM,
- "RSL");
-}
-
static void pad_macblock(uint8_t *out, const uint8_t *in, int len)
{
memcpy(out, in, len);
@@ -160,13 +154,44 @@ static void pad_macblock(uint8_t *out, const uint8_t *in, int len)
memset(out+len, 0x2b, GSM_MACBLOCK_LEN - len);
}
-/* Chapter 9.3.7: Encryption Information */
+/* Chapter 9.3.7: Encryption Information
+ * Return negative on error, number of bytes written to 'out' on success.
+ * 'out' must provide room for 17 bytes. */
static int build_encr_info(uint8_t *out, struct gsm_lchan *lchan)
{
*out++ = lchan->encr.alg_id & 0xff;
- if (lchan->encr.key_len)
- memcpy(out, lchan->encr.key, lchan->encr.key_len);
- return lchan->encr.key_len + 1;
+ switch (lchan->encr.alg_id) {
+ case GSM0808_ALG_ID_A5_1:
+ case GSM0808_ALG_ID_A5_2:
+ case GSM0808_ALG_ID_A5_3:
+ if (!lchan->encr.key_len) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "A5/%d encryption chosen, but missing Kc\n", lchan->encr.alg_id);
+ return -EINVAL;
+ }
+ /* fall through */
+ case GSM0808_ALG_ID_A5_0:
+ /* When A5/0 is chosen, no encryption is active, so technically, no key is needed. However, 3GPP TS
+ * 48.058 9.3.7 Encryption Information stays quite silent about presence or absence of a key for A5/0.
+ * The only thing specified is how to indicate the length of the key; the possibility that the key may
+ * be zero length is not explicitly mentioned. So it seems that we should always send the key along,
+ * even for A5/0. Currently our ttcn3 test suite does expect the key to be present also for A5/0, see
+ * f_cipher_mode() in bsc/MSC_ConnectionHandler.ttcn. */
+ if (lchan->encr.key_len)
+ memcpy(out, lchan->encr.key, lchan->encr.key_len);
+ return 1 + lchan->encr.key_len;
+
+ case GSM0808_ALG_ID_A5_4:
+ if (!lchan->encr.kc128_present) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "A5/4 encryption chosen, but missing Kc128\n");
+ return -EINVAL;
+ }
+ memcpy(out, lchan->encr.kc128, sizeof(lchan->encr.kc128));
+ return 1 + sizeof(lchan->encr.kc128);
+
+ default:
+ LOG_LCHAN(lchan, LOGL_ERROR, "A5/%d encryption not supported\n", lchan->encr.alg_id);
+ return -EINVAL;
+ }
}
/* If the TLV contain an RSL Cause IE, return pointer to the cause value. If there is no Cause IE, return
@@ -193,8 +218,52 @@ static const char *rsl_cause_name(struct tlv_parsed *tp)
return "";
}
+static void add_power_control_params(struct msgb *msg, enum abis_rsl_ie iei,
+ const struct gsm_lchan *lchan)
+{
+ const struct gsm_bts *bts = lchan->ts->trx->bts;
+ const struct gsm_power_ctrl_params *cp;
+
+ /* Since {MS,BS}_POWER_PARAM IE content is operator dependent, it's not
+ * known how different BTS models will interpret an empty IE, so let's
+ * better skip sending it unless we know for sure what each expects. */
+ if (bts->model->power_ctrl_enc_rsl_params == NULL)
+ return;
+
+ if (iei == RSL_IE_MS_POWER_PARAM)
+ cp = &bts->ms_power_ctrl;
+ else
+ cp = &bts->bs_power_ctrl;
+
+ /* These parameters are only valid for dynamic mode */
+ if (cp->mode != GSM_PWR_CTRL_MODE_DYN_BTS)
+ return;
+
+ /* No dynamic BS power control if the maximum is 0 dB */
+ if (cp->dir == GSM_PWR_CTRL_DIR_DL) {
+ if (lchan->bs_power_db == 0)
+ return;
+ }
+
+ /* Put tag first, length will be updated later */
+ uint8_t *ie_len = msgb_tl_put(msg, iei);
+ uint8_t msg_len = msgb_length(msg);
+
+ if (bts->model->power_ctrl_enc_rsl_params(msg, cp) != 0) {
+ LOGP(DRSL, LOGL_ERROR, "Failed to encode MS/BS Power Control "
+ "parameters, omitting this IE (tag 0x%02x)\n", iei);
+ msgb_get(msg, msg_len - 2);
+ return;
+ }
+
+ /* Update length part of the containing IE */
+ *ie_len = msgb_length(msg) - msg_len;
+}
+
/* 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;
@@ -223,12 +292,13 @@ int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type,
msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data);
}
- msg->dst = trx->rsl_link;
+ msg->dst = trx->rsl_link_primary;
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;
@@ -242,7 +312,7 @@ int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type,
if (data)
msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data);
- msg->dst = trx->rsl_link;
+ msg->dst = trx->rsl_link_primary;
return abis_rsl_sendmsg(msg);
}
@@ -251,8 +321,12 @@ int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type,
const uint8_t *data, int len)
{
struct abis_rsl_dchan_hdr *dh;
- struct msgb *msg = rsl_msgb_alloc();
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ struct msgb *msg;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
+
+ msg = rsl_msgb_alloc();
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_SACCH_INFO_MODIFY);
@@ -262,7 +336,7 @@ int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type,
if (data)
msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
@@ -271,7 +345,10 @@ int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db)
{
struct abis_rsl_dchan_hdr *dh;
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t bs_power_enc;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
db = abs(db);
if (db > 30)
@@ -279,28 +356,31 @@ int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db)
msg = rsl_msgb_alloc();
- lchan->bs_power = db/2;
+ bs_power_enc = db / 2;
if (fpc)
- lchan->bs_power |= 0x10;
+ bs_power_enc |= 0x10;
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL);
dh->chan_nr = chan_nr;
- msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
+ msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power_enc);
+
+ /* BS Power Control Parameters (if supported by BTS model) */
+ add_power_control_params(msg, RSL_IE_BS_POWER_PARAM, lchan);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan)
{
- struct gsm_bts_trx *trx = lchan->ts->trx;
- struct gsm_bts *bts = trx->bts;
struct abis_rsl_dchan_hdr *dh;
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
LOG_LCHAN(lchan, LOGL_DEBUG, "Tx MS POWER CONTROL (ms_power_lvl=%" PRIu8 ")\n",
lchan->ms_power);
@@ -312,21 +392,21 @@ int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan)
dh->chan_nr = chan_nr;
msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
- /* indicate MS power control to be performed by BTS: */
- if (bts->type == GSM_BTS_TYPE_OSMOBTS)
- msgb_tl_put(msg, RSL_IE_MS_POWER_PARAM);
- /* else: Since IE MS_POWER_PARAM content is operator dependent, it's not
- known if non-osmocom BTS models will support an empty IE, so let's
- better skip sending it unless we know for sure what each expects. */
- msg->dst = trx->rsl_link;
+ /* MS Power Control Parameters (if supported by BTS model) */
+ add_power_control_params(msg, RSL_IE_MS_POWER_PARAM, lchan);
+
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
- struct gsm_lchan *lchan)
+ struct gsm_lchan *lchan,
+ const struct channel_mode_and_rate *ch_mode_rate,
+ bool vamos)
{
+ int rc;
memset(cm, 0, sizeof(*cm));
/* FIXME: what to do with data calls ? */
@@ -337,22 +417,22 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
cm->dtx_dtu |= RSL_CMOD_DTXd;
/* set TCH Speech/Data */
- cm->spd_ind = lchan->rsl_cmode;
-
- if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN &&
- lchan->tch_mode != GSM48_CMODE_SIGN)
- LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, "
- "but tch_mode != signalling\n");
+ rc = chan_mode_to_rsl_cmod_spd(ch_mode_rate->chan_mode);
+ if (rc < 0) {
+ LOGP(DRSL, LOGL_ERROR, "unsupported: chan_mode = 0x%02x\n", ch_mode_rate->chan_mode);
+ return rc;
+ }
+ cm->spd_ind = rc;
switch (lchan->type) {
case GSM_LCHAN_SDCCH:
cm->chan_rt = RSL_CMOD_CRT_SDCCH;
break;
case GSM_LCHAN_TCH_F:
- cm->chan_rt = RSL_CMOD_CRT_TCH_Bm;
+ cm->chan_rt = vamos ? RSL_CMOD_CRT_OSMO_TCH_VAMOS_Bm : RSL_CMOD_CRT_TCH_Bm;
break;
case GSM_LCHAN_TCH_H:
- cm->chan_rt = RSL_CMOD_CRT_TCH_Lm;
+ cm->chan_rt = vamos ? RSL_CMOD_CRT_OSMO_TCH_VAMOS_Lm : RSL_CMOD_CRT_TCH_Lm;
break;
case GSM_LCHAN_NONE:
case GSM_LCHAN_UNKNOWN:
@@ -363,7 +443,7 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
return -EINVAL;
}
- switch (lchan->tch_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(ch_mode_rate->chan_mode)) {
case GSM48_CMODE_SIGN:
cm->chan_rate = 0;
break;
@@ -379,10 +459,10 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
case GSM48_CMODE_DATA_14k5:
case GSM48_CMODE_DATA_12k0:
case GSM48_CMODE_DATA_6k0:
- switch (lchan->csd_mode) {
+ switch (ch_mode_rate->csd_mode) {
case LCHAN_CSD_M_NT:
/* non-transparent CSD with RLP */
- switch (lchan->tch_mode) {
+ switch (ch_mode_rate->chan_mode) {
case GSM48_CMODE_DATA_14k5:
cm->chan_rate = RSL_CMOD_SP_NT_14k5;
break;
@@ -395,7 +475,7 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
default:
LOGP(DRSL, LOGL_ERROR,
"unsupported lchan->tch_mode %u\n",
- lchan->tch_mode);
+ ch_mode_rate->chan_mode);
return -EINVAL;
}
break;
@@ -425,36 +505,91 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
cm->chan_rate = RSL_CMOD_CSD_T_32000;
break;
default:
- LOGP(DRSL, LOGL_ERROR,
- "unsupported lchan->csd_mode %u\n",
- lchan->csd_mode);
+ LOGP(DRSL, LOGL_ERROR, "unsupported csd_mode %u\n", ch_mode_rate->csd_mode);
return -EINVAL;
}
break;
default:
- LOGP(DRSL, LOGL_ERROR,
- "unsupported lchan->tch_mode %u\n",
- lchan->tch_mode);
+ LOGP(DRSL, LOGL_ERROR, "unsupported channel mode %u\n", ch_mode_rate->chan_mode);
return -EINVAL;
}
return 0;
}
-static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg)
+static int put_mr_config_for_bts(struct msgb *msg, const struct gsm48_multi_rate_conf *mr_conf_filtered,
+ const struct amr_multirate_conf *mr_modes)
{
- uint8_t len;
+ msgb_put_u8(msg, RSL_IE_MR_CONFIG);
+ return gsm48_multirate_config(msg, mr_conf_filtered, mr_modes->bts_mode, mr_modes->num_modes);
+}
+
+/* indicate FACCH/SACCH Repetition to be performed by BTS,
+ * see also: 3GPP TS 44.006, section 10 and 11 */
+static void put_rep_acch_cap_ie(const struct gsm_lchan *lchan,
+ struct msgb *msg)
+{
+ struct abis_rsl_osmo_rep_acch_cap *cap;
+ const struct gsm_bts *bts = lchan->ts->trx->bts;
- if (lchan->tch_mode != GSM48_CMODE_SPEECH_AMR)
+ /* The RSL_IE_OSMO_REP_ACCH_CAP 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_REP)))
return;
- len = lchan->mr_bts_lv[0];
- if (!len) {
- LOG_LCHAN(lchan, LOGL_ERROR, "Missing Multirate Config (len is zero)\n");
+ 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->rep_acch_cap);
+
+ if (!(lchan->conn && lchan->conn->cm3_valid
+ && lchan->conn->cm3.repeated_acch_capability)) {
+ /* MS supports only FACCH repetition for command frames, so
+ * we mask out all other features, even when they are enabled
+ * on this BTS. */
+ cap->dl_facch_all = 0;
+ cap->dl_sacch = 0;
+ cap->ul_sacch = 0;
+ }
+}
+
+/* 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_MR_CONFIG, lchan->mr_bts_lv[0],
- lchan->mr_bts_lv + 1);
+
+ 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)
+{
+ uint8_t *len = msgb_tl_put(msg, RSL_IE_OSMO_TRAINING_SEQUENCE);
+ *len = 2;
+ /* Convert from spec conforming "human readable" TSC Set 1-4 to 0-3 on the wire */
+ msgb_put_u8(msg, tsc_set - 1);
+ /* TSC is 0-7 both on the wire and in spec descriptions */
+ msgb_put_u8(msg, tsc);
}
/* Chapter 8.4.1 */
@@ -466,10 +601,12 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
struct msgb *msg;
int rc;
uint8_t *len;
- uint8_t ta;
struct rsl_ie_chan_mode cm;
struct gsm48_chan_desc cd;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
DEBUGP(DRSL, "%s Tx RSL Channel Activate with act_type=%s\n",
gsm_ts_and_pchan_name(lchan->ts),
@@ -478,7 +615,7 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
/* PDCH activation is a job for rsl_tx_dyn_ts_pdch_act_deact(); */
OSMO_ASSERT(act_type != RSL_ACT_OSMO_PDCH);
- rc = channel_mode_from_lchan(&cm, lchan);
+ rc = channel_mode_from_lchan(&cm, lchan, &lchan->activate.ch_mode_rate, lchan->activate.info.vamos);
if (rc < 0) {
LOGP(DRSL, LOGL_ERROR,
"%s Cannot find channel mode from lchan type\n",
@@ -486,20 +623,18 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
return rc;
}
- ta = lchan->rqd_ta;
-
- /* BS11 requires TA shifted by 2 bits */
- if (bts->type == GSM_BTS_TYPE_BS11)
- ta <<= 2;
-
memset(&cd, 0, sizeof(cd));
- gsm48_lchan2chan_desc(&cd, lchan);
+ rc = gsm48_lchan2chan_desc(&cd, lchan, lchan->activate.tsc, true);
+ if (rc) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "Error encoding Channel Number\n");
+ return rc;
+ }
msg = rsl_msgb_alloc();
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
+ dh->chan_nr = chan_nr;
msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
@@ -530,6 +665,10 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
rc = build_encr_info(encr_info, lchan);
if (rc > 0)
msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
+ if (rc < 0) {
+ msgb_free(msg);
+ return rc;
+ }
}
switch (act_type) {
@@ -541,21 +680,60 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
break;
}
- msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
- msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
- msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
- /* indicate MS power control to be performed by BTS: */
- if (bts->type == GSM_BTS_TYPE_OSMOBTS)
- msgb_tl_put(msg, RSL_IE_MS_POWER_PARAM);
- /* else: Since IE MS_POWER_PARAM content is operator dependent, it's not
- known if non-osmocom BTS models will support an empty IE, so let's
- better skip sending it unless we know for sure what each expects. */
+ if (bts->bs_power_ctrl.mode != GSM_PWR_CTRL_MODE_NONE)
+ msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power_db / 2);
+ if (bts->ms_power_ctrl.mode != GSM_PWR_CTRL_MODE_NONE)
+ msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
+
+ if (lchan->activate.info.ta_known) {
+ uint8_t ta = lchan->activate.info.ta;
+ /* BS11 requires TA shifted by 2 bits */
+ 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 (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) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "Cannot encode MultiRate Configuration IE\n");
+ msgb_free(msg);
+ return rc;
+ }
+ }
- mr_config_for_bts(lchan, msg);
+ put_rep_acch_cap_ie(lchan, msg);
+ put_top_acch_cap_ie(lchan, &cm, msg);
- msg->dst = trx->rsl_link;
+ /* Selecting a specific TSC Set is only applicable to VAMOS mode */
+ if (lchan->activate.info.vamos && lchan->activate.tsc_set >= 1)
+ put_osmo_training_sequence_ie(msg, lchan->activate.tsc_set, lchan->activate.tsc);
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHAN_ACT_TOTAL]);
+ msg->dst = rsl_chan_link(lchan);
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_ACT_TOTAL));
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_ACT_SDCCH));
+ break;
+ case GSM_LCHAN_TCH_H:
+ case GSM_LCHAN_TCH_F:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_ACT_TCH));
+ break;
+ default:
+ break;
+ }
return abis_rsl_sendmsg(msg);
}
@@ -567,10 +745,14 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
struct msgb *msg;
int rc;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
struct rsl_ie_chan_mode cm;
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
- rc = channel_mode_from_lchan(&cm, lchan);
+ rc = channel_mode_from_lchan(&cm, lchan, &lchan->modify.ch_mode_rate, lchan->modify.info.vamos);
if (rc < 0)
return rc;
@@ -587,11 +769,31 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
rc = build_encr_info(encr_info, lchan);
if (rc > 0)
msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
+ if (rc < 0) {
+ msgb_free(msg);
+ return rc;
+ }
+ }
+
+ 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) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "Cannot encode MultiRate Configuration IE\n");
+ msgb_free(msg);
+ return rc;
+ }
}
- mr_config_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. */
+ if (lchan->modify.info.vamos && lchan->modify.tsc_set >= 1 && bts->model->type == GSM_BTS_TYPE_OSMOBTS)
+ put_osmo_training_sequence_ie(msg, lchan->modify.tsc_set, lchan->modify.tsc);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
@@ -601,11 +803,14 @@ int rsl_encryption_cmd(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh;
struct gsm_lchan *lchan = msg->lchan;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
uint8_t encr_info[MAX_A5_KEY_LEN+2];
uint8_t l3_len = msg->len;
int rc;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
+
/* First push the L3 IE tag and length */
msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
@@ -623,7 +828,7 @@ int rsl_encryption_cmd(struct msgb *msg)
init_dchan_hdr(dh, RSL_MT_ENCR_CMD);
dh->chan_nr = chan_nr;
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
@@ -634,12 +839,16 @@ int rsl_deact_sacch(struct gsm_lchan *lchan)
struct abis_rsl_dchan_hdr *dh;
struct msgb *msg = rsl_msgb_alloc();
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
+
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH);
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
+ dh->chan_nr = chan_nr;
msg->lchan = lchan;
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan));
@@ -652,13 +861,17 @@ int rsl_tx_rf_chan_release(struct gsm_lchan *lchan)
struct abis_rsl_dchan_hdr *dh;
struct msgb *msg;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
+
msg = rsl_msgb_alloc();
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL);
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
+ dh->chan_nr = chan_nr;
msg->lchan = lchan;
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
@@ -693,7 +906,7 @@ int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group,
if (bts->type == GSM_BTS_TYPE_RBS2000 && is_gprs)
msgb_tv_put(msg, RSL_IE_ERIC_PACKET_PAG_IND, 0);
- msg->dst = bts->c0->rsl_link;
+ msg->dst = bts->c0->rsl_link_primary;
return abis_rsl_sendmsg(msg);
}
@@ -753,7 +966,7 @@ struct msgb *rsl_imm_assign_cmd_common(struct gsm_bts *bts, uint8_t len, uint8_t
break;
}
- msg->dst = bts->c0->rsl_link;
+ msg->dst = bts->c0->rsl_link_primary;
return msg;
}
@@ -784,37 +997,153 @@ int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len,
/* Send Siemens specific MS RF Power Capability Indication */
int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci)
{
- struct msgb *msg = rsl_msgb_alloc();
+ struct msgb *msg;
struct abis_rsl_dchan_hdr *dh;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
+
+ msg = rsl_msgb_alloc();
+
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_SIEMENS_MRPCI);
dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
+ dh->chan_nr = chan_nr;
msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(uint8_t *)mrpci);
DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n",
gsm_lchan_name(lchan), *(uint8_t *)mrpci);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
+/* 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)
{
+ int chan_nr;
+
if (msg->lchan == NULL) {
LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n");
+ msgb_free(msg);
return -EINVAL;
}
- rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, gsm_lchan2chan_nr(msg->lchan),
- link_id, 1);
+ count_unsucc_reqs_for_service(msg);
+
+ chan_nr = gsm_lchan2chan_nr(msg->lchan, true);
+ if (chan_nr < 0) {
+ msgb_free(msg);
+ return chan_nr;
+ }
+
+ rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, chan_nr, link_id, 1);
- msg->dst = msg->lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(msg->lchan);
return abis_rsl_sendmsg(msg);
}
@@ -824,10 +1153,12 @@ int rsl_data_request(struct msgb *msg, uint8_t link_id)
int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id)
{
struct msgb *msg;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
- msg = rsl_rll_simple(RSL_MT_EST_REQ, gsm_lchan2chan_nr(lchan),
- link_id, 0);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg = rsl_rll_simple(RSL_MT_EST_REQ, chan_nr, link_id, 0);
+ msg->dst = rsl_chan_link(lchan);
DEBUGP(DRLL, "%s RSL RLL ESTABLISH REQ (link_id=0x%02x)\n",
gsm_lchan_name(lchan), link_id);
@@ -845,13 +1176,15 @@ int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id,
{
struct msgb *msg;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
- msg = rsl_rll_simple(RSL_MT_REL_REQ, gsm_lchan2chan_nr(lchan),
- link_id, 0);
+ msg = rsl_rll_simple(RSL_MT_REL_REQ, chan_nr, link_id, 0);
/* 0 is normal release, 1 is local end */
msgb_tv_put(msg, RSL_IE_RELEASE_MODE, release_mode);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
DEBUGP(DRLL, "%s RSL RLL RELEASE REQ (link_id=0x%02x, reason=%u)\n",
gsm_lchan_name(lchan), link_id, release_mode);
@@ -864,7 +1197,7 @@ int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id,
static bool msg_for_osmocom_dyn_ts(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
- if (msg->lchan->ts->pchan_on_init != GSM_PCHAN_TCH_F_TCH_H_PDCH)
+ if (msg->lchan->ts->pchan_on_init != GSM_PCHAN_OSMO_DYN)
return false;
/* dyn TS messages always come in on the first lchan of a timeslot */
if (msg->lchan->nr != 0)
@@ -880,7 +1213,7 @@ static int rsl_rx_chan_act_nack(struct msgb *msg)
struct gsm_lchan *lchan = msg->lchan;
const uint8_t *cause_p;
- rate_ctr_inc(&msg->lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_ACT_NACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msg->lchan->ts->trx->bts->bts_ctrs, BTS_CTR_CHAN_ACT_NACK));
if (dh->ie_chan != RSL_IE_CHAN_NR) {
LOG_LCHAN(msg->lchan, LOGL_ERROR, "Invalid IE: expected CHAN_NR IE (0x%x), got 0x%x\n",
@@ -904,6 +1237,7 @@ static int rsl_rx_conn_fail(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
+ struct rate_ctr_group *bts_ctrs = lchan->ts->trx->bts->bts_ctrs;
struct tlv_parsed tp;
const uint8_t *cause_p;
@@ -912,78 +1246,108 @@ static int rsl_rx_conn_fail(struct msgb *msg)
LOG_LCHAN(lchan, LOGL_ERROR, "CONNECTION FAIL%s\n", rsl_cause_name(&tp));
- rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_RF_FAIL]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_CHAN_RF_FAIL));
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_CHAN_RF_FAIL_SDCCH));
+ break;
+ case GSM_LCHAN_TCH_H:
+ case GSM_LCHAN_TCH_F:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_CHAN_RF_FAIL_TCH));
+ break;
+ default:
+ break;
+ }
/* If the lchan is associated with a conn, we shall notify the MSC of the RSL Conn Failure, and
* the connection will presumably be torn down and lead to an lchan release. During initial
* Channel Request from the MS, an lchan has no conn yet, so in that case release now. */
if (!lchan->conn)
- lchan_release(lchan, false, true, *cause_p);
+ lchan_release(lchan, false, true, *cause_p, NULL);
else
osmo_fsm_inst_dispatch(lchan->conn->fi, GSCON_EV_RSL_CONN_FAIL, (void*)cause_p);
return 0;
}
-static void print_meas_rep_uni(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)
{
- DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ",
- prefix, rxlev2dbm(mru->full.rx_lev),
- prefix, rxlev2dbm(mru->sub.rx_lev));
- DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ",
- prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual);
+ OSMO_STRBUF_PRINTF(*sb, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ",
+ prefix, rxlev2dbm(mru->full.rx_lev),
+ prefix, rxlev2dbm(mru->sub.rx_lev));
+ OSMO_STRBUF_PRINTF(*sb, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ",
+ prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual);
}
-static void print_meas_rep(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)
{
- int i;
- 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) {
- log_set_context(LOG_CTX_BSC_SUBSCR, bsub);
- name = bsc_subscr_name(bsub);
- } else
- name = lchan->name;
- }
-
- DEBUGP(DMEAS, "[%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)
- DEBUGPC(DMEAS, "DTXd ");
+ OSMO_STRBUF_PRINTF(sb, "DTXd ");
- print_meas_rep_uni(&mr->ul, "ul");
- DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power);
+ print_meas_rep_uni(&sb, &mr->ul, "ul");
+ OSMO_STRBUF_PRINTF(sb, "BS_POWER=%ddB ", mr->bs_power_db);
if (mr->flags & MEAS_REP_F_MS_TO)
- DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset);
+ OSMO_STRBUF_PRINTF(sb, "MS_TO=%d ", mr->ms_timing_offset);
if (mr->flags & MEAS_REP_F_MS_L1) {
- DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr);
- DEBUGPC(DMEAS, "L1_FPC=%u ",
- mr->flags & MEAS_REP_F_FPC ? 1 : 0);
- DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta);
+ OSMO_STRBUF_PRINTF(sb, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr);
+ OSMO_STRBUF_PRINTF(sb, "L1_FPC=%u ", mr->flags & MEAS_REP_F_FPC ? 1 : 0);
+ OSMO_STRBUF_PRINTF(sb, "L1_TA=%u ", mr->ms_l1.ta);
}
if (mr->flags & MEAS_REP_F_UL_DTX)
- DEBUGPC(DMEAS, "DTXu ");
+ OSMO_STRBUF_PRINTF(sb, "DTXu ");
if (mr->flags & MEAS_REP_F_BA1)
- DEBUGPC(DMEAS, "BA1 ");
+ OSMO_STRBUF_PRINTF(sb, "BA1 ");
if (!(mr->flags & MEAS_REP_F_DL_VALID))
- DEBUGPC(DMEAS, "NOT VALID ");
+ OSMO_STRBUF_PRINTF(sb, "NOT VALID ");
else
- print_meas_rep_uni(&mr->dl, "dl");
+ print_meas_rep_uni(&sb, &mr->dl, "dl");
- DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell);
- if (mr->num_cell == 7)
- return;
- for (i = 0; i < mr->num_cell; i++) {
- 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));
+ OSMO_STRBUF_PRINTF(sb, "NUM_NEIGH=%u", mr->num_cell);
+
+ return sb.chars_needed;
+}
+
+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, 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) {
+ log_set_context(LOG_CTX_BSC_SUBSCR, bsub);
+ name = bsc_subscr_name(bsub);
+ } else {
+ name = lchan->name;
+ }
+ }
+
+ 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++) {
+ 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));
+ }
}
if (bsub)
@@ -1011,6 +1375,7 @@ static int rsl_rx_meas_res(struct msgb *msg)
uint8_t len;
const uint8_t *val;
int rc;
+ uint8_t bs_power_enc;
if (!lchan_may_receive_data(msg->lchan)) {
LOG_LCHAN(msg->lchan, LOGL_DEBUG, "MEAS RES for inactive channel\n");
@@ -1044,7 +1409,8 @@ static int rsl_rx_meas_res(struct msgb *msg)
mr->ul.sub.rx_qual = val[2] & 0x7;
}
- mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
+ bs_power_enc = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
+ mr->bs_power_db = (bs_power_enc & 0x0f) * 2;
/* Optional Parts */
if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) {
@@ -1062,12 +1428,13 @@ 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 next assignment/handover */
- mr->lchan->rqd_ta = mr->ms_l1.ta;
+ /* store TA for handover decision, and for intra-cell re-assignment */
+ mr->lchan->last_ta = mr->ms_l1.ta;
}
if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
msg->l3h = (uint8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO);
@@ -1083,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;
@@ -1190,7 +1559,7 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
break;
case RSL_MT_MODE_MODIFY_NACK:
LOG_LCHAN(msg->lchan, LOGL_DEBUG, "CHANNEL MODE MODIFY NACK\n");
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_MODE_MODIFY_NACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(sign_link->trx->bts->bts_ctrs, BTS_CTR_MODE_MODIFY_NACK));
osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_RSL_CHAN_MODE_MODIFY_NACK, NULL);
break;
case RSL_MT_IPAC_PDCH_ACT_ACK:
@@ -1215,12 +1584,12 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
case RSL_MT_MR_CODEC_MOD_PER:
LOG_LCHAN(msg->lchan, LOGL_NOTICE, "Unimplemented Abis RSL DChan msg 0x%02x\n",
rslh->c.msg_type);
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(sign_link->trx->bts->bts_ctrs, BTS_CTR_RSL_UNKNOWN));
break;
default:
LOG_LCHAN(msg->lchan, LOGL_NOTICE, "Unknown Abis RSL DChan msg 0x%02x\n",
rslh->c.msg_type);
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(sign_link->trx->bts->bts_ctrs, BTS_CTR_RSL_UNKNOWN));
return -EINVAL;
}
@@ -1241,6 +1610,81 @@ static int rsl_rx_error_rep(struct msgb *msg)
return 0;
}
+static int rsl_rx_resource_indication(struct msgb *msg)
+{
+ struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+ struct tlv_parsed tp;
+ struct e1inp_sign_link *sign_link = msg->dst;
+ struct tlv_p_entry *res_info_ie;
+ struct gsm_bts_trx *trx = sign_link->trx;
+ struct gsm_lchan *lchan;
+ int ts_nr;
+ int rc, i;
+
+ LOGP(DRSL, LOGL_DEBUG, "%s Rx Resource Indication\n", gsm_trx_name(trx));
+
+ /* First clear out all ratings, because only the last resource indication counts. If we can't parse the message,
+ * then there are no ratings. */
+ for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
+ lchan->interf_dbm = INTERF_DBM_UNKNOWN;
+ lchan->interf_band = INTERF_BAND_UNKNOWN;
+ }
+ }
+
+ 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");
+ return -EINVAL;
+ }
+
+ /* The IE value is defined in 3GPP TS 48.058 9.3.21 Resource Information:
+ * one octet channel nr, one octet interference level, channel nr, interference level, ...
+ * Where channel nr is cbits + tn (as usual),
+ * and interference level is a 3bit value in the most significant bits of the octet.
+ * Evaluate each pair and update interference ratings for all lchans in this trx. */
+
+ /* There must be an even amount of octets in the value */
+ if (res_info_ie->len & 1) {
+ LOGP(DRSL, LOGL_ERROR, "Rx Resource Indication: Resource Info IE has odd length\n");
+ return -EINVAL;
+ }
+
+ /* Now iterate the reported levels and update corresponding lchans.
+ * Note that an empty res_info_ie can also make sense, if no lchans are idle and no interference ratings are
+ * present. The practical effect of the message then is to invalidate previous interference ratings. */
+ for (i = 0; i < res_info_ie->len; i += 2) {
+ struct gsm_bts *bts = trx->bts;
+ uint8_t chan_nr = res_info_ie->val[i];
+ uint8_t interf_band = res_info_ie->val[i + 1] >> 5;
+
+ lchan = lchan_lookup(trx, chan_nr, "Abis RSL Rx Resource Indication: ");
+ if (!lchan)
+ continue;
+
+ /* Store the actual received index */
+ lchan->interf_band = interf_band;
+ /* Clamp the index to 5 before accessing array of interference band bounds */
+ interf_band = OSMO_MIN(interf_band, ARRAY_SIZE(bts->interf_meas_params_used.bounds_dbm)-1);
+ /* FIXME: when testing with ip.access nanoBTS, we observe a value range of 1..6. According to spec, it
+ * seems like values 0..5 are intended: 3GPP TS 48.058 9.3.21 Resource Information says:
+ * "The Interf Band field (bits 6-8) indicates in binary the interference level expressed as one of five
+ * possible interference level bands as defined by O&M."
+ * and 3GPP TS 52.021 9.4.25 "Interference level Boundaries" (OML) defines values 0, X1, X2, X3, X4, X5.
+ * If nanoBTS sends 6, the above code clamps it to 5, so that we lose one band in accuracy. */
+ lchan->interf_dbm = -((int16_t)bts->interf_meas_params_used.bounds_dbm[interf_band]);
+ }
+
+ return 0;
+}
+
static int abis_rsl_rx_trx(struct msgb *msg)
{
struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
@@ -1253,7 +1697,7 @@ static int abis_rsl_rx_trx(struct msgb *msg)
break;
case RSL_MT_RF_RES_IND:
/* interference on idle channels of TRX */
- //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(sign_link->trx));
+ rc = rsl_rx_resource_indication(msg);
break;
case RSL_MT_OVERLOAD:
/* indicate CCCH / ACCH / processor overload */
@@ -1269,7 +1713,7 @@ static int abis_rsl_rx_trx(struct msgb *msg)
default:
LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message "
"type 0x%02x\n", gsm_trx_name(sign_link->trx), rslh->msg_type);
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(sign_link->trx->bts->bts_ctrs, BTS_CTR_RSL_UNKNOWN));
return -EINVAL;
}
return rc;
@@ -1446,13 +1890,54 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
return -EINVAL;
}
rqd->ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2];
+ if (rqd->ta > bts->rach_max_delay) {
+ LOG_BTS(bts, DRSL, LOGL_INFO, "Ignoring CHAN RQD: Access Delay(%d) greater than %u\n",
+ rqd->ta, bts->rach_max_delay);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_MAX_DELAY_EXCEEDED));
+ talloc_free(rqd);
+ return -EINVAL;
+ }
/* Determine channel request cause code */
rqd->reason = get_reason_by_chreq(rqd->ref.ra, bts->network->neci);
LOG_BTS(bts, DRSL, LOGL_INFO, "CHAN RQD: reason: %s (ra=0x%02x, neci=0x%02x, chreq_reason=0x%02x)\n",
get_value_string(gsm_chreq_descs, rqd->reason), rqd->ref.ra, bts->network->neci, rqd->reason);
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_TOTAL));
+ switch (rqd->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;
+ }
+
+ /* Block emergency calls if we explicitly disable them via sysinfo. */
+ if (rqd->reason == GSM_CHREQ_REASON_EMERG) {
+ if (bts->si_common.rach_control.t2 & 0x4) {
+ LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: MS attempts EMERGENCY CALL although EMERGENCY CALLS "
+ "are not allowed in sysinfo (spec violation by MS!)\n");
+ rsl_tx_imm_ass_rej(bts, &rqd->ref);
+ talloc_free(rqd);
+ return 0;
+ }
+ }
/* Enqueue request */
llist_add_tail(&rqd->entry, &bts->chan_rqd_queue);
@@ -1479,21 +1964,14 @@ static struct gsm_lchan *get_any_lchan(struct gsm_bts *bts)
trx = gsm_bts_trx_num(bts, trx_nr);
for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
ts = &trx->ts[ts_nr];
- ts_for_each_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {
if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) {
- if (bts->chan_alloc_reverse) {
- if (lchan->fi->state == LCHAN_ST_ESTABLISHED)
+ if (lchan->fi->state == LCHAN_ST_ESTABLISHED) {
+ if (!lchan_est || bts->chan_alloc_reverse)
lchan_est = lchan;
- else
- lchan_any = lchan;
} else {
- if (lchan->fi->state == LCHAN_ST_ESTABLISHED) {
- if (!lchan_est)
- lchan_est = lchan;
- } else {
- if (!lchan_any)
- lchan_any = lchan;
- }
+ if (!lchan_any || bts->chan_alloc_reverse)
+ lchan_any = lchan;
}
}
}
@@ -1518,12 +1996,12 @@ static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)
/* First check the situation on the BTS, if we have TCH/H or TCH/F resources available for another (EMERGENCY)
* call. If yes, then no (further) action has to be carried out. */
- if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_F)) {
+ if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_F, true)) {
LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,
"CHAN RQD/EMERGENCY-PRIORITY: at least one TCH/F is (now) available!\n");
return false;
}
- if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_H)) {
+ if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_H, true)) {
LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,
"CHAN RQD/EMERGENCY-PRIORITY: at least one TCH/H is (now) available!\n");
return false;
@@ -1533,10 +2011,11 @@ static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)
* This will take a short amount of time. We need to come back and check regulary to see if we managed to
* free up another lchan. */
if (!rqd->release_lchan) {
+ struct gsm_lchan *release_lchan;
/* Pick any busy TCH/F or TCH/H lchan and inititate a channel
* release to make room for the incoming emergency call */
- rqd->release_lchan = get_any_lchan(rqd->bts);
- if (!rqd->release_lchan) {
+ rqd->release_lchan = release_lchan = get_any_lchan(rqd->bts);
+ if (!release_lchan) {
/* It can not happen that we first find out that there
* is no TCH/H or TCH/F available and at the same time
* we ware unable to find any busy TCH/H or TCH/F. In
@@ -1549,9 +2028,16 @@ static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)
LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,
"CHAN RQD/EMERGENCY-PRIORITY: inducing termination of lchan %s (state:%s) in favor of incoming EMERGENCY CALL!\n",
- gsm_lchan_name(rqd->release_lchan), osmo_fsm_inst_state_name(rqd->release_lchan->fi));
+ gsm_lchan_name(release_lchan), osmo_fsm_inst_state_name(release_lchan->fi));
+
+ lchan_release(release_lchan, !!(release_lchan->conn), true, 0,
+ gscon_last_eutran_plmn(release_lchan->conn));
- lchan_release(rqd->release_lchan, !!(rqd->release_lchan->conn), true, 0);
+ /* Also release any overlapping VAMOS multiplexes on this lchan */
+ release_lchan = gsm_lchan_primary_to_vamos(release_lchan);
+ if (release_lchan)
+ lchan_release(release_lchan, !!(release_lchan->conn), true, 0,
+ gscon_last_eutran_plmn(release_lchan->conn));
} else {
/* BTS is shutting down, give up... */
if (rqd->release_lchan->ts->fi->state == TS_ST_NOT_INITIALIZED)
@@ -1575,6 +2061,64 @@ static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)
return true;
}
+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;
+
+ lchan = lchan_avail_by_type(bts, GSM_LCHAN_SDCCH, false);
+ if (!lchan)
+ return NULL;
+
+ needs_dyn_switch = lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN &&
+ lchan->ts->pchan_is != GSM_PCHAN_SDCCH8_SACCH8C;
+
+ 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",
+ rqd->ref.ra, gsm_lchant_name(lctype));
+ return NULL;
+ }
+
+ /* There's a TCH available and we'll not switch any dyn ts, so we are
+ * fine (we can switch one of them to SDCCH8 and still have one left) */
+ if (!needs_dyn_switch)
+ goto select_lchan;
+
+ /* We need to switch, but there's at least 2 TCH TS available so we are fine: */
+ if (free_tchf > 1 || free_tchh > 2)
+ goto select_lchan;
+
+ /* At this point (needs_dyn_switch==true), following cases are possible:
+ * [A] H=0, F=1
+ * [B] H=1, F=0
+ * [B] H=1, F=1
+ * [C] H=2, F=1
+ * If condition [C] is met, it means there's 1 dynamic TS (because a dyn
+ * TS is counted both as 1 free TCH/F and 2 free TCH/H at the same time)
+ * and it's the same as the dynamic TS available for SDCCH requiring
+ * switch, so selecting it would basically leave us without free TCH, so
+ * avoid selecting it. Regarding the other conditions, it basically
+ * results in them being different TS than the one we want to switch, so
+ * we are fine selecting the TS for SDCCH */
+ if (free_tchf == 1 && free_tchh == 2) {
+ LOG_BTS(bts, DRSL, LOGL_INFO,
+ "CHAN RQD: 0x%x Requesting %s reason=call but dyn TS switch to "
+ "SDCCH would starve the single available TCH timeslot\n",
+ rqd->ref.ra, gsm_lchant_name(lctype));
+ return NULL;
+ }
+
+select_lchan:
+ lchan_select_set_type(lchan, GSM_LCHAN_SDCCH);
+ return lchan;
+}
+
void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts)
{
struct lchan_activate_info info;
@@ -1610,36 +2154,34 @@ void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts)
* - If there is still no channel available, try a TCH/F.
*
*/
- if (rqd->reason == GSM_CHREQ_REASON_EMERG) {
- if (bts->si_common.rach_control.t2 & 0x4) {
- LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: MS attempts EMERGENCY CALL although EMERGENCY CALLS "
- "are not allowed in sysinfo (spec violation by MS!)\n");
- rsl_tx_imm_ass_rej(bts, &rqd->ref);
- llist_del(&rqd->entry);
- talloc_free(rqd);
- return;
- }
- }
- /* Emergency calls will be put on a free TCH/H or TCH/F directly in the code below, all other channel requests
- * will get an SDCCH first (if possible). */
- if (rqd->reason != GSM_CHREQ_REASON_EMERG)
+ if (rqd->reason == GSM_CHREQ_REASON_CALL) {
+ lchan = _select_sdcch_for_call(bts, rqd, lctype);
+ } else if (rqd->reason != GSM_CHREQ_REASON_EMERG) {
lchan = lchan_select_by_type(bts, GSM_LCHAN_SDCCH);
-
- if (!lchan) {
- LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: no resources for %s 0x%x, retrying with %s\n",
- gsm_lchant_name(GSM_LCHAN_SDCCH), rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_H));
- lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_H);
}
- if (!lchan) {
- LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: no resources for %s 0x%x, retrying with %s\n",
- gsm_lchant_name(GSM_LCHAN_SDCCH), rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_F));
- lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_F);
+ /* else: Emergency calls will be put on a free TCH/H or TCH/F directly
+ * in the code below, all other channel requests will get an SDCCH first
+ * (if possible). */
+
+ if (gsm_chreq_reason_is_voicecall(rqd->reason) || bts->chan_alloc_allow_tch_for_signalling) {
+ if (!lchan) {
+ LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD[%s]: no resources for %s 0x%x, retrying with %s\n",
+ get_value_string(gsm_chreq_descs, rqd->reason), gsm_lchant_name(GSM_LCHAN_SDCCH),
+ rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_H));
+ lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_H);
+ }
+ if (!lchan) {
+ LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD[%s]: no resources for %s 0x%x, retrying with %s\n",
+ get_value_string(gsm_chreq_descs, rqd->reason), gsm_lchant_name(GSM_LCHAN_SDCCH),
+ rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_F));
+ lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_F);
+ }
}
if (!lchan) {
- LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: no resources for %s 0x%x\n",
- gsm_lchant_name(lctype), rqd->ref.ra);
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_NO_CHANNEL]);
+ LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD[%s]: no resources for %s 0x%x\n",
+ get_value_string(gsm_chreq_descs, rqd->reason), gsm_lchant_name(lctype), rqd->ref.ra);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_NO_CHANNEL));
rsl_tx_imm_ass_rej(bts, &rqd->ref);
llist_del(&rqd->entry);
talloc_free(rqd);
@@ -1651,13 +2193,19 @@ void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts)
OSMO_ASSERT(lchan->rqd_ref);
*(lchan->rqd_ref) = rqd->ref;
- lchan->rqd_ta = rqd->ta;
LOG_LCHAN(lchan, LOGL_DEBUG, "MS: Channel Request: reason=%s ra=0x%02x ta=%d\n",
gsm_chreq_name(rqd->reason), rqd->ref.ra, rqd->ta);
info = (struct lchan_activate_info){
- .activ_for = FOR_MS_CHANNEL_REQUEST,
- .chan_mode = GSM48_CMODE_SIGN,
+ .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,
+ .imm_ass_time = bts->imm_ass_time,
};
lchan_activate(lchan, &info);
@@ -1666,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));
@@ -1679,11 +2257,23 @@ 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;
- gsm48_lchan2chan_desc(&ia->chan_desc, lchan);
+
+ /* 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;
+ }
/* use request reference extracted from CHAN_RQD */
memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref));
- ia->timing_advance = lchan->rqd_ta;
+ ia->timing_advance = lchan->last_ta;
if (!lchan->ts->hopping.enabled) {
ia->mob_alloc_len = 0;
} else {
@@ -1697,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(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_SUCCESSFUL]);
+ imm_ass_rate_ctr(lchan);
return rc;
}
@@ -1725,7 +2315,7 @@ static int rsl_rx_ccch_load(struct msgb *msg)
osmo_signal_dispatch(SS_CCCH, S_CCCH_PAGING_LOAD, &sd);
break;
case RSL_IE_RACH_LOAD:
- if (msg->data_len >= 7) {
+ if (msgb_length(msg) >= 7) {
int32_t busy_percent, access_percent;
/* build data for signal */
sd.rach_slot_count = rslh->data[2] << 8 | rslh->data[3];
@@ -1740,8 +2330,8 @@ static int rsl_rx_ccch_load(struct msgb *msg)
busy_percent = 100;
}
- osmo_stat_item_set(sd.bts->bts_statg->items[BTS_STAT_RACH_BUSY], busy_percent);
- osmo_stat_item_set(sd.bts->bts_statg->items[BTS_STAT_RACH_ACCESS], access_percent);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(sd.bts->bts_statg, BTS_STAT_RACH_BUSY), busy_percent);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(sd.bts->bts_statg, BTS_STAT_RACH_ACCESS), access_percent);
/* dispatch signal */
osmo_signal_dispatch(SS_CCCH, S_CCCH_RACH_LOAD, &sd);
}
@@ -1810,6 +2400,7 @@ static int abis_rsl_rx_cchan(struct msgb *msg)
{
struct e1inp_sign_link *sign_link = msg->dst;
struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
+ struct rate_ctr_group *bts_ctrs = sign_link->trx->bts->bts_ctrs;
int rc = 0;
msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr,
@@ -1827,7 +2418,7 @@ static int abis_rsl_rx_cchan(struct msgb *msg)
case RSL_MT_DELETE_IND:
/* CCCH overloaded, IMM_ASSIGN was dropped */
LOGPLCHAN(msg->lchan, DRSL, LOGL_NOTICE, "DELETE INDICATION (Downlink CCCH overload)\n");
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_DELETE_IND]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_RSL_DELETE_IND));
break;
case RSL_MT_CBCH_LOAD_IND:
/* current load on the CBCH */
@@ -1839,7 +2430,7 @@ static int abis_rsl_rx_cchan(struct msgb *msg)
default:
LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type "
"0x%02x\n", rslh->c.msg_type);
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_RSL_UNKNOWN));
return -EINVAL;
}
@@ -1863,7 +2454,7 @@ static int rsl_rx_rll_err_ind(struct msgb *msg)
rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
- rate_ctr_inc(&msg->lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_RLL_ERR]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msg->lchan->ts->trx->bts->bts_ctrs, BTS_CTR_CHAN_RLL_ERR));
osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_RLL_ERR_IND, &rlm_cause);
@@ -1964,7 +2555,7 @@ static int abis_rsl_rx_rll(struct msgb *msg)
default:
LOG_LCHAN(msg->lchan, LOGL_NOTICE, "SAPI=%u Unknown Abis RLL message type 0x%02x\n",
sapi, rllh->c.msg_type);
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(sign_link->trx->bts->bts_ctrs, BTS_CTR_RSL_UNKNOWN));
}
return rc;
}
@@ -1972,7 +2563,7 @@ static int abis_rsl_rx_rll(struct msgb *msg)
/* Return an ip.access BTS speech mode value (uint8_t) or negative on error. */
int ipacc_speech_mode(enum gsm48_chan_mode tch_mode, enum gsm_chan_t type)
{
- switch (tch_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(tch_mode)) {
case GSM48_CMODE_SPEECH_V1:
switch (type) {
case GSM_LCHAN_TCH_F:
@@ -2020,7 +2611,7 @@ void ipacc_speech_mode_set_direction(uint8_t *speech_mode, bool send)
/* Return an ip.access BTS payload type value (uint8_t) or negative on error. */
int ipacc_payload_type(enum gsm48_chan_mode tch_mode, enum gsm_chan_t type)
{
- switch (tch_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(tch_mode)) {
case GSM48_CMODE_SPEECH_V1:
switch (type) {
case GSM_LCHAN_TCH_F:
@@ -2117,13 +2708,19 @@ static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv, const
*/
int rsl_tx_ipacc_crcx(const struct gsm_lchan *lchan)
{
- struct msgb *msg = rsl_msgb_alloc();
+ struct msgb *msg;
struct abis_rsl_dchan_hdr *dh;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
+
+ msg = rsl_msgb_alloc();
+
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_IPAC_CRCX);
dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
+ dh->chan_nr = chan_nr;
/* 0x1- == receive-only, 0x-1 == EFR codec */
msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
@@ -2132,7 +2729,7 @@ int rsl_tx_ipacc_crcx(const struct gsm_lchan *lchan)
LOG_LCHAN(lchan, LOGL_DEBUG, "Sending IPACC CRCX to BTS: speech_mode=0x%02x RTP_PAYLOAD=%d\n",
lchan->abis_ip.speech_mode, lchan->abis_ip.rtp_payload);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
@@ -2144,14 +2741,20 @@ int rsl_tx_ipacc_crcx(const struct gsm_lchan *lchan)
*/
struct msgb *rsl_make_ipacc_mdcx(const struct gsm_lchan *lchan, uint32_t dest_ip, uint16_t dest_port)
{
- struct msgb *msg = rsl_msgb_alloc();
+ struct msgb *msg;
struct abis_rsl_dchan_hdr *dh;
uint32_t *att_ip;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return NULL;
+
+ msg = rsl_msgb_alloc();
+
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_IPAC_MDCX);
dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
+ dh->chan_nr = chan_nr;
msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP);
@@ -2163,7 +2766,7 @@ struct msgb *rsl_make_ipacc_mdcx(const struct gsm_lchan *lchan, uint32_t dest_ip
if (lchan->abis_ip.rtp_payload2)
msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, lchan->abis_ip.rtp_payload2);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return msg;
}
@@ -2176,6 +2779,9 @@ int rsl_tx_ipacc_mdcx(const struct gsm_lchan *lchan)
{
struct msgb *msg = rsl_make_ipacc_mdcx(lchan, lchan->abis_ip.connect_ip, lchan->abis_ip.connect_port);
+ if (!msg)
+ return -EINVAL;
+
LOG_LCHAN(lchan, LOGL_DEBUG, "Sending IPACC MDCX to BTS:"
" %s:%u rtp_payload=%u rtp_payload2=%u conn_id=%u speech_mode=0x%02x\n",
ip_to_a(lchan->abis_ip.connect_ip),
@@ -2223,7 +2829,7 @@ static int abis_rsl_rx_ipacc_crcx_nack(struct msgb *msg)
struct e1inp_sign_link *sign_link = msg->dst;
struct gsm_lchan *lchan = msg->lchan;
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(sign_link->trx->bts->bts_ctrs, BTS_CTR_RSL_IPA_NACK));
if (!lchan->fi_rtp) {
LOG_LCHAN(msg->lchan, LOGL_ERROR, "Rx RSL IPACC: CRCX NACK message for unconfigured lchan\n");
@@ -2261,7 +2867,7 @@ static int abis_rsl_rx_ipacc_mdcx_nack(struct msgb *msg)
struct e1inp_sign_link *sign_link = msg->dst;
struct gsm_lchan *lchan = msg->lchan;
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(sign_link->trx->bts->bts_ctrs, BTS_CTR_RSL_IPA_NACK));
if (!lchan->fi_rtp) {
LOG_LCHAN(msg->lchan, LOGL_ERROR, "Rx RSL IPACC: MDCX NACK message for unconfigured lchan\n");
@@ -2330,7 +2936,7 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
default:
LOG_LCHAN(msg->lchan, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n",
rllh->c.msg_type);
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(sign_link->trx->bts->bts_ctrs, BTS_CTR_RSL_UNKNOWN));
break;
}
@@ -2360,22 +2966,28 @@ static int send_osmocom_style_pdch_chan_act(struct gsm_bts_trx_ts *ts, bool acti
}
}
- msg->dst = ts->trx->rsl_link;
+ msg->dst = ts->trx->rsl_link_primary;
return abis_rsl_sendmsg(msg);
}
/*! Tx simplified channel (de-)activation message for non-standard ip.access dyn TS PDCH type. */
static int send_ipacc_style_pdch_act(struct gsm_bts_trx_ts *ts, bool activate)
{
- struct msgb *msg = rsl_msgb_alloc();
+ struct msgb *msg;
struct abis_rsl_dchan_hdr *dh;
+ int chan_nr = gsm_pchan2chan_nr(GSM_PCHAN_TCH_F, ts->nr, 0, false);
+ if (chan_nr < 0)
+ return chan_nr;
+
+ msg = rsl_msgb_alloc();
+
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, activate ? RSL_MT_IPAC_PDCH_ACT : RSL_MT_IPAC_PDCH_DEACT);
dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
- dh->chan_nr = gsm_pchan2chan_nr(GSM_PCHAN_TCH_F, ts->nr, 0);
+ dh->chan_nr = chan_nr;
- msg->dst = ts->trx->rsl_link;
+ msg->dst = ts->trx->rsl_link_primary;
return abis_rsl_sendmsg(msg);
}
@@ -2386,7 +2998,7 @@ int rsl_tx_dyn_ts_pdch_act_deact(struct gsm_bts_trx_ts *ts, bool activate)
const char *act;
switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
what = "Osmocom dyn TS";
act = activate? "PDCH Chan Activ" : "PDCH Chan RF Release";
@@ -2459,7 +3071,7 @@ int abis_rsl_rcvmsg(struct msgb *msg)
default:
LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator "
"0x%02x\n", rslh->msg_discr);
- rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(sign_link->trx->bts->bts_ctrs, BTS_CTR_RSL_UNKNOWN));
rc = -EINVAL;
}
msgb_free(msg);
@@ -2480,7 +3092,7 @@ int rsl_etws_pn_command(struct gsm_bts *bts, uint8_t chan_nr, const uint8_t *dat
msgb_tlv_put(msg, RSL_IE_SMSCB_MSG, len, data);
- msg->dst = bts->c0->rsl_link;
+ msg->dst = bts->c0->rsl_link_primary;
return abis_rsl_sendmsg(msg);
}
@@ -2506,7 +3118,7 @@ int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number,
if (use_extended_cbch)
msgb_tv_put(cb_cmd, RSL_IE_SMSCB_CHAN_INDICATOR, 0x01);
- cb_cmd->dst = bts->c0->rsl_link;
+ cb_cmd->dst = bts->c0->rsl_link_primary;
return abis_rsl_sendmsg(cb_cmd);
}
@@ -2520,7 +3132,7 @@ int rsl_nokia_si_begin(struct gsm_bts_trx *trx)
ch->msg_discr = ABIS_RSL_MDISC_TRX;
ch->msg_type = 0x40; /* Nokia SI Begin */
- msg->dst = trx->rsl_link;
+ msg->dst = trx->rsl_link_primary;
return abis_rsl_sendmsg(msg);
}
@@ -2536,7 +3148,7 @@ int rsl_nokia_si_end(struct gsm_bts_trx *trx)
msgb_tv_put(msg, 0xFD, 0x00); /* Nokia Pagemode Info, No paging reorganisation required */
- msg->dst = trx->rsl_link;
+ msg->dst = trx->rsl_link_primary;
return abis_rsl_sendmsg(msg);
}
@@ -2553,7 +3165,12 @@ int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduc
msgb_tv_put(msg, RSL_IE_CHAN_NR, channel);
msgb_tv_put(msg, RSL_IE_BS_POWER, reduction); /* reduction in 2dB steps */
- msg->dst = trx->rsl_link;
+ msg->dst = trx->rsl_link_primary;
return abis_rsl_sendmsg(msg);
}
+
+struct e1inp_sign_link *rsl_chan_link(const struct gsm_lchan *lchan)
+{
+ return lchan->ts->trx->rsl_link_primary;
+}
diff --git a/src/osmo-bsc/acc.c b/src/osmo-bsc/acc.c
index 06f96c625..9734a28e3 100644
--- a/src/osmo-bsc/acc.c
+++ b/src/osmo-bsc/acc.c
@@ -439,7 +439,7 @@ static int acc_ramp_nm_sig_cb(unsigned int subsys, unsigned int signal, void *ha
return 0;
/* RSL must already be up. We cannot send RACH system information to the BTS otherwise. */
- if (trx->rsl_link == NULL) {
+ if (trx->rsl_link_primary == NULL) {
LOG_TRX(trx, DRSL, LOGL_DEBUG,
"ACC RAMP: ignoring state change because RSL link is down\n");
return 0;
diff --git a/src/osmo-bsc/arfcn_range_encode.c b/src/osmo-bsc/arfcn_range_encode.c
deleted file mode 100644
index 54d98a967..000000000
--- a/src/osmo-bsc/arfcn_range_encode.c
+++ /dev/null
@@ -1,340 +0,0 @@
-/* gsm 04.08 system information (si) encoding and decoding
- * 3gpp ts 04.08 version 7.21.0 release 1998 / etsi ts 100 940 v7.21.0 */
-
-/*
- * (C) 2012 Holger Hans Peter Freyther
- * (C) 2012 by On-Waves
- * 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/arfcn_range_encode.h>
-#include <osmocom/bsc/debug.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-
-#include <osmocom/core/utils.h>
-
-#include <errno.h>
-
-static inline int greatest_power_of_2_lesser_or_equal_to(int index)
-{
- int power_of_2 = 1;
-
- do {
- power_of_2 *= 2;
- } while (power_of_2 <= index);
-
- /* now go back one step */
- return power_of_2 / 2;
-}
-
-static inline int mod(int data, int range)
-{
- int res = data % range;
- while (res < 0)
- res += range;
- return res;
-}
-
-/**
- * Determine at which index to split the ARFCNs to create an
- * equally size partition for the given range. Return -1 if
- * no such partition exists.
- */
-int range_enc_find_index(enum gsm48_range range, const int *freqs, const int size)
-{
- int i, j, n;
-
- const int RANGE_DELTA = (range - 1) / 2;
-
- for (i = 0; i < size; ++i) {
- n = 0;
- for (j = 0; j < size; ++j) {
- if (mod(freqs[j] - freqs[i], range) <= RANGE_DELTA)
- n += 1;
- }
-
- if (n - 1 == (size - 1) / 2)
- return i;
- }
-
- return -1;
-}
-
-/* Worker for range_enc_arfcns(), do not call directly. */
-int _range_enc_arfcns(enum gsm48_range range,
- const int *arfcns, int size, int *out,
- const int index)
-{
- int split_at;
- int i;
-
- /*
- * The below is a GNU extension and we can remove it when
- * we move to a quicksort like in-situ swap with the pivot.
- */
- int arfcns_left[size / 2];
- int arfcns_right[size / 2];
- int l_size;
- int r_size;
- int l_origin;
- int r_origin;
-
- /* Now do the processing */
- split_at = range_enc_find_index(range, arfcns, size);
- if (split_at < 0)
- return -EINVAL;
-
- /* we now know where to split */
- out[index] = 1 + arfcns[split_at];
-
- /* calculate the work that needs to be done for the leafs */
- l_origin = mod(arfcns[split_at] + ((range - 1) / 2) + 1, range);
- r_origin = mod(arfcns[split_at] + 1, range);
- for (i = 0, l_size = 0, r_size = 0; i < size; ++i) {
- if (mod(arfcns[i] - l_origin, range) < range / 2)
- arfcns_left[l_size++] = mod(arfcns[i] - l_origin, range);
- if (mod(arfcns[i] - r_origin, range) < range / 2)
- arfcns_right[r_size++] = mod(arfcns[i] - r_origin, range);
- }
-
- /*
- * Now recurse and we need to make this iterative... but as the
- * tree is balanced the stack will not be too deep.
- */
- if (l_size)
- range_enc_arfcns(range / 2, arfcns_left, l_size,
- out, index + greatest_power_of_2_lesser_or_equal_to(index + 1));
- if (r_size)
- range_enc_arfcns((range - 1) / 2, arfcns_right, r_size,
- out, index + (2 * greatest_power_of_2_lesser_or_equal_to(index + 1)));
- return 0;
-}
-
-/**
- * Range encode the ARFCN list.
- * \param range The range to use.
- * \param arfcns The list of ARFCNs
- * \param size The size of the list of ARFCNs
- * \param out Place to store the W(i) output.
- */
-int range_enc_arfcns(enum gsm48_range range,
- const int *arfcns, int size, int *out,
- const int index)
-{
- if (size <= 0)
- return 0;
-
- if (size == 1) {
- out[index] = 1 + arfcns[0];
- return 0;
- }
-
- return _range_enc_arfcns(range, arfcns, size, out, index);
-}
-
-/*
- * The easiest is to use f0 == arfcns[0]. This means that under certain
- * circumstances we can encode less ARFCNs than possible with an optimal f0.
- *
- * TODO: Solve the optimisation problem and pick f0 so that the max distance
- * is the smallest. Taking into account the modulo operation. I think picking
- * size/2 will be the optimal arfcn.
- */
-/**
- * This implements the range determination as described in GSM 04.08 J4. The
- * result will be a base frequency f0 and the range to use. Note that for range
- * 1024 encoding f0 always refers to ARFCN 0 even if it is not an element of
- * the arfcns list.
- *
- * \param[in] arfcns The input frequencies, they must be sorted, lowest number first
- * \param[in] size The length of the array
- * \param[out] f0 The selected F0 base frequency. It might not be inside the list
- */
-int range_enc_determine_range(const int *arfcns, const int size, int *f0)
-{
- int max = 0;
-
- /* don't dereference arfcns[] array if size is 0 */
- if (size == 0)
- return ARFCN_RANGE_128;
-
- /*
- * Go for the easiest. And pick arfcns[0] == f0.
- */
- max = arfcns[size - 1] - arfcns[0];
- *f0 = arfcns[0];
-
- if (max < 128 && size <= 29)
- return ARFCN_RANGE_128;
- if (max < 256 && size <= 22)
- return ARFCN_RANGE_256;
- if (max < 512 && size <= 18)
- return ARFCN_RANGE_512;
- if (max < 1024 && size <= 17) {
- *f0 = 0;
- return ARFCN_RANGE_1024;
- }
-
- return ARFCN_RANGE_INVALID;
-}
-
-static void write_orig_arfcn(uint8_t *chan_list, int f0)
-{
- chan_list[0] |= (f0 >> 9) & 1;
- chan_list[1] = (f0 >> 1);
- chan_list[2] = (f0 & 1) << 7;
-}
-
-static void write_all_wn(uint8_t *chan_list, int bit_offs,
- int *w, int w_size, int w1_len)
-{
- int octet_offs = 0; /* offset into chan_list */
- int wk_len = w1_len; /* encoding size in bits of w[k] */
- int k; /* 1 based */
- int level = 0; /* tree level, top level = 0 */
- int lvl_left = 1; /* nodes per tree level */
-
- /* W(2^i) to W(2^(i+1)-1) are on w1_len-i bits when present */
-
- for (k = 1; k <= w_size; k++) {
- int wk_left = wk_len;
- DEBUGP(DRR,
- "k=%d, wk_len=%d, offs=%d:%d, level=%d, "
- "lvl_left=%d\n",
- k, wk_len, octet_offs, bit_offs, level, lvl_left);
-
- while (wk_left > 0) {
- int cur_bits = 8 - bit_offs;
- int cur_mask;
- int wk_slice;
-
- if (cur_bits > wk_left)
- cur_bits = wk_left;
-
- cur_mask = ((1 << cur_bits) - 1);
-
- DEBUGP(DRR,
- " wk_left=%d, cur_bits=%d, offs=%d:%d\n",
- wk_left, cur_bits, octet_offs, bit_offs);
-
- /* advance */
- wk_left -= cur_bits;
- bit_offs += cur_bits;
-
- /* right aligned wk data for current out octet */
- wk_slice = (w[k-1] >> wk_left) & cur_mask;
-
- /* cur_bits now contains the number of bits
- * that are to be copied from wk to the chan_list.
- * wk_left is set to the number of bits that must
- * not yet be copied.
- * bit_offs points after the bit area that is going to
- * be overwritten:
- *
- * wk_left
- * |
- * v
- * wk: WWWWWWWWWWW
- * |||||<-- wk_slice, cur_bits=5
- * --WWWWW-
- * ^
- * |
- * bit_offs
- */
-
- DEBUGP(DRR,
- " wk=%02x, slice=%02x/%02x, cl=%02x\n",
- w[k-1], wk_slice, cur_mask, wk_slice << (8 - bit_offs));
-
- chan_list[octet_offs] &= ~(cur_mask << (8 - bit_offs));
- chan_list[octet_offs] |= wk_slice << (8 - bit_offs);
-
- /* adjust output */
- if (bit_offs == 8) {
- bit_offs = 0;
- octet_offs += 1;
- }
- }
-
- /* adjust bit sizes */
- lvl_left -= 1;
- if (!lvl_left) {
- /* completed tree level, advance to next */
- level += 1;
- lvl_left = 1 << level;
- wk_len -= 1;
- }
- }
-}
-
-int range_enc_range128(uint8_t *chan_list, int f0, int *w)
-{
- chan_list[0] = 0x8C;
- write_orig_arfcn(chan_list, f0);
-
- write_all_wn(&chan_list[2], 1, w, 28, 7);
- return 0;
-}
-
-int range_enc_range256(uint8_t *chan_list, int f0, int *w)
-{
- chan_list[0] = 0x8A;
- write_orig_arfcn(chan_list, f0);
-
- write_all_wn(&chan_list[2], 1, w, 21, 8);
- return 0;
-}
-
-int range_enc_range512(uint8_t *chan_list, int f0, int *w)
-{
- chan_list[0] = 0x88;
- write_orig_arfcn(chan_list, f0);
-
- write_all_wn(&chan_list[2], 1, w, 17, 9);
- return 0;
-}
-
-int range_enc_range1024(uint8_t *chan_list, int f0, int f0_included, int *w)
-{
- chan_list[0] = 0x80 | (f0_included << 2);
-
- write_all_wn(&chan_list[0], 6, w, 16, 10);
- return 0;
-}
-
-int range_enc_filter_arfcns(int *arfcns,
- const int size, const int f0, int *f0_included)
-{
- int i, j = 0;
- *f0_included = 0;
-
- for (i = 0; i < size; ++i) {
- /*
- * Appendix J.4 says the following:
- * All frequencies except F(0), minus F(0) + 1.
- * I assume we need to exclude it here.
- */
- if (arfcns[i] == f0) {
- *f0_included = 1;
- continue;
- }
-
- arfcns[j++] = mod(arfcns[i] - (f0 + 1), 1024);
- }
-
- return j;
-}
diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c
index fde028ed9..656bd3e94 100644
--- a/src/osmo-bsc/assignment_fsm.c
+++ b/src/osmo-bsc/assignment_fsm.c
@@ -32,9 +32,11 @@
#include <osmocom/bsc/osmo_bsc_lcls.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/gsm_08_08.h>
+#include <osmocom/bsc/gsm_04_08_rr.h>
#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>
@@ -72,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 { \
@@ -80,10 +82,23 @@ static const struct osmo_tdef_state_timeout assignment_fsm_timeouts[32] = {
LOG_ASSIGNMENT(conn, LOGL_DEBUG, "incrementing rate counter: %s %s\n", \
bsc_ctr_description[BSC_##counter].name, \
bsc_ctr_description[BSC_##counter].description); \
- rate_ctr_inc(&conn->network->bsc_ctrs->ctr[BSC_##counter]); \
- if (bts) \
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_##counter]); \
- } while(0)
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->network->bsc_ctrs, BSC_##counter)); \
+ if (bts) { \
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_##counter)); \
+ switch (gsm48_chan_mode_to_non_vamos(conn->assignment.req.ch_mode_rate_list[0].chan_mode)) { \
+ case GSM48_CMODE_SIGN: \
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_##counter##_SIGN)); \
+ break; \
+ case GSM48_CMODE_SPEECH_V1: \
+ case GSM48_CMODE_SPEECH_EFR: \
+ case GSM48_CMODE_SPEECH_AMR: \
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_##counter##_SPEECH)); \
+ break; \
+ default: \
+ break; \
+ } \
+ } \
+ } while (0)
#define assignment_count_result(counter) do { \
if (!conn->assignment.result_rate_ctr_done) { \
@@ -94,14 +109,14 @@ 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)
{
if (conn->assignment.new_lchan) {
struct gsm_lchan *lchan = conn->assignment.new_lchan;
conn->assignment.new_lchan = NULL;
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
}
if (conn->assignment.created_ci_for_msc) {
@@ -118,13 +133,17 @@ void assignment_reset(struct gsm_subscriber_connection *conn)
static void on_assignment_failure(struct gsm_subscriber_connection *conn)
{
- struct msgb *resp = gsm0808_create_assignment_failure(conn->assignment.failure_cause, NULL);
-
- if (!resp) {
- LOG_ASSIGNMENT(conn, LOGL_ERROR, "Unable to compose BSSMAP Assignment Failure message\n");
- } else {
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_FAILURE]);
- gscon_sigtran_send(conn, resp);
+ /* Send Assignment Failure to MSC only when the assignment was requested via BSSAP. Do not send anything to the
+ * MSC if re-assignment was requested for congestion resolution, for VAMOS multiplexing, or by VTY. */
+ if (conn->assignment.req.assign_for == ASSIGN_FOR_BSSMAP_REQ) {
+ struct msgb *resp = gsm0808_create_assignment_failure(conn->assignment.failure_cause, NULL);
+
+ if (!resp) {
+ LOG_ASSIGNMENT(conn, LOGL_ERROR, "Unable to compose BSSMAP Assignment Failure message\n");
+ } else {
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_FAILURE));
+ gscon_sigtran_send(conn, resp);
+ }
}
/* If assignment failed as early as in assignment_fsm_start(), there may not be an fi yet. */
@@ -155,18 +174,18 @@ static void send_assignment_complete(struct gsm_subscriber_connection *conn)
struct gsm_lchan *lchan = conn->lchan;
struct osmo_fsm_inst *fi = conn->fi;
- chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode);
+ chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode);
if (!chosen_channel) {
assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE,
"Unable to compose Chosen Channel for mode=%s type=%s",
- get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
+ get_value_string(gsm48_chan_mode_names, lchan->current_ch_mode_rate.chan_mode),
gsm_lchant_name(lchan->type));
return;
}
/* Generate voice related fields */
if (conn->assignment.requires_voice_stream) {
- perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode);
+ perm_spch = gsm0808_permitted_speech(lchan->type, lchan->current_ch_mode_rate.chan_mode);
if (gscon_is_aoip(conn)) {
if (!osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(conn->user_plane.mgw_endpoint_ci_msc,
@@ -192,7 +211,7 @@ static void send_assignment_complete(struct gsm_subscriber_connection *conn)
if (gscon_is_aoip(conn)) {
/* Extrapolate speech codec from speech mode */
gsm0808_speech_codec_from_chan_type(&sc, perm_spch);
- sc.cfg = conn->lchan->activate.info.s15_s0;
+ sc.cfg = conn->lchan->activate.ch_mode_rate.s15_s0;
sc_ptr = &sc;
}
}
@@ -212,7 +231,7 @@ static void send_assignment_complete(struct gsm_subscriber_connection *conn)
conn->assignment.req.use_osmux)
_gsm0808_ass_compl_extend_osmux(resp, osmux_cid);
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_COMPLETE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_COMPLETE));
rc = gscon_sigtran_send(conn, resp);
if (rc) {
assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE,
@@ -224,32 +243,49 @@ static void send_assignment_complete(struct gsm_subscriber_connection *conn)
static void assignment_success(struct gsm_subscriber_connection *conn)
{
- /* Take on the new lchan */
- gscon_change_primary_lchan(conn, conn->assignment.new_lchan);
- conn->assignment.new_lchan = NULL;
+ struct gsm_bts *bts;
+ bool lchan_changed = (conn->assignment.new_lchan != NULL);
+
+ /* Take on the new lchan. If there only was a Channel Mode Modify, then there is no new lchan to take on. */
+ if (lchan_changed) {
+ gscon_change_primary_lchan(conn, conn->assignment.new_lchan);
+
+ OSMO_ASSERT((bts = conn_get_bts(conn)) != NULL);
+ if (is_siemens_bts(bts) && ts_is_tch(conn->lchan->ts)) {
+ /* HACK: store the actual Classmark 2 LV from the subscriber and use it here! */
+ uint8_t cm2_lv[] = { 0x02, 0x00, 0x00 };
+ send_siemens_mrpci(conn->lchan, cm2_lv);
+ }
- /* apply LCLS configuration (if any) */
- lcls_apply_config(conn);
+ /* apply LCLS configuration (if any) */
+ lcls_apply_config(conn);
+ }
+ conn->assignment.new_lchan = NULL;
- send_assignment_complete(conn);
- /* If something went wrong during send_assignment_complete(), the fi will be gone from
- * error handling in there. Almost a success, but then again the whole thing failed. */
- if (!conn->assignment.fi) {
- /* The lchan was ready, and we failed to tell the MSC about it. By releasing this lchan,
- * the conn will notice that its primary lchan is gone and should clean itself up. */
- lchan_release(conn->lchan, true, true, RSL_ERR_EQUIPMENT_FAIL);
- return;
+ if (conn->assignment.req.assign_for == ASSIGN_FOR_BSSMAP_REQ) {
+ send_assignment_complete(conn);
+ /* If something went wrong during send_assignment_complete(), the fi will be gone from
+ * error handling in there. Almost a success, but then again the whole thing failed. */
+ if (!conn->assignment.fi) {
+ /* The lchan was ready, and we failed to tell the MSC about it. By releasing this lchan,
+ * the conn will notice that its primary lchan is gone and should clean itself up. */
+ lchan_release(conn->lchan, true, true, RSL_ERR_EQUIPMENT_FAIL,
+ gscon_last_eutran_plmn(conn));
+ return;
+ }
}
- /* Rembered this only for error handling: should assignment fail, assignment_reset() will release
- * the MGW endpoint right away. If successful, the conn continues to use the endpoint. */
- conn->assignment.created_ci_for_msc = NULL;
+ if (lchan_changed) {
+ /* Rembered this only for error handling: should assignment fail, assignment_reset() will release
+ * the MGW endpoint right away. If successful, the conn continues to use the endpoint. */
+ conn->assignment.created_ci_for_msc = NULL;
- /* New RTP information is now accepted */
- conn->user_plane.msc_assigned_cic = conn->assignment.req.msc_assigned_cic;
- osmo_strlcpy(conn->user_plane.msc_assigned_rtp_addr, conn->assignment.req.msc_rtp_addr,
- sizeof(conn->user_plane.msc_assigned_rtp_addr));
- conn->user_plane.msc_assigned_rtp_port = conn->assignment.req.msc_rtp_port;
+ /* New RTP information is now accepted */
+ conn->user_plane.msc_assigned_cic = conn->assignment.req.msc_assigned_cic;
+ osmo_strlcpy(conn->user_plane.msc_assigned_rtp_addr, conn->assignment.req.msc_rtp_addr,
+ sizeof(conn->user_plane.msc_assigned_rtp_addr));
+ conn->user_plane.msc_assigned_rtp_port = conn->assignment.req.msc_rtp_port;
+ }
LOG_ASSIGNMENT(conn, LOGL_DEBUG, "Assignment successful\n");
osmo_fsm_inst_term(conn->assignment.fi, OSMO_FSM_TERM_REGULAR, 0);
@@ -259,20 +295,23 @@ static void assignment_success(struct gsm_subscriber_connection *conn)
static void assignment_fsm_update_id(struct gsm_subscriber_connection *conn)
{
- struct gsm_lchan *new_lchan = conn->assignment.new_lchan;
+ /* Assignment can do a new channel activation, in which case new_lchan points at the new lchan.
+ * Or assignment can Channel Mode Modify the already used lchan, in which case new_lchan == NULL. */
+ struct gsm_lchan *new_lchan = (conn->assignment.new_lchan ? : conn->lchan);
if (!new_lchan) {
osmo_fsm_inst_update_id(conn->assignment.fi, conn->fi->id);
return;
}
- osmo_fsm_inst_update_id_f(conn->assignment.fi, "%s_%u-%u-%u-%s%s%s-%u",
+ osmo_fsm_inst_update_id_f(conn->assignment.fi, "%s_%u-%u-%u-%s%s%s-%s%u",
conn->fi->id,
new_lchan->ts->trx->bts->nr, new_lchan->ts->trx->nr, new_lchan->ts->nr,
gsm_pchan_id(new_lchan->ts->pchan_on_init),
(new_lchan->ts->pchan_on_init == new_lchan->ts->pchan_is)? "" : "as",
(new_lchan->ts->pchan_on_init == new_lchan->ts->pchan_is)? ""
: gsm_pchan_id(new_lchan->ts->pchan_is),
- new_lchan->nr);
+ new_lchan->vamos.is_secondary ? "shadow" : "",
+ new_lchan->nr - (new_lchan->vamos.is_secondary ? new_lchan->ts->max_primary_lchans : 0));
}
static bool lchan_type_compat_with_mode(enum gsm_chan_t type, const struct channel_mode_and_rate *ch_mode_rate)
@@ -280,7 +319,7 @@ static bool lchan_type_compat_with_mode(enum gsm_chan_t type, const struct chann
enum gsm48_chan_mode chan_mode = ch_mode_rate->chan_mode;
enum channel_rate chan_rate = ch_mode_rate->chan_rate;
- switch (chan_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(chan_mode)) {
case GSM48_CMODE_SIGN:
switch (type) {
case GSM_LCHAN_TCH_F: return chan_rate == CH_RATE_FULL;
@@ -324,7 +363,7 @@ static int check_requires_voice(bool *requires_voice, enum gsm48_chan_mode chan_
{
*requires_voice = false;
- switch (chan_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(chan_mode)) {
case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_EFR:
case GSM48_CMODE_SPEECH_AMR:
@@ -356,11 +395,11 @@ static int check_requires_voice_stream(struct gsm_subscriber_connection *conn)
* a mismatch is not permitted */
for (i = 0; i < req->n_ch_mode_rate; i++) {
- rc = check_requires_voice(&requires_voice_alt, req->ch_mode_rate[i].chan_mode);
+ rc = check_requires_voice(&requires_voice_alt, req->ch_mode_rate_list[i].chan_mode);
if (rc < 0) {
assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
"Channel mode not supported (prev level %d): %s", i,
- gsm48_chan_mode_name(req->ch_mode_rate[i].chan_mode));
+ gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode));
return -EINVAL;
}
@@ -368,9 +407,9 @@ static int check_requires_voice_stream(struct gsm_subscriber_connection *conn)
requires_voice_pref = requires_voice_alt;
else if (requires_voice_alt != requires_voice_pref) {
assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
- "Inconsistent channel modes: %s != %s",
- gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode),
- gsm48_chan_mode_name(req->ch_mode_rate[i].chan_mode));
+ "Requested a mix of Signalling and non-Signalling channel modes: %s != %s",
+ gsm48_chan_mode_name(req->ch_mode_rate_list[0].chan_mode),
+ gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode));
return -EINVAL;
}
}
@@ -391,15 +430,67 @@ static bool reuse_existing_lchan(struct gsm_subscriber_connection *conn)
/* Check if the currently existing lchan is compatible with the
* preferred rate/codec. */
- for (i = 0; i < req->n_ch_mode_rate; i++)
- if (lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate[i])) {
- conn->lchan->ch_mode_rate = req->ch_mode_rate[i];
- return true;
- }
+ for (i = 0; i < req->n_ch_mode_rate; i++) {
+ if (!lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate_list[i]))
+ continue;
+ conn->assignment.selected_ch_mode_rate = req->ch_mode_rate_list[i];
+ return true;
+ }
return false;
}
+static int _reassignment_request(enum assign_for assign_for, struct gsm_lchan *lchan, struct gsm_lchan *to_lchan,
+ enum gsm_chan_t new_lchan_type, int tsc_set, int tsc)
+{
+ struct gsm_subscriber_connection *conn = lchan->conn;
+ struct assignment_request req = {
+ .assign_for = assign_for,
+ .aoip = gscon_is_aoip(conn),
+ .msc_assigned_cic = conn->user_plane.msc_assigned_cic,
+ .msc_rtp_port = conn->user_plane.msc_assigned_rtp_port,
+ .n_ch_mode_rate = 1,
+ .ch_mode_rate_list = { lchan->current_ch_mode_rate },
+ .target_lchan = to_lchan,
+ .tsc_set = {
+ .present = (tsc_set >= 0),
+ .val = tsc_set,
+ },
+ .tsc = {
+ .present = (tsc >= 0),
+ .val = tsc,
+ },
+ };
+
+ if (to_lchan)
+ new_lchan_type = to_lchan->type;
+ req.ch_mode_rate_list[0].chan_rate = chan_t_to_chan_rate(new_lchan_type);
+ /* lchan activation will automatically convert chan_mode to a VAMOS equivalent if required.
+ * So rather always pass the plain non-VAMOS mode. */
+ req.ch_mode_rate_list[0].chan_mode = gsm48_chan_mode_to_non_vamos(lchan->current_ch_mode_rate.chan_mode);
+
+ OSMO_STRLCPY_ARRAY(req.msc_rtp_addr, conn->user_plane.msc_assigned_rtp_addr);
+
+ if (conn->user_plane.mgw_endpoint_ci_msc) {
+ req.use_osmux = osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(conn->user_plane.mgw_endpoint_ci_msc,
+ &req.osmux_cid);
+ }
+
+ return osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_ASSIGNMENT_START, &req);
+}
+
+int reassignment_request_to_lchan(enum assign_for assign_for, struct gsm_lchan *lchan, struct gsm_lchan *to_lchan,
+ int tsc_set, int tsc)
+{
+ return _reassignment_request(assign_for, lchan, to_lchan, 0, tsc_set, tsc);
+}
+
+int reassignment_request_to_chan_type(enum assign_for assign_for, struct gsm_lchan *lchan,
+ enum gsm_chan_t new_lchan_type)
+{
+ return _reassignment_request(assign_for, lchan, NULL, new_lchan_type, -1, -1);
+}
+
void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts *bts,
struct assignment_request *req)
{
@@ -409,7 +500,6 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
[CH_RATE_FULL] = "FR",
};
struct osmo_fsm_inst *fi;
- struct lchan_activate_info info;
int i;
OSMO_ASSERT(conn);
@@ -417,8 +507,6 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
OSMO_ASSERT(!conn->assignment.fi);
OSMO_ASSERT(!conn->assignment.new_lchan);
- assignment_count(CTR_ASSIGNMENT_ATTEMPTED);
-
fi = osmo_fsm_inst_alloc_child(&assignment_fsm, conn->fi, GSCON_EV_ASSIGNMENT_END);
OSMO_ASSERT(fi);
conn->assignment.fi = fi;
@@ -428,26 +516,29 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
conn->assignment.req = *req;
req = &conn->assignment.req;
+ assignment_count(CTR_ASSIGNMENT_ATTEMPTED);
+
/* Check if we need a voice stream. If yes, set the appropriate struct
* members in conn */
if (check_requires_voice_stream(conn) < 0)
return;
- /* There may be an already existing lchan, if yes, try to work with
- * the existing lchan. */
- if (reuse_existing_lchan(conn)) {
+ if (!req->target_lchan && reuse_existing_lchan(conn)) {
+ /* The already existing lchan is suitable for this mode */
+ conn->assignment.new_lchan = NULL;
/* If the requested mode and the current TCH mode matches up, just send the
* assignment complete directly and be done with the assignment procedure. */
- if (conn->lchan->tch_mode == conn->lchan->ch_mode_rate.chan_mode) {
+ if (conn->lchan->current_ch_mode_rate.chan_mode == conn->assignment.selected_ch_mode_rate.chan_mode) {
LOG_ASSIGNMENT(conn, LOGL_DEBUG,
"Current lchan mode is compatible with requested chan_mode,"
" sending BSSMAP Assignment Complete directly."
" requested chan_mode=%s; current lchan is %s\n",
- gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode),
+ gsm48_chan_mode_name(conn->assignment.selected_ch_mode_rate.chan_mode),
gsm_lchan_name(conn->lchan));
- send_assignment_complete(conn);
+ if (req->assign_for == ASSIGN_FOR_BSSMAP_REQ)
+ send_assignment_complete(conn);
/* If something went wrong during send_assignment_complete(),
* the fi will be gone from error handling in there. */
if (conn->assignment.fi) {
@@ -462,39 +553,58 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
LOG_ASSIGNMENT(conn, LOGL_DEBUG,
"Current lchan mode is not compatible with requested chan_mode,"
" so we will modify it. requested chan_mode=%s; current lchan is %s\n",
- gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode),
+ gsm48_chan_mode_name(conn->assignment.selected_ch_mode_rate.chan_mode),
gsm_lchan_name(conn->lchan));
- info = (struct lchan_activate_info){
- .activ_for = FOR_ASSIGNMENT,
- .for_conn = conn,
- .chan_mode = conn->lchan->ch_mode_rate.chan_mode,
- .encr = conn->lchan->encr,
- .s15_s0 = conn->lchan->ch_mode_rate.s15_s0,
- .requires_voice_stream = conn->assignment.requires_voice_stream,
- .msc_assigned_cic = req->msc_assigned_cic,
- .re_use_mgw_endpoint_from_lchan = conn->lchan,
- };
-
- osmo_fsm_inst_dispatch(conn->lchan->fi, LCHAN_EV_REQUEST_MODE_MODIFY, &info);
-
- /* Since we opted not to allocate a new lchan, the new lchan is still the old lchan. */
- conn->assignment.new_lchan = conn->lchan;
-
- /* Also we need to skip the RR assignment, so we jump forward and wait for the lchan_fsm until it
- * reaches the established state again. */
- assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_LCHAN_ESTABLISHED);
-
+ assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_LCHAN_MODIFIED);
return;
}
- /* Try to allocate a new lchan in order of preference */
- for (i = 0; i < req->n_ch_mode_rate; i++) {
- conn->assignment.new_lchan = lchan_select_by_chan_mode(bts,
- req->ch_mode_rate[i].chan_mode, req->ch_mode_rate[i].chan_rate);
- conn->lchan->ch_mode_rate = req->ch_mode_rate[i];
- if (conn->assignment.new_lchan)
+ if (req->target_lchan) {
+ bool matching_mode;
+
+ /* The caller already picked a target lchan to assign to. No need to try re-using the current lchan or
+ * picking a new one. */
+ if (!lchan_state_is(req->target_lchan, LCHAN_ST_UNUSED)) {
+ assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
+ "Assignment to lchan %s requested, but lchan is already in use (state=%s)\n",
+ gsm_lchan_name(req->target_lchan),
+ osmo_fsm_inst_state_name(req->target_lchan->fi));
+ return;
+ }
+
+ conn->assignment.new_lchan = req->target_lchan;
+ matching_mode = false;
+ for (i = 0; i < req->n_ch_mode_rate; i++) {
+ if (!lchan_type_compat_with_mode(conn->assignment.new_lchan->type, &req->ch_mode_rate_list[i]))
+ continue;
+ conn->assignment.selected_ch_mode_rate = req->ch_mode_rate_list[i];
+ matching_mode = true;
+ }
+ if (!matching_mode) {
+ OSMO_ASSERT(conn->lchan);
+ assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
+ "Assignment of lchan %s to %s type %s requested, but lchan is not compatible",
+ gsm_lchan_name(conn->lchan),
+ gsm_lchan_name(req->target_lchan),
+ gsm_lchant_name(conn->assignment.new_lchan->type));
+ return;
+ }
+ } else {
+ /* Try to allocate a new lchan in order of preference */
+ for (i = 0; i < req->n_ch_mode_rate; i++) {
+ conn->assignment.new_lchan = lchan_select_by_chan_mode(bts,
+ req->ch_mode_rate_list[i].chan_mode, req->ch_mode_rate_list[i].chan_rate);
+ if (!conn->assignment.new_lchan)
+ continue;
+ LOG_ASSIGNMENT(conn, LOGL_DEBUG, "selected new lchan %s for mode[%d] = %s channel_rate=%d\n",
+ gsm_lchan_name(conn->assignment.new_lchan),
+ i, gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode),
+ req->ch_mode_rate_list[i].chan_rate);
+
+ conn->assignment.selected_ch_mode_rate = req->ch_mode_rate_list[i];
break;
+ }
}
/* Check whether the lchan allocation was successful or not and tear
@@ -503,13 +613,13 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
assignment_count_result(CTR_ASSIGNMENT_NO_CHANNEL);
assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
"BSSMAP Assignment Command:"
- " No lchan available for: pref=%s:%s / alt1=%s:%s / alt2=%s:%s\n",
- gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode),
- rate_names[req->ch_mode_rate[0].chan_rate],
- req->n_ch_mode_rate >= 1 ? gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode) : "",
- req->n_ch_mode_rate >= 1 ? rate_names[req->ch_mode_rate[0].chan_rate] : "",
- req->n_ch_mode_rate >= 2 ? gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode) : "",
- req->n_ch_mode_rate >= 2 ? rate_names[req->ch_mode_rate[0].chan_rate] : ""
+ " No lchan available for: pref=%s:%s / alt1=%s:%s / alt2=%s:%s",
+ gsm48_chan_mode_name(req->ch_mode_rate_list[0].chan_mode),
+ rate_names[req->ch_mode_rate_list[0].chan_rate],
+ req->n_ch_mode_rate > 1 ? gsm48_chan_mode_name(req->ch_mode_rate_list[1].chan_mode) : "",
+ req->n_ch_mode_rate > 1 ? rate_names[req->ch_mode_rate_list[1].chan_rate] : "",
+ req->n_ch_mode_rate > 2 ? gsm48_chan_mode_name(req->ch_mode_rate_list[2].chan_mode) : "",
+ req->n_ch_mode_rate > 2 ? rate_names[req->ch_mode_rate_list[2].chan_rate] : ""
);
return;
}
@@ -517,33 +627,46 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
assignment_fsm_update_id(conn);
LOG_ASSIGNMENT(conn, LOGL_INFO, "Starting Assignment: chan_mode=%s, chan_type=%s,"
" aoip=%s MSC-rtp=%s:%u (osmux=%s)\n",
- gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode),
- rate_names[conn->lchan->ch_mode_rate.chan_rate],
+ gsm48_chan_mode_name(conn->assignment.selected_ch_mode_rate.chan_mode),
+ rate_names[conn->assignment.selected_ch_mode_rate.chan_rate],
req->aoip ? "yes" : "no", req->msc_rtp_addr, req->msc_rtp_port,
req->use_osmux ? "yes" : "no");
assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE);
- info = (struct lchan_activate_info){
- .activ_for = FOR_ASSIGNMENT,
+}
+
+static void assignment_fsm_wait_lchan_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
+ struct assignment_request *req = &conn->assignment.req;
+ struct lchan_activate_info activ_info = {
+ .activ_for = ACTIVATE_FOR_ASSIGNMENT,
.for_conn = conn,
- .chan_mode = conn->lchan->ch_mode_rate.chan_mode,
+ .ch_mode_rate = conn->assignment.selected_ch_mode_rate,
.encr = conn->lchan->encr,
- .s15_s0 = conn->lchan->ch_mode_rate.s15_s0,
.requires_voice_stream = conn->assignment.requires_voice_stream,
.msc_assigned_cic = req->msc_assigned_cic,
.re_use_mgw_endpoint_from_lchan = conn->lchan,
+ .ta = conn->lchan->last_ta,
+ .ta_known = true,
+ .tsc_set = req->tsc_set,
+ .tsc = req->tsc,
+ .vamos = conn->assignment.new_lchan->vamos.is_secondary,
};
- lchan_activate(conn->assignment.new_lchan, &info);
+ lchan_activate(conn->assignment.new_lchan, &activ_info);
}
-static void assignment_fsm_wait_lchan(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+static void assignment_fsm_wait_lchan_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
switch (event) {
case ASSIGNMENT_EV_LCHAN_ACTIVE:
- if (data != conn->assignment.new_lchan)
+ if (data != conn->assignment.new_lchan) {
+ LOG_ASSIGNMENT(conn, LOGL_ERROR, "Some unrelated lchan was activated, ignoring: %s\n",
+ gsm_lchan_name(data));
return;
+ }
/* The TS may have changed its pchan_is */
assignment_fsm_update_id(conn);
@@ -561,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);
@@ -645,8 +777,10 @@ static void assignment_fsm_wait_mgw_endpoint_to_msc_onenter(struct osmo_fsm_inst
conn->assignment.req.msc_rtp_addr,
conn->assignment.req.msc_rtp_port);
+ /* Assignment can do a new channel activation, in which case new_lchan points at the new lchan.
+ * Or assignment can Channel Mode Modify the already used lchan, in which case new_lchan == NULL. */
if (!gscon_connect_mgw_to_msc(conn,
- conn->assignment.new_lchan,
+ conn->assignment.new_lchan ? : conn->lchan,
conn->assignment.req.msc_rtp_addr,
conn->assignment.req.msc_rtp_port,
fi,
@@ -692,19 +826,57 @@ static void assignment_fsm_wait_mgw_endpoint_to_msc(struct osmo_fsm_inst *fi, ui
}
}
+static void assignment_fsm_wait_lchan_modified_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
+ struct gsm_lchan *lchan = conn->lchan;
+ struct assignment_request *req = &conn->assignment.req;
+ struct lchan_modify_info modif_info = {
+ .modify_for = MODIFY_FOR_ASSIGNMENT,
+ .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 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);
+}
+
+static void assignment_fsm_wait_lchan_modified(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+
+ case ASSIGNMENT_EV_LCHAN_MODIFIED:
+ assignment_fsm_post_lchan_established(fi);
+ return;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
#define S(x) (1 << (x))
static const struct osmo_fsm_state assignment_fsm_states[] = {
[ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE] = {
.name = "WAIT_LCHAN_ACTIVE",
- .action = assignment_fsm_wait_lchan,
+ .onenter = assignment_fsm_wait_lchan_active_onenter,
+ .action = assignment_fsm_wait_lchan_active,
.in_event_mask = 0
| S(ASSIGNMENT_EV_LCHAN_ACTIVE)
,
.out_state_mask = 0
| S(ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE)
| S(ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE)
- | S(ASSIGNMENT_ST_WAIT_LCHAN_ESTABLISHED) /* MODE MODIFY */
+ | S(ASSIGNMENT_ST_WAIT_LCHAN_MODIFIED)
,
},
[ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE] = {
@@ -740,11 +912,23 @@ static const struct osmo_fsm_state assignment_fsm_states[] = {
| S(ASSIGNMENT_EV_MSC_MGW_FAIL)
,
},
+ [ASSIGNMENT_ST_WAIT_LCHAN_MODIFIED] = {
+ .name = "WAIT_LCHAN_MODIFIED",
+ .onenter = assignment_fsm_wait_lchan_modified_onenter,
+ .action = assignment_fsm_wait_lchan_modified,
+ .in_event_mask = 0
+ | S(ASSIGNMENT_EV_LCHAN_MODIFIED)
+ ,
+ .out_state_mask = 0
+ | S(ASSIGNMENT_ST_WAIT_MGW_ENDPOINT_TO_MSC)
+ ,
+ },
};
static const struct value_string assignment_fsm_event_names[] = {
OSMO_VALUE_STRING(ASSIGNMENT_EV_LCHAN_ACTIVE),
OSMO_VALUE_STRING(ASSIGNMENT_EV_LCHAN_ESTABLISHED),
+ OSMO_VALUE_STRING(ASSIGNMENT_EV_LCHAN_MODIFIED),
OSMO_VALUE_STRING(ASSIGNMENT_EV_LCHAN_ERROR),
OSMO_VALUE_STRING(ASSIGNMENT_EV_MSC_MGW_OK),
OSMO_VALUE_STRING(ASSIGNMENT_EV_MSC_MGW_FAIL),
@@ -754,9 +938,14 @@ 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);
+
+ /* Assignment can do a new channel activation, in which case new_lchan points at the new lchan.
+ * Or assignment can Channel Mode Modify the already used lchan, in which case new_lchan == NULL. */
+ struct gsm_lchan *new_lchan = conn->assignment.new_lchan ? : conn->lchan;
+
switch (event) {
case ASSIGNMENT_EV_CONN_RELEASING:
@@ -765,11 +954,12 @@ void assignment_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, vo
return;
case ASSIGNMENT_EV_LCHAN_ERROR:
- if (data != conn->assignment.new_lchan)
+ if (data != new_lchan)
return;
- assignment_fail(conn->assignment.new_lchan->activate.gsm0808_error_cause,
- "Failed to activate lchan %s",
- gsm_lchan_name(conn->assignment.new_lchan));
+ assignment_fail(new_lchan->activate.gsm0808_error_cause,
+ "Failed to %s lchan %s",
+ conn->assignment.new_lchan ? "activate" : "modify",
+ gsm_lchan_name(new_lchan));
return;
default:
@@ -777,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 9383167fb..fb8bd0cec 100644
--- a/src/osmo-bsc/bsc_ctrl_commands.c
+++ b/src/osmo-bsc/bsc_ctrl_commands.c
@@ -22,6 +22,10 @@
#include <time.h>
#include <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/misc.h>
+
#include <osmocom/gsm/gsm48.h>
#include <osmocom/bsc/ipaccess.h>
#include <osmocom/bsc/gsm_data.h>
@@ -31,6 +35,79 @@
#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)
+{
+ FILE *cfile;
+
+ if (!cmd->value || cmd->value[0] == '\0')
+ return -1;
+
+ cfile = fopen(cmd->value, "r");
+ if (!cfile)
+ return -1;
+
+ fclose(cfile);
+
+ return 0;
+}
+static int set_net_apply_config_file(struct ctrl_cmd *cmd, void *_data)
+{
+ int rc;
+ FILE *cfile;
+ unsigned cmd_ret = CTRL_CMD_ERROR;
+
+ LOGP(DCTRL, LOGL_NOTICE, "Applying VTY snippet from %s...\n", cmd->value);
+ cfile = fopen(cmd->value, "r");
+ if (!cfile) {
+ LOGP(DCTRL, LOGL_NOTICE, "Applying VTY snippet from %s: fopen() failed: %d\n",
+ cmd->value, errno);
+ cmd->reply = "NoFile";
+ return cmd_ret;
+ }
+
+ rc = vty_read_config_filep(cfile, NULL);
+ LOGP(DCTRL, LOGL_NOTICE, "Applying VTY snippet from %s returned %d\n", cmd->value, rc);
+ if (rc) {
+ cmd->reply = talloc_asprintf(cmd, "ParseError=%d", rc);
+ if (!cmd->reply)
+ cmd->reply = "OOM";
+ goto close_ret;
+ }
+
+ cmd->reply = "OK";
+ cmd_ret = CTRL_CMD_REPLY;
+close_ret:
+ fclose(cfile);
+ return cmd_ret;
+}
+CTRL_CMD_DEFINE_WO(net_apply_config_file, "apply-config-file");
+
+static int verify_net_write_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return 0;
+}
+static int set_net_write_config_file(struct ctrl_cmd *cmd, void *_data)
+{
+ const char *cfile_name;
+ unsigned cmd_ret = CTRL_CMD_ERROR;
+
+ if (strcmp(cmd->value, "overwrite"))
+ host_config_set(cmd->value);
+
+ cfile_name = host_config_file();
+
+ LOGP(DCTRL, LOGL_NOTICE, "Writing VTY config to file %s...\n", cfile_name);
+ if (osmo_vty_write_config_file(cfile_name) < 0)
+ goto ret;
+
+ cmd->reply = "OK";
+ cmd_ret = CTRL_CMD_REPLY;
+ret:
+ return cmd_ret;
+}
+CTRL_CMD_DEFINE_WO(net_write_config_file, "write-config-file");
CTRL_CMD_DEFINE(net_mcc, "mcc");
static int get_net_mcc(struct ctrl_cmd *cmd, void *_data)
@@ -102,7 +179,7 @@ static int set_net_apply_config(struct ctrl_cmd *cmd, void *data)
* so let's us just reboot it. For the sysmoBTS we can just
* restart the process as all state is gone.
*/
- if (!is_sysmobts_v2(bts) && strcmp(cmd->value, "restart") == 0) {
+ if (!is_osmobts(bts) && strcmp(cmd->value, "restart") == 0) {
struct gsm_bts_trx *trx;
llist_for_each_entry_reverse(trx, &bts->trx_list, list)
abis_nm_ipaccess_restart(trx);
@@ -344,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;
@@ -424,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;
@@ -474,15 +620,180 @@ static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data)
}
CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction");
+static int verify_bts_c0_power_red(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ const int red = atoi(value);
+
+ if (red < 0 || red > 6) {
+ cmd->reply = "Value is out of range";
+ return 1;
+ } else if (red % 2 != 0) {
+ cmd->reply = "Value must be even";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_bts_c0_power_red(struct ctrl_cmd *cmd, void *data)
+{
+ const struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_asprintf(cmd, "%u", bts->c0_max_power_red_db);
+ if (!cmd->reply) {
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_c0_power_red(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ const int red = atoi(cmd->value);
+ int rc;
+
+ rc = gsm_bts_set_c0_power_red(bts, red);
+ if (rc == -ENOTSUP) {
+ cmd->reply = "BCCH carrier power reduction is not supported";
+ return CTRL_CMD_ERROR;
+ } else if (rc != 0) {
+ cmd->reply = "Failed to enable BCCH carrier power reduction";
+ return CTRL_CMD_ERROR;
+ }
+
+ return get_bts_c0_power_red(cmd, 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;
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config_file);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_write_config_file);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mnc);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config);
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);
@@ -493,9 +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 7e839532a..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)
{
@@ -85,6 +76,18 @@ static void update_t3122_chan_load_timer(void *data)
osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0);
}
+static void bsc_store_bts_uptime(void *data)
+{
+ struct gsm_network *net = data;
+ struct gsm_bts *bts;
+
+ llist_for_each_entry(bts, &net->bts_list, list)
+ bts_store_uptime(bts);
+
+ /* Keep this timer ticking. */
+ osmo_timer_schedule(&net->bts_store_uptime_timer, BTS_STORE_UPTIME_INTERVAL, 0);
+}
+
static struct gsm_network *bsc_network_init(void *ctx)
{
struct gsm_network *net = gsm_network_init(ctx);
@@ -100,7 +103,6 @@ static struct gsm_network *bsc_network_init(void *ctx)
net->ho = ho_cfg_init(net, NULL);
net->hodec2.congestion_check_interval_s = HO_CFG_CONGESTION_CHECK_DEFAULT;
- net->neighbor_bss_cells = neighbor_ident_init(net);
/* init statistics */
net->bsc_ctrs = rate_ctr_group_alloc(net, &bsc_ctrg_desc, 0);
@@ -118,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);
@@ -129,6 +176,10 @@ static struct gsm_network *bsc_network_init(void *ctx)
osmo_timer_setup(&net->t3122_chan_load_timer, update_t3122_chan_load_timer, net);
osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0);
+ /* Init uptime tracking timer. */
+ osmo_timer_setup(&net->bts_store_uptime_timer, bsc_store_bts_uptime, net);
+ osmo_timer_schedule(&net->bts_store_uptime_timer, BTS_STORE_UPTIME_INTERVAL, 0);
+
net->cbc->net = net;
net->cbc->mode = BSC_CBC_LINK_MODE_DISABLED;
net->cbc->server.local_addr = bsc_cbc_default_server_local_addr;
@@ -137,8 +188,6 @@ static struct gsm_network *bsc_network_init(void *ctx)
net->cbc->client.remote_addr = (struct osmo_sockaddr_str){ .port = CBSP_TCP_PORT, };
net->cbc->client.local_addr = (struct osmo_sockaddr_str){};
- net->smlc.ctrs = rate_ctr_group_alloc(net, &smlc_ctrg_desc, 0);
-
return net;
err_free_all:
diff --git a/src/osmo-bsc/bsc_rf_ctrl.c b/src/osmo-bsc/bsc_rf_ctrl.c
index 1e04f21c3..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;
@@ -372,9 +462,7 @@ static int rf_ctrl_accept(struct osmo_fd *bfd, unsigned int what)
}
osmo_wqueue_init(&conn->queue, 10);
- conn->queue.bfd.data = conn;
- conn->queue.bfd.fd = fd;
- conn->queue.bfd.when = OSMO_FD_READ | OSMO_FD_WRITE;
+ osmo_fd_setup(&conn->queue.bfd, fd, OSMO_FD_READ | OSMO_FD_WRITE, osmo_wqueue_bfd_cb, conn, 0);
conn->queue.read_cb = rf_read_cmd;
conn->queue.write_cb = rf_write_cmd;
conn->rf = rf;
@@ -484,9 +572,7 @@ static int rf_create_socket(struct osmo_bsc_rf *rf, const char *path)
return -1;
}
- bfd->when = OSMO_FD_READ;
- bfd->cb = rf_ctrl_accept;
- bfd->data = rf;
+ osmo_fd_setup(bfd, bfd->fd, OSMO_FD_READ, rf_ctrl_accept, rf, 0);
if (osmo_fd_register(bfd) != 0) {
LOGP(DLINP, LOGL_ERROR, "Failed to register bfd.\n");
diff --git a/src/osmo-bsc/bsc_sccp.c b/src/osmo-bsc/bsc_sccp.c
index 52858fe42..0cd1dc9f3 100644
--- a/src/osmo-bsc/bsc_sccp.c
+++ b/src/osmo-bsc/bsc_sccp.c
@@ -23,6 +23,7 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/lb.h>
/* We need an unused SCCP conn_id across all SCCP users. */
int bsc_sccp_inst_next_conn_id(struct osmo_sccp_instance *sccp)
@@ -47,7 +48,7 @@ int bsc_sccp_inst_next_conn_id(struct osmo_sccp_instance *sccp)
}
}
- if (bsc_gsmnet->smlc.sccp == sccp
+ if (bsc_gsmnet->smlc->sccp == sccp
&& conn->lcs.lb.state != SUBSCR_SCCP_ST_NONE) {
if (conn_id == conn->lcs.lb.conn_id) {
conn_id_already_used = true;
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 ed08e86ad..c18079f34 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>
@@ -161,7 +162,7 @@ static void gscon_bssmap_clear(struct gsm_subscriber_connection *conn,
return;
}
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_CLEAR_RQST]);
+ 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)
LOGPFSML(conn->fi, LOGL_ERROR, "Unable to deliver BSSMAP Clear Request message\n");
@@ -176,7 +177,7 @@ static void forward_dtap(struct gsm_subscriber_connection *conn, struct msgb *ms
OSMO_ASSERT(conn);
resp = gsm0808_create_dtap(msg, OBSC_LINKID_CB(msg));
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_DTAP]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_DTAP));
gscon_sigtran_send(conn, resp);
}
@@ -195,7 +196,8 @@ static void gscon_release_lchan(struct gsm_subscriber_connection *conn, struct g
conn->ho.new_lchan = NULL;
if (conn->assignment.new_lchan == lchan)
conn->assignment.new_lchan = NULL;
- lchan_release(lchan, do_rr_release, err, cause_rr);
+ lchan_release(lchan, do_rr_release, err, cause_rr,
+ gscon_last_eutran_plmn(conn));
}
void gscon_release_lchans(struct gsm_subscriber_connection *conn, bool do_rr_release, enum gsm48_rr_cause cause_rr)
@@ -517,39 +519,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;
@@ -599,7 +609,7 @@ bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn,
ci = conn->user_plane.mgw_endpoint_ci_msc;
if (ci) {
- const struct mgcp_conn_peer *prev_crcx_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
+ const struct mgcp_conn_peer *prev_crcx_info = osmo_mgcpc_ep_ci_get_remote_rtp_info(ci);
if (!conn->user_plane.mgw_endpoint) {
LOGPFSML(conn->fi, LOGL_ERROR, "Internal error: conn has a CI but no endpoint\n");
@@ -617,6 +627,8 @@ bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn,
LOGPFSML(conn->fi, LOGL_DEBUG,
"MSC side MGW endpoint ci is already configured to %s\n",
osmo_mgcpc_ep_ci_name(ci));
+ /* Immediately dispatch the success event */
+ osmo_fsm_inst_dispatch(notify, event_success, notify_data);
return true;
}
@@ -696,9 +708,19 @@ void gscon_change_primary_lchan(struct gsm_subscriber_connection *conn, struct g
/* On release, do not receive release events that look like the primary lchan is gone. */
struct gsm_lchan *old_lchan = conn->lchan;
+ OSMO_ASSERT(new_lchan);
+
if (old_lchan == new_lchan)
return;
+ if (!old_lchan)
+ LOGPFSML(conn->fi, LOGL_DEBUG, "setting primary lchan for this conn to %s\n",
+ new_lchan->fi? osmo_fsm_inst_name(new_lchan->fi) : gsm_lchan_name(new_lchan));
+ else
+ LOGPFSML(conn->fi, LOGL_DEBUG, "primary lchan for this conn changes from %s to %s\n",
+ old_lchan->fi? osmo_fsm_inst_name(old_lchan->fi) : gsm_lchan_name(old_lchan),
+ new_lchan->fi? osmo_fsm_inst_name(new_lchan->fi) : gsm_lchan_name(new_lchan));
+
conn->lchan = new_lchan;
conn->lchan->conn = conn;
@@ -788,6 +810,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;
@@ -808,8 +836,9 @@ 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 struct gscon_clear_cmd_data *ccd;
- struct osmo_mobile_identity *mi_imsi;
+ const enum gsm0808_cause *cause_0808;
+ const struct tlv_parsed *tp;
+ struct osmo_mobile_identity mi_imsi;
/* Regular allstate event processing */
switch (event) {
@@ -825,14 +854,12 @@ static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *d
osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_CONN_CLEAR, NULL);
OSMO_ASSERT(data);
- ccd = data;
- if (conn->lchan)
- conn->lchan->release.is_csfb = ccd->is_csfb;
+ 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(ccd->cause_0808));
+ 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
@@ -841,7 +868,7 @@ static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *d
/* Close MGCP connections */
osmo_mgcpc_ep_clear(conn->user_plane.mgw_endpoint);
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_CLEAR_COMPLETE]);
+ 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());
break;
case GSCON_EV_A_DISC_IND:
@@ -868,14 +895,26 @@ static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *d
break;
case GSCON_EV_A_COMMON_ID_IND:
OSMO_ASSERT(data);
- mi_imsi = data;
+ tp = data;
+ if (osmo_mobile_identity_decode(&mi_imsi, TLVP_VAL(tp, GSM0808_IE_IMSI), TLVP_LEN(tp, GSM0808_IE_IMSI), false)
+ || mi_imsi.type != GSM_MI_TYPE_IMSI) {
+ LOGPFSML(fi, LOGL_ERROR, "CommonID: could not parse IMSI\n");
+ return;
+ }
if (!conn->bsub)
- conn->bsub = bsc_subscr_find_or_create_by_imsi(conn->network->bsc_subscribers, mi_imsi->imsi,
+ conn->bsub = bsc_subscr_find_or_create_by_imsi(conn->network->bsc_subscribers, mi_imsi.imsi,
BSUB_USE_CONN);
else {
/* we already have a bsc_subscr associated; maybe that subscriber has no IMSI yet? */
if (!conn->bsub->imsi[0])
- bsc_subscr_set_imsi(conn->bsub, mi_imsi->imsi);
+ bsc_subscr_set_imsi(conn->bsub, mi_imsi.imsi);
+ }
+ if (TLVP_PRESENT(tp, GSM0808_IE_LAST_USED_EUTRAN_PLMN_ID)) {
+ conn->fast_return.allowed = true; /* Always allowed for CSFB */
+ conn->fast_return.last_eutran_plmn_valid = true;
+ osmo_plmn_from_bcd(TLVP_VAL(tp, GSM0808_IE_LAST_USED_EUTRAN_PLMN_ID), &conn->fast_return.last_eutran_plmn);
+ LOGPFSML(fi, LOGL_DEBUG, "subscr comes from E-UTRAN PLMN %s\n",
+ osmo_plmn_name(&conn->fast_return.last_eutran_plmn));
}
gscon_update_id(conn);
break;
@@ -916,8 +955,14 @@ 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;
if (conn->lcls.fi) {
/* request termination of LCLS FSM */
@@ -933,7 +978,7 @@ static void gscon_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
/* drop pending messages */
gscon_dtap_queue_flush(conn, 0);
- penalty_timers_free(&conn->hodec2.penalty_timers);
+ penalty_timers_clear(&conn->hodec2.penalty_timers, NULL);
}
static int gscon_timer_cb(struct osmo_fsm_inst *fi)
@@ -1004,7 +1049,7 @@ struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *ne
conn->network = net;
INIT_LLIST_HEAD(&conn->dtap_queue);
- /* BTW, penalty timers will be initialized on-demand. */
+ INIT_LLIST_HEAD(&conn->hodec2.penalty_timers);
conn->sccp.conn_id = -1;
/* don't allocate from 'conn' context, as gscon_cleanup() will call talloc_free(conn) before
@@ -1123,7 +1168,6 @@ static void gsm0808_send_rsl_dtap(struct gsm_subscriber_connection *conn,
sapi = link_id & 0x7;
msg->lchan = conn->lchan;
- msg->dst = msg->lchan->ts->trx->rsl_link;
/* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */
if (allow_sacch && sapi != 0) {
diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c
index cd7d0e001..c1a6e4404 100644
--- a/src/osmo-bsc/bsc_vty.c
+++ b/src/osmo-bsc/bsc_vty.c
@@ -22,6 +22,8 @@
#include <unistd.h>
#include <time.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
#include <osmocom/vty/vty.h>
@@ -30,107 +32,44 @@
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/misc.h>
#include <osmocom/vty/tdef_vty.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/gsm0502.h>
#include <osmocom/ctrl/control_if.h>
-#include <osmocom/gsm/gsm48.h>
-#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/gsm23236.h>
-#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/gsm/gsm0502.h>
-#include <arpa/inet.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/core/linuxlist.h>
-#include <osmocom/core/socket.h>
+#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/abis/e1_input.h>
#include <osmocom/bsc/abis_nm.h>
#include <osmocom/bsc/abis_om2000.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/abis_nm.h>
#include <osmocom/bsc/chan_alloc.h>
-#include <osmocom/bsc/meas_rep.h>
-#include <osmocom/bsc/vty.h>
-#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/ipaccess.h>
#include <osmocom/bsc/abis_rsl.h>
-#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/osmo_bsc_rf.h>
-#include <osmocom/bsc/pcu_if.h>
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/handover_cfg.h>
#include <osmocom/bsc/handover_vty.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
-#include <osmocom/bsc/acc.h>
#include <osmocom/bsc/meas_feed.h>
-#include <osmocom/bsc/neighbor_ident.h>
-#include <osmocom/bsc/handover.h>
#include <osmocom/bsc/timeslot_fsm.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/lchan_select.h>
#include <osmocom/bsc/smscb.h>
#include <osmocom/bsc/osmo_bsc.h>
#include <osmocom/bsc/bts.h>
-#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
+#include <osmocom/bsc/assignment_fsm.h>
+#include <osmocom/bsc/bssmap_reset.h>
#include <inttypes.h>
#include "../../bscconfig.h"
-#define BTS_NR_STR "BTS Number\n"
-#define TRX_NR_STR "TRX Number\n"
-#define TS_NR_STR "Timeslot Number\n"
-#define SS_NR_STR "Sub-slot Number\n"
-#define LCHAN_NR_STR "Logical Channel Number\n"
-#define BTS_TRX_STR BTS_NR_STR TRX_NR_STR
-#define BTS_TRX_TS_STR BTS_TRX_STR TS_NR_STR
-#define BTS_TRX_TS_LCHAN_STR BTS_TRX_TS_STR LCHAN_NR_STR
-#define BTS_NR_TRX_TS_STR2 \
- "BTS for manual command\n" BTS_NR_STR \
- "TRX for manual command\n" TRX_NR_STR \
- "Timeslot for manual command\n" TS_NR_STR
-#define BTS_NR_TRX_TS_SS_STR2 \
- BTS_NR_TRX_TS_STR2 \
- "Sub-slot for manual command\n" SS_NR_STR
-
-/* FIXME: this should go to some common file */
-static const struct value_string gprs_ns_timer_strs[] = {
- { 0, "tns-block" },
- { 1, "tns-block-retries" },
- { 2, "tns-reset" },
- { 3, "tns-reset-retries" },
- { 4, "tns-test" },
- { 5, "tns-alive" },
- { 6, "tns-alive-retries" },
- { 0, NULL }
-};
-
-static const struct value_string gprs_bssgp_cfg_strs[] = {
- { 0, "blocking-timer" },
- { 1, "blocking-retries" },
- { 2, "unblocking-retries" },
- { 3, "reset-timer" },
- { 4, "reset-retries" },
- { 5, "suspend-timer" },
- { 6, "suspend-retries" },
- { 7, "resume-timer" },
- { 8, "resume-retries" },
- { 9, "capability-update-timer" },
- { 10, "capability-update-retries" },
- { 0, NULL }
-};
-
-static const struct value_string bts_neigh_mode_strs[] = {
- { NL_MODE_AUTOMATIC, "automatic" },
- { NL_MODE_MANUAL, "manual" },
- { NL_MODE_MANUAL_SI5SEP, "manual-si5" },
- { 0, NULL }
-};
+#define X(x) (1 << x)
const struct value_string bts_loc_fix_names[] = {
{ BTS_LOC_FIX_INVALID, "invalid" },
@@ -139,30 +78,12 @@ const struct value_string bts_loc_fix_names[] = {
{ 0, NULL }
};
-struct cmd_node net_node = {
+static struct cmd_node net_node = {
GSMNET_NODE,
"%s(config-net)# ",
1,
};
-struct cmd_node bts_node = {
- BTS_NODE,
- "%s(config-net-bts)# ",
- 1,
-};
-
-struct cmd_node trx_node = {
- TRX_NODE,
- "%s(config-net-bts-trx)# ",
- 1,
-};
-
-struct cmd_node ts_node = {
- TS_NODE,
- "%s(config-net-bts-trx-ts)# ",
- 1,
-};
-
static struct gsm_network *vty_global_gsm_network = NULL;
struct gsm_network *gsmnet_from_vty(struct vty *v)
@@ -177,12 +98,40 @@ struct gsm_network *gsmnet_from_vty(struct vty *v)
return vty_global_gsm_network;
}
-static int dummy_config_write(struct vty *v)
+int dummy_config_write(struct vty *v)
{
return CMD_SUCCESS;
}
-static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
+/* resolve a gsm_bts_trx_ts basd on the given numeric identifiers */
+static struct gsm_bts_trx_ts *vty_get_ts(struct vty *vty, const char *bts_str, const char *trx_str,
+ const char *ts_str)
+{
+ int bts_nr = atoi(bts_str);
+ int trx_nr = atoi(trx_str);
+ int ts_nr = atoi(ts_str);
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+
+ 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 NULL;
+ }
+
+ trx = gsm_bts_trx_num(bts, trx_nr);
+ if (!trx) {
+ vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE);
+ return NULL;
+ }
+
+ ts = &trx->ts[ts_nr];
+
+ return ts;
+}
+
+void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
{
vty_out(vty,"Oper '%s', Admin '%s', Avail '%s'%s",
abis_nm_opstate_name(nms->operational),
@@ -190,7 +139,7 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
abis_nm_avail_name(nms->availability), VTY_NEWLINE);
}
-static void dump_pchan_load_vty(struct vty *vty, char *prefix,
+void dump_pchan_load_vty(struct vty *vty, char *prefix,
const struct pchan_load *pl)
{
int i;
@@ -276,271 +225,6 @@ DEFUN(bsc_show_net, bsc_show_net_cmd, "show network",
return CMD_SUCCESS;
}
-
-static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l)
-{
- struct e1inp_line *line;
-
- if (!e1l) {
- vty_out(vty, " None%s", VTY_NEWLINE);
- return;
- }
-
- line = e1l->ts->line;
-
- vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s",
- line->num, line->driver->name, e1l->ts->num,
- e1inp_signtype_name(e1l->type), VTY_NEWLINE);
- vty_out(vty, " E1 TEI %u, SAPI %u%s",
- e1l->tei, e1l->sapi, VTY_NEWLINE);
-}
-
-/*! Dump the IP addresses and ports of the input signal link's timeslot.
- * This only makes sense for links connected with ipaccess.
- * Example output: "(r=10.1.42.1:55416<->l=10.1.42.123:3003)" */
-static void e1isl_dump_vty_tcp(struct vty *vty, const struct e1inp_sign_link *e1l)
-{
- if (e1l) {
- char *name = osmo_sock_get_name(NULL, e1l->ts->driver.ipaccess.fd.fd);
- vty_out(vty, "%s", name);
- talloc_free(name);
- }
- vty_out(vty, "%s", VTY_NEWLINE);
-}
-
-static void vty_out_neigh_list(struct vty *vty, struct bitvec *bv)
-{
- int count = 0;
- int i;
- for (i = 0; i < 1024; i++) {
- if (!bitvec_get_bit_pos(bv, i))
- continue;
- vty_out(vty, " %u", i);
- count ++;
- }
- if (!count)
- vty_out(vty, " (none)");
- else
- vty_out(vty, " (%d)", count);
-}
-
-static void bts_dump_vty_cbch(struct vty *vty, const struct bts_smscb_chan_state *cstate)
-{
- vty_out(vty, " CBCH %s: %u messages, %u pages, %lu-entry sched_arr, %u%% load%s",
- bts_smscb_chan_state_name(cstate), llist_count(&cstate->messages),
- bts_smscb_chan_page_count(cstate), cstate->sched_arr_size,
- bts_smscb_chan_load_percent(cstate), VTY_NEWLINE);
-}
-
-static void bts_dump_vty_features(struct vty *vty, struct gsm_bts *bts)
-{</