aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore11
-rw-r--r--README26
-rw-r--r--README.md95
-rw-r--r--README.vty-tests2
-rw-r--r--TODO-RELEASE5
-rw-r--r--configure.ac53
-rwxr-xr-xcontrib/jenkins.sh20
-rw-r--r--contrib/osmo-bsc.spec.in23
-rw-r--r--contrib/systemd/osmo-bsc.service2
-rw-r--r--debian/changelog1395
-rw-r--r--debian/control18
-rw-r--r--debian/copyright2
-rw-r--r--doc/Makefile.am7
-rw-r--r--doc/assignment-fsm.dot10
-rw-r--r--doc/assignment.msc2
-rw-r--r--doc/bts-features.txt42
-rw-r--r--doc/codec_resolution.ladder136
-rw-r--r--doc/codec_resolution.msc48
-rw-r--r--doc/examples/Makefile.am2
-rw-r--r--doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus01-4trx.cfg4
-rw-r--r--doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-4trx.cfg4
-rw-r--r--doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-8trx.cfg4
-rw-r--r--doc/examples/osmo-bsc/ericsson/osmo-bsc.rbs2308.cfg4
-rw-r--r--doc/examples/osmo-bsc/nokia/osmo-bsc.insite.cfg2
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc-4trx-fh.confmerge216
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc-4trx.cfg162
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc-minimal.cfg5
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc.cfg18
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg9
-rw-r--r--doc/examples/osmo-bsc/siemens/osmo-bsc.bs11.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/Makefile.am6
-rw-r--r--doc/manuals/aoip-mgw-options-docinfo.xml10
-rw-r--r--doc/manuals/aoip-mgw-options.adoc30
-rw-r--r--doc/manuals/cbsp/messages.adoc73
-rw-r--r--doc/manuals/cbsp/procedures.adoc83
-rw-r--r--doc/manuals/chapters/aoip-flows.adoc107
-rw-r--r--doc/manuals/chapters/bsc.adoc26
-rw-r--r--doc/manuals/chapters/bts-examples.adoc125
-rw-r--r--doc/manuals/chapters/bts.adoc162
-rw-r--r--doc/manuals/chapters/chan_alloc.adoc156
-rw-r--r--doc/manuals/chapters/control.adoc63
-rw-r--r--doc/manuals/chapters/handover.adoc163
-rw-r--r--doc/manuals/chapters/handover_inter_bsc.dot2
-rw-r--r--doc/manuals/chapters/interf_meas.adoc73
-rw-r--r--doc/manuals/chapters/osmux_bsc.adoc55
-rw-r--r--doc/manuals/chapters/power_control.adoc645
-rw-r--r--doc/manuals/chapters/qos-example.adoc46
-rw-r--r--doc/manuals/chapters/running.adoc88
-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-abis_a.msc4
-rw-r--r--doc/manuals/message-sequences/mo_call-bsc-msc-mgw-aoip.msc75
-rw-r--r--doc/manuals/mgw/classic-bsc.msc4
-rw-r--r--doc/manuals/mgw/osmo-bsc-new-mgw-e1.msc10
-rw-r--r--doc/manuals/mgw/osmo-bsc-new-mgw.msc10
-rw-r--r--doc/manuals/mgw/osmo-bsc-old-sccplite.msc4
-rw-r--r--doc/manuals/osmobsc-cbsp-docinfo.xml36
-rw-r--r--doc/manuals/osmobsc-cbsp.adoc69
-rw-r--r--doc/manuals/osmobsc-usermanual.adoc14
-rw-r--r--doc/mscpool-attach.dot30
-rw-r--r--include/osmocom/bsc/Makefile.am15
-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.h16
-rw-r--r--include/osmocom/bsc/acc.h2
-rw-r--r--include/osmocom/bsc/arfcn_range_encode.h26
-rw-r--r--include/osmocom/bsc/assignment_fsm.h11
-rw-r--r--include/osmocom/bsc/bsc_msc_data.h14
-rw-r--r--include/osmocom/bsc/bsc_stats.h116
-rw-r--r--include/osmocom/bsc/bsc_subscr_conn_fsm.h19
-rw-r--r--include/osmocom/bsc/bsc_subscriber.h19
-rw-r--r--include/osmocom/bsc/bss.h2
-rw-r--r--include/osmocom/bsc/bssmap_reset.h31
-rw-r--r--include/osmocom/bsc/bts.h455
-rw-r--r--include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h15
-rw-r--r--include/osmocom/bsc/bts_setup_ramp.h69
-rw-r--r--include/osmocom/bsc/bts_sm.h79
-rw-r--r--include/osmocom/bsc/bts_trx.h25
-rw-r--r--include/osmocom/bsc/chan_counts.h111
-rw-r--r--include/osmocom/bsc/codec_pref.h1
-rw-r--r--include/osmocom/bsc/ctrl.h14
-rw-r--r--include/osmocom/bsc/debug.h2
-rw-r--r--include/osmocom/bsc/gsm_04_08_rr.h14
-rw-r--r--include/osmocom/bsc/gsm_08_08.h2
-rw-r--r--include/osmocom/bsc/gsm_data.h726
-rw-r--r--include/osmocom/bsc/handover.h9
-rw-r--r--include/osmocom/bsc/handover_cfg.h42
-rw-r--r--include/osmocom/bsc/handover_ctrl.h3
-rw-r--r--include/osmocom/bsc/handover_fsm.h4
-rw-r--r--include/osmocom/bsc/handover_vty.h2
-rw-r--r--include/osmocom/bsc/lb.h26
-rw-r--r--include/osmocom/bsc/lchan.h388
-rw-r--r--include/osmocom/bsc/lchan_fsm.h24
-rw-r--r--include/osmocom/bsc/lchan_rtp_fsm.h6
-rw-r--r--include/osmocom/bsc/lchan_select.h27
-rw-r--r--include/osmocom/bsc/lcs_loc_req.h19
-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.h125
-rw-r--r--include/osmocom/bsc/osmo_bsc.h5
-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/paging.h70
-rw-r--r--include/osmocom/bsc/pcu_if.h2
-rw-r--r--include/osmocom/bsc/pcuif_proto.h79
-rw-r--r--include/osmocom/bsc/penalty_timers.h47
-rw-r--r--include/osmocom/bsc/power_control.h99
-rw-r--r--include/osmocom/bsc/rest_octets.h121
-rw-r--r--include/osmocom/bsc/signal.h32
-rw-r--r--include/osmocom/bsc/smscb.h11
-rw-r--r--include/osmocom/bsc/system_information.h6
-rw-r--r--include/osmocom/bsc/timeslot_fsm.h15
-rw-r--r--include/osmocom/bsc/vty.h63
-rw-r--r--m4/ax_check_compile_flag.m474
-rw-r--r--src/ipaccess/Makefile.am17
-rw-r--r--src/ipaccess/abisip-find.c19
-rw-r--r--src/ipaccess/ipaccess-config.c162
-rw-r--r--src/ipaccess/ipaccess-proxy.c53
-rw-r--r--src/ipaccess/stubs.c24
-rw-r--r--src/osmo-bsc/Makefile.am60
-rw-r--r--src/osmo-bsc/a_reset.c221
-rw-r--r--src/osmo-bsc/abis_nm.c375
-rw-r--r--src/osmo-bsc/abis_nm_vty.c5
-rw-r--r--src/osmo-bsc/abis_om2000.c783
-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.c1345
-rw-r--r--src/osmo-bsc/acc.c153
-rw-r--r--src/osmo-bsc/arfcn_range_encode.c340
-rw-r--r--src/osmo-bsc/assignment_fsm.c465
-rw-r--r--src/osmo-bsc/bsc_ctrl.c (renamed from src/osmo-bsc/osmo_bsc_ctrl.c)995
-rw-r--r--src/osmo-bsc/bsc_ctrl_commands.c501
-rw-r--r--src/osmo-bsc/bsc_ctrl_lookup.c11
-rw-r--r--src/osmo-bsc/bsc_init.c93
-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.c236
-rw-r--r--src/osmo-bsc/bsc_subscr_conn_fsm.c508
-rw-r--r--src/osmo-bsc/bsc_subscriber.c88
-rw-r--r--src/osmo-bsc/bsc_vty.c5108
-rw-r--r--src/osmo-bsc/bssmap_reset.c257
-rw-r--r--src/osmo-bsc/bts.c1361
-rw-r--r--src/osmo-bsc/bts_ctrl.c652
-rw-r--r--src/osmo-bsc/bts_ericsson_rbs2000.c52
-rw-r--r--src/osmo-bsc/bts_init.c2
-rw-r--r--src/osmo-bsc/bts_ipaccess_nanobts.c665
-rw-r--r--src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c85
-rw-r--r--src/osmo-bsc/bts_nokia_site.c27
-rw-r--r--src/osmo-bsc/bts_osmobts.c214
-rw-r--r--src/osmo-bsc/bts_setup_ramp.c249
-rw-r--r--src/osmo-bsc/bts_siemens_bs11.c17
-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.c271
-rw-r--r--src/osmo-bsc/bts_trx_ctrl.c212
-rw-r--r--src/osmo-bsc/bts_trx_vty.c874
-rw-r--r--src/osmo-bsc/bts_vty.c4914
-rw-r--r--src/osmo-bsc/cbch_scheduler.c8
-rw-r--r--src/osmo-bsc/cbsp_link.c350
-rw-r--r--src/osmo-bsc/chan_alloc.c71
-rw-r--r--src/osmo-bsc/chan_counts.c310
-rw-r--r--src/osmo-bsc/codec_pref.c116
-rw-r--r--src/osmo-bsc/e1_config.c12
-rw-r--r--src/osmo-bsc/gsm_04_08_rr.c339
-rw-r--r--src/osmo-bsc/gsm_08_08.c104
-rw-r--r--src/osmo-bsc/gsm_data.c393
-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.c1255
-rw-r--r--src/osmo-bsc/handover_fsm.c271
-rw-r--r--src/osmo-bsc/handover_logic.c111
-rw-r--r--src/osmo-bsc/handover_vty.c29
-rw-r--r--src/osmo-bsc/lb.c386
-rw-r--r--src/osmo-bsc/lchan.c150
-rw-r--r--src/osmo-bsc/lchan_fsm.c761
-rw-r--r--src/osmo-bsc/lchan_rtp_fsm.c104
-rw-r--r--src/osmo-bsc/lchan_select.c436
-rw-r--r--src/osmo-bsc/lcs_loc_req.c81
-rw-r--r--src/osmo-bsc/lcs_ta_req.c30
-rw-r--r--src/osmo-bsc/meas_feed.c5
-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.c753
-rw-r--r--src/osmo-bsc/neighbor_ident_vty.c860
-rw-r--r--src/osmo-bsc/net_init.c89
-rw-r--r--src/osmo-bsc/nm_bb_transc_fsm.c430
-rw-r--r--src/osmo-bsc/nm_bts_fsm.c412
-rw-r--r--src/osmo-bsc/nm_bts_sm_fsm.c307
-rw-r--r--src/osmo-bsc/nm_channel_fsm.c363
-rw-r--r--src/osmo-bsc/nm_common_fsm.c90
-rw-r--r--src/osmo-bsc/nm_gprs_cell_fsm.c382
-rw-r--r--src/osmo-bsc/nm_gprs_nse_fsm.c384
-rw-r--r--src/osmo-bsc/nm_gprs_nsvc_fsm.c408
-rw-r--r--src/osmo-bsc/nm_rcarrier_fsm.c405
-rw-r--r--src/osmo-bsc/osmo_bsc_bssap.c759
-rw-r--r--src/osmo-bsc/osmo_bsc_filter.c47
-rw-r--r--src/osmo-bsc/osmo_bsc_lcls.c18
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c488
-rw-r--r--src/osmo-bsc/osmo_bsc_msc.c64
-rw-r--r--src/osmo-bsc/osmo_bsc_sigtran.c47
-rw-r--r--src/osmo-bsc/paging.c729
-rw-r--r--src/osmo-bsc/pcu_sock.c447
-rw-r--r--src/osmo-bsc/penalty_timers.c121
-rw-r--r--src/osmo-bsc/power_control.c476
-rw-r--r--src/osmo-bsc/rest_octets.c899
-rw-r--r--src/osmo-bsc/smscb.c423
-rw-r--r--src/osmo-bsc/smscb_vty.c421
-rw-r--r--src/osmo-bsc/system_information.c374
-rw-r--r--src/osmo-bsc/timeslot_fsm.c168
-rw-r--r--src/utils/Makefile.am17
-rw-r--r--src/utils/bs11_config.c21
-rw-r--r--src/utils/meas_db.c99
-rw-r--r--src/utils/meas_db.h6
-rw-r--r--src/utils/meas_json.c13
-rw-r--r--src/utils/meas_pcap2db.c12
-rw-r--r--src/utils/meas_udp2db.c9
-rw-r--r--src/utils/meas_vis.c2
-rw-r--r--tests/Makefile.am19
-rw-r--r--tests/abis/Makefile.am13
-rw-r--r--tests/abis/abis_test.c13
-rw-r--r--tests/acc/Makefile.am13
-rw-r--r--tests/acc/acc_test.c46
-rw-r--r--tests/acch_overpower.vty88
-rw-r--r--tests/bsc/Makefile.am22
-rw-r--r--tests/bsc/bsc_test.c91
-rw-r--r--tests/bsc/bsc_test.ok51
-rw-r--r--tests/bts_features.vty27
-rw-r--r--tests/codec_pref/Makefile.am5
-rw-r--r--tests/codec_pref/codec_pref_test.c133
-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.cfg202
-rwxr-xr-xtests/ctrl_test_runner.py321
-rw-r--r--tests/gsm0408/Makefile.am18
-rw-r--r--tests/gsm0408/gsm0408_test.c520
-rw-r--r--tests/gsm0408/gsm0408_test.ok72
-rw-r--r--tests/handover/Makefile.am79
-rw-r--r--tests/handover/handover_test.c2593
-rw-r--r--tests/handover/handover_test.ok1
-rw-r--r--tests/handover/handover_tests.ok57
-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_meas_rep_multi_band.ho_vty47
-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_vty49
-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.vty96
-rw-r--r--tests/interf_meas.vty42
-rw-r--r--tests/msc.vty119
-rw-r--r--tests/nanobts_omlattr/Makefile.am11
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.c213
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.ok16
-rw-r--r--tests/neighbor_ident.vty183
-rw-r--r--tests/nri_cfg.vty32
-rw-r--r--tests/osmo-bsc.vty269
-rw-r--r--tests/paging/Makefile.am38
-rw-r--r--tests/paging/paging_test.c296
-rw-r--r--tests/paging/paging_test.ok9694
-rw-r--r--tests/power_ctrl.vty381
-rw-r--r--tests/smlc.vty73
-rw-r--r--tests/subscr/Makefile.am6
-rw-r--r--tests/subscr/bsc_subscr_test.c3
-rw-r--r--tests/testsuite.at217
-rw-r--r--tests/timer.vty20
-rw-r--r--tests/timeslot.vty73
-rwxr-xr-xtests/vty_test_runner.py15
332 files changed, 48667 insertions, 16828 deletions
diff --git a/.gitignore b/.gitignore
index c7b14bff3..dd2d5c0ad 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?
@@ -42,15 +44,11 @@ osmo-bsc-*.tar.bz2
osmo-bsc-*.tar.gz
# apps and app data
-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
-src/osmo-bsc_nat/*.cfg*
src/osmo-bsc/osmo-bsc
src/osmo-bsc/*.cfg*
src/utils/meas_vis
@@ -71,6 +69,9 @@ tests/package.m4
tests/testsuite
tests/testsuite.log
+tests/handover/test*.ok
+tests/handover/test*.err
+
writtenconfig/
# manuals
@@ -80,7 +81,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/README b/README
deleted file mode 100644
index e84849b10..000000000
--- a/README
+++ /dev/null
@@ -1,26 +0,0 @@
-About OsmoBSC
-=============
-
-OsmoBSC originated from the OpenBSC project, which started as a minimalistic
-all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
-maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
-IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
-approach to fully independent separate programs as in typical GSM networks.
-
-OsmoBSC was one of the parts split off from the old openbsc.git. Before, it
-worked as a standalone osmo-bsc binary as well as a combination of libbsc and
-libmsc, i.e. the old OsmoNITB. Since the standalone OsmoMSC with a true A
-interface (and IuCS for 3G support) is available, OsmoBSC exists only as a
-separate standalone entity.
-
-OsmoBSC exposes
-- A over IP towards an MSC (e.g. OsmoMSC);
-- Abis interfaces towards various kinds of BTS;
-- The Osmocom typical telnet VTY and CTRL interfaces.
-
-Find OsmoBSC issue tracker and wiki online at
-https://osmocom.org/projects/osmobsc
-https://osmocom.org/projects/osmobsc/wiki
-
-OsmoBSC-NAT is a specialized solution to navigating RTP streams through a NAT.
-(Todo: describe in more detail)
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..5eed2f2e1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,95 @@
+osmo-bsc - Osmocom BSC Implementation
+=====================================
+
+This repository contains a C-language implementation of a GSM Base Station
+Controller (BSC). IT is part of the
+[Osmocom](https://osmocom.org/) Open Source Mobile Communications
+project.
+
+OsmoBSC exposes
+ * A over IP towards an MSC (e.g. OsmoMSC): 3GPP AoIP or SCCPlite
+ * Abis interfaces towards various kinds of BTS (osmo-bts, sysmobts, nanoBTS, Siemens, Nokia, Ericsson)
+ * The Osmocom typical telnet VTY and CTRL interfaces.
+ * The Osmocom typical statsd exporter.
+ * Cell Broadcast Service Protocol (CBSP) towards a CBC (Cell Broadcast Centre, such as osmo-cbc).
+ * Lb interface towards a SMLC (Serving Mobile Location Centre, such as osmo-smlc).
+
+
+Homepage
+--------
+
+You can find the OsmoBSC issue tracker and wiki online at
+<https://osmocom.org/projects/osmobsc> and <https://osmocom.org/projects/osmobsc/wiki>.
+
+
+GIT Repository
+--------------
+
+You can clone from the official osmo-bsc.git repository using
+
+ git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-bsc
+
+There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-bsc>
+
+
+Documentation
+-------------
+
+User Manuals and VTY reference manuals are [optionally] built in PDF form
+as part of the build process.
+
+Pre-rendered PDF version of the current "master" can be found at
+[User Manual](https://ftp.osmocom.org/docs/latest/osmobsc-usermanual.pdf)
+as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmobsc-vty-reference.pdf)
+
+There also is an
+[Abis reference Manual](https://ftp.osmocom.org/docs/latest/osmobts-abis.pdf)
+describing the OsmoBTS specific A-bis dialect, as well as a [CBSP Reference
+Maunal](https://downloads.osmocom.org/docs/latest/osmobsc-cbsp.pdf)
+describing the level of CBSP conformance.
+
+
+Mailing List
+------------
+
+Discussions related to osmo-bsc are happening on the
+openbsc@lists.osmocom.org mailing list, please see
+<https://lists.osmocom.org/mailman/listinfo/openbsc> for subscription
+options and the list archive.
+
+Please observe the [Osmocom Mailing List
+Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
+when posting.
+
+Contributing
+------------
+
+Our coding standards are described at
+<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
+
+We us a gerrit based patch submission/review process for managing
+contributions. Please see
+<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
+more details
+
+The current patch queue for osmo-bsc can be seen at
+<https://gerrit.osmocom.org/#/q/project:osmo-bsc+status:open>
+
+
+History
+-------
+
+OsmoBSC originated from the OpenBSC project, which started as a minimalistic
+all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
+maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
+IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
+approach to fully independent separate programs as in typical GSM networks.
+
+OsmoBSC was one of the parts split off from the old openbsc.git. Before, it
+worked as a standalone osmo-bsc binary as well as a combination of libbsc and
+libmsc, i.e. the old OsmoNITB. Since the standalone OsmoMSC with a true A
+interface (and IuCS for 3G support) is available, OsmoBSC exists only as a
+separate standalone entity.
+
+OsmoBSC-NAT is a specialized solution to navigating RTP streams through a NAT.
+(Todo: describe in more detail)
diff --git a/README.vty-tests b/README.vty-tests
index 0669ea8e8..dc34916ee 100644
--- a/README.vty-tests
+++ b/README.vty-tests
@@ -1,6 +1,6 @@
To run the configuration parsing and output (VTY) test suite, first install
- git://git.osmocom.org/python/osmo-python-tests
+ https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests
and pass the following configure options here:
diff --git a/TODO-RELEASE b/TODO-RELEASE
index f5d70c2b4..78fcea38f 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -7,5 +7,6 @@
# 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
+ osmo-bsc VTY Timeslot phys_chan_config will now write back with new dynamic timeslot names: 'DYNAMIC/OSMOCOM' instead of 'TCH/F_TCH/H_SDCCH8_PDCH' and 'DYNAMIC/IPACCESS' instead of 'TCH/F_PDCH'
+ osmo-bsc CTRL CTRL commands like 'bts.N.channel-load' will now respond with new dynamic timeslot names: 'DYNAMIC/OSMOCOM' instead of 'TCH/F_TCH/H_SDCCH8_PDCH' and 'DYNAMIC/IPACCESS' instead of 'TCH/F_PDCH'
+ osmo-bsc CTRL,VTY osmo_fsm instance IDs now use new dynamic timeslot names 'DYNAMIC_OSMOCOM' and 'DYNAMIC_IPACCESS'
diff --git a/configure.ac b/configure.ac
index ed9b2cb3f..ebcb839f6 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,30 +36,21 @@ 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)
-
# Enable/disable ipaccess-utils (src/ipacces/)?
AC_ARG_ENABLE([ipaccess-utils], [AS_HELP_STRING([--enable-ipaccess-utils], [Build ipaccess utils: abisip-find, ipaccess-config, ...])],
[osmo_ac_ipa_utils="$enableval"],[osmo_ac_ipa_utils="yes"])
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)
+dnl checks for libraries
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.8.0)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.8.0)
+PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.8.0)
+PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.8.0)
+PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.4.0)
+PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.3.0)
+PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.7.0)
+PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.11.0)
dnl checks for header files
AC_HEADER_STDC
@@ -119,13 +112,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,13 +140,13 @@ 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
- AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
+ AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
fi
fi
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
@@ -241,14 +227,15 @@ AC_OUTPUT(
src/utils/Makefile
tests/Makefile
tests/atlocal
+ tests/abis/Makefile
tests/acc/Makefile
- tests/gsm0408/Makefile
tests/bsc/Makefile
tests/codec_pref/Makefile
- tests/abis/Makefile
- tests/subscr/Makefile
- tests/nanobts_omlattr/Makefile
+ tests/gsm0408/Makefile
tests/handover/Makefile
+ tests/nanobts_omlattr/Makefile
+ tests/paging/Makefile
+ tests/subscr/Makefile
doc/Makefile
doc/examples/Makefile
doc/manuals/Makefile
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index 73f117428..4380f162c 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"
@@ -27,6 +27,19 @@ osmo-build-dep.sh libosmocore "" '--disable-doxygen --enable-gnutls'
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
+# Check for wrong use of osmo_bts_has_feature (OS#5538)
+bts_features_wrong_use="$(grep -r -n 'osmo_bts_has_feature.*->model->features' \
+ | grep -v 'jenkins.sh' \
+ | grep -v 'intentional check against bts model')" || true
+if [ -n "$bts_features_wrong_use" ]; then
+ set +x
+ echo
+ echo "ERROR: Don't use osmo_bts_has_feature with bts->model->features. Use bts->features instead."
+ echo
+ echo "$bts_features_wrong_use"
+ exit 1
+fi
+
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
@@ -39,7 +52,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 +71,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..8f7e936b2 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.11.0
+BuildRequires: pkgconfig(libosmo-netif) >= 1.3.0
+BuildRequires: pkgconfig(libosmo-sigtran) >= 1.7.0
+BuildRequires: pkgconfig(libosmoabis) >= 1.4.0
+BuildRequires: pkgconfig(libosmocore) >= 1.8.0
+BuildRequires: pkgconfig(libosmoctrl) >= 1.8.0
+BuildRequires: pkgconfig(libosmogb) >= 1.8.0
+BuildRequires: pkgconfig(libosmogsm) >= 1.8.0
+BuildRequires: pkgconfig(libosmovty) >= 1.8.0
BuildRequires: pkgconfig(talloc)
%{?systemd_requires}
@@ -117,12 +116,14 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%files
%license COPYING
-%doc AUTHORS README
+%doc AUTHORS README.md
%{_bindir}/osmo-bsc
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-bsc
%{_docdir}/%{name}/examples/osmo-bsc/osmo-bsc.cfg
%{_docdir}/%{name}/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg
+%{_docdir}/%{name}/examples/osmo-bsc/osmo-bsc-4trx-fh.confmerge
+%{_docdir}/%{name}/examples/osmo-bsc/osmo-bsc-4trx.cfg
%{_docdir}/%{name}/examples/osmo-bsc/osmo-bsc-minimal.cfg
%dir %{_docdir}/%{name}/examples/osmo-bsc/ericsson
%dir %{_docdir}/%{name}/examples/osmo-bsc/nokia
diff --git a/contrib/systemd/osmo-bsc.service b/contrib/systemd/osmo-bsc.service
index 67dcd7ed1..0f8819b98 100644
--- a/contrib/systemd/osmo-bsc.service
+++ b/contrib/systemd/osmo-bsc.service
@@ -5,6 +5,8 @@ Wants=osmo-mgw.service
[Service]
Type=simple
Restart=always
+StateDirectory=osmocom
+WorkingDirectory=%S/osmocom
ExecStart=/usr/bin/osmo-bsc -c /etc/osmocom/osmo-bsc.cfg -s
RestartSec=2
diff --git a/debian/changelog b/debian/changelog
index b65bc0dd8..6b53523bb 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,1398 @@
+osmo-bsc (1.10.0) unstable; urgency=medium
+
+ [ Vadim Yanitskiy ]
+ * fix uninitialized err pointer passed to osmo_bssap_le_dec()
+ * lchan_select: prepare a list of timeslots once, iterate over it
+ * lchan_select: allow different alloc order for assignment and handover
+ * lchan_select: implement dynamic selection mode for assignment
+ * lchan_select: fix 'chan_alloc_reverse' may be used uninitialized
+ * osmo-bsc/Makefile.am: fix undefined reference to symbol pow()
+ * .gitignore: remove non-existing files
+ * call osmo_timer_del() unconditionally, without osmo_timer_pending()
+ * configure.ac: do not require unused dlopen
+ * power_control: add CTRL command for sending default params
+ * vty: fix description of 'nokia_site bts-reset-timer'
+ * bts_vty: fix wrong description used for C/I threshold values
+ * fix bsc_vty_go_parent(): add missing case for MGW_NODE
+ * bsc_vty: use gsmnet_from_vty() everywhere, not vty->index
+
+ [ Pau Espin Pedrol ]
+ * cbsp: Change log level ERROR->INFO on CBSP tx and link down
+ * smscb: Tx Failure and Restart Ind using CGI as cellID
+ * smscb: Tx Failure and Restart Ind for each Bcast Msg Type
+ * tests/ctrl: Avoid creating logfile
+ * doc: Fix typo in several diagrams
+ * Use libosmocore available API to get value_list
+ * split lchan specific defines and code to its own file
+ * lchan: Move init logic to a specific function
+ * cosmetic: Clean initiating whitespace
+ * Add Osmux support on the Abis-side data plane
+ * oml: Integrate signal S_NM_IPACC_SET_ATTR_ACK inside S_NM_IPACC_ACK
+ * vty: Allow setting LAC as hexadecimal value
+ * doc: Document use of Osmux in IPA Abis against OsmoBTS
+ * oml: Delay Tx of OPSTART(BBTRANSC) after rx of RSL CONNECT ACK
+ * ipaccess-config: Initiate missing IPA osmo_link
+ * ipaccess-config: Initialize RSL ts driver fd to proper value
+ * ipaccess-config: use available API to set e1inp_line_ops
+ * ipaccess-config: Exit program with error if OML link is dropped
+ * mgcp: Set up Osmux only when AMR codec is selected
+ * ipacces-config: override gsm_bts_check_cfg() to void checking unset bts configuration
+ * ipaccess: Remove unused stub
+ * ipaccess-config: Fix writing pcap output to fd=0 (stdin)
+ * Drop Osmux call setup if BTS didn't provide a remote CID
+ * cosmetic: vty: Fix formatting of if-else brackets
+ * vty: Print Osmux CID on lchans using Osmux
+ * cosmetic: Fix typo in comment
+ * Rearrange ctrl interface code
+ * ctrl: Introduce hopping-arfcn-{add,del} commands
+ * vty: 'hopping arfcn add': succeed if adding arfcn already in set
+ * cosmetic: Fix whitespace indentation
+ * vty: Fix indentation in osmux cmd during write-config output
+ * Introduce support for MGW-pinning per BTS
+ * vty: Move all MSC_NODE commands to be together
+ * gscon_ensure_mgw_endpoint(): Set mgw_enpoint ptr to NULL not needed
+ * cosmetic: Fix indentation whitespace
+ * cosmetic: Fix typo in comment
+ * cosmetic: Fix indentation whitespace
+ * vty: Make use of new mgcp_client_pool_config_write() API
+ * Update examples to use mgw pooling VTY commands
+ * Use new mgcp-client VTY commands under mgw node
+ * doc: Generalize mgwpool.adoc and move BSC-specific sections to runnning.adoc
+ * doc: Use mgwpool.adoc from osmo-gsm-manuals.git
+ * doc: running.adoc: Fix typo in MGW pinning section
+ * sccplite: Use mgwpool config to set up socket forwarding IPA-MGCP from MSC to MGW
+ * vty: Fix and deprecate typo in cmd 'amr-payload bandwith-efficient'
+ * paging: Avoid repeated paging req lookup on BTS receiving paging resp
+ * paging: Get rid of unneeded count returned
+ * paging: Store list of gsm_paging_request in bsc_subscr
+ * paging: Use bsub->active_paging_requests to allow early loop termination adding paging_req
+ * lcs: Fix passing NULL bsc_subscr to paging_request_cancel()
+ * paging: Use bsub->active_paging_requests to optimize cancelling based on reason
+ * paging: Rename stat t3113 -> paging:t3113
+ * paging: Introduce BTS stat paging:request_queue_length
+ * paging: Introduce BTS stat paging:available_slots
+ * paging: constify bts param in some functions
+ * subscriber: constify bsub func param
+ * subscriber: Add comment documenting struct type hold in list
+ * paging: Fix regression stopping active requests on unanswered BTS
+ * paging: paging: Drop unneeded extra param in paging_remove_request()
+ * vty: Fix lost 'no timer-dynamic T3113' config when writing current config
+ * paging: Introduce VTY configurable X3113 (Maximum Paging Request Transmit Delay Threshold)
+ * paging: Fix wrong count of reqs_before if queue only contains retransmissions
+ * paging: Split paging queue into 2 queues: initial and retrans
+ * paging: Replace reqs waiting for retransmission with new incoming inital req if queue is full
+ * paging: Optimize retrieving number of request per paging group
+ * vty: Write amount of timeouts elapsed for a paging request
+ * bsc: Fix crash if PagingResponse with invalid MobileIdentity is received
+ * bsc_subscriber: Allow creating subscribers identified by IMEI
+ * bsc_compl_l3(): Update documentation regarding no bsc_subscr in conn
+
+ [ Philipp Maier ]
+ * bsc_subscr_conn_fsm: fix use after free
+ * abis_om2000: fix missing signal
+ * signal.h: make om2k mo const
+ * abis_om2000: move switch-case to function
+ * abis_om2000: constify mo in mo2obj
+ * abis_om2000: update_op_state() does not send signal
+ * abis_om2000: om2k_trx_s_done_onenter() does not send signal
+ * abis_om2000: duplicate nmstate of bb_trxc mo to trx mo
+ * bts_trx_vty: prefix bb_trxc mo with [Virtual] for ericsson BTS
+ * pcu_sock: fix memleak
+ * pcu_sock: check size of primitive
+ * pcu_sock: add some guard space to message buffer
+ * abis_rsl: delete CHAN RQD from queue when rach ind was sent
+ * abis_rsl: be more clear about vendor specif RSL extension
+ * pcuif_proto: cosmetic: add constant PCU_IF_NUM_NSVC and replace magic numbers
+ * pcuif_proto: use define constant to specify nax number of trx
+ * abis_rsl: add spec ref for frame number calculation
+ * abis_rsl: show full request reference in log
+ * abis_rsl: when doing packet access, log t1,t3,t2 and fn
+ * pcu_sock: drop unused function prototype
+ * pcu_sock: drop unused variable pcu_direct
+ * abis_rsl: fix frame number calculation
+ * abis_rsl: fix sourcecode formatting
+ * pcu_sock: guard against too many TRX
+ * pcu_sock: move code that fills in trx info to helper function
+ * pcu_sock: check trx mo state
+ * pcu_sock: fix sourcecode formatting
+ * pcu_sock: clean up logging in info_ind_fill_trx
+ * abis_om2000: send TS_EV_OML_READY when TRX is fully done
+ * pcu_sock: rework check logic for ts
+ * timeslot_fsm: Warn in case Ercisson RBS uses static PDCH
+ * pcuif_proto: add indication to communicate E1 parameters
+ * pcu_sock: fix endianess when populating gsm_pcu_if_info_ind
+ * pcu_sock: Also fill in BSIC in gsm_pcu_if_info_ind
+ * pcu_sock: remove unecessary OSMO_ASSERT()s
+ * pcu_sock: rework log output
+ * timeslot_fsm: remove duplicate TS_ST_BORKEN from out_state_mask
+ * pcuif_proto: move gsm_pcu_if_e1_ccu_ind into right place
+
+ [ Harald Welte ]
+ * bts_nokia_site: Implement channel config for CBCH
+ * Support building with -Werror=strict-prototypes / -Werror=old-style-definition
+ * update very outdated vty copyright statement
+
+ [ Max ]
+ * BSSMAP: add assert to reset resending
+ * Set working directory in systemd service file
+ * ctrl: take both address and port from vty config
+ * SI: use defined constant instead of magic number
+ * SI: set type 10 length properly
+ * HO: use defined constant instead of magic number
+
+ [ Neels Hofmeyr ]
+ * gscon_pre_term: properly clear mgw ep
+ * fix Speech Codec cfg in BSSMAP Assignment Complete
+ * doc: add mscpool-attach.dot
+
+ [ Oliver Smith ]
+ * tests: add test_meas_rep_multi_band.ho_vty
+ * gsm48_parse_meas_rep: fix parsing multi-band list
+ * Cosmetic: fix desc of neighbor_ident_add_neighbor
+ * Cosmetic: lchan_fsm: drop obsolete comment
+ * lchan_fsm: fix lchan_fsm_on_error size
+ * Cosmetic: fix spaces around timers
+
+ [ Alexander Couzens ]
+ * bts_ipaccess_nanobts: remove unused assignment
+ * gsm_data.h: replace white spaces with tabs
+ * Add BTS setup ramping to prevent BSC overloading
+ * nm_rcarrier_fsm: fix indention of ENABLED state
+ * bts: ipa/osmo-bts/sysmobts: MO: add support for the second NSVC
+
+ [ arehbein ]
+ * osmo-bsc: Transition to use of 'telnet_init_default'
+ * vty: Add support for Ny1 configuration
+ * vty: Add check against sensible default value for Ny1
+ * osmo-bsc: Log error regarding BTS number explicitly
+ * osmo-bsc: Fix 'apply-config-file' CTRL command
+ * bsc_ctrl_commands: Add GET for bts neighbor-list (local bts numbers)
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 17:21:10 +0100
+
+osmo-bsc (1.9.0) unstable; urgency=medium
+
+ [ Philipp Maier ]
+ * abis_nm: fix typo
+ * abis_nm: use struct sdp_firmware from libosmocore
+ * bts-examples: add example for E1 connected BTS
+ * abis_nm: actively block BTSs with invalid configuration
+ * bts: add missing return -EINVAL statements
+ * bts: fix sourcecode formatting (excess whitespace)
+ * nm_bts_fsm: fix sourcecode formatting
+ * bssmap_reset: make T4 user configurable
+
+ [ Oliver Smith ]
+ * bsc_subscr_conn_fsm: fix crash if !conn
+ * Cosmetic: osmo-bsc/osmo_bsc_bssap: fix formatting
+ * stats: add bsc.paging:expired
+ * treewide: remove FSF address
+ * contrib/osmo-bsc.spec.in: add new config files
+ * contrib/osmo-bsc.spec.in: fix 4trx example paths
+ * bts_model_osmobts_init: order feats alphabetically
+ * Cosmetic: bts_model_osmobts_init: update comment
+ * abis_nm: don't compare assumed/reported features
+ * gsm_data: add gsm_set_bts_model
+ * Always use reported features if available
+ * contrib/jenkins.sh: check osmo_bts_has_feature use
+ * abis_nm_ipaccess_rsl_connect: initialize ia
+ * bts_model_*_start: move set_feature calls to _init
+ * abis_nm: add bts model features to reported ones
+ * Check VTY config against features reported by BTS
+ * abis_nm: run gsm_bts_check_cfg during oml bring up
+ * Cosmetic: bsc_vty: tweak msc pooling strings
+
+ [ Neels Hofmeyr ]
+ * hodec2: fix segv for inter-BSC ho target
+ * dbg log: abis_rsl print_meas_rep(): clarify rxlev
+ * fix chreq:* counters: typos in chreq:successful_* constants
+ * dbg log: also log assignment counters on BTS level
+ * fix assignment success counters: count *before* cleanup of fsm state
+ * log: always include timeouts in FSM transition logging
+ * lchan/gscon: always clear both cross ref pointers
+ * lcs: fix bsc_subscr use_count leak
+ * fix gscon clear 1/n: store clear cause in gscon
+ * fix gscon clear 2/n: proper state transition to ST_CLEARING
+ * fix gscon clear 3/n: separate state for SCCP RLSD
+ * gscon clear: without SCCP conn, no need to wait for RLSD timeout
+ * fix two comments in chan_counts.h
+ * rename RSL_ENC_ALG_A5 to ALG_A5_NR_TO_RSL, clarify
+ * fix inter-BSC-in handover encryption
+ * cosmetics around select_best_cipher()
+ * inter-BSC HO in: add speech IEs only on speech mode lchans
+ * inter-BSC HO in: add Speech Codec (Chosen) IE to HO Req Ack
+ * inter-BSC HO in: add Codec List (BSS Supported) IE to HO Req Ack
+ * tweak logl to ERROR for invalid initial BSSMAP
+ * silence bogus error: event not permitted: READY_TO_SWITCH_RTP
+ * add missing counter increment for Perform Location Request
+ * add counter for inter-BSC incoming Handover Request
+ * support "empty" SCCP N-Connect from MSC
+ * tweak error msg: s/inter-BSC MT/inter-BSC incoming
+ * inter-BSC incoming HO: store Codec List (MSC Preferred)
+ * fix extraneous newlines in ho_fail() invocations
+ * fix typo in name of BSS_MAP_MSG_ASSIGNMENT_RQST
+ * hodec2: show reported ho oscillation from bad rxqual
+ * hodec2: add penalty-time low-rxqual-ho
+ * assignment_fsm: always mark MGCP ci as completed
+ * assignment_fsm: always update RTP info
+ * hodec2: apply penalty_low_rxqual_as only on assignment
+ * emerg call: fix RR release cause for pre-emption
+ * emerg call: tweak log, comments
+ * emerg call deny: fix RR release cause
+ * emerg call deny: log on LOGL_ERROR
+ * emerg call: send BSSMAP Clear Req cause as preemption
+ * fix fallout from: 'stats: new trackers for lchan life duration'
+ * do not BSSMAP Clear on lchan rel when LCS is still ongoing
+ * fix rare segfault in MGCP client handling
+ * drop log from ts_is_usable()
+ * code cleanup for all_allocated timers, no functional change
+ * fix performance for chan_counts and all_allocated stats
+
+ [ Pau Espin Pedrol ]
+ * Disable C/I based MS Power Control Loop by default
+ * Drop unneeded ax_check_compile_flag.m4
+ * Fix DLCI CC bits transmitted in SAPI "n" REJECT
+ * bts: Add explicit switch case for Cell Id SAI
+ * doc: bsc.adoc: Update timer info from code (gsm_network_T_defs)
+ * tests: nanobts_omlattr_test: Use msgb_eq_data_print() helper
+ * ipa oml: Fix encoding of T3105
+ * bts_vty.c: Fix typo in comment
+ * SI13: Always send ext_info
+ * SI13: Make sure egprs_supported field is always updated
+ * SI13: Avoid enabling use_egprs_p_ch_req if egprs not supported
+ * system_information: Move all si13 specific val update to generate_si13()
+ * SI13: Get rid of si13_default
+ * bsc_vty: Add missing header
+ * bts: Simplify bts->paging initialization
+ * tests/bsc/Makefile.am: remove duplicate CFLAGS
+ * Move struct gsm_bts_paging_state to paging.h
+ * paging: Use define available in libosmogsm
+ * rsl_rx_ccch_load: Use UINT16_MAX instead of -1
+ * paging: Avoid setting up credit_timer every time
+ * paging: Use llist_first_entry() macro
+ * paging: Log skip paging due to not enough free channels
+ * paging: Submit up to 20 paging requests in a single work iteration
+ * paging: Prioritize requests for new subscribers over retransmitions
+ * cosmetic:: Document TLVs in nanobts_attr_bts_get()
+ * Rename functions generating OML SetAttr messages
+ * bts: Use uint8_t instead of int for ccch_load_ind_thresh
+ * Introduce VTY command 'ccch load-indication-period <0-255>'
+ * bts: Make sure paging timers are deleted when struct gsm_bts is freed
+ * tests: acc_test: fix typo in talloc ctx name
+ * tests: acc_test: Get rid on unrelated logs in expect file
+ * paging: Estimate available_slots based on BTS config when no CCCH Load Ind received
+ * paging: Rework timer lifecycle logic
+ * paging: Check C0 RSL link instead of OML link
+ * paging: Decouple retransmit period from regular worker interval
+ * paging: Improve calculate_timer_3113()
+ * paging: Improve logging
+ * paging: Increase T3113 based on paging group load in BSC queue
+ * paging: Early stop work_timer when paging queue becomes empty
+ * paging: Recalculate work timer if waiting for retrans
+ * paging: Document 'ccch_load_ind_period * 2' value
+ * tests: Order tests alphabetically
+ * tests/acc: Remove unused var
+ * abis_rsl.c: Drop unused function
+ * abis_rsl: Use proper struct in rsl_paging_cmd
+ * bts: Properly free ctr/stat when bts object is freed
+ * paging: Flush pending paging requests when bts obj freed
+ * paging: Take into account extra delay of all paging groups in BSC queue
+ * tests: Introduce paging_test
+ * paging: Remove TODO comment
+ * Drop duplicate function helper
+ * Remove commented out code
+ * abis_nm: Use proper define for avail state 0xff
+ * ipaccess-config: Use proper define for nm availability value
+ * abis_nm: Merge signals S_NM_STATECHG_ADM and S_NM_STATECHG_OPER
+ * cosmetic: Fix open brace in same line as func definition
+ * constify state pointers of struct gsm_nm_state
+ * acc: Fix erratic ramping behavior when several BTS configured
+ * nm_statechg_signal_data: Convert state ptr to data
+ * nm_is_running(): Drop duplicate check
+ * bts_ipa: Send NM_EV_OML_DOWN following object tree in order
+ * paging: Improve logging
+ * paging: Fix recalculate work timer if waiting for retrans
+ * paging: Avoid unnecessary immediate polling in mainloop
+ * Revert "fix fallout from: 'stats: new trackers for lchan life duration'"
+ * Revert "stats: new trackers for lchan life duration"
+ * Update current NM object state before signalling S_NM_STATECHG
+ * Introduce new signal S_NM_RUNNING_CHG and implement it for rcarrier,bbtransc
+ * paging: start/stop credit_timer based on C0 running
+ * acc: Simplify start/stop by using new signal S_NM_RUNNING_CHG
+ * nm_*_fsm: Remove comment no longer applying
+ * nm_rcarrier_fsm: Trigger S_NM_RUNNING_CHG when Admin st changes in op=Enabled
+ * Move all SMSCB/CBC vty code to its own file
+ * smscb: Base cell operational life cycle on CBCH being operative
+ * smscb: Tx CBSP FAILURE/RESTART for specific cell when it becomes (un)operational
+ * cbsp: Avoid encoding CBSP message if link is down
+
+ [ Vadim Yanitskiy ]
+ * ipaccess-config: improve readability of printed attribute response
+ * ipaccess-config: request and print NM_ATT_IPACC_NV_FLAGS
+ * system_information: fix unused 'mask' parameter in list_arfcn()
+ * tests/gsm0408: add testing coverage for generate_cell_chan_list()
+ * bitvec2freq_list(): determine empty set by checking the ARFCN count
+ * bitvec2freq_list(): fix handling of E-GSM ARFCNs
+ * Fix description of BTS_CTR_BTS_RSL_FAIL: s/OML/RSL/
+ * fix inp_sig_cb(): dispatch TS_EV_OML_DOWN to all transceivers
+ * system_information: use is_ipaccess_bts() helper
+ * system_information: fix DCS/PCS band indicator in generate_si6()
+ * gsm48_send_ho_cmd(): this function is not used, remove it
+ * gsm48_make_ho_cmd(): cosmetic: use existing BTS pointer
+ * gsm48_make_ho_cmd(): make 'struct gsm_lchan' pointer const
+ * gsm48_make_ho_cmd(): optionally add Cipher Mode Setting IE
+ * gsm48_make_ho_cmd(): optionally add Synchronization Indication IE
+ * fixup: gsm48_make_ho_cmd(): optionally add Synchronization Indication IE
+ * gsm_bts_trx_set_system_infos(): cosmetic: improve readability
+ * System Information Type 3: allow updating T3212 at run-time
+ * parse_bssmap_perf_loc_req(): make 'struct tlv_p_entry' pointer const
+ * BSSMAP LE: fix handling of LCS Client Type IE
+ * BSSMAP LE: handle optional LCS {Client Type, QoS} IEs
+ * struct gsm_encr: store alg_id in human-readable format
+ * cbch_scheduler: cosmetic: s/bts_cbch_timer/bts_cbch_timer_cb/
+ * fix gsm_bts_get_cbch(): CBCH can be allocated on Cn
+ * abis_rsl: always check return value of rsl_tlv_parse()
+ * doc/examples: avoid using deprecated configuration commands
+ * doc/examples: add a multi-trx config config example
+ * doc/examples: add a confmerge file with example hopping parameters
+ * power_ctrl_params_def_reset(): set .ctrl_interval for both UL/DL
+ * Move power control related definitions to power_control.h
+ * VTY: clarify help for the Adaptive Multi Rate settings
+ * bts: gsm_bts_alloc(): rework default multi-rate configuration
+ * bts: gsm_bts_alloc(): use reasonable multi-rate config defaults
+ * tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
+ * abis_rsl: fix NULL pointer dereference in abis_rsl_rx_rll()
+ * gsm_data: use llist_for_each_entry() in gsm_bts_by_lac()
+ * bssap: always check return value of tlv_parse()
+ * abis_nm: always check return value of tlv_parse()
+ * utils/meas_db: fix -Wunused-variable warnings
+ * utils/meas_pcap2db: fix -Wpointer-sign in pcap_cb()
+ * smscb: fix meaningless condition in etws_primary_to_bts()
+ * bsc_vty: use llist_for_each_entry in lchan_act_all_cmd
+ * fix gsm_lchan_and_pchan2chan_nr(): log proper lchan_nr value
+ * VTY: cosmetic: define and use CHAN_ALLOC_{CMD,DESC}
+ * tests/handover_cfg.vty: use more precise regex for forbidden lines
+ * bts_chan_load(): also calculate per-TRX channel load
+ * doc/manuals: document channel allocation parameters
+ * gsm_04_08_rr: ensure lchan info is present in all logging messages
+ * VTY: fix wrong enum value s/ACTIVATE_FOR_VTY/ASSIGN_FOR_VTY/
+ * ipaccess-config: check value returned by abis_nm_tlv_parse()
+
+ [ Harald Welte ]
+ * om2000: Fix memory leak in OM2000 message handling
+ * om2000: Don't print "should not generate any message" on CAL_TIME_REQ
+ * om2000: Don't print "should not generate any message" on FAULT_REP
+ * SMSCB: Preserve padding at end of page in CBSP -> RSL conversion
+ * CBSP: implement MESSAGE STATUS QUERY
+ * abis_rsl: Ensure message length is sufficient for respective header
+ * smscb: Populate "Number of Broadcasts Completed" to KILL COMPLETE
+ * abis_rsl: Fix typo in log message
+ * Handle unknown rllr_ind enum values in rll_ind_cb()
+ * lchan_fsm: Ignore other SAPIs of RLL_REL_IND for SAPI=0 is received
+ * lchan_fsm.c: Fix misleading comment
+ * Fix compile errors on #warning with '-Wall' on gcc-11.2
+ * smscb: Don't include extraneous IEs in CBSP KILL COMPLETE / FAILURE
+ * smscb: Store ETWS input state from CBSP
+ * smscb: Always start ETWS timer even in cells without ETWS support
+ * cbsp: Implement KILL for Emergency Broadcast
+ * cbsp: Reject CBSP WRITE for emergency if emergency already active
+ * bts.c: prevent signed integer overflow in depends_on code
+ * paging: Avoid queueing more than 60 second estimated requests
+ * paging: Implement upper bound of 60s for dynamic T3113
+ * Add stat_item for per-bts [dynamic] T3113 timer
+ * smscb: Send ETWS PN to BTS if ETWS active before BTS connects
+ * smscb: "Warning Security Information is always present in ETWS
+ * Add new Manual "OsmoBSC CBSP Protocol Specification"
+ * convert README to README.md; expand on all fronts
+ * update git URLs (git -> https; gitea)
+
+ [ Michael Iedema ]
+ * stats: new trackers for lchan life duration
+ * stats: new trackers for lchan life duration (v2)
+ * Expand VTY option which controls use of TCH for signalling
+ * stats: track TCH/SDCCH lchans reaching fully-established state
+ * logs: also record which BTS is signaling a SAPI REJECT
+
+ [ Keith ]
+ * Improve parsing of om2000 fault reports
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 29 Jun 2022 11:18:10 +0200
+
+osmo-bsc (1.8.0) unstable; urgency=medium
+
+ [ Harald Welte ]
+ * remove obsolete dependency on libosmo-sccp
+ * manual: Include QoS chapter and add osmo-bsc specific example
+
+ [ Javi ]
+ * Add vty command for Ericsson RBS2000 sync
+ * Add command to enable RX diversity to RBS2000
+
+ [ Oliver Smith ]
+ * stats: T3122 related: num_values 16 -> 60
+ * src/utils/meas_vis.c: fix bs_power -> bs_power_db
+ * handover_test: fix ts_str may be uninitialized
+ * bsc_ctrl_commands: fix uninitialized value (mode)
+ * Revert "Turn some compiler warnings into errors"
+
+ [ Neels Hofmeyr ]
+ * refactor handover penalty timers
+ * drop neighbor_ident_test.c
+ * fix/refactor neighbor config
+ * debug log, lchan_fsm: explain leaving wait_rll_rtp_establish state
+ * fixup for neighbor config for coverity
+ * drop unused func decl rsl_lchan_mark_broken()
+ * drop unused gsm_bts_trx->description
+ * deprecation: use osmo_bts_features_*()
+ * fix wrong ARFCNs in local-cell neighbor config
+ * update neighbor ARFCNs on startup and config changes
+ * manual: Location Services: clarify BSC side address
+ * abis_nm_ipaccess_rsl_connect(): use msgb to compose attr
+ * Lb: stop RESET FSM when sccp_user is unbound
+ * Lb: RESET FSM: never send sccp_user == NULL
+ * Lb: add missing X12 timer configurability
+ * Lb: make sure we never have missing timer configurability
+ * gsm_lchan_name_compute with ctx
+ * log: drop duplicate logging in ts_setup_lchans()
+ * comment: tweak pchan_subslots() description
+ * lchan_release(): do not release UNUSED lchan
+ * lchan_fsm: mode modify: fix missing timeouts and error transitions
+ * fix test_gsm48_multirate_config: dump the complete AMR lv buffer
+ * test_gsm48_multirate_config: rather keep 4x amr_mode
+ * assignment_fsm: fix failure log message for lchan unavailable
+ * select_codecs(): do not confuse bool 'true' with integer value 1
+ * gsm48_send_rr_ass_cmd(): rename dest_lchan to current_lchan
+ * gsm48_send_rr_ass_cmd(): rename lchan to new_lchan
+ * log: show src file,line of lchan_set_last_error
+ * add missing arg braces in LOG_LCHAN macro
+ * gscon MGCP: properly skip redundant MDCX towards MSC
+ * cosmetic: rename FOR_* to ACTIVATE_FOR_*
+ * cosmetic: rename lchan_activate_mode to lchan_activate_for
+ * lchan and assignment FSMs: make Channel Mode Modify more sane
+ * assignment_fsm: tweak error log msg for mixed modes
+ * handover_test: ack release only when lchan is still waiting
+ * hodec2: remove code dup of rxlev averaging
+ * handover_test: add bspower to meas-rep cmd
+ * add test_bs_power.ho_vty to show BS Power HO oscillation
+ * assignment_fsm: send BSSMAP response only after Assignment Request
+ * cosmetic scoping in reuse_existing_lchan()
+ * potential segfault: vty chan act: do not set AMR bits for EFR
+ * make sure channel mode and s15_s0 are updated only after an ACK
+ * remove special case from assignment_count_result()
+ * eliminate lchan->rsl_cmode
+ * move lchan->csd_mode into channel_mode_and_rate
+ * AMR config cleanup step 1: split lchan_mr_config()
+ * AMR config cleanup step 2: filter modes also for VTY
+ * AMR config cleanup step 3: generate AMR LV on msg composition
+ * handover dot charts: fix wrong transitions regarding MGW
+ * handover_test: fix naming/wording: 'handover-req' should be 'handover-cmd'
+ * assignment_fsm: tweak state transitions (prep for reassignment)
+ * assignment_fsm: allow assignment to a specific lchan
+ * vty: actually trigger Assignment for 'assignment', not HO
+ * hodec 2: do intra-cell congestion resolution by Assignment
+ * RSL link: explicitly select rsl_link based on lchan
+ * add chan_mode_to_chan_type()
+ * gsm48_lchan2chan_desc(): expose TSC as param
+ * allow explixit TSC Set and TSC on chan activ / modif / assignment
+ * VTY: dump TSC Set and TSC for each timeslot
+ * add fields to reflect nr of lchans in ts struct
+ * replace ts_*_for_each_lchan() with ts_for_n_lchans()
+ * ensure chan_mode comparisons in non-VAMOS mode
+ * lchan_fsm: introduce lchan.modify.ch_mode_rate to allow tweaking
+ * Drop duplicated arfcn_range_encode.c available in libosmocore
+ * fixup for Mode Modify TSC
+ * fix rc handling in channel_mode_from_lchan()
+ * drop unused func decl gsm_lchan_as_pchan2chan_nr()
+ * handover: apply meas report BS Power to RXLEV, fix ho oscillation
+ * change bs_power to bs_power_db
+ * meas rep logging: replace a dozen DEBUGPC() with one DEBUGP()
+ * meas rep logging: use log_check_level() to skip a logging loop
+ * fixup: pass tsc = -1 for previous default training sequence code
+ * hodec2: add handover_test cases for upgrade of TCH/H -> TCH/F
+ * cosmetic prep: hodec2: move is_upgrade_to_tchf() further up
+ * hodec2: fix is_upgrade_to_tchf() for requirement A
+ * allow mode modify when RTP stream is active
+ * implement Channel Mode Modify to VAMOS mode
+ * vty: add lchan modify '(vamos|non-vamos)' command
+ * add lchan->vamos.is_secondary flag
+ * vty-test: osmo-bsc.vty: test doc of lchan activate cmd
+ * lchan_fsm: introduce lchan.activate.ch_mode_rate to allow tweaking
+ * add missing AMR config for RTP activation after mode modify
+ * implement CHANnel ACTIVate to VAMOS mode
+ * RR Assignment for VAMOS: send TSC Set
+ * add VAMOS secondary lchans to timeslot struct
+ * update the lchan name to always reflect VAMOS shadowness
+ * rsl_lchan_lookup(): turn cbits if-cascade into a switch()
+ * rsl_lchan_lookup(): add comment explaining ts_is_capable_of_pchan()
+ * RSL chan_nr: replace OSMO_ASSERT with error handling
+ * RSL: rx and tx VAMOS Channel Number cbits for VAMOS lchans
+ * VTY: 'show lchan': show that lchan is in VAMOS mode
+ * VTY: add 'vamos-subslot' to activate a secondary lchan
+ * VTY: add lchan re-assignment command
+ * log: assignment_fsm: drop newline from assignment_fail
+ * log: assignment_fsm: tweak err msg for incompatible chan
+ * clarify bts_chan_load
+ * get_any_lchan(): reduce minor code dup
+ * cosmetic loop simplification in gsm48_multirate_config()
+ * RSL: set default TEI according to TRX number
+ * lchan_fsm: lchan_fail() strings should not have a terminating newline
+ * gsm_data.h: add comments about immutable activ/modif/assign request info
+ * hodec2: implement upgrade TCH/H -> TCH/F (without AFS bias)
+ * fixup for vamos: fix wrong cbits in rsl_lchan_lookup()
+ * vty: reassign: add missing check for valid target lchan
+ * hodec2: don't apply AFS bias to same-cell lchans
+ * rsl_data_request() check lchan pointer before access
+ * rsl_lchan_lookup: drop redundant condition
+ * rewire build_encr_info() to return errors
+ * dissolve gsm0808_cipher_mode() into bssmap_handle_cipher_mode()
+ * implement A5/4 in Ciphering Mode procedure
+ * support A5/4 in inter-BSC Handover
+ * use osmo_select_shutdown to get rid of SIGTERM sleep
+ * vty: allow 5 encryption algo entries
+ * hodec2: add test case showing low rxlev tch/h<->tch/f oscillation
+ * hodec2: add test case showing low rxqual tch/h<->tch/f oscillation
+ * handover_decision_2.c: add is_low_rxlev()
+ * hodec2: fix low rxlev tch/h<->tch/f oscillation
+ * handover_decision_2.c: add current_rxqual()
+ * hodec2: fix low rxqual tch/h<->tch/f oscillation
+ * vty: fix doc: default value for 'nri bitlen'
+ * vty: add vty doc test for 'nri null' commands
+ * hodec2: [1/2] implement automatic choice between FULL and SUBSET measurements
+ * hodec2: [2/2] implement automatic choice between FULL and SUBSET measurements
+ * hodec1: use same automatic FULL/SUBSET choice as in hodec2
+ * lchan: call reset() upon alloc
+ * RES IND: parse msg and store interference levels in lchans
+ * RES IND: add VTY: bts / channel allocator avoid-interference (0|1)
+ * RES IND: add test_resource_indication.ho_vty
+ * RES IND: pick lchan with least interference
+ * ensure trigger_ho() returns zero only when HO or AS was indeed triggered
+ * handover_test: also show when an lchan is busy
+ * switch handover penalty timers to CLOCK_MONOTONIC
+ * handover_test: add fake-time 'wait'
+ * test_penalty_timer.ho_vty: show lchan recovery
+ * handover tests: test passing of penalty timeout
+ * hodec2: add low-rxqual-assignment penalty timer (1/2)
+ * hodec2: add low-rxqual-assignment penalty timer (2/2)
+ * separate 'interference-meas level-bounds' cfg and used
+ * vty 'interference-meas level-bounds': explain duality in ordering
+ * add test_dyn_ts_favor_static_ts_as_target.ho_vty
+ * extend test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty
+ * RES IND: allow empty Resource Information IE
+ * RES IND: tweak error code on missing Resource Information IE
+ * fix CM Re-Establishment Request: allocate new A conn
+ * log: fix missing newline in lchan_select.c
+ * debug log: indicate change of primary lchan on a conn
+ * vty: lchan deact: allow omitting the lchan type arg
+ * vty: improve err msg for invalid subslot nr
+ * coverity: quench null deref warning in gscon_change_primary_lchan()
+ * lchan_fsm_post_activ_ack(): return upon release
+ * introduce gsm48_lchan_and_pchan2chan_desc()
+ * error log: improve lchan lookup error msg
+ * early IMM ASS 1/n: add vty config option
+ * early IMM ASS 2/n: implement 'pre-chan-ack'
+ * early IMM ASS 3/n: implement 'pre-ts-ack'
+ * stat_item desc: add explicit indexes for clarity
+ * vty: add "msc N bssmap reset" command
+ * move BSC level stats and rate counters to new bsc_stats.[hc]
+ * add stat items bsc.0.num_msc:connected, .num_msc:total
+ * add stat_items for BTS and TRX connection stati
+ * fixup for Early IMM ASS: use proper TSC
+ * add CTRL 'rf_states' and 'bts.N.rf_states'
+ * add CTRL bts.N.trx.M.rf_locked (RW)
+ * fixup comments for 'rf_states' CTRL command
+ * fix comment in lchan_fsm.c: s/modification/activation
+ * fix TSC / TSC Set used for Handover
+ * tsc fixup: tweak condition for coverity
+ * drop obsolete comment
+ * stat: add bts.N.num_trx:total
+ * stat: add bts.N.num_trx:rsl_connected
+ * constify lchan_state_is()
+ * add chreq:successful_<reason> rate counters
+ * drop unused gsm48_tx_mm_serv_ack()
+ * for linter: s/while(0)/while (0)
+ * cosmetic tweaks on handover counting code
+ * tweak intra-bsc ho counter descriptions
+ * implement incoming_intra_bsc_ho:* rate counters
+ * refactor lchan counting
+ * add time_cc API: cumlative counter for time, reported as rate_ctr
+ * implement all_allocated:{sdcch,tch} rate counters
+ * implement all_allocated:{static_sdcch,static_tch} rate counters
+ * implement bts.N.cm_serv_rej:<cause> rate counters
+ * move time_cc to libosmocore osmo_time_cc
+
+ [ Keith ]
+ * Ignore CHANnel ReQuireD with Access Delay IE > 63
+ * Fix MEAS parsing, as Ericsson RBS reports TA shifted by 2 bits.
+ * Add vty command to manually force MS Uplink Power
+ * Implement MS Uplink Power Control Loop
+
+ [ Vadim Yanitskiy ]
+ * [hopping] vty: ensure no duplicate hopping ARFCN entries
+ * [hopping] generate_cell_chan_list(): make some pointers const
+ * [hopping] gsm48_send_rr_ass_cmd(): use Cell Channel Description from SI1
+ * [hopping] generate_ma_for_ts() returns no meaningful value
+ * abis_nm: rework warnings about unknown / not supported features
+ * abis_nm: cosmetic: use osmo_bts_feature_name()
+ * [hopping] Rework generation of Cell/Mobile Allocation
+ * [hopping] bootstrap_rsl(): do not call generate_ma_for_ts() again
+ * vty: deprecate BTS type 'sysmobts' in favor of 'osmo-bts'
+ * Replace all references to 'sysmobts' with 'osmo-bts'
+ * VTY: fix NULL-pointer dereference in lchan_act_single()
+ * bts_uptime(): do not spam logs with 'OML link uptime unavailable'
+ * Make interference measurement parameters configurable
+ * VTY: fix typo in a command description: s/send/sent/
+ * PCUIF protocol: add message definition for interference report
+ * abis_rsl: fix rsl_rx_ccch_load(): properly check the message length
+ * gsm_data.h: remove declaration of non-existing ts_pchan()
+ * power_control: omit BS Power Parameters IE if the maximum is 0 dB
+ * power_control: implement BCCH carrier power reduction operation
+ * power_control: constrain BS power reduction on BCCH carrier
+ * lchan_fsm: fix potential NULL-pointer dereference
+ * gsm_04_08_rr: silently ignore RR UTRAN Classmark Change
+ * rsl_rx_resource_indication(): check result of rsl_tlv_parse()
+ * rsl_lchan_lookup(): fix handling of ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH
+ * [overpower] VTY: cosmetic: fix lower case in command description
+ * [overpower] VTY: fix copy-pasted warning message
+ * [overpower] VTY: add more ACCH overpower related parameters
+ * [overpower] Add VTY transcript tests for all commands
+ * .gitignore: also ignore *.la files
+ * .gitignore: remove non-existing ipaccess-firmware binary
+ * lchan_fsm: cosmetic: move a 'case' below the 'default' branch
+ * rsl_tx_chan_activ(): fix manual channel activation for nanoBTS
+ * abis_rsl: do not pass lchan to print_meas_rep_buf()
+ * abis_rsl: print_meas_rep_{buf,uni}() accept const *mr
+ * abis_rsl: permit simultaneous ACCH repetition and overpower
+ * ipaccess-config: reduce verbosity of the OML logging
+ * ipaccess-config: fix wrong flag name in ipa_nvflag_strs[]
+ * ipaccess-config: warn about unknown flag name
+ * doc/manuals: add documentation for interference reporting
+ * gsm_data: use ascending order for interference boundaries
+ * doc/manuals: add documentation for Temporary ACCH Overpower
+ * doc/manuals: fix wrong CI in the inter-BSC handover diagram
+ * tests/Makefile.am: do not try removing non-existing files
+ * abis_rsl: simplify checking if channel mode is AMR
+ * abis_rsl: cosmetic: fix coding style rep_acch_cap_for_bts()
+ * bts_vty: fix tabs-vs-spaces issues in cfg_bts_rep_dl_facch
+ * struct gsm_bts: simplify comments for ACCH repetition/overpower
+ * struct gsm_bts: s/repeated_acch_policy/rep_acch_cap/g
+ * struct gsm_bts: s/temporary_overpower/top_acch_cap/g
+ * abis_rsl: {rep,top}_acch_cap_for_bts(): make *lchan const
+ * abis_rsl: s/*_acch_cap_for_bts/put_*_acch_cap_ie/g
+ * [overpower] Allow configuring specific channel mode(s)
+ * [overpower] By default, permit only for speech channels using AMR
+
+ [ Alexander Chemeris ]
+ * stats: Count transitions from BORKEN state due to LCHAN_EV_TS_ERROR signal.
+
+ [ Pau Espin Pedrol ]
+ * cosmetic: Fix typo in func description
+ * bssap: pass whole tlv_parsed to event GSCON_EV_A_COMMON_ID_IND
+ * Send EUTRAN neighs based on whether Common Id msg contained Last used E-UTRAN PLMN ID
+ * Revert "update neighbor ARFCNs on startup and config changes"
+ * SRVCC: Parse Last Used E-UTRAN PLMN Id in Handover Request
+ * SRVCC: Forward Last EUTRAN PLMN Id in Handover Required
+ * osmo-bsc: Avoid erroring every few secs about unconnected BTS
+ * Fix bts->description field not printed in config write
+ * ipaccess-config: Clean up sign_link setup helper
+ * bsc: Clean up TS selection in ipaccess_sign_link_up/down
+ * bsc: Use osmo_clock_gettime everywhere
+ * ctrl: Introduce CTRL SET cmd to apply VTY cfg file
+ * ctrl: Avoid fclose() on NULL pointer
+ * Use new stat item/ctr getter APIs
+ * vty: Drop unused old node enum fields
+ * Introduce VTY command to disable srvcc fast-return on target BTS
+ * Introduce counters to track SRVCC procedures
+ * pcuif_proto.h: Add new container messages
+ * Support proto IPAC_PROTO_EXT_PCU BSC<->PCU
+ * lchan-select: Avoid setting variable for no reason
+ * assignment_fsm: Fix null pointer dereference rx ASSIGNMENT_EV_LCHAN_ERROR
+ * assignment_fsm: Add assert to guard ptr access
+ * Rename osmo dyn ts enums to contain SDCCH8
+ * Support SDCCH8 in osmo dyn ts
+ * lchan_fsm: Allow rx LCHAN_EV_RLL_REL_IND in WAIT_RF_RELEASE_ACK
+ * doc: bts.adoc: Update dyn ts section to include SDCCH8 support
+ * lchan_fsm: Improve timeout logging line in state WAIT_RLL_RTP_ESTABLISH
+ * Avoid switching dyn ts to sdcch8 if it starves later TCH
+ * cosmetic: Small improvements to _select_sdcch_for_call
+ * Add new lchan_select_set_type() API helper
+ * _select_sdcch_for_call: Avoid 2nd lchan lookup when finally selecting it
+ * lchan_fsm: Allow rx LCHAN_EV_RLL_REL_IND in state BORKEN
+ * abis_rsl: Log chan rqd reason on resource exhaustion log message
+ * Fix recent regression in CHREQ allocation
+ * Split bsc_vty.c creating bts_vty.c
+ * Split bts_vty.c creating bts_trx_vty.c
+ * Introduce libbsc to avoid linking long lists of .o files
+ * Introduce VTY option to forbid use of TCH for non-voicecall signalling
+ * Clarify string name for GSM_CHREQ_REASON_CALL
+ * doc: Improve ACC ramp documentation
+ * doc: manual: Fix typo in text
+ * vty: Fix wrong TSC sent when activating lchan through VTY
+ * vty: Fix wrongs params passed in vty warning message
+ * cosmetic: power_ctrl_params_def: Fix typo in comment
+ * doc: power_control.adoc: Improve VTY snippet foot notes
+ * MS Power Control Loop: Support set up of C/I parameters for osmo-bts
+ * doc: power_control.adoc: Add small time graph showcasing P_CON_INTERVAL
+ * lchan_fsm: Fix comment
+ * lchan_fsm: Fix comment
+ * lchan_fsm: Avoid inheriting bs_power from old lchan
+ * Support Neighbor Address Resolution over PCUIF IPA multiplex
+ * Power Control Loop: Set P_CON_INTERVAL to 1 by default
+ * MS Power Control Loop: Support turn off C/I based logic
+ * bts_vty: Print C/I power params for osmo-bts only
+ * MS Power Control Loop: Allow Turn off/on C/I independent from value setting
+ * cosmetic: fix comment typos in signal.h
+ * cosmetic: Fix typo in comment
+ * MS Power Control Loop: Use P_CON_INTERVAL=2 by default
+ * bts_trx: Fix timeslot_fsm not properly freed during trx free() [1/4]
+ * Move global var bsc_gsmnet into libbsc [2/4]
+ * Move ts_fsm_init to static constructor [3/4]
+ * Get rid of lots of stubs [4/4]
+ * nm_channel_fsm: drop ipa link if SetChannelAttr fails
+ * gitignore: Fix typo
+ * jenkins.sh: Fix typo
+ * Set subslots_per_pchan[GSM_PCHAN_OSMO_DYN] = 8
+ * timeslot_fsm: Add assert to make sure we never go out of bounds in ts->lchan array
+ * Set subslots_per_pchan_vamos[GSM_PCHAN_OSMO_DYN] = 0
+ * assignment_fsm: Log modified lchan in assignment_fsm_allstate_action()
+ * lchan_fsm: Fix possible NULL ptr dereference in _lchan_on_mode_modify_failure()
+ * Properly handle dyn TS TCH with vamos after updating subslots_per_pchan
+
+ [ Michael Iedema ]
+ * stats: add BTS uptime counter
+
+ [ Philipp Maier ]
+ * bts.adoc: fix typo BGSGP -> BSSGP
+ * handover_cfg: add missing VTY_CMD_PREFIX in comment
+ * bsc_ctrl_commands: add command to write vty config
+ * control.adoc: add doc for apply-config-file
+ * bsc_vty: add vty option to allow call-reestablishment
+ * handover_ctrl: add control interface for handover settings
+ * running.adoc: explain mgw reset-endpoint VTY setting
+ * osmo_bsc_main: remove unused commandline option -l
+ * bty_vty: add VTY settungs for temporary overpower
+ * osmo_bsc_main: integrate MGW pooling into osmo-bsc
+ * osmo_bsc_msc: do not initalize MGCP proxy for AoIP MSCs
+ * doc/mgwpool: update documentation
+ * assignment_fsm: make assignment_fsm_timer_cb static
+ * assignment_fsm: make assignment_fsm_allstate_action static
+ * assignment_fsm: Check for conn->lchan
+ * osmo_bsc_main: remove code dup in bootstrap_bts()
+ * bts: set R99 MSC flag in SI13 in bts_alloc
+ * bts: set pwrc value in bts_alloc
+ * bts: set acs value in bts_alloc
+ * drop chan_load_samples_idx initalization from bootstrap_bts()
+ * bts: set ncc_permitted from bts_alloc
+ * osmo_bsc_main: remove not longer needed fixme note
+ * osmo_bsc_main: remove unused option -t --testmode
+ * bsc_subscr_conn_fsm: fix mgw-pool ref counting
+ * neighbor_ident: add comment about Neighbor Address Resolution Service
+ * control.adoc: improve description of command bts.N.send-new-system-informations
+ * heighbor_ident: add/del neighbor cells via ctrl interface
+ * bsc_ctrl_commands: change neighbor-list mode/arfcn via control interface
+ * control.adoc: comment out fixme note
+ * osmo_bsc_main: bootstrap_bts: print errornous ARFCN number
+ * osmo_bsc_main: separate checks from bootstrap_bts
+ * osmo_bsc_main: move inp_sig_cb() below check_bts and bootstrap_bts
+ * osmo_bsc_ctrl: make sure strtok results are checked
+ * osmo_bsc_main: call bootstrap_bts when OML TEI comes up
+ * osmo_bsc_main: move generate_ma_for_bts() into bootstrap_bts()
+
+ [ Eric ]
+ * vty: allow A5/4 encryption in config
+
+ [ Daniel Willmann ]
+ * bts: Clear BTS_STAT_CHAN_*_{TOTAL,USED} on bts disconnect
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 17:21:59 +0100
+
+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..f87298cf9 100644
--- a/debian/control
+++ b/debian/control
@@ -12,22 +12,22 @@ 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.8.0),
+ libosmo-sigtran-dev (>= 1.7.0),
+ libosmo-abis-dev (>= 1.4.0),
+ libosmo-netif-dev (>= 1.3.0),
+ libosmo-mgcp-client-dev (>= 1.11.0),
+ osmo-gsm-manuals-dev (>= 1.4.0)
Standards-Version: 3.9.8
-Vcs-Git: git://git.osmocom.org/osmo-bsc.git
-Vcs-Browser: https://git.osmocom.org/osmo-bsc/
+Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-bsc
+Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-bsc
Homepage: https://projects.osmocom.org/projects/osmo-bsc
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/debian/copyright b/debian/copyright
index c748589c8..8980ffb8e 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,6 +1,6 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: osmo-bsc
-Source: git://git.osmocom.org/osmo-bsc
+Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-bsc
Files: *
Copyright: 2008-2015 Holger Hans Peter Freyther <zecke@selfish.org>
diff --git a/doc/Makefile.am b/doc/Makefile.am
index d3a04c62f..4b0903c38 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -13,6 +13,8 @@ msc: \
$(builddir)/handover-inter-bsc-out.png \
$(builddir)/handover-inter-bsc-in.png \
$(builddir)/mgw-endpoint.png \
+ $(builddir)/location_services_ta.png \
+ $(builddir)/codec_resolution.png \
$(NULL)
dot: \
@@ -25,6 +27,7 @@ dot: \
$(builddir)/handover-intra-bsc-fsm.png \
$(builddir)/handover-inter-bsc-out-fsm.png \
$(builddir)/handover-inter-bsc-in-fsm.png \
+ $(builddir)/mscpool-attach.png \
$(NULL)
$(builddir)/%.png: $(srcdir)/%.msc
@@ -36,3 +39,7 @@ $(builddir)/%.png: $(srcdir)/%.dot
.PHONY: poll
poll:
while true; do $(MAKE) msc dot; sleep 1; done
+
+$(srcdir)/%.msc: $(srcdir)/%.ladder
+ @which ladder_to_msc.py || (echo 'PLEASE POINT YOUR $$PATH AT libosmocore/contrib/ladder_to_msc.py' && false)
+ ladder_to_msc.py -i $< -o $@
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/bts-features.txt b/doc/bts-features.txt
new file mode 100644
index 000000000..a38b9a769
--- /dev/null
+++ b/doc/bts-features.txt
@@ -0,0 +1,42 @@
+Notes about BTS feature check code
+---
+
+Feature reporting:
+- For most BTS we hardcode a list of assumed features in the BTS model's
+ _init() function, e.g. bts_model_bs11_init(). These features get copied to
+ bts->features once the BTS model is set.
+- nanobts and osmo-bts are special, they support reporting features during OML
+ bring up (features_get_reported set in struct_gsm_bts_model):
+ - For osmo-bts, we do not assume any features in the BTS model and just let
+ it report all available features.
+ - For nanobts, we wait for the reported features and then extend them with
+ the features set in the bts model. This is needed because the features enum
+ gets extended by us for osmo-bts, it may have features that nanobts does
+ not report but has implemented.
+- Once features are available (either through feature reporting or copied from
+ the bts model), features_known is true in struct gsm_bts.
+
+Implementing a feature check:
+- Check that features_known is true, in case the check may be done before the
+ BTS is connected and has reported its features (e.g. in VTY config parsing)
+- Use osmo_bts_has_feature()
+- Example:
+ if (bts->features_known && !osmo_bts_has_feature(&bts->features, BTS_FEAT_MULTI_TSC))
+
+VTY and feature checks:
+- Some VTY commands only make sense if a BTS supports a certain feature
+- Implement the following checks:
+ - In the VTY command, check if the BTS has the feature.
+ - In gsm_bts_check_cfg() (or called funcs like trx_has_valid_pchan_config),
+ check if the VTY command for the feature is set and if the BTS has the
+ feature.
+- In both cases, do not fail the checks if bts->features_known is false.
+
+Resulting functionality:
+- For BTS that do not support feature reporting, the VTY config is checked
+ against the hardcoded feature set as it gets parsed.
+- For BTS that do support feature reporting, the VTY config is checked when
+ features get reported. The BTS gets rejected if the config is invalid for the
+ available features.
+- Once a BTS is up and running, VTY commands changing the behavior check
+ against the available feature sets.
diff --git a/doc/codec_resolution.ladder b/doc/codec_resolution.ladder
new file mode 100644
index 000000000..016b33994
--- /dev/null
+++ b/doc/codec_resolution.ladder
@@ -0,0 +1,136 @@
+{hscale=1.7}
+ms = MS/BTS
+cfg = osmo-bsc.cfg
+bsc = osmo-bsc
+msc = MSC
+sip = SIP
+
+ms -> bsc EST IND / Compl L3
+cfg -> bsc 'msc 0'
+ 'codec-list fr3 hr3 fr2 fr1 hr1'
+bsc () . build Speech Codec List
+ gen_bss_supported_codec_list()
+bsc -> msc Compl L3
+bsc [] msc Speech Codec List (BSS Supported)
+ {GSM0808_SCT_FR3 + AMR-cfg,
+ GSM0808_SCT_HR3 + AMR-cfg,
+ GSM0808_SCT_FR2,
+ GSM0808_SCT_FR1,
+ GSM0808_SCT_HR1}
+--- AMR-cfg:
+bsc [] msc S0-S15: 16bit flags
+ S0 = 1: 4.75 ---- ---- ---- ---- ---- ---- ----
+ S1 = 1: 4.75 ---- 5.90 ---- 7.40 ---- ---- 12.2
+ S2 = 1: ---- ---- 5.90 ---- ---- ---- ---- ----
+ S3 = 1: ---- ---- ---- 6.70 ---- ---- ---- ----
+ S4 = 1: ---- ---- ---- ---- 7.40 ---- ---- ----
+ S5 = 1: ---- ---- ---- ---- ---- 7.95 ---- ----
+ S6 = 1: ---- ---- ---- ---- ---- ---- 10.2 ----
+ S7 = 1: ---- ---- ---- ---- ---- ---- ---- 12.2
+
+ S8 = 1: 4.75 ---- 5.90 ---- ---- ---- ---- ----
+ S9 = 1: 4.75 ---- 5.90 6.70 ---- ---- ---- ----
+ S10= 1: 4.75 ---- 5.90 6.70 7.40 ---- ---- ----
+ S11= 1: ---- ---- ---- ---- ---- ---- ---- ----
+ S12= 1: 4.75 ---- 5.90 6.70 ---- ---- 10.2 ----
+ S13= 1: ---- ---- ---- ---- ---- ---- ---- ----
+ S14= 1: 4.75 ---- 5.90 ---- ---- 7.95 ---- 12.2
+ S15= 1: ---- ---- ---- ---- ---- ---- ---- ----
+
+ 3GPP TS 28.062 Table 7.11.3.1.3-2: "Preferred Configurations",
+ some removed as specified in 3GPP TS 48.008 3.2.2.103
+
+cfg -> bsc 'bts 0'
+ 'amr tch-x modes 0 2 4 7'
+bsc () . convert AMR modes to
+ bts-S0-S15
+cfg -> bsc 'msc 0'
+ 'amr-config 4_75k allowed'
+bsc () . convert AMR modes to
+ msc-S0-S15
+bsc -> msc Compl L3 Speech Codec List:
+ bitwise AND:
+ bts-S0-S15 & msc-S0-S15
+---
+
+ms -> msc Bearer Capabilities
+msc <- sip SDP
+msc [] sip m=audio 12345 RTP/AVP 112 3 111 110
+ a=rtpmap:112 AMR/8000
+ a=fmtp:112 mode-set=0,2,4,7
+ a=rtpmap:3 GSM/8000
+ a=rtpmap:111 GSM-HR-08/8000
+ a=rtpmap:110 GSM-EFR/8000
+
+msc () . combine:
+ BSC: Speech Codec List
+ MS: Bearer Cap
+ SIP: SDP
+
+bsc <- msc BSSMAP Assignment Request
+ contains
+ Channel Type
+ Speech Codec List (MSC Preferred)
+bsc [] msc Channel Type
+ Channel Rate And Type:
+ - [prefer] full rate
+ - [prefer] half rate
+ - indicated by Permitted Speech list
+ Permitted Speech [1..9]:
+ {GSM0808_PERM_FR3,
+ GSM0808_PERM_HR3,
+ GSM0808_PERM_FR2,
+ GSM0808_PERM_FR1,
+ GSM0808_PERM_HR1}
+bsc [] msc Speech Codec List (MSC Preferred)
+ {GSM0808_SCT_FR3 + AMR-cfg,
+ GSM0808_SCT_HR3 + AMR-cfg,
+ GSM0808_SCT_FR2,
+ GSM0808_SCT_FR1,
+ GSM0808_SCT_HR1}
+
+cfg -> bsc 'msc 0'
+ 'codec-list fr3 hr3 fr2 fr1 hr1'
+cfg -> bsc 'bts 0'
+ 'phys_chan_cfg TCH/F'
+cfg -> bsc 'bts 0'
+ 'codec-support amr efr fr hr'
+
+cfg () bsc combine:
+ 'msc 0' 'codec-list fr3 hr3 fr2 fr1 hr1'
+ 'bts 0' 'phys_chan_cfg TCH/F'
+ 'bts 0' 'codec-support amr efr fr hr'
+ MSC: Channel Type
+ MSC: Speech Codec List (MSC Preferred)
+ =>
+ {GSM48_CMODE_SPEECH_AMR, FR, S0-S15},
+ {GSM48_CMODE_SPEECH_AMR, HR, S0-S15}
+
+cfg -> bsc 'bts 0'
+ 'amr tch-x bts threshold'
+ 'amr tch-x bts hysteresis'
+ms <- bsc RSL CHANnel ACTIVation
+ms [] bsc Channel Rate and Type: Full/Half rate
+ Speech Coding Algorithm Version: 3 (=AMR)
+ MultiRate Configuration:
+ - 4.75 | 5.90 | 7.40 | 12.2
+ - Threshold / Hysteresis x 3
+cfg -> bsc 'bts 0'
+ 'amr tch-x ms threshold'
+ 'amr tch-x ms hysteresis'
+ms <- bsc RSL Assignment Command
+ms [] bsc Channel Description: TCH/F
+ Speech Coding Algorithm Version: 3 (=AMR)
+ MultiRate Configuration:
+ - 4.75 | 5.90 | 7.40 | 12.2
+ - Threshold / Hysteresis x 3
+
+bsc -> msc BSSMAP Assignment Complete
+bsc [] msc Chosen Channel: Speech, Full Rate
+ Speech Version (Chosen): FR3
+ Speech Codec (Chosen): FR AMR, S0-S15
+
+msc -> sip SDP (optional)
+msc [] sip m=audio 12345 RTP/AVP 112
+ a=rtpmap:112 AMR/8000
+ a=fmtp:112 mode-set=0,2,4,7
diff --git a/doc/codec_resolution.msc b/doc/codec_resolution.msc
new file mode 100644
index 000000000..455044805
--- /dev/null
+++ b/doc/codec_resolution.msc
@@ -0,0 +1,48 @@
+msc {
+ hscale="1.7";
+ ms[label="MS/BTS"],cfg[label="osmo-bsc.cfg"],bsc[label="osmo-bsc"],__msc[label="MSC"],sip[label="SIP"];
+
+ ms => bsc [label="EST IND / Compl L3"];
+ cfg => bsc [label="'msc 0'\n'codec-list fr3 hr3 fr2 fr1 hr1'"];
+ bsc rbox bsc [label="build Speech Codec List\ngen_bss_supported_codec_list()"];
+ bsc => __msc [label="Compl L3"];
+ bsc note __msc [label="Speech Codec List (BSS Supported)\n{GSM0808_SCT_FR3 + AMR-cfg,\nGSM0808_SCT_HR3 + AMR-cfg,\nGSM0808_SCT_FR2,\nGSM0808_SCT_FR1,\nGSM0808_SCT_HR1}"];
+ --- [label="AMR-cfg:"];
+ bsc note __msc [label="S0-S15: 16bit flags\nS0 = 1: 4.75 ---- ---- ---- ---- ---- ---- ----\nS1 = 1: 4.75 ---- 5.90 ---- 7.40 ---- ---- 12.2\nS2 = 1: ---- ---- 5.90 ---- ---- ---- ---- ----\nS3 = 1: ---- ---- ---- 6.70 ---- ---- ---- ----\nS4 = 1: ---- ---- ---- ---- 7.40 ---- ---- ----\nS5 = 1: ---- ---- ---- ---- ---- 7.95 ---- ----\nS6 = 1: ---- ---- ---- ---- ---- ---- 10.2 ----\nS7 = 1: ---- ---- ---- ---- ---- ---- ---- 12.2\n\nS8 = 1: 4.75 ---- 5.90 ---- ---- ---- ---- ----\nS9 = 1: 4.75 ---- 5.90 6.70 ---- ---- ---- ----\nS10= 1: 4.75 ---- 5.90 6.70 7.40 ---- ---- ----\nS11= 1: ---- ---- ---- ---- ---- ---- ---- ----\nS12= 1: 4.75 ---- 5.90 6.70 ---- ---- 10.2 ----\nS13= 1: ---- ---- ---- ---- ---- ---- ---- ----\nS14= 1: 4.75 ---- 5.90 ---- ---- 7.95 ---- 12.2\nS15= 1: ---- ---- ---- ---- ---- ---- ---- ----\n\n3GPP TS 28.062 Table 7.11.3.1.3-2: \"Preferred Configurations\",\nsome removed as specified in 3GPP TS 48.008 3.2.2.103"];
+
+ cfg => bsc [label="'bts 0'\n'amr tch-x modes 0 2 4 7'"];
+ bsc rbox bsc [label="convert AMR modes to\nbts-S0-S15"];
+ cfg => bsc [label="'msc 0'\n'amr-config 4_75k allowed'"];
+ bsc rbox bsc [label="convert AMR modes to\nmsc-S0-S15"];
+ bsc => __msc [label="Compl L3 Speech Codec List:\nbitwise AND:\nbts-S0-S15 & msc-S0-S15"];
+ ---;
+
+ ms => __msc [label="Bearer Capabilities"];
+ __msc <= sip [label="SDP"];
+ __msc note sip [label="m=audio 12345 RTP/AVP 112 3 111 110\na=rtpmap:112 AMR/8000\na=fmtp:112 mode-set=0,2,4,7\na=rtpmap:3 GSM/8000\na=rtpmap:111 GSM-HR-08/8000\na=rtpmap:110 GSM-EFR/8000"];
+
+ __msc rbox __msc [label="combine:\nBSC: Speech Codec List\nMS: Bearer Cap\nSIP: SDP"];
+
+ bsc <= __msc [label="BSSMAP Assignment Request\ncontains\nChannel Type\nSpeech Codec List (MSC Preferred)"];
+ bsc note __msc [label="Channel Type\nChannel Rate And Type:\n- [prefer] full rate\n- [prefer] half rate\n- indicated by Permitted Speech list\nPermitted Speech [1..9]:\n{GSM0808_PERM_FR3,\nGSM0808_PERM_HR3,\nGSM0808_PERM_FR2,\nGSM0808_PERM_FR1,\nGSM0808_PERM_HR1}"];
+ bsc note __msc [label="Speech Codec List (MSC Preferred)\n{GSM0808_SCT_FR3 + AMR-cfg,\nGSM0808_SCT_HR3 + AMR-cfg,\nGSM0808_SCT_FR2,\nGSM0808_SCT_FR1,\nGSM0808_SCT_HR1}"];
+
+ cfg => bsc [label="'msc 0'\n'codec-list fr3 hr3 fr2 fr1 hr1'"];
+ cfg => bsc [label="'bts 0'\n'phys_chan_cfg TCH/F'"];
+ cfg => bsc [label="'bts 0'\n'codec-support amr efr fr hr'"];
+
+ cfg rbox bsc [label="combine:\n'msc 0' 'codec-list fr3 hr3 fr2 fr1 hr1'\n'bts 0' 'phys_chan_cfg TCH/F'\n'bts 0' 'codec-support amr efr fr hr'\nMSC: Channel Type\nMSC: Speech Codec List (MSC Preferred)\n=>\n{GSM48_CMODE_SPEECH_AMR, FR, S0-S15},\n{GSM48_CMODE_SPEECH_AMR, HR, S0-S15}"];
+
+ cfg => bsc [label="'bts 0'\n'amr tch-x bts threshold'\n'amr tch-x bts hysteresis'"];
+ ms <= bsc [label="RSL CHANnel ACTIVation"];
+ ms note bsc [label="Channel Rate and Type: Full/Half rate\nSpeech Coding Algorithm Version: 3 (=AMR)\nMultiRate Configuration:\n- 4.75 | 5.90 | 7.40 | 12.2\n- Threshold / Hysteresis x 3"];
+ cfg => bsc [label="'bts 0'\n'amr tch-x ms threshold'\n'amr tch-x ms hysteresis'"];
+ ms <= bsc [label="RSL Assignment Command"];
+ ms note bsc [label="Channel Description: TCH/F\nSpeech Coding Algorithm Version: 3 (=AMR)\nMultiRate Configuration:\n- 4.75 | 5.90 | 7.40 | 12.2\n- Threshold / Hysteresis x 3"];
+
+ bsc => __msc [label="BSSMAP Assignment Complete"];
+ bsc note __msc [label="Chosen Channel: Speech, Full Rate\nSpeech Version (Chosen): FR3\nSpeech Codec (Chosen): FR AMR, S0-S15"];
+
+ __msc => sip [label="SDP (optional)"];
+ __msc note sip [label="m=audio 12345 RTP/AVP 112\na=rtpmap:112 AMR/8000\na=fmtp:112 mode-set=0,2,4,7"];
+}
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
index 9d8cd75d6..c25ebc506 100644
--- a/doc/examples/Makefile.am
+++ b/doc/examples/Makefile.am
@@ -6,7 +6,7 @@ osmoconf_DATA = $(OSMOCONF_FILES)
EXTRA_DIST = $(OSMOCONF_FILES)
-CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
+CFG_FILES = find $(srcdir) -name '*.cfg*' -o -name '*.confmerge*' | sed -e 's,^$(srcdir),,'
dist-hook:
for f in $$($(CFG_FILES)); do \
diff --git a/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus01-4trx.cfg b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus01-4trx.cfg
index 68837caf7..7d676d64c 100644
--- a/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus01-4trx.cfg
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus01-4trx.cfg
@@ -33,13 +33,13 @@ network
band GSM900
om2000 version-limit oml gen 12 rev 10
cell_identity 0
- location_area_code 1
+ location_area_code 0x0001
training_sequence_code 7
base_station_id_code 63
ms max power 15
cell reselection hysteresis 4
rxlev access min 0
- channel allocator descending
+ channel allocator mode set-all descending
rach tx integer 9
rach max transmission 7
oml e1 line 0 timeslot 1 sub-slot full
diff --git a/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-4trx.cfg b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-4trx.cfg
index ae0b7db3a..464b37f03 100644
--- a/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-4trx.cfg
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-4trx.cfg
@@ -33,13 +33,13 @@ network
band GSM900
om2000 version-limit oml gen 12 rev 10
cell_identity 0
- location_area_code 1
+ location_area_code 0x0001
training_sequence_code 7
base_station_id_code 63
ms max power 15
cell reselection hysteresis 4
rxlev access min 0
- channel allocator descending
+ channel allocator mode set-all descending
rach tx integer 9
rach max transmission 7
oml e1 line 0 timeslot 1 sub-slot full
diff --git a/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-8trx.cfg b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-8trx.cfg
index ff0b27797..844bf05ba 100644
--- a/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-8trx.cfg
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-8trx.cfg
@@ -33,13 +33,13 @@ network
band GSM900
om2000 version-limit oml gen 12 rev 10
cell_identity 0
- location_area_code 1
+ location_area_code 0x0001
training_sequence_code 7
base_station_id_code 63
ms max power 15
cell reselection hysteresis 4
rxlev access min 0
- channel allocator descending
+ channel allocator mode set-all descending
rach tx integer 9
rach max transmission 7
oml e1 line 0 timeslot 1 sub-slot full
diff --git a/doc/examples/osmo-bsc/ericsson/osmo-bsc.rbs2308.cfg b/doc/examples/osmo-bsc/ericsson/osmo-bsc.rbs2308.cfg
index f79d45735..82283c8e1 100644
--- a/doc/examples/osmo-bsc/ericsson/osmo-bsc.rbs2308.cfg
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.rbs2308.cfg
@@ -39,13 +39,13 @@ network
type rbs2000
band PCS1900
cell_identity 0
- location_area_code 1
+ location_area_code 0x0001
training_sequence_code 7
base_station_id_code 63
ms max power 33
cell reselection hysteresis 4
rxlev access min 0
- channel allocator descending
+ channel allocator mode set-all descending
rach tx integer 9
rach max transmission 7
oml e1 line 0 timeslot 1 sub-slot full
diff --git a/doc/examples/osmo-bsc/nokia/osmo-bsc.insite.cfg b/doc/examples/osmo-bsc/nokia/osmo-bsc.insite.cfg
index a8e8e5c3f..0b0afa8a8 100644
--- a/doc/examples/osmo-bsc/nokia/osmo-bsc.insite.cfg
+++ b/doc/examples/osmo-bsc/nokia/osmo-bsc.insite.cfg
@@ -27,7 +27,7 @@ network
type nokia_site
band GSM1900
cell_identity 1
- location_area_code 1
+ location_area_code 0x0001
base_station_id_code 63
training_sequence_code 7
diff --git a/doc/examples/osmo-bsc/osmo-bsc-4trx-fh.confmerge b/doc/examples/osmo-bsc/osmo-bsc-4trx-fh.confmerge
new file mode 100644
index 000000000..0a14631f8
--- /dev/null
+++ b/doc/examples/osmo-bsc/osmo-bsc-4trx-fh.confmerge
@@ -0,0 +1,216 @@
+! This is a confmerge file with example frequency hopping parameters.
+! Use the osmo-config-merge tool to apply it to osmo-bsc-4trx.cfg.
+network
+ bts 0
+ trx 0
+ timeslot 0
+ ! Shall not be hopping
+ timeslot 1
+ ! Intentionally non-hopping
+ timeslot 2
+ ! (c) HSN=2, MAIO=0,1
+ hopping enabled 1
+ hopping sequence-number 2
+ hopping maio 0
+ hopping arfcn add 871
+ hopping arfcn add 873
+ timeslot 3
+ ! (e) HSN=3, MAIO=3,2,1,0
+ hopping enabled 1
+ hopping sequence-number 3
+ hopping maio 3
+ hopping arfcn add 871
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 4
+ ! Intentionally non-hopping
+ timeslot 5
+ ! (f) HSN=5, MAIO=0,1,2,3
+ hopping enabled 1
+ hopping sequence-number 5
+ hopping maio 0
+ hopping arfcn add 871
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 6
+ ! (g) HSN=6, MAIO=1,0
+ hopping enabled 1
+ hopping sequence-number 6
+ hopping maio 1
+ hopping arfcn add 871
+ hopping arfcn add 877
+ timeslot 7
+ ! (i) HSN=0, MAIO=1,3
+ hopping enabled 1
+ hopping sequence-number 0
+ hopping maio 1
+ hopping arfcn add 871
+ hopping arfcn add 875
+ trx 1
+ timeslot 0
+ ! (a) HSN=0, MAIO=0,1,2
+ hopping enabled 1
+ hopping sequence-number 0
+ hopping maio 0
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 1
+ ! Intentionally non-hopping
+ timeslot 2
+ ! (c) HSN=2, MAIO=0,1
+ hopping enabled 1
+ hopping sequence-number 2
+ hopping maio 1
+ hopping arfcn add 871
+ hopping arfcn add 873
+ timeslot 3
+ ! (e) HSN=3, MAIO=3,2,1,0
+ hopping enabled 1
+ hopping sequence-number 3
+ hopping maio 2
+ hopping arfcn add 871
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 4
+ ! Intentionally non-hopping
+ timeslot 5
+ ! (f) HSN=5, MAIO=0,1,2,3
+ hopping enabled 1
+ hopping sequence-number 5
+ hopping maio 1
+ hopping arfcn add 871
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 6
+ ! (h) HSN=6, MAIO=1,0
+ hopping enabled 1
+ hopping sequence-number 6
+ hopping maio 1
+ hopping arfcn add 873
+ hopping arfcn add 875
+ timeslot 7
+ ! (j) HSN=0, MAIO=0,2
+ hopping enabled 1
+ hopping sequence-number 0
+ hopping maio 0
+ hopping arfcn add 873
+ hopping arfcn add 877
+ trx 2
+ timeslot 0
+ ! (a) HSN=0, MAIO=0,1,2
+ hopping enabled 1
+ hopping sequence-number 0
+ hopping maio 1
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 1
+ ! (b) HSN=1, MAIO=3,5
+ hopping enabled 1
+ hopping sequence-number 1
+ hopping maio 3
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 2
+ ! (d) HSN=2, MAIO=2,3
+ hopping enabled 1
+ hopping sequence-number 2
+ hopping maio 2
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 3
+ ! (e) HSN=3, MAIO=3,2,1,0
+ hopping enabled 1
+ hopping sequence-number 3
+ hopping maio 1
+ hopping arfcn add 871
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 4
+ ! Intentionally non-hopping
+ timeslot 5
+ ! (f) HSN=5, MAIO=0,1,2,3
+ hopping enabled 1
+ hopping sequence-number 5
+ hopping maio 2
+ hopping arfcn add 871
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 6
+ ! (h) HSN=6, MAIO=1,0
+ hopping enabled 1
+ hopping sequence-number 6
+ hopping maio 0
+ hopping arfcn add 873
+ hopping arfcn add 875
+ timeslot 7
+ ! (i) HSN=0, MAIO=1,3
+ hopping enabled 1
+ hopping sequence-number 0
+ hopping maio 3
+ hopping arfcn add 871
+ hopping arfcn add 875
+ trx 3
+ timeslot 0
+ ! (a) HSN=0, MAIO=0,1,2
+ hopping enabled 1
+ hopping sequence-number 0
+ hopping maio 2
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 1
+ ! (b) HSN=1, MAIO=3,5
+ hopping enabled 1
+ hopping sequence-number 1
+ hopping maio 5
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 2
+ ! (d) HSN=2, MAIO=2,3
+ hopping enabled 1
+ hopping sequence-number 2
+ hopping maio 3
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 3
+ ! (e) HSN=3, MAIO=3,2,1,0
+ hopping enabled 1
+ hopping sequence-number 3
+ hopping maio 0
+ hopping arfcn add 871
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 4
+ ! Intentionally non-hopping
+ timeslot 5
+ ! (f) HSN=5, MAIO=0,1,2,3
+ hopping enabled 1
+ hopping sequence-number 5
+ hopping maio 3
+ hopping arfcn add 871
+ hopping arfcn add 873
+ hopping arfcn add 875
+ hopping arfcn add 877
+ timeslot 6
+ ! (g) HSN=6, MAIO=1,0
+ hopping enabled 1
+ hopping sequence-number 6
+ hopping maio 0
+ hopping arfcn add 871
+ hopping arfcn add 877
+ timeslot 7
+ ! (j) HSN=0, MAIO=0,2
+ hopping enabled 1
+ hopping sequence-number 0
+ hopping maio 2
+ hopping arfcn add 873
+ hopping arfcn add 877
diff --git a/doc/examples/osmo-bsc/osmo-bsc-4trx.cfg b/doc/examples/osmo-bsc/osmo-bsc-4trx.cfg
new file mode 100644
index 000000000..786813a4e
--- /dev/null
+++ b/doc/examples/osmo-bsc/osmo-bsc-4trx.cfg
@@ -0,0 +1,162 @@
+! osmo-bsc configuration example with 4 TRX
+e1_input
+ e1_line 0 driver ipa
+network
+ network country code 1
+ mobile network code 1
+ encryption a5 0
+ neci 1
+ paging any use tch 0
+ mgw 0
+ remote-ip 127.0.0.1
+ remote-port 2427
+ local-port 2727
+ bts 0
+ type osmo-bts
+ band DCS1800
+ cell_identity 6969
+ location_area_code 0x0001
+ base_station_id_code 63
+ ms max power 15
+ cell reselection hysteresis 4
+ rxlev access min 0
+ radio-link-timeout 32
+ channel allocator mode set-all ascending
+ rach tx integer 9
+ rach max transmission 7
+ channel-description attach 1
+ channel-description bs-pa-mfrms 5
+ channel-description bs-ag-blks-res 1
+ early-classmark-sending forbidden
+ ipa unit-id 6969 0
+ oml ipa stream-id 255 line 0
+ codec-support fr hr amr
+ gprs mode none
+ trx 0
+ rf_locked 0
+ arfcn 871
+ nominal power 23
+ ! to use full TRX power, set max_power_red 0
+ max_power_red 20
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH
+ hopping enabled 0
+ timeslot 1
+ phys_chan_config SDCCH8+CBCH
+ hopping enabled 0
+ timeslot 2
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 3
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 4
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 5
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 6
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 7
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ trx 1
+ rf_locked 0
+ arfcn 873
+ nominal power 23
+ ! to use full TRX power, set max_power_red 0
+ max_power_red 20
+ rsl e1 tei 1
+ timeslot 0
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 1
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 2
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 3
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 4
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 5
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 6
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 7
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ trx 2
+ rf_locked 0
+ arfcn 875
+ nominal power 23
+ ! to use full TRX power, set max_power_red 0
+ max_power_red 20
+ rsl e1 tei 2
+ timeslot 0
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 1
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 2
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 3
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 4
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 5
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 6
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 7
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ trx 3
+ rf_locked 0
+ arfcn 877
+ nominal power 23
+ ! to use full TRX power, set max_power_red 0
+ max_power_red 20
+ rsl e1 tei 3
+ timeslot 0
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 1
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 2
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 3
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 4
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 5
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 6
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+ timeslot 7
+ phys_chan_config DYNAMIC/OSMOCOM
+ hopping enabled 0
+msc 0
+ allow-emergency allow
+bsc
+ mid-call-timeout 0
diff --git a/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg b/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg
index b8cd78db0..b4e6e4a8e 100644
--- a/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg
@@ -2,9 +2,9 @@ network
network country code 901
mobile network code 70
bts 0
- type sysmobts
+ type osmo-bts
band GSM-1800
- location_area_code 23
+ location_area_code 0x0017
ipa unit-id 1800 0
trx 0
rf_locked 0
@@ -30,4 +30,3 @@ e1_input
e1_line 0 driver ipa
msc 0
allow-emergency deny
- codec-list fr1
diff --git a/doc/examples/osmo-bsc/osmo-bsc.cfg b/doc/examples/osmo-bsc/osmo-bsc.cfg
index 828875d9e..5c2cf34e5 100644
--- a/doc/examples/osmo-bsc/osmo-bsc.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc.cfg
@@ -17,18 +17,23 @@ network
handover1 power budget interval 6
handover1 power budget hysteresis 3
handover1 maximum distance 9999
- periodic location update 30
+ ! T3212 is in units of 6min, so below we set 5 * 6 = 30min
+ timer net T3212 5
+ mgw 0
+ remote-ip 127.0.0.1
+ remote-port 2427
+ local-port 2727
bts 0
- type sysmobts
+ type osmo-bts
band DCS1800
cell_identity 6969
- location_area_code 1
+ location_area_code 0x0001
base_station_id_code 63
ms max power 15
cell reselection hysteresis 4
rxlev access min 0
radio-link-timeout 32
- channel allocator ascending
+ channel allocator mode set-all ascending
rach tx integer 9
rach max transmission 7
channel-description attach 1
@@ -71,7 +76,6 @@ network
phys_chan_config TCH/F
hopping enabled 0
msc 0
- type normal
allow-emergency allow
amr-config 12_2k forbidden
amr-config 10_2k forbidden
@@ -81,9 +85,5 @@ msc 0
amr-config 5_90k allowed
amr-config 5_15k forbidden
amr-config 4_75k forbidden
- mgw remote-ip 127.0.0.1
- mgw remote-port 2427
- mgw local-port 2727
- mgw endpoint-range 1 31
bsc
mid-call-timeout 0
diff --git a/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg b/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg
index 0ecb5fc52..a053dbe08 100644
--- a/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg
@@ -16,18 +16,19 @@ network
handover1 power budget interval 6
handover1 power budget hysteresis 3
handover1 maximum distance 9999
- periodic location update 30
+ ! T3212 is in units of 6min, so below we set 5 * 6 = 30min
+ timer net T3212 5
bts 0
- type sysmobts
+ type osmo-bts
band DCS1800
cell_identity 0
- location_area_code 1
+ location_area_code 0x0001
base_station_id_code 63
ms max power 15
cell reselection hysteresis 4
rxlev access min 0
radio-link-timeout 32
- channel allocator ascending
+ channel allocator mode set-all ascending
rach tx integer 9
rach max transmission 7
channel-description attach 1
diff --git a/doc/examples/osmo-bsc/siemens/osmo-bsc.bs11.cfg b/doc/examples/osmo-bsc/siemens/osmo-bsc.bs11.cfg
index a308db5be..8d7529628 100644
--- a/doc/examples/osmo-bsc/siemens/osmo-bsc.bs11.cfg
+++ b/doc/examples/osmo-bsc/siemens/osmo-bsc.bs11.cfg
@@ -39,7 +39,7 @@ network
type bs11
band GSM900
cell_identity 1
- location_area_code 1
+ location_area_code 0x0001
training_sequence_code 7
base_station_id_code 63
oml e1 line 2 timeslot 1 sub-slot full
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/Makefile.am b/doc/manuals/Makefile.am
index c86f21563..8b2a8eff9 100644
--- a/doc/manuals/Makefile.am
+++ b/doc/manuals/Makefile.am
@@ -1,10 +1,13 @@
EXTRA_DIST = aoip-mgw-options.adoc \
aoip-mgw-options-docinfo.xml \
+ osmobsc-cbsp.adoc \
+ osmobsc-cbsp-docinfo.xml \
osmobsc-usermanual.adoc \
osmobsc-usermanual-docinfo.xml \
osmobsc-vty-reference.xml \
osmux-reference.adoc \
osmux-reference-docinfo.xml \
+ cbsp \
chapters \
message-sequences \
mgw \
@@ -13,9 +16,10 @@ EXTRA_DIST = aoip-mgw-options.adoc \
vty
if BUILD_MANUALS
- ASCIIDOC = osmobsc-usermanual.adoc osmux-reference.adoc aoip-mgw-options.adoc
+ ASCIIDOC = osmobsc-usermanual.adoc osmobsc-cbsp.adoc osmux-reference.adoc aoip-mgw-options.adoc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
osmobsc-usermanual.pdf: $(srcdir)/chapters/*.adoc $(srcdir)/chapters/*.dot
+ osmobsc-cbsp.pdf: $(srcdir)/cbsp/*.adoc #$(srcdir)/cbsp/*.dot $(srcdir)/abis/*.msc
aoip-mgw-options.pdf: $(srcdir)/aoip-mgw-options.adoc $(srcdir)/mgw/*.msc
VTY_REFERENCE = osmobsc-vty-reference.xml
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/cbsp/messages.adoc b/doc/manuals/cbsp/messages.adoc
new file mode 100644
index 000000000..d595b958f
--- /dev/null
+++ b/doc/manuals/cbsp/messages.adoc
@@ -0,0 +1,73 @@
+== CBSP Messages
+
+=== List of Messages
+
+The following tables list the CBSP messages used by OsmoBSC BSC-CBC interface,
+grouped by their level of compliance with 3GPP TS 48.049.
+
+==== Messages Compliant With TS 48.049
+
+Specific additions and limitations apply, see the linked sections.
+
+.Messages compliant with TS 48.049
+[options="header",cols="10%,20%,45%,5%,20%"]
+|===
+| TS 48.049 § | This document § | Message | <-/-> | Received/Sent by OsmoBSC
+| 8.1.3.1 | - | WRITE-REPLACE | <- | Received
+| 8.1.3.2 | - | WRITE-REPLACE COMPLETE | -> | Sent
+| 8.1.3.3 | - | WRITE-REPLACE FAILURE | -> | Sent
+| 8.1.3.4 | - | KILL | <- | Received
+| 8.1.3.5 | - | KILL COMPLETE | -> | Sent
+| 8.1.3.6 | - | KILL FAILURE | -> | Sent
+| 8.1.3.10 | - | MESSAGE STATUS QUERY | <- | Received
+| 8.1.3.11 | - | MESSAGE STATUS QUERY COMPLETE | -> | Sent
+| 8.1.3.12 | - | MESSAGE STATUS QUERY FAILURE | -> | Sent
+| 8.1.3.16 | - | RESET | <- | Received
+| 8.1.3.17 | - | RESET COMPLETE | -> | Sent
+| 8.1.3.18 | <<RESET_FAILURE>> | RESET FAILURE | -> | Sent
+| 8.1.3.18a | <<KEEP_ALIVE>> | KEEP-ALIVE | <- | Received
+| 8.1.3.18b | - | KEEP-ALIVE COMPLETE | -> | Sent
+| 8.1.3.19 | <<RESTART>> | RESTART | -> | Sent
+|===
+
+==== Messages Not Implemented by OsmoBSC
+
+.3GPP TS 48.049 messages not implemented by OsmoBSC
+[options="header",cols="30%,45%,5%,20%"]
+|===
+| TS 48.049 § | Message | <-/-> | Received/Sent by OsmoBSC
+| 8.1.3.7 | LOAD QUERY | <- | Received
+| 8.1.3.8 | LOAD QUERY COMPLETE | -> | Sent
+| 8.1.3.9 | LOAD QUERY FAILURE | -> | Sent
+| 8.1.3.13 | SET-DRX | <- | Received
+| 8.1.3.14 | SET-DRX COMPLETE | -> | Sent
+| 8.1.3.15 | SET-DRX FAILURE | -> | Sent
+| 8.1.3.20 | FAILURE | -> | Sent
+| 8.1.3.21 | ERROR INDICATION | -> | Sent
+|===
+
+
+=== Message Limitation Details
+
+[[RESET_FAILURE]]
+==== RESET FAILURE
+
+Encoding of this message is implemented, but there is currently no
+condition in the OsmoBSC code that would make a RESET operation fail on
+an existing cell, except if the CBC were to identify
+a non-existent cell in its _Cell List IE_.
+
+[[KEEP_ALIVE]]
+==== KEEP-ALIVE
+
+The message is received and generates a corresponding KEEP-ALIVE
+COMPLETE answer. However, the _Keep Alive Repetition Period IE_ is not
+interpreted.
+
+[[RESTART]]
+==== RESTART
+
+The RESTART message is sent only at the time of establishment of every
+CBSP link. It is not sent when subsequent cells become available during
+runtime of the CBSP link.
+
diff --git a/doc/manuals/cbsp/procedures.adoc b/doc/manuals/cbsp/procedures.adoc
new file mode 100644
index 000000000..192014916
--- /dev/null
+++ b/doc/manuals/cbsp/procedures.adoc
@@ -0,0 +1,83 @@
+== CBSP Procedures
+
+=== List of Procedures
+
+The following tables list the CBSP procedures used by the OsmoBSC BSC-CBC interface,
+grouped by their level of compliance with 3GPP TS 48.049.
+
+==== Procedures Compliant With TS 48.049
+
+Specific additions and limitations apply, see the linked sections.
+
+.Procedures compliant with TS 48.049
+[options="header",cols="10%,20%,40%,30%"]
+|===
+| TS 48.049 § | This document § | Procedure | Originated/Terminated by OsmoBSC
+| 7.2 | <<PROC_WRITE_REPLACE>> | Write-Replace | Terminated
+| 7.3 | - | Kill | Terminated
+| 7.5 | - | Message Status Query | Terminated
+| 7.7a | <<PROC_KEEP_ALIVE>> | Keep Alive | Terminated
+| 7.8 | <<RESTART_IND>> | Restart Indication | Originated
+|===
+
+
+[[PROC_WRITE_REPLACE]]
+===== Write-Replace
+
+Procedures for _Write_ and _Replace_ of CBS messages as per 3GPP TS 48.049 Section 7.2.2.2
+are fully supported.
+
+Procedures for _Write_ and _Replace_ of ETWS messages as per 3GPP TS
+48.059 Section 7.2.2.2 are fully supported. Transmission of the ETWS
+Primary Notification is implemented as follows, assuming related support
+is present in the related BTS and PCU software (true for OsmoBTS >= 1.2.0
+and OsmoPCU >= 0.8.0):
+
+* broadcast to MS in idle mode / packet idle mode by sending a
+ vendor-specific A-bis RSL message to each affected BTS. A
+ vendor-specific mechanism is needed as 3GPP TS 48.058 does not specify
+ any standard message for this. See the section on _Osmocom ETWS
+ Command_ in <<osmobts-abis-spec>> for more details.
+* broadcast to MS in dedicated mode by sending the ETWS PN via every
+ currently active dedicated channel (SDCCH, FACCH) within the affected
+ BTSs.
+
+As an additional clarification to 3GPP TS 48.049, OsmoBSC rejects (via
+WRITE-REPLACE FAILURE) any _write_ procedure for an emergency message if
+there already is another emergency message active in a cell. The
+_replace_ procedure must be used (by specifying the _Old Serial Number
+IE_) if the only existing emergency message of a cell shall be replaced.
+
+[[PROC_KEEP_ALIVE]]
+===== Keep-Alive
+
+The Keep-Alive procedure is implemented only in as far as incoming
+Keep-Alive requests are responded to.
+
+The BSC currently does not use the _Keep Alive Repetition Period IE_.
+This is permitted as 3GPP TS 48.049 states the information _may_ be used
+by the BSC.
+
+[[PROC_RESTART_IND]]
+===== Restart Indication
+
+Restart indications are currently only sent whenever any BSC-CBC link is
+established. They are not sent once subsequent cells become available
+or are re-initialized due to A-bis link failure.
+
+However, CBSP state for both CBS and Emergency messages is kept
+persistent in the BSC and if cells reboot / restart during the duration
+of a CBS / emergency message, they will resume broadcasts as expected.
+
+
+==== Procedures Not Implemented by OsmoBSC
+
+.3GPP TS 48.049 procedures not implemented by OsmoBSC
+[options="header",cols="30%,40%,30%"]
+|===
+| TS 48.049 § | Procedure | Originated/Terminated by OsmoBSC
+| 7.4 | Load Status Enquiry | Terminated
+| 7.6 | Set DRX | Terminated
+| 7.9 | Failure Indication | Originated
+| 7.10 | Error Indication | Originated
+|===
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..bdc3d2bb1 100644
--- a/doc/manuals/chapters/bsc.adoc
+++ b/doc/manuals/chapters/bsc.adoc
@@ -63,18 +63,18 @@ Those timers can be configured using the `timer tXXXX` command.
.Configurable Timers
|===
|node|timer|default|description
-|network|t3101|10|Timeout for 'Immediate Assignment' (sec)
-|network|t3103|?|Timeout for Handover (sec)
-|network|t3105|40|Repetition of 'Physical Information' (sec)
-|network|t3107|?|?
-|network|t3109|?|RSL SACCH deactivation timeout (sec)
-|network|t3111|?|RSL timeout to wait before releasing the RF channel (sec)
-|network|t3113|60|Time to try paging for a subscriber (sec)
-|network|t3115|?|?
-|network|t3117|?|?
-|network|t3119|?|?
+|network|t3101|3|Timeout for 'Immediate Assignment' (sec)
+|network|t3103|5|Timeout for Handover (sec)
+|network|t3105|100|Repetition of 'Physical Information' (millisec)
+|network|t3107|5|?
+|network|t3109|5|RSL SACCH deactivation timeout (sec)
+|network|t3111|2|RSL timeout to wait before releasing the RF channel (sec)
+|network|t3113|7|Time to try paging for a subscriber (sec)
+|network|t3115|10|?
+|network|t3117|10|?
+|network|t3119|10|?
|network|t3122|10|Waiting time after 'Immediate Assignment Reject'
-|network|t3141|?|?
+|network|t3141|10|?
|===
//TODO: split between BSC and MSC timers
@@ -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-examples.adoc b/doc/manuals/chapters/bts-examples.adoc
index 58cb3ab17..1f3652bbe 100644
--- a/doc/manuals/chapters/bts-examples.adoc
+++ b/doc/manuals/chapters/bts-examples.adoc
@@ -26,13 +26,13 @@ network
type nanobts <2>
band DCS1800 <3>
cell_identity 0
- location_area_code 1
+ location_area_code 0x0001
training_sequence_code 7
base_station_id_code 63
ms max power 15
cell reselection hysteresis 4
rxlev access min 0
- channel allocator ascending
+ channel allocator mode set-all ascending
rach tx integer 9
rach max transmission 7
ipa unit-id 1801 0 <4>
@@ -104,13 +104,13 @@ network
type nanobts
band DCS1800
cell_identity 0
- location_area_code 1
+ location_area_code 0x0001
training_sequence_code 7
base_station_id_code 63
ms max power 15
cell reselection hysteresis 4
rxlev access min 0
- channel allocator ascending
+ channel allocator mode set-all ascending
rach tx integer 9
rach max transmission 7
ipa unit-id 1800 0 <1>
@@ -174,3 +174,120 @@ network
For building a multi-TRX setup, you also need to connect the TIB cables
between the two nanoBTS units, as well as the coaxial/RF AUX cabling.
====
+
+=== Example configuration for OsmoBSC with E1 BTS
+
+The following configuration sample illustrates the usage of BTSs that are
+connected via an E1/T1 backhaul.
+
+.OsmoBSC configured for single-TRX E1 Ericsson DUG20
+====
+----
+e1_input <1>
+ e1_line 0 driver dahdi
+ e1_line 0 port 3
+network
+ network country code 1
+ mobile network code 1
+ encryption a5 0
+ neci 1
+ handover 0
+ bts 0
+ type rbs2000
+ band GSM900
+ om2000 version-limit oml gen 12 rev 10 <2>
+ cell_identity 0
+ location_area_code 0x0001
+ training_sequence_code 7
+ base_station_id_code 63
+ ms max power 15
+ cell reselection hysteresis 4
+ rxlev access min 0
+ channel allocator mode set-all ascending
+ rach tx integer 9
+ rach max transmission 7
+ oml e1 line 0 timeslot 1 sub-slot full <3>
+ oml e1 tei 62 <4>
+ gprs mode none
+ is-connection-list add 4 512 12 <5>
+ is-connection-list add 16 524 12
+ is-connection-list add 28 536 12
+ is-connection-list add 40 548 12
+ trx 0
+ rf_locked 0
+ arfcn 123
+ nominal power 42
+ max_power_red 12
+ rsl e1 line 0 timeslot 1 sub-slot full <6>
+ rsl e1 tei 0 <7>
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ e1 line 0 timeslot 1 sub-slot full <8>
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 2 sub-slot 1 <9>
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 2 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 2 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 3 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 3 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 3 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 3 sub-slot 3
+----
+====
+
+<1> In this example we use a dahdi E1 card. This card has 4 ports. Here we use port numer 3. It should be noted that the dahdi driver also requires additional configuration, which is not covered by this manual.
+
+<2> In this example we use an E1 Ericsson DUG20, which uses an OML dialect, called "OM2000".
+
+<3> The first usable timeslot on an E1 line is TS1. In this example we will assume that TS1-TS3 are connected to the BTS stright through. TS1 will handle all signaling traffic. Here we assign this timeslot to OML.
+
+<4> OML always requires a TEI (Terminal Equipment Identifier) to set up. This number can be found in the manual of the BTS.
+
+<5> This BTS has an built in “Interface Switch” (IS) that offers flexible way to reconfigure the interconnection between the internal components of the BTS and the external E1 line. This depends on the exact BTS type and configuration.
+
+<6> Similar to OML we assign TS1 to RSL as well.
+
+<7> Like with OML, RSL also requires a TEI to be configured. Usually each TRX will have a specific TEI assigned.
+
+<8> CCCH+SDCCH4 will also be mapped on TS1. The traffic for those control channels will be multiplexed alongside the RSL and OML traffic.
+
+<9> The bandwidth of one E1 timeslot matches the bandwidth of 4 GSM air interface timeslots. The E1 timeslot is split up into four sub-slots, which are then assigned to one GSM air interface timeslot each. Since the first timeslot on the first TRX is already used for signaling we begin the sub-slot counting with sub-slot 1 for alignment reasons.
+
+=== E1 Line number and MGCP trunk number
+The switching of the voice channels is done via OsmoMGW, which acts as a media
+converter between E1 and VoIP (RTP). OsmoBSC will use the E1 line number to
+address the trunk via MGCP.
+
+When configuring OsmoMGW, one needs to make sure that the trunk number that is
+set up on OsmoMGW, matches the line number that is set up on OsmoBSC. When those
+numbers mismatch the trunk cannot be addressed correctly.
+
+.OsmoMGW trunk configuration that matches the OsmoBSC configuration above
+====
+----
+ trunk 0
+ rtp keep-alive once
+ no rtp keep-alive
+ line 0
+----
+==== \ No newline at end of file
diff --git a/doc/manuals/chapters/bts.adoc b/doc/manuals/chapters/bts.adoc
index 6e7a308c7..d119acca1 100644
--- a/doc/manuals/chapters/bts.adoc
+++ b/doc/manuals/chapters/bts.adoc
@@ -105,10 +105,10 @@ like this:
----
network
bts 0
- type sysmobts
+ type osmo-bts
band DCS1800
description The new BTS in Baikonur
- location_area_code 2342
+ location_area_code 0x0926
cell_identity 5
base_station_id_code 63
ip.access unit_id 8888 0
@@ -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`
+| |`DYNAMIC/OSMOCOM` |`DYNAMIC/IPACCESS`
|ip.access nanoBTS |- |supported
|Ericsson RBS |supported |-
|sysmoBTS using _osmo-bts-sysmo_ |supported |supported
@@ -310,11 +311,13 @@ 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 (DYNAMIC/OSMOCOM)
-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.
+`DYNAMIC/OSMOCOM` is an alias for `TCH/F_TCH/H_SDCCH8_PDCH`.
+
+Timeslots of the `DYNAMIC/OSMOCOM` type dynamically switch between TCH/F,
+TCH/H, SDCCH8 and PDCH, depending on the channel kind requested by the MSC. The RSL
+messaging for these timeslots is compatible with Ericsson RBS.
BTS models supporting this timeslot kind are shown in <<dyn_ts_compat>>.
@@ -333,10 +336,12 @@ network
In OsmoNITB, disabling TCH/F on Osmocom dynamic timeslots is the default. In
OsmoBSC, the default is to allow both.
-==== ip.access Style Dynamic Timeslots (TCH/F_PDCH)
+==== ip.access Style Dynamic Timeslots (DYNAMIC/IPACCESS)
+
+`DYNAMIC/IPACCESS` is an alias for `TCH/F_PDCH`.
-Timeslots of the `TCH/F_PDCH` type dynamically switch between TCH/F and PDCH.
-The RSL messaging for `TCH/F_PDCH` timeslots is compatible with ip.access
+Timeslots of the `DYNAMIC/IPACCESS` type dynamically switch between TCH/F and PDCH.
+The RSL messaging for `DYNAMIC/IPACCESS` timeslots is compatible with ip.access
nanoBTS.
BTS models supporting this timeslot kind are shown in <<dyn_ts_compat>>.
@@ -349,7 +354,7 @@ timeslots to TCH, and no PDCH timeslots would be left for GPRS service.
==== Dynamic Timeslot Configuration Examples
-This is an extract of an `osmo-bsc`` config file. A timeslot configuration with
+This is an extract of an `osmo-bsc` config file. A timeslot configuration with
five Osmocom style dynamic timeslots and one dedicated PDCH may look like this:
----
@@ -361,20 +366,20 @@ network
timeslot 1
phys_chan_config SDCCH8
timeslot 2
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config DYNAMIC/OSMOCOM
timeslot 3
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config DYNAMIC/OSMOCOM
timeslot 4
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config DYNAMIC/OSMOCOM
timeslot 5
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config DYNAMIC/OSMOCOM
timeslot 6
- phys_chan_config TCH/F_TCH/H_PDCH
+ phys_chan_config DYNAMIC/OSMOCOM
timeslot 7
phys_chan_config PDCH
----
-With the ip.access nanoBTS, only `TCH/F_PDCH` dynamic timeslots are supported,
+With the ip.access nanoBTS, only `DYNAMIC/IPACCESS` dynamic timeslots are supported,
and hence a nanoBTS configuration may look like this:
----
@@ -386,15 +391,15 @@ network
timeslot 1
phys_chan_config SDCCH8
timeslot 2
- phys_chan_config TCH/F_PDCH
+ phys_chan_config DYNAMIC/IPACCESS
timeslot 3
- phys_chan_config TCH/F_PDCH
+ phys_chan_config DYNAMIC/IPACCESS
timeslot 4
- phys_chan_config TCH/F_PDCH
+ phys_chan_config DYNAMIC/IPACCESS
timeslot 5
- phys_chan_config TCH/F_PDCH
+ phys_chan_config DYNAMIC/IPACCESS
timeslot 6
- phys_chan_config TCH/F_PDCH
+ phys_chan_config DYNAMIC/IPACCESS
timeslot 7
phys_chan_config PDCH
----
@@ -469,23 +474,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 +519,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/chan_alloc.adoc b/doc/manuals/chapters/chan_alloc.adoc
new file mode 100644
index 000000000..ae2ce211e
--- /dev/null
+++ b/doc/manuals/chapters/chan_alloc.adoc
@@ -0,0 +1,156 @@
+== Channel allocation
+
+Radio resource management is one of the main tasks of the Base Station Controller.
+This involves selection, activation, and deactivation of logical channels, which
+are maintained by connected Base Stations. The number of usable logical channels
+is limited by total number of radio carriers and may vary depending on the physical
+channel combinations assigned to their timeslots. Thus a major goal of the this
+task is to manage all the available resources in an efficient way, shifting the
+balance between service quality and the overall capacity.
+
+=== Channel allocation parameters
+
+OsmoBSC's channel allocator can be configured via the VTY interface. All
+relevant parameters are limited by the scope of a BTS node they belong to.
+There is currently no way to define global configuration for all BTS.
+
+All parameters with their respective default values are listed below:
+
+----
+network
+ bts 0
+ channel allocator mode chan-req ascending
+ channel allocator mode assignment ascending
+ channel allocator mode handover ascending
+ channel allocator avoid-interference 0
+ channel allocator tch-signalling-policy always
+----
+
+==== Channel allocation modes
+
+Currently the following channel allocation modes are supported:
+
+- ascending (default): allocates channels in ascending order,
+starting from timeslot 0 of the first TRX (also called C0, the BCCH carrier);
+- descending: allocates channels in descending order,
+starting from timeslot 7 of the last TRX;
+- dynamic (only for assignment): dynamically choose between ascending
+and descending order depending on some additional parameters
+(see <<chan_alloc_dyn_mode>>).
+
+NOTE: Regardless of the chosen mode, logical channels (sub-slots) are always
+selected in ascending order. For example, if a timeslot is configured as SDCCH/8
+and all 8 sub-slots are not in use, then the first SDCCH(0) sub-slot will be
+selected in both ascending and descending modes.
+
+The allocation mode to be used can be configured using the following VTY command:
+
+----
+OsmoBSC(config-net-bts)# channel allocator mode ? <1>
+ set-all Set a single mode for all variants
+ chan-req Channel allocation for CHANNEL REQUEST (RACH)
+ assignment Channel allocation for assignment
+ handover Channel allocation for handover
+
+OsmoBSC(config-net-bts)# channel allocator mode set-all ? <2>
+ ascending Allocate Timeslots and Transceivers in ascending order
+ descending Allocate Timeslots and Transceivers in descending order
+
+OsmoBSC(config-net-bts)# channel allocator mode assignment ? <3>
+ ascending Allocate Timeslots and Transceivers in ascending order
+ descending Allocate Timeslots and Transceivers in descending order
+ dynamic Dynamic lchan selection based on configured parameters <3>
+----
+<1> It's optionally possible to configure different allocation modes for
+different allocation causes, e.g. `ascending` for `chan-req` and `descending`
+for both `assignment` and `handover`.
+<2> `set-all` is equivalent to the old (deprecated) command syntax:
+`channel allocator (ascending|descending)`.
+<3> The `dynamic` mode can be selected only for `assignment`.
+
+[[chan_alloc_dyn_mode]]
+===== Dynamic channel allocation mode
+
+There exists an additional channel allocation mode, which can be employed
+during a TCH channel allocation for assignment. This mode selects between
+ascending and descending order depending on pre-configured parameters:
+
+- Uplink RxLev threshold and number of samples for averaging,
+- C0 (BCCH carrier) channel load threshold.
+
+This is useful in setups where Tx power of the RF carriers cannot be adjusted
+dynamically at run-time and thus no BS Power Control can be performed. In
+such setups the BCCH carrier is transmitting at relatively higher power than
+the other RF carriers. The key idea is to allocate channels in a smarter way,
+so that UEs with poor signal would get channels on carriers with high Tx power,
+while UEs with good signal could use carriers with lower Tx power.
+
+The configuration parameters for dynamic selection are listed below:
+
+----
+OsmoBSC(config-net-bts)# channel allocator dynamic-param ?
+ sort-by-trx-power Whether to sort TRX instances by their respective power levels
+ ul-rxlev Uplink RxLev
+ c0-chan-load C0 (BCCH carrier) channel load
+
+channel allocator dynamic-param sort-by-trx-power ?
+ 0 Do not sort, use the same order as in the configuration file
+ 1 Sort TRX instances by their power levels in descending order
+
+OsmoBSC(config-net-bts)# channel allocator dynamic-param ul-rxlev thresh ?
+ <0-63> Uplink RxLev threshold
+OsmoBSC(config-net-bts)# channel allocator dynamic-param ul-rxlev thresh 50 avg-num ?
+ <1-10> Minimum number of RxLev samples for averaging
+OsmoBSC(config-net-bts)# channel allocator dynamic-param c0-chan-load thresh ?
+ <0-100> Channel load threshold (in %)
+----
+
+The default values are:
+
+----
+network
+ bts 0
+ channel allocator dynamic-param sort-by-trx-power 0 <1>
+ channel allocator dynamic-param ul-rxlev thresh 50 avg-num 2 <2>
+ channel allocator dynamic-param c0-chan-load thresh 60 <3>
+----
+<1> Assume that RF carriers are listed in descending order sorted by Tx power.
+<2> Use descending order if AVG of at least two Uplink RxLev samples >= 50 (-60 dBm).
+<3> Use descending order if more than 60% logical channels of C0 are occupied.
+
+NOTE: The final ascending/descending order decision is based on the two conditions.
+The descending order will be used only if *both conditions are met*, otherwise the
+allocator will use ascending order.
+
+==== Interference aware channel allocation
+
+The channel allocator can be configured to prefer logical channels with least
+interference, based on interference measurements periodically sent by the BTSs
+(see <<interf_rep>>). This is an optional feature, which is disabled by default.
+
+----
+OsmoBSC(config-net-bts)# channel allocator avoid-interference ?
+ 0 Ignore interference levels (default). Always assign lchans
+ in a deterministic order.
+ 1 In channel allocation, prefer lchans with less interference.
+----
+
+NOTE: Interference levels are compared within the scope of the whole BTS. This
+means that the selection logic may pick channels on the other TRXes, if they are
+better according to the interference reports from the BTS. This feature makes
+the allocation order non-deterministic and therefore nullifies the meaning of
+channel allocation modes described above.
+
+==== TCH sigalling policy
+
+By default, in a situation when all SDCCHs are exhausted, OsmoBSC will be using TCH
+channels for signalling (e.g for Location Updating or call establishment). This
+behavior can be restricted to certain kinds of signalling or disabled completely.
+
+----
+OsmoBSC(config-net-bts)# channel allocator tch-signalling-policy ?
+ never Never allow TCH for signalling purposes
+ emergency Only allow TCH for signalling purposes when establishing an emergency call
+ voice Allow TCH for signalling purposes when establishing any voice call
+ always Always allow TCH for signalling purposes (default)
+----
diff --git a/doc/manuals/chapters/control.adoc b/doc/manuals/chapters/control.adoc
index 6b1609ae5..8009ec72e 100644
--- a/doc/manuals/chapters/control.adoc
+++ b/doc/manuals/chapters/control.adoc
@@ -27,10 +27,13 @@ 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.send-power-control-defaults|WO|No|Ignored|Resend default power control parameters 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 +41,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 +124,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..4affe3b41 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
@@ -221,15 +223,15 @@ network
network
bts 0
- # this cell's LAC=23 CI=5
- location_area_code 23
+ # this cell's LAC=0x0017 CI=5
+ location_area_code 0x0017
cell_identity 5
# reference bts 1
neighbor lac-ci 23 6
bts 1
- # this cell's LAC=23 CI=6
- location_area_code 23
+ # this cell's LAC=0x0017 CI=6
+ location_area_code 0x0017
cell_identity 6
# reference bts 0
neighbor lac-ci 23 5
@@ -250,8 +252,8 @@ desirable to use the `neighbor bts <0-255>` format, or omit the redundant
network
bts 0
- # this cell's LAC=23 CI=5
- location_area_code 23
+ # this cell's LAC=0x0017 CI=5
+ location_area_code 0x0017
cell_identity 5
# this cell's ARFCN=1 BSIC=1
trx 0
@@ -261,8 +263,8 @@ network
neighbor lac-ci 23 6 arfcn 2 bsic 2
bts 1
- # LAC=23 CI=6
- location_area_code 23
+ # LAC=0x0017 CI=6
+ location_area_code 0x0017
cell_identity 6
# this cell's ARFCN=2 BSIC=2
trx 0
@@ -284,8 +286,8 @@ Indication and 3.2.1.9 HANDOVER REQUIRED).
# BSC Alpha's osmo-bsc.cfg
network
bts 0
- # this cell's LAC=23 CI=6
- location_area_code 23
+ # this cell's LAC=0x0017 CI=6
+ location_area_code 0x0017
cell_identity 6
# this cell's ARFCN=2 BSIC=2
trx 0
@@ -297,8 +299,8 @@ network
# BSC Beta's osmo-bsc.cfg
network
bts 0
- # this cell's LAC=42 CI=3
- location_area_code 42
+ # this cell's LAC=0x002A CI=3
+ location_area_code 0x002A
cell_identity 3
# this cell's ARFCN=1 BSIC=3
trx 0
@@ -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..47c524f55
--- /dev/null
+++ b/doc/manuals/chapters/interf_meas.adoc
@@ -0,0 +1,73 @@
+[[interf_rep]]
+== 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 (`DYNAMIC/OSMOCOM` and `DYNAMIC/IPACCESS`), 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/osmux_bsc.adoc b/doc/manuals/chapters/osmux_bsc.adoc
index 0a11d17bf..268ffdfc8 100644
--- a/doc/manuals/chapters/osmux_bsc.adoc
+++ b/doc/manuals/chapters/osmux_bsc.adoc
@@ -14,14 +14,14 @@ one) need to be configured explicitly.
==== {program-name} in a 3GPP AoIP network setup
Osmux usage in {program-name} in managed through the VTY command `osmux
-(on|off|only)`. Once enabled (`on` or `only`), {program-name} will start
-appending the vendor specific _Osmux Support_ IE in _BSSMAP RESET_ and _BSSMAP
-RESET-ACK_ message towards the MSC in order to announce it supports Osmux. This
-way, the MSC can decide whether to use Osmux or not based on this information
-when setting up a call (this time using _Osmux CID_ IE). It should be noted that
-this option should not be enabled unless MSC managing {program-name} supports
-handling this extension IE (like OsmoMSC), a 3rd-party MSC might otherwise
-refuse the related _RESET_/_RESET-ACK_ messages.
+(on|off|only)` under the `msc` node. Once enabled (`on` or `only`),
+{program-name} will start appending the vendor specific _Osmux Support_ IE in
+_BSSMAP RESET_ and _BSSMAP RESET-ACK_ message towards the MSC in order to
+announce it supports Osmux. This way, the MSC can decide whether to use Osmux or
+not based on this information when setting up a call (this time using _Osmux
+CID_ IE). It should be noted that this option should not be enabled unless MSC
+managing {program-name} supports handling this extension IE (like OsmoMSC), a
+3rd-party MSC might otherwise refuse the related _RESET_/_RESET-ACK_ messages.
{program-name} will behave differently during call set up based on the VTY
command presented above:
@@ -41,3 +41,42 @@ command presented above:
calls on the CN-side, this is, if _BSSMAP Assign Request_ from MSC doesn't
contain an _Osmux CID_ IE, it will reject the assignment and the call set up
will fail.
+
+==== Osmux in the ip.access Abis interface
+
+{program-name} can also talk Osmux instead of RTP to an OsmoBTS which supports
+the feature. Osmux usage agains the BTS in {program-name} in managed through the
+VTY command `osmux (on|off|only)` under the `bts` node.
+
+If a BTS supports Osmux, it may announce the _OSMUX_ BTS feature towards the BSC
+over OML. This way, the {program-name} becomes aware that this BTS supports
+using Osmux to transfer voice call user data when the AMR codec is selected.
+
+It is then up to {program-name} to decide whether to use Osmux or not when
+establishing a new call. If {program-name} decides to use Osmux for a given
+call, it will instruct its co-located MGW to set up an Osmux connection in the
+endpoint (using the `X-Osmux extension`) and then it will forward the received
+Osmux CID to the BTS in the the _IPACC CRCX/MDCX_ messages by means of an extra _Osmux
+CID_ IE appended to it.
+The IP address and port provided in the same messages refer to the
+address and port where Osmux frames with the provided CID are expected to be
+received. Similarly, the BTS appends an _Osmux CID_ IE to the _IPACC
+CRCX/MDCX ACK_ message it generates, this time with its own local Osmux CID,
+which {program-name} will in turn forward back to the co-located MGW.
+Same goes for the BTS' local IP address and port where Osmux frames are expected
+to be received.
+
+{program-name} will behave differently during call set up based on the VTY
+command `use (on|off|only)` under each `bts` node presented above:
+
+* `off`: {program-name} will never attempt use of Osmux against this BTS (default).
+* `on`: {program-name} will use Osmux against the BTS if the BTS announced Osmux
+ support during OML bringup, and if MGW provided a valid Osmux CID during _MGCP
+ CRCX_. Otherwise BSC will simply automatically fall back to using RTP for each
+ call. For non-AMR calls, RTP will always be used.
+* `only`: Same as per `on`, except that {program-name} will accept only Osmux
+ calls on the BTS-side. This is, if _MGCP CRCX ACK_ from MGW doesn't
+ contain an _Osmux CID_ IE or _IPACC CRCX ACK_ from BSC doesn't
+ contain an _Osmux CID_ IE, it will reject the assignment and the call set up
+ will fail. This means also that only AMR calls (`Channel Mode GSM3`) are
+ allowed.
diff --git a/doc/manuals/chapters/power_control.adoc b/doc/manuals/chapters/power_control.adoc
new file mode 100644
index 000000000..9b48f1551
--- /dev/null
+++ b/doc/manuals/chapters/power_control.adoc
@@ -0,0 +1,645 @@
+== 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:
+
+.Example: Resending default power control parameters via the VTY
+----
+# Resending from the 'enable' node:
+OsmoBSC# bts 0 <1> resend-power-control-defaults
+
+# Resending from any configuration node (note prefix 'do'):
+OsmoBSC(config-ms-power-ctrl)# do bts 0 <1> resend-power-control-defaults
+----
+<1> BTS number for which to resend default power control parameters.
+
+.Example: Resending default power control parameters via the CTRL
+----
+$ osmo_ctrl.py \
+ --host 127.0.0.1 <1> -p 4249 \
+ --set "bts.0.send-power-control-defaults" 1 <2>
+----
+<1> Remote address of the host running osmo-bsc (localhost in this example).
+<2> An arbitrary dummy value (ignored). Required because SET command is used.
+
+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..70f9d283f 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.
@@ -136,7 +134,87 @@ default port for MGCP (2427) on local host (127.0.0.1).
Here is an example configuration for a remote MGW:
----
-msc 0
- mgw remote-ip 10.9.8.7
- mgw remote-port 2427
+network
+ mgw 0
+ remote-ip 10.9.8.7
+ remote-port 2427
+ 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.
+
+OsmoBSC is also able to handle a pool of media gateways for load
+distribution since mid 2021. See also <<mgw_pooling>>.
+
+[NOTE]
+====
+Previous versions of OsmoBSC didn't have the 'mgw' VTY node and
+hence didn't support the MGW pooling feature. Therefore, historically the MGW
+related commands where placed under the `msc` VTY node. The MGW related commands
+under the `msc` VTY are still parsed and used but its use is deprecated and
+hence discouraged in favour of the new `mgw` node. Writing the config to a file
+from within OsmoBSC will automatically convert the config to use the new `mgw`
+node.
+====
+
+===== Pinning a BTS to a specific MGW
+
+It is sometimes desirable to assign a specific MGW to a given BTS, so that all
+calls where the BTS is involved use the assigned MGW with a higher precedence if
+possible.
+
+This is specially important if the BTS is configured to serve calls using Osmux
+instead of RTP. Osmux features trunking optimizations, which allow transmission
+of audio payload from different concurrent calls inside the same underlaying UDP
+packet, hence reducing the total required throughput and saving costs on the
+required link.
+
+In order for Osmux trunking optimization to work, the source and destination IP
+address of uderlaying UDP packet must be of course the same for all the calls
+involved. That essentially boils down to having all the concurrent calls of the
+BTS be connected to the same MGW so that they can be trunked over the same UDP
+connection.
+
+The pinning to a specific MGW can be configured per BTS, and hence it is
+configured under the `bts` VTY node:
+
+----
+OsmoBSC> enable
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 1
+OsmoBSC(config-bts)# mgw pool-target 1 <1>
+OsmoBSC(config-bts)# exit
+OsmoBSC(config-net)# bts 2
+OsmoBSC(config-mgw)# mgw pool-target 7 strict <2>
+OsmoBSC(config-net)# bts 3
+OsmoBSC(config-mgw)# no mgw pool-target <3>
----
+
+<1> Pin BTS1 to prefer MGW1 (node `mgw 1`). If MGW1 is not configured,
+administrateivly blocked or not connected at the time a new call is to be
+established, then another MGW from the pool is selected following the usual
+procedures. This allows applying pinning in the usual scenario while still
+keeping call service ongoing against another MGW if the preferred MGW is not
+available at a given time.
+
+<2> Pin BTS2 to prefer MGW7 (node `mgw 7`). If MGW7 is not configured,
+administrateivly blocked or not connected at the time a new call is to be
+established, then the MGW assignment will fail and ultimately the call will be
+terminated during establishment.
+
+<3> Apply no pinning at all (default). The MGW with the lowest load is the one
+being selected for each new call.
+
+==== 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-abis_a.msc b/doc/manuals/message-sequences/mo_call-abis_a.msc
index ba7f0aa18..2d988fcf3 100644
--- a/doc/manuals/message-sequences/mo_call-abis_a.msc
+++ b/doc/manuals/message-sequences/mo_call-abis_a.msc
@@ -85,8 +85,8 @@ linecolor="green"];
...;
--- [label="BSC finally can report successful TCH assignment"];
- bsc -> m_sc [label="SCCP DT1 (BSSMAP ASSGN CMPL (3GPP AoIP MGW:3000))"];
- m_sc box m_sc [label="Connect remote RTP to MGW addr from ASSGN CMPL"];
+ bsc -> m_sc [label="SCCP DT1 (BSSMAP ASSIGN CMPL (3GPP AoIP MGW:3000))"];
+ m_sc box m_sc [label="Connect remote RTP to MGW addr from ASSIGN CMPL"];
...;
mgw <-> m_sc [label="RTP Audio MGW:3000 MSC:4000", textcolor="blue", linecolor="blue"];
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/classic-bsc.msc b/doc/manuals/mgw/classic-bsc.msc
index 97b65131e..e2257298b 100644
--- a/doc/manuals/mgw/classic-bsc.msc
+++ b/doc/manuals/mgw/classic-bsc.msc
@@ -10,13 +10,13 @@ msc {
ms -> m_sc [label="DTAP CC SETUP"];
ms <- m_sc [label="DTAP CC CALL PROCEEDING"];
- bsc <- m_sc [label="BSSAP ASSGN REQ"];
+ bsc <- m_sc [label="BSSAP ASSIGN REQ"];
bsc box m_sc [label="E1 TS for PCM specified by CIC"];
bts <- bsc [label="RSL CHAN ACT"];
bts -> bsc [label="RSL CHAN ACT ACK"];
bts box bsc [label="E1 TS + 16k sub-slot configured for given lchan"];
ms <-> bsc [label="Assignment"];
- bsc -> m_sc [label="BSSAP ASSGN CMPL"];
+ bsc -> m_sc [label="BSSAP ASSIGN CMPL"];
...;
trau <- m_sc [label="PCM Audio in full E1 slot"];
diff --git a/doc/manuals/mgw/osmo-bsc-new-mgw-e1.msc b/doc/manuals/mgw/osmo-bsc-new-mgw-e1.msc
index 04b114fff..d58987f6d 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"];
...;
@@ -11,7 +11,7 @@ msc {
ms <- m_sc [label="DTAP CC CALL PROCEEDING"];
m_sc box m_sc [label="Bind arbitrary local port (4000)"];
- bsc <- m_sc [label="BSSAP ASSGN REQ (3GPP AoIP MSC:4000)"];
+ bsc <- m_sc [label="BSSAP ASSIGN REQ (3GPP AoIP MSC:4000)"];
bts <- bsc [label="RSL CHAN ACT"];
bts -> bsc [label="RSL CHAN ACT ACK"];
ms <-> bsc [label="Assignment"];
@@ -22,8 +22,8 @@ msc {
mgcp -> bsc [label="MGCP CRCX ts1/ss2@mgw OK (MGW:3000)"];
...;
- bsc -> m_sc [label="BSSAP ASSGN CMPL (3GPP AoIP MGW:3000)"];
- m_sc box m_sc [label="Connect remote RTP to MGW addr from ASSGN CMPL"];
+ bsc -> m_sc [label="BSSAP ASSIGN CMPL (3GPP AoIP MGW:3000)"];
+ m_sc box m_sc [label="Connect remote RTP to MGW addr from ASSIGN CMPL"];
...;
mgcp <=> m_sc [label="RTP Audio MGW:3000 MSC:4000"];
diff --git a/doc/manuals/mgw/osmo-bsc-new-mgw.msc b/doc/manuals/mgw/osmo-bsc-new-mgw.msc
index e618bb721..d60a79f5e 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"];
...;
@@ -10,7 +10,7 @@ msc {
ms <- m_sc [label="DTAP CC CALL PROCEEDING"];
m_sc box m_sc [label="Bind arbitrary local port (4000)"];
- bsc <- m_sc [label="BSSAP ASSGN REQ (3GPP AoIP MSC:4000)"];
+ bsc <- m_sc [label="BSSAP ASSIGN REQ (3GPP AoIP MSC:4000)"];
bts <- bsc [label="RSL CHAN ACT"];
bts -> bsc [label="RSL CHAN ACT ACK"];
ms <-> bsc [label="Assignment"];
@@ -35,8 +35,8 @@ msc {
mgcp -> bsc [label="MGCP CRCX rtpbridge/2@mgw OK (MGW:3000)"];
...;
- bsc -> m_sc [label="BSSAP ASSGN CMPL (3GPP AoIP MGW:3000)"];
- m_sc box m_sc [label="Connect remote RTP to MGW addr from ASSGN CMPL"];
+ bsc -> m_sc [label="BSSAP ASSIGN CMPL (3GPP AoIP MGW:3000)"];
+ m_sc box m_sc [label="Connect remote RTP to MGW addr from ASSIGN CMPL"];
...;
mgcp <=> m_sc [label="RTP Audio MGW:3000 MSC:4000"];
diff --git a/doc/manuals/mgw/osmo-bsc-old-sccplite.msc b/doc/manuals/mgw/osmo-bsc-old-sccplite.msc
index 067721c46..cd6cd648c 100644
--- a/doc/manuals/mgw/osmo-bsc-old-sccplite.msc
+++ b/doc/manuals/mgw/osmo-bsc-old-sccplite.msc
@@ -10,11 +10,11 @@ msc {
ms -> m_sc [label="DTAP CC SETUP"];
ms <- m_sc [label="DTAP CC CALL PROCEEDING"];
- bsc <- m_sc [label="BSSAP ASSGN REQ"];
+ bsc <- m_sc [label="BSSAP ASSIGN REQ"];
bts <- bsc [label="RSL CHAN ACT"];
bts -> bsc [label="RSL CHAN ACT ACK"];
ms <-> bsc [label="Assignment"];
- bsc -> m_sc [label="BSSAP ASSGN CMPL"];
+ bsc -> m_sc [label="BSSAP ASSIGN CMPL"];
...;
bts <- bsc [label="IPA CRCX"];
diff --git a/doc/manuals/osmobsc-cbsp-docinfo.xml b/doc/manuals/osmobsc-cbsp-docinfo.xml
new file mode 100644
index 000000000..a86fd286e
--- /dev/null
+++ b/doc/manuals/osmobsc-cbsp-docinfo.xml
@@ -0,0 +1,36 @@
+
+<authorgroup>
+ <author>
+ <firstname>Harald</firstname>
+ <surname>Welte</surname>
+ <email>hwelte@sysmocom.de</email>
+ <authorinitials>HW</authorinitials>
+ <affiliation>
+ <shortaffil>sysmocom</shortaffil>
+ <orgname>sysmocom - s.f.m.c. GmbH</orgname>
+ <jobtitle>Managing Director</jobtitle>
+ </affiliation>
+ </author>
+</authorgroup>
+
+<copyright>
+ <year>2022</year>
+ <holder>sysmocom - s.f.m.c. GmbH</holder>
+</copyright>
+
+<legalnotice>
+ <para>
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the GNU Free Documentation License,
+ Version 1.3 or any later version published by the Free Software
+ Foundation; with no Invariant Sections, no Front-Cover Texts,
+ and no Back-Cover Texts. A copy of the license is included in
+ the section entitled "GNU Free Documentation License".
+ </para>
+ <para>
+ The Asciidoc source code of this manual can be found at
+ <ulink url="https://git.osmocom.org/osmo-bsc/">
+ https://git.osmocom.org/osmo-bsc/
+ </ulink>
+ </para>
+</legalnotice>
diff --git a/doc/manuals/osmobsc-cbsp.adoc b/doc/manuals/osmobsc-cbsp.adoc
new file mode 100644
index 000000000..89af8dfcc
--- /dev/null
+++ b/doc/manuals/osmobsc-cbsp.adoc
@@ -0,0 +1,69 @@
+:gfdl-enabled:
+
+OsmoBSC CBSP Protocol Specification
+===================================
+Harald Welte <hwelte@sysmocom.de>
+
+== Introduction
+
+This document describes the CBSP interface of *OsmoBSC* as spoken on the
+BSC-CBC interface. Based on 3GPP TS 48.049 <<3gpp-ts-48-049>>, this document indicates
+which of the 3GPP specified CBSP messages and IEs are implemented
+according to 3GPP specifications, which of these are not or not fully
+implemented, as well as OsmoBSC-specific extensions to the CBSP
+interface not specified by 3GPP.
+
+For details on the standard CBSP messages and IE definitions,
+please refer to the 3GPP documents.
+
+.3GPP document versions referred to by this document
+[cols="20%,80%"]
+|===
+|3GPP TS 48.049 | version 12.0.0 Release 12
+|===
+
+.IETF documents referred to by his document
+[cols="20%,80%"]
+|===
+|IETF RFC 793 | Transmission Control Protocol
+|===
+
+== Overview
+
+The OsmoBSC BSC-CBC interface consists of CBSP messages transmitted over
+TCP.
+
+The default TCP destination port number is TCP port 48049; this can be
+changed by configuration, as described in the OsmoBSC user manual
+<<userman-osmobsc>> and/or VTY reference manual <<vty-ref-osmobsc>>.
+
+.TCP port numbers used by OsmoBTS Abis/IP
+[options="header",width="50%",cols="35%,65%"]
+|===
+|TCP Port Number|Usage
+|48049|CBSP
+|===
+
+OsmoBSC implements both _TCP server_ and _TCP client_ role; it is hence
+configurable whether the CBC establishes the TCP connection to the BSC
+(BSC in _TCP server_ role) or if the BSC establishes the TCP connection
+to the CBC (BSC in _TCP client_ role).
+
+Currently, only transport of TCP via IPv4 is implemented.
+
+Any IP-capable link-layer protocol implemented in the underlying Linux
+operating system can be used to transport the IP/TCP/CBSP of OsmoBSC.
+
+
+include::{srcdir}/cbsp/procedures.adoc[]
+
+include::{srcdir}/cbsp/messages.adoc[]
+
+
+include::./common/chapters/port_numbers.adoc[]
+
+include::./common/chapters/bibliography.adoc[]
+
+include::./common/chapters/glossary.adoc[]
+
+include::./common/chapters/gfdl.adoc[]
diff --git a/doc/manuals/osmobsc-usermanual.adoc b/doc/manuals/osmobsc-usermanual.adoc
index a34d3d422..2e77e1b5f 100644
--- a/doc/manuals/osmobsc-usermanual.adoc
+++ b/doc/manuals/osmobsc-usermanual.adoc
@@ -24,12 +24,24 @@ include::{srcdir}/chapters/bts-examples.adoc[]
include::{srcdir}/chapters/bsc.adoc[]
+include::{srcdir}/chapters/chan_alloc.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::./common/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 +56,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/doc/mscpool-attach.dot b/doc/mscpool-attach.dot
new file mode 100644
index 000000000..f3d0a524f
--- /dev/null
+++ b/doc/mscpool-attach.dot
@@ -0,0 +1,30 @@
+digraph G {
+ rankdir=LR
+ labelloc=t; label="OsmoBSC MSC-pool conn (re)direction"
+
+ subgraph cluster_msc_usable {
+ style=dotted; label="MSC usable\n(successful BSSMAP RESET)"
+
+ allowed [label="MSC\nallow-attach"]
+ notallowed [label="MSC\nno allow-attach\n(MSC should respond\nwith null-NRI)"]
+ }
+
+ subgraph cluster_msc_unusable {
+ style=dotted; label="MSC unusable\n(no link)"
+ allowed_unusable [label="MSC\nallow-attach"]
+ notallowed_unusable [label="MSC\nno allow-attach"]
+ }
+
+ TMSI_unknown_NRI [label="TMSI, NRI unknown"]
+ TMSI_null_NRI [label="TMSI, null-NRI"]
+ TMSI_known_NRI [label="TMSI, NRI known"]
+
+ IMSI -> allowed
+ TMSI_unknown_NRI -> allowed
+ TMSI_null_NRI -> allowed
+ TMSI_known_NRI -> allowed
+ TMSI_known_NRI -> notallowed
+
+ never [style=dotted]
+ never -> {allowed_unusable, notallowed_unusable} [style=dotted]
+}
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index 1ee96ed50..53d45adbf 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -2,18 +2,22 @@ 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_setup_ramp.h \
bts_trx.h \
bts_ipaccess_nanobts_omlattr.h \
chan_alloc.h \
+ chan_counts.h \
codec_pref.h \
ctrl.h \
debug.h \
@@ -22,19 +26,25 @@ 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.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 +55,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 +65,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..ea029e601 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);
@@ -46,7 +55,7 @@ int rsl_encryption_cmd(struct msgb *msg);
int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group,
const struct osmo_mobile_identity *mi,
uint8_t chan_needed, bool is_gprs);
-int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val);
+int rsl_imm_assign_cmd(const struct gsm_bts *bts, uint8_t len, const uint8_t *val);
int rsl_tx_imm_assignment(struct gsm_lchan *lchan);
int rsl_tx_imm_ass_rej(struct gsm_bts *bts, struct gsm48_req_ref *rqd_ref);
int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command);
@@ -56,7 +65,8 @@ int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id);
int rsl_relase_request(struct gsm_lchan *lchan, uint8_t link_id);
/* Ericcson vendor specific RSL extensions */
-int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len, uint8_t *val);
+int rsl_ericsson_imm_assign_cmd(const struct gsm_bts *bts, uint32_t tlli, uint8_t len,
+ const uint8_t *val, uint8_t pag_grp);
/* Siemens vendor-specific RSL extensions */
int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci);
@@ -73,8 +83,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/acc.h b/include/osmocom/bsc/acc.h
index c7be38cb3..531a69acf 100644
--- a/include/osmocom/bsc/acc.h
+++ b/include/osmocom/bsc/acc.h
@@ -161,6 +161,8 @@ static inline unsigned int acc_ramp_is_running(struct acc_ramp *acc_ramp)
return acc_ramp->step_interval_sec;
}
+void acc_ramp_global_init(void);
+
void acc_ramp_init(struct acc_ramp *acc_ramp, struct gsm_bts *bts);
int acc_ramp_set_step_size(struct acc_ramp *acc_ramp, unsigned int step_size);
int acc_ramp_set_step_interval(struct acc_ramp *acc_ramp, unsigned int step_interval);
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..70cb88cae 100644
--- a/include/osmocom/bsc/assignment_fsm.h
+++ b/include/osmocom/bsc/assignment_fsm.h
@@ -4,6 +4,7 @@
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/lchan.h>
/* This macro automatically includes a final \n, if omitted. */
#define LOG_ASSIGNMENT(conn, level, fmt, args...) do { \
@@ -17,18 +18,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,
@@ -37,8 +40,12 @@ enum assignment_fsm_event {
ASSIGNMENT_EV_CONN_RELEASING,
};
-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);
+void assignment_fsm_update_id(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..47bdf123a 100644
--- a/include/osmocom/bsc/bsc_msc_data.h
+++ b/include/osmocom/bsc/bsc_msc_data.h
@@ -59,8 +59,9 @@ enum {
MSC_CTR_BSSMAP_RX_UDT_UNKNOWN,
MSC_CTR_BSSMAP_RX_DT1_CLEAR_CMD,
MSC_CTR_BSSMAP_RX_DT1_CIPHER_MODE_CMD,
- MSC_CTR_BSSMAP_RX_DT1_ASSIGMENT_RQST,
+ MSC_CTR_BSSMAP_RX_DT1_ASSIGNMENT_RQST,
MSC_CTR_BSSMAP_RX_DT1_LCLS_CONNECT_CTRL,
+ MSC_CTR_BSSMAP_RX_DT1_HANDOVER_RQST,
MSC_CTR_BSSMAP_RX_DT1_HANDOVER_CMD,
MSC_CTR_BSSMAP_RX_DT1_CLASSMARK_RQST,
MSC_CTR_BSSMAP_RX_DT1_CONFUSION,
@@ -128,14 +129,17 @@ 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;
bool amr_octet_aligned;
- struct gsm_audio_support **audio_support;
+
+ /* Practically, there can be only 5 entries in osmo-bsc: FR1 FR2 FR3 HR1 HR3. Historically, osmo-bsc allowed
+ * *any* number of entries -- we should not break too strictly on old cfg files. Theoretically, there should be
+ * room for FR1 to FR7 plus HR1 to HR7 less HR2. Let's just give ample room for all these aspects: */
+ struct gsm_audio_support audio_support[16];
int audio_length;
+
enum bsc_lcls_mode lcls_mode;
bool lcls_codec_mismatch_allow;
@@ -171,7 +175,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..eae97509c
--- /dev/null
+++ b/include/osmocom/bsc/bsc_stats.h
@@ -0,0 +1,116 @@
+/* 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;
+struct gsm_bts;
+
+/* 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_EXPIRED,
+ 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 all_allocated_update_bts(struct gsm_bts *bts);
+void all_allocated_update_bsc(void);
diff --git a/include/osmocom/bsc/bsc_subscr_conn_fsm.h b/include/osmocom/bsc/bsc_subscr_conn_fsm.h
index 142d535c2..766d56a49 100644
--- a/include/osmocom/bsc/bsc_subscr_conn_fsm.h
+++ b/include/osmocom/bsc/bsc_subscr_conn_fsm.h
@@ -2,12 +2,14 @@
#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"
enum gscon_fsm_event {
/* local SCCP stack tells us incoming conn from MSC */
GSCON_EV_A_CONN_IND,
+ GSCON_EV_A_INITIAL_USER_DATA,
/* RSL side requests CONNECT to MSC */
GSCON_EV_MO_COMPL_L3,
/* MSC confirms the SCCP connection */
@@ -48,11 +50,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;
@@ -60,8 +57,6 @@ struct osmo_mgcpc_ep_ci;
struct assignment_request;
struct gsm_lchan;
-void bsc_subscr_conn_fsm_init();
-
/* Allocate a subscriber connection and its associated FSM */
struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net);
void gscon_update_id(struct gsm_subscriber_connection *conn);
@@ -69,6 +64,7 @@ void gscon_update_id(struct gsm_subscriber_connection *conn);
void gscon_submit_rsl_dtap(struct gsm_subscriber_connection *conn,
struct msgb *msg, int link_id, int allow_sacch);
int gscon_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg);
+void gscon_bssmap_clear(struct gsm_subscriber_connection *conn, enum gsm0808_cause cause);
struct osmo_mgcpc_ep *gscon_ensure_mgw_endpoint(struct gsm_subscriber_connection *conn,
uint16_t msc_assigned_cic, struct gsm_lchan *for_lchan);
@@ -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/bsc_subscriber.h b/include/osmocom/bsc/bsc_subscriber.h
index 6fffafdb3..2beb99850 100644
--- a/include/osmocom/bsc/bsc_subscriber.h
+++ b/include/osmocom/bsc/bsc_subscriber.h
@@ -10,13 +10,19 @@
#include <osmocom/gsm/gsm48.h>
struct log_target;
+struct gsm_bts;
struct bsc_subscr {
struct llist_head entry;
struct osmo_use_count use_count;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
+ char imei[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
uint32_t tmsi;
+
+ /* List head of (struct gsm_paging_request).bsub_entry */
+ uint32_t active_paging_requests_len;
+ struct llist_head active_paging_requests;
};
const char *bsc_subscr_name(struct bsc_subscr *bsub);
@@ -25,6 +31,9 @@ const char *bsc_subscr_id(struct bsc_subscr *bsub);
struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list,
const char *imsi,
const char *use_token);
+struct bsc_subscr *bsc_subscr_find_or_create_by_imei(struct llist_head *list,
+ const char *imei,
+ const char *use_token);
struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list,
uint32_t tmsi,
const char *use_token);
@@ -34,6 +43,9 @@ struct bsc_subscr *bsc_subscr_find_or_create_by_mi(struct llist_head *list, cons
struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list,
const char *imsi,
const char *use_token);
+struct bsc_subscr *bsc_subscr_find_by_imei(struct llist_head *list,
+ const char *imei,
+ const char *use_token);
struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list,
uint32_t tmsi,
const char *use_token);
@@ -41,6 +53,7 @@ struct bsc_subscr *bsc_subscr_find_by_mi(struct llist_head *list, const struct o
const char *use_token);
void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi);
+void bsc_subscr_set_imei(struct bsc_subscr *bsub, const char *imei);
#define bsc_subscr_get(bsc_subscr, use) \
OSMO_ASSERT(osmo_use_count_get_put(&(bsc_subscr)->use_count, use, 1) == 0)
@@ -49,3 +62,9 @@ void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi);
void log_set_filter_bsc_subscr(struct log_target *target,
struct bsc_subscr *bsub);
+
+struct gsm_paging_request;
+void bsc_subscr_add_active_paging_request(struct bsc_subscr *bsub, struct gsm_paging_request *req);
+void bsc_subscr_remove_active_paging_request(struct bsc_subscr *bsub, struct gsm_paging_request *req);
+void bsc_subscr_remove_active_paging_request_all(struct bsc_subscr *bsub);
+struct gsm_paging_request *bsc_subscr_find_req_by_bts(const struct bsc_subscr *bsub, const struct gsm_bts *bts);
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..4faf5f291 100644
--- a/include/osmocom/bsc/bts.h
+++ b/include/osmocom/bsc/bts.h
@@ -12,14 +12,36 @@
#include <osmocom/abis/e1_input.h>
+#include "osmocom/bsc/power_control.h"
#include "osmocom/bsc/gsm_data.h"
#include "osmocom/bsc/bts_trx.h"
+#include "osmocom/bsc/bts_sm.h"
+#include "osmocom/bsc/abis_om2000.h"
+#include "osmocom/bsc/paging.h"
+#include "osmocom/bsc/bts_setup_ramp.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,
@@ -34,8 +56,15 @@ enum bts_counter_id {
BTS_CTR_PAGING_EXPIRED,
BTS_CTR_PAGING_NO_ACTIVE_PAGING,
BTS_CTR_PAGING_MSC_FLUSH,
+ BTS_CTR_PAGING_OVERLOAD,
BTS_CTR_CHAN_ACT_TOTAL,
+ BTS_CTR_CHAN_ACT_SDCCH,
+ BTS_CTR_CHAN_ACT_TCH,
BTS_CTR_CHAN_ACT_NACK,
+ BTS_CTR_CHAN_TCH_ACTIVE_MILLISECONDS_TOTAL,
+ BTS_CTR_CHAN_SDCCH_ACTIVE_MILLISECONDS_TOTAL,
+ BTS_CTR_CHAN_TCH_FULLY_ESTABLISHED,
+ BTS_CTR_CHAN_SDCCH_FULLY_ESTABLISHED,
BTS_CTR_RSL_UNKNOWN,
BTS_CTR_RSL_IPA_NACK,
BTS_CTR_RSL_DELETE_IND,
@@ -50,6 +79,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 +94,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 +139,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 +159,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 +217,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 +226,15 @@ enum {
BTS_STAT_RSL_CONNECTED,
BTS_STAT_LCHAN_BORKEN,
BTS_STAT_TS_BORKEN,
+ BTS_STAT_NUM_TRX_RSL_CONNECTED,
+ BTS_STAT_NUM_TRX_TOTAL,
+ BTS_STAT_PAGING_REQ_QUEUE_LENGTH,
+ BTS_STAT_PAGING_AVAILABLE_SLOTS,
+ BTS_STAT_PAGING_T3113,
};
-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,
@@ -331,6 +264,13 @@ enum bts_attribute {
TRX_PHY_VERSION,
};
+enum bts_tch_signalling_policy {
+ BTS_TCH_SIGNALLING_NEVER,
+ BTS_TCH_SIGNALLING_EMERG,
+ BTS_TCH_SIGNALLING_VOICE,
+ BTS_TCH_SIGNALLING_ALWAYS,
+};
+
struct vty;
struct gsm_bts_model {
@@ -341,12 +281,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);
@@ -356,9 +311,19 @@ struct gsm_bts_model {
struct tlv_definition nm_att_tlvdef;
- /* features of a given BTS model set via gsm_bts_model_register() locally */
+ /* features of a given BTS model set via gsm_bts_model_register()
+ * locally, see doc/bts-features.txt */
struct bitvec features;
uint8_t _features_data[MAX_BTS_FEATURES/8];
+ /* BTS reports features during OML bring up */
+ bool features_get_reported;
+};
+
+struct gsm_gprs_cell {
+ struct gsm_abis_mo mo;
+ uint16_t bvci;
+ uint8_t timer[11];
+ struct gprs_rlc_cfg rlc_cfg;
};
/* One BTS */
@@ -388,12 +353,18 @@ struct gsm_bts {
char version[MAX_VERSION_LENGTH];
char sub_model[MAX_VERSION_LENGTH];
- /* features of a given BTS set/reported via OML */
+ /* features of a given BTS either hardcoded or set/reported via OML,
+ * see doc/bts-features.txt */
struct bitvec features;
uint8_t _features_data[MAX_BTS_FEATURES/8];
+ /* Features have been reported by the BTS or were copied from the BTS
+ * model */
+ bool features_known;
/* 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 +396,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 +430,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 +463,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,26 +480,24 @@ 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 */
- int ccch_load_ind_thresh;
+ /* CCCH Load Threshold: threshold (in percent) when BTS shall send CCCH LOAD IND */
+ uint8_t ccch_load_ind_thresh;
+ /* CCCH Load Indication Period: how often (secs) to send CCCH LOAD IND when over CCCH Load Threshold. */
+ uint8_t ccch_load_ind_period;
/* RACH NM values */
int rach_b_thresh;
@@ -551,7 +520,26 @@ struct gsm_bts {
/* should the channel allocator allocate channels from high TRX to TRX0,
* rather than starting from TRX0 and go upwards? */
- int chan_alloc_reverse;
+ bool chan_alloc_chan_req_reverse;
+ bool chan_alloc_assignment_reverse;
+ bool chan_alloc_handover_reverse;
+
+ /* Whether to use dynamic allocation mode for assignment */
+ bool chan_alloc_assignment_dynamic;
+ /* Parameters used for dynamic mode of allocation */
+ struct {
+ bool sort_by_trx_power;
+ uint8_t ul_rxlev_thresh;
+ uint8_t ul_rxlev_avg_num;
+ uint8_t c0_chan_load_thresh;
+ } chan_alloc_dyn_params;
+
+ /* 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;
+
+ /* If SDCCHs are exhausted, when can we use TCH for signalling purposes. */
+ enum bts_tch_signalling_policy chan_alloc_tch_signalling_policy;
enum neigh_list_manual_mode neigh_list_manual_mode;
/* parameters from which we build SYSTEM INFORMATION */
@@ -559,7 +547,7 @@ struct gsm_bts {
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 +555,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];
@@ -594,6 +583,10 @@ struct gsm_bts {
/* exclude the BTS from the global RF Lock handling */
int excl_from_rf_lock;
+ /* MGW specificities for this BTS: */
+ int mgw_pool_target; /* Pin to specific MGW. -1 = wildcard */
+ bool mgw_pool_target_strict; /* Only allow pinned MGW */
+
/* supported codecs beside FR */
struct bts_codec_conf codec;
@@ -604,6 +597,9 @@ struct gsm_bts {
struct amr_multirate_conf mr_full;
struct amr_multirate_conf mr_half;
+ /* osmux config: */
+ enum osmux_usage use_osmux;
+
/* PCU socket state */
char *pcu_sock_path;
struct pcu_sock_state *pcu_state;
@@ -613,10 +609,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 */
@@ -631,10 +630,47 @@ struct gsm_bts {
struct osmo_timer_list cbch_timer;
struct bts_smscb_chan_state cbch_basic;
struct bts_smscb_chan_state cbch_extended;
- struct osmo_timer_list etws_timer; /* when to stop ETWS PN */
+ struct bts_etws_state etws;
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 chan_counts chan_counts;
+ struct all_allocated all_allocated;
+
+ struct bts_setup_ramp bts_setup_ramp;
};
#define GSM_BTS_SI2Q(bts, i) (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i])
@@ -654,7 +690,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 +761,15 @@ 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);
+int gsm_bts_check_cfg(struct gsm_bts *bts);
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);
@@ -738,18 +777,30 @@ int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighb
/* return the gsm_lchan for the CBCH (if it exists at all) */
struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts);
+int gsm_set_bts_model(struct gsm_bts *bts, struct gsm_bts_model *model);
int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type);
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);
+#define BTS_STORE_LCHAN_DURATIONS_INTERVAL 1 /* in seconds */
+void bts_store_lchan_durations(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 +812,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);
@@ -776,3 +829,5 @@ const char *btsatttr2str(enum bts_attribute v);
enum gsm_bts_type_variant str2btsvariant(const char *arg);
const char *btsvariant2str(enum gsm_bts_type_variant v);
+
+bool gsm_bts_check_ny1(const struct gsm_bts *bts);
diff --git a/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h b/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h
index bc7860b2d..dd97a0a76 100644
--- a/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h
+++ b/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h
@@ -24,9 +24,14 @@
#include <stdint.h>
#include <osmocom/core/msgb.h>
-struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts);
-struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts);
-struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts);
-struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts);
-struct msgb *nanobts_attr_radio_get(struct gsm_bts *bts,
+struct gsm_bts_sm;
+struct gsm_bts;
+struct gsm_bts_trx;
+struct gsm_gprs_nsvc;
+
+struct msgb *nanobts_gen_set_bts_attr(struct gsm_bts *bts);
+struct msgb *nanobts_gen_set_nse_attr(struct gsm_bts_sm *bts_sm);
+struct msgb *nanobts_gen_set_cell_attr(struct gsm_bts *bts);
+struct msgb *nanobts_gen_set_nsvc_attr(struct gsm_gprs_nsvc *nsvc);
+struct msgb *nanobts_gen_set_radio_attr(struct gsm_bts *bts,
struct gsm_bts_trx *trx);
diff --git a/include/osmocom/bsc/bts_setup_ramp.h b/include/osmocom/bsc/bts_setup_ramp.h
new file mode 100644
index 000000000..0278ee034
--- /dev/null
+++ b/include/osmocom/bsc/bts_setup_ramp.h
@@ -0,0 +1,69 @@
+/* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Alexander Couzens <acouzens@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 <stdbool.h>
+#include <stdint.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+
+struct gsm_bts;
+struct gsm_network;
+
+enum bts_setup_ramp_state {
+ BTS_SETUP_RAMP_INIT, /*!< initial state */
+ BTS_SETUP_RAMP_WAIT, /*!< BTS has to wait, too many BTS configuring */
+ BTS_SETUP_RAMP_READY, /*!< BTS is allowed to configure */
+};
+
+struct bts_setup_ramp {
+ enum bts_setup_ramp_state state;
+ struct llist_head list;
+};
+
+struct bts_setup_ramp_net {
+ unsigned count; /*!< max count */
+ unsigned step_size; /*!< also the maximum concurrent bts to configure */
+
+ struct llist_head head;
+ struct osmo_timer_list timer;
+ unsigned int step_interval; /*!< in seconds */
+ bool enabled; /*!< enabled by vty */
+ bool active; /*!< if currently active */
+};
+
+void bts_setup_ramp_init_bts(struct gsm_bts *bts);
+void bts_setup_ramp_init_network(struct gsm_network *net);
+
+bool bts_setup_ramp_active(struct gsm_network *net);
+bool bts_setup_ramp_wait(struct gsm_bts *bts);
+void bts_setup_ramp_remove(struct gsm_bts *bts);
+int bts_setup_ramp_unblock_bts(struct gsm_bts *bts);
+
+/* vty related functions */
+void bts_setup_ramp_enable(struct gsm_network *net);
+void bts_setup_ramp_disable(struct gsm_network *net);
+void bts_setup_ramp_set_step_interval(struct gsm_network *net, unsigned int step_interval);
+void bts_setup_ramp_set_step_size(struct gsm_network *net, unsigned int step_size);
+
+const char *bts_setup_ramp_get_state_str(struct gsm_bts *bts);
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..8d2493fb5 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,19 @@ 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];
+
+ struct chan_counts chan_counts;
+ struct load_counter lchan_load;
};
+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 +98,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..a830e8745
--- /dev/null
+++ b/include/osmocom/bsc/chan_counts.h
@@ -0,0 +1,111 @@
+/* API to count total, allocated and free channels of all types */
+#pragma once
+
+struct gsm_bts;
+struct gsm_bts_trx;
+struct gsm_bts_trx_ts;
+struct gsm_lchan;
+
+void chan_counts_sig_init(void);
+void chan_counts_ts_update(struct gsm_bts_trx_ts *ts);
+void chan_counts_ts_clear(struct gsm_bts_trx_ts *ts);
+void chan_counts_trx_update(struct gsm_bts_trx *trx);
+void chan_counts_bsc_verify(void);
+
+/* First array index to chan_counts.val. */
+enum chan_counts_dim1 {
+ CHAN_COUNTS1_ALL = 0,
+ CHAN_COUNTS1_STATIC = 1,
+ CHAN_COUNTS1_DYNAMIC = 2,
+ _CHAN_COUNTS1_NUM
+};
+
+/* Second array index to chan_counts.val. */
+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 {
+ /* Signed type, so that chan_counts_diff() can return negative values. */
+ int val[_CHAN_COUNTS1_NUM][_CHAN_COUNTS2_NUM][_GSM_LCHAN_MAX];
+};
+
+static inline void chan_counts_zero(struct chan_counts *counts)
+{
+ *counts = (struct chan_counts){0};
+}
+
+static inline bool chan_counts_is_zero(const struct chan_counts *counts)
+{
+ int i1, i2, i3;
+ for (i1 = 0; i1 < _CHAN_COUNTS1_NUM; i1++) {
+ for (i2 = 0; i2 < _CHAN_COUNTS2_NUM; i2++) {
+ for (i3 = 0; i3 < _GSM_LCHAN_MAX; i3++) {
+ if (counts->val[i1][i2][i3])
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+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_dim2_sub(struct chan_counts *dst, enum chan_counts_dim1 dst_dim1,
+ const struct chan_counts *sub, enum chan_counts_dim1 sub_dim1)
+{
+ int i;
+ for (i = 0; i < _CHAN_COUNTS2_NUM; i++)
+ chan_counts_dim3_sub(dst, dst_dim1, i, sub, sub_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);
+}
+
+static inline void chan_counts_sub(struct chan_counts *dst, const struct chan_counts *sub)
+{
+ int i;
+ for (i = 0; i < _CHAN_COUNTS1_NUM; i++)
+ chan_counts_dim2_sub(dst, i, sub, i);
+}
diff --git a/include/osmocom/bsc/codec_pref.h b/include/osmocom/bsc/codec_pref.h
index adefe0473..3c4266d38 100644
--- a/include/osmocom/bsc/codec_pref.h
+++ b/include/osmocom/bsc/codec_pref.h
@@ -32,3 +32,4 @@ int calc_amr_rate_intersection(struct gsm48_multi_rate_conf *c,
const struct gsm48_multi_rate_conf *a);
int check_codec_pref(struct llist_head *mscs);
+int check_amr_modes(struct llist_head *mscs);
diff --git a/include/osmocom/bsc/ctrl.h b/include/osmocom/bsc/ctrl.h
index 04ca2cb5a..86d1bdae4 100644
--- a/include/osmocom/bsc/ctrl.h
+++ b/include/osmocom/bsc/ctrl.h
@@ -2,8 +2,18 @@
#include <osmocom/ctrl/control_cmd.h>
-struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net,
- const char *bind_addr, uint16_t port);
+struct gsm_network;
+struct gsm_bts;
+struct bsc_msc_data;
+
+struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net, uint16_t port);
+
+/* Used internally in different ctrl source code files: */
+int bsc_bts_ctrl_cmds_install(void);
+int bsc_bts_trx_ctrl_cmds_install(void);
+void ctrl_generate_bts_location_state_trap(struct gsm_bts *bts, struct bsc_msc_data *msc);
+void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_data *msc_data);
+
enum bsc_ctrl_node {
CTRL_NODE_MSC = _LAST_CTRL_NODE,
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..7d68fc85f 100644
--- a/include/osmocom/bsc/gsm_04_08_rr.h
+++ b/include/osmocom/bsc/gsm_04_08_rr.h
@@ -3,6 +3,8 @@
#include <stdint.h>
#include <osmocom/core/msgb.h>
+enum handover_scope;
+
struct amr_mode;
struct amr_multirate_conf;
struct bsc_subscr;
@@ -21,20 +23,18 @@ 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,
- uint8_t power_command, uint8_t ho_ref);
+struct msgb *gsm48_make_ho_cmd(const struct gsm_lchan *new_lchan,
+ enum handover_scope ho_scope, bool async,
+ uint8_t power_command, uint8_t ho_ref);
int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command);
int gsm48_send_rr_app_info(struct gsm_lchan *lchan, uint8_t apdu_id, uint8_t apdu_flags,
const uint8_t *apdu_data, ssize_t apdu_data_len);
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_08_08.h b/include/osmocom/bsc/gsm_08_08.h
index da5e2f1ac..07f08214d 100644
--- a/include/osmocom/bsc/gsm_08_08.h
+++ b/include/osmocom/bsc/gsm_08_08.h
@@ -7,7 +7,7 @@
struct gsm_subscriber_connection;
void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, uint8_t dlci, enum gsm0808_cause cause);
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr);
+void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_a5_n);
int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel);
void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg);
void bsc_cm_update(struct gsm_subscriber_connection *conn,
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 691ab5af4..cf66a041f 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -18,26 +18,39 @@
#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>
#include <osmocom/gsm/rxlev_stat.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
+#include <osmocom/gsm/protocol/gsm_03_41.h>
#include <osmocom/abis/e1_input.h>
+#include <osmocom/bsc/bts_setup_ramp.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>
+#include <osmocom/bsc/chan_counts.h>
+#include <osmocom/bsc/lchan.h>
#define GSM_T3122_DEFAULT 10
+#define GSM_T3105_DEFAULT 100UL
+#define GSM_T3124_SDCCH 675UL
+#define GSM_T3124_OTHER_CH 320UL
+#define GSM_T3124_MAX GSM_T3124_SDCCH
+
+/* Some guess for delta (see comment below) */
+#define GSM_NY1_REQ_DELTA 1000UL
+/* Requirements: We want Ny1 to be as low as possible, while respecting T3105 * Ny1 > T3124 + delta
+ * with delta = time between expiration of T3124 and receiving HANDOVER FAILURE by the serving BSC. */
+#define GSM_NY1_DEFAULT ((unsigned long)((GSM_T3124_MAX + GSM_NY1_REQ_DELTA)/GSM_T3105_DEFAULT + 1))
+
struct mgcp_client_conf;
struct mgcp_client;
struct gsm0808_cell_id;
@@ -54,9 +67,30 @@ struct bsc_subscr;
struct gprs_ra_id;
struct handover;
struct osmo_sccp_instance;
+struct smlc_config;
#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3]
+/* Data Link Connection Identifier (DLCI) is defined in 3GPP TS 48.006, section 9.3.2.
+ * .... .SSS - SAPI value used on the radio link;
+ * CC.. .... - control channel identification:
+ * 00.. .... - indicates that the control channel is not further specified,
+ * 10.. .... - represents the FACCH or the SDCCH,
+ * 11.. .... - represents the SACCH,
+ * other values are reserved. */
+#define RSL_LINK_ID2DLCI(link_id) \
+ ((link_id & 0x40 ? 0xc0 : 0x80) | (link_id & 0x07))
+
+/* RSL Link Identifier is defined in 3GPP TS 3GPP TS 48.058, section 9.3.2.
+ * .... .SSS - SAPI value used on the radio link;
+ * ...P P... - priority for SAPI0 messages;
+ * CC.. .... - control channel identification:
+ * 00.. .... - main signalling channel (FACCH or SDCCH),
+ * 01.. .... - SACCH,
+ * other values are reserved. */
+#define DLCI2RSL_LINK_ID(dlci) \
+ ((dlci & 0xc0) == 0xc0 ? 0x40 : 0x00) | (dlci & 0x07)
+
/* 3-bit long values */
#define EARFCN_PRIO_INVALID 8
#define EARFCN_MEAS_BW_INVALID 8
@@ -70,22 +104,6 @@ typedef int gsm_cbfn(unsigned int hooknum,
struct msgb *msg,
void *data, void *param);
-/* Maximum number of neighbor cells whose average we track */
-#define MAX_NEIGH_MEAS 10
-/* Maximum size of the averaging window for neighbor cells */
-#define MAX_WIN_NEIGH_AVG 10
-/* Maximum number of report history we store */
-#define MAX_MEAS_REP 10
-
-/* processed neighbor measurements for one cell */
-struct neigh_meas_proc {
- uint16_t arfcn;
- uint8_t bsic;
- uint8_t rxlev[MAX_WIN_NEIGH_AVG];
- unsigned int rxlev_cnt;
- uint8_t last_seen_nr;
-};
-
struct gsm_classmark {
bool classmark1_set;
struct gsm48_classmark1 classmark1;
@@ -101,22 +119,23 @@ enum subscr_sccp_state {
SUBSCR_SCCP_ST_CONNECTED
};
-enum channel_rate {
- CH_RATE_SDCCH,
- CH_RATE_HALF,
- CH_RATE_FULL,
+enum assign_for {
+ ASSIGN_FOR_NONE,
+ ASSIGN_FOR_BSSMAP_REQ,
+ ASSIGN_FOR_CONGESTION_RESOLUTION,
+ ASSIGN_FOR_VTY,
};
-struct channel_mode_and_rate {
- enum gsm48_chan_mode chan_mode;
- enum channel_rate chan_rate;
- uint16_t s15_s0;
-};
+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); }
/* 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 +147,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 +216,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 +233,10 @@ struct handover_in_req {
struct gsm0808_channel_type ct;
struct gsm0808_speech_codec_list scl;
struct gsm0808_encrypt_info ei;
+ /* The same information as in 'ei' but as the handy bitmask as on the wire. */
+ uint8_t ei_as_bitmask;
+ 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 +249,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 +259,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 +306,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 +380,17 @@ 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;
+
+ enum gsm0808_cause clear_cause;
};
@@ -337,16 +405,6 @@ struct osmo_bsc_data;
struct osmo_bsc_sccp_con;
-/* Channel Request reason */
-enum gsm_chreq_reason_t {
- GSM_CHREQ_REASON_EMERG,
- GSM_CHREQ_REASON_PAG,
- GSM_CHREQ_REASON_CALL,
- GSM_CHREQ_REASON_LOCATION_UPD,
- GSM_CHREQ_REASON_OTHER,
- GSM_CHREQ_REASON_PDCH,
-};
-
/* 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 +452,16 @@ 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 rsl_connect_sent;
+ bool rsl_connect_ack_received;
+ bool force_rf_lock;
};
/* Ericsson OM2000 Managed Object */
@@ -412,17 +480,30 @@ struct om2k_mo {
#define A38_XOR_MIN_KEY_LEN 12
#define A38_XOR_MAX_KEY_LEN 16
#define A38_COMP128_KEY_LEN 16
-#define RSL_ENC_ALG_A5(x) (x+1)
+
+/* There are these representations of A5/n:
+ *
+ * - (uint8_t)(1<<n), either as a single bit, or combined as a list of
+ * permitted algorithms.
+ * A5/0 == 0x01, A5/3 == 0x08, none = 0
+ *
+ * - n+1, used on the RSL wire.
+ * A5/0 == 1, A5/3 == 4, none = 0
+ *
+ * - n, used for human interaction and returned by select_best_cipher().
+ * A5/0 == 0, A5/3 == 3, none <= -1
+ *
+ * These macros convert from n to the other representations:
+ */
+#define ALG_A5_NR_TO_RSL(A5_N) ((int)(A5_N) >= 0 ? (A5_N)+1 : 0)
+#define ALG_A5_NR_TO_BSSAP(A5_N) ALG_A5_NR_TO_RSL(A5_N)
+#define ALG_A5_NR_TO_PERM_ALG_BITS(A5_N) ((int)(A5_N) >= 0 ? 1<<(A5_N) : 0)
+
/* Up to 16 SI2quater are multiplexed; each fits 3 EARFCNS, so the practical maximum is 3*16.
* The real maximum that fits in a total of 16 SI2quater rest octets also depends on the bits left by other SI2quater
* rest octets elements, so to really fit 48 EARFCNs most other SI2quater elements need to be omitted. */
#define MAX_EARFCN_LIST (3*16)
-/* is the data link established? who established it? */
-#define LCHAN_SAPI_UNUSED 0
-#define LCHAN_SAPI_MS 1
-#define LCHAN_SAPI_NET 2
-
/* BTS ONLY */
#define MAX_NUM_UL_MEAS 104
#define LC_UL_M_F_L1_VALID (1 << 0)
@@ -461,216 +542,23 @@ 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,
- LCHAN_SAPI_S_REQ,
- LCHAN_SAPI_S_ASSIGNED,
- LCHAN_SAPI_S_REL,
- LCHAN_SAPI_S_ERROR,
-};
-
-#define MAX_A5_KEY_LEN (128/8)
-
-struct gsm_encr {
- uint8_t alg_id;
- uint8_t key_len;
- uint8_t key[MAX_A5_KEY_LEN];
-};
-
-#define LOGPLCHAN(lchan, ss, level, fmt, args...) \
- LOGP(ss, level, "%s (ss=%d,%s) (%s) " fmt, \
- lchan ? gsm_ts_and_pchan_name(lchan->ts) : "-", \
- lchan ? lchan->nr : 0, \
- lchan ? gsm_lchant_name(lchan->type) : "-", \
- 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.
- */
-#define ts_as_pchan_for_each_lchan(lchan, ts, as_pchan) \
- for (lchan = (ts)->lchan; \
- ((lchan - (ts)->lchan) < ARRAY_SIZE((ts)->lchan)) \
- && lchan->fi \
- && lchan->nr < pchan_subslots(as_pchan); \
- lchan++)
-
-/* Iterate lchans that have an FSM allocated based on current PCHAN
- * mode set in \ref ts.
+/* 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_each_lchan(lchan, ts) {
- * LOGPLCHAN(DMAIN, LOGL_DEBUG, "hello world\n");
+ * ts_for_n_lchans(lchan, ts, 3) {
+ * LOG_LCHAN(lchan, 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,
-};
-
-extern const struct value_string lchan_activate_mode_names[];
-static inline const char *lchan_activate_mode_name(enum lchan_activate_mode activ_for)
-{ return get_value_string(lchan_activate_mode_names, activ_for); }
-
-struct lchan_activate_info {
- enum lchan_activate_mode activ_for;
- 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 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;
-};
-
-struct gsm_lchan {
- /* The TS that we're part of */
- struct gsm_bts_trx_ts *ts;
- /* The logical subslot number in the TS */
- uint8_t nr;
- char *name;
-
- char *last_error;
-
- struct osmo_fsm_inst *fi;
- struct osmo_fsm_inst *fi_rtp;
- struct osmo_mgcpc_ep_ci *mgw_endpoint_ci_bts;
-
- struct {
- struct lchan_activate_info info;
- 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
- * sent ACKs like Immediate Assignment or BSSMAP Assignment Complete, and if other errors
- * occur later, e.g. during release, that we don't send a NACK out of context. */
- bool concluded;
- enum gsm0808_cause gsm0808_error_cause;
- } activate;
+#define ts_for_n_lchans(lchan, ts, N) \
+ for (lchan = (ts)->lchan; \
+ ((lchan - (ts)->lchan) < ARRAY_SIZE((ts)->lchan)) \
+ && lchan->fi \
+ && ((lchan - (ts)->lchan) < (N)); \
+ lchan++)
- 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;
-
- /* There is an RSL error cause of value 0, so we need a separate flag. */
- bool in_error;
- /* RSL error code, RSL_ERR_* */
- uint8_t rsl_error_cause;
-
- /* 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 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];
-
- struct {
- uint32_t bound_ip; /*< where the BTS receives RTP */
- uint16_t bound_port;
- uint32_t connect_ip; /*< where the BTS sends RTP to (MGW) */
- uint16_t connect_port;
- uint16_t conn_id;
- uint8_t rtp_payload;
- uint8_t rtp_payload2;
- uint8_t speech_mode;
-
- /* info we need to postpone the AoIP
- * assignment completed message */
- struct {
- uint8_t rr_cause;
- bool valid;
- } ass_compl;
- } abis_ip;
-
- uint8_t rqd_ta;
-
- /* table of neighbor cell measurements */
- struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS];
-
- /* cache of last measurement reports on this lchan */
- struct gsm_meas_rep meas_rep[MAX_MEAS_REP];
- int meas_rep_idx;
- int meas_rep_count;
- uint8_t meas_rep_last_seen_nr;
-
- /* GSM Random Access data */
- /* TODO: don't allocate this, rather keep an "is_present" flag */
- struct gsm48_req_ref *rqd_ref;
-
- 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;
-};
+#define INTERF_DBM_UNKNOWN 0
+#define INTERF_BAND_UNKNOWN 0xff
/* One Timeslot in a TRX */
struct gsm_bts_trx_ts {
@@ -685,7 +573,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,46 +618,24 @@ struct gsm_bts_trx_ts {
} rbs2000;
};
- struct gsm_lchan lchan[TS_MAX_LCHAN];
-};
-
-#define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0])
+ /* Maximum BCCH carrier power reduction */
+ uint8_t c0_max_power_red_db;
-/*
- * This keeps track of the paging status of one BTS. It
- * includes a number of pending requests, a back pointer
- * to the gsm_bts, a timer and some more state.
- */
-struct gsm_bts_paging_state {
- /* pending requests */
- struct llist_head pending_requests;
- struct gsm_bts *bts;
-
- struct osmo_timer_list work_timer;
- struct osmo_timer_list credit_timer;
-
- /* free chans needed */
- int free_chans_need;
+ /* 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];
- /* load */
- uint16_t available_slots;
+ struct chan_counts chan_counts;
};
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 +709,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 */
@@ -896,6 +756,27 @@ struct bts_smscb_chan_state {
uint8_t overflow;
};
+#define ETWS_PRIM_NOTIF_SIZE 56
+#define ETWS_SEC_INFO_SIZE (ETWS_PRIM_NOTIF_SIZE - sizeof(struct gsm341_etws_message))
+
+/* per-BTS ETWS/PWS (Emergency) state */
+struct bts_etws_state {
+ /* are we actively broadcasting emergency in this cell? */
+ bool active;
+ /* input parameters received from CBC */
+ struct {
+ uint16_t msg_id;
+ uint16_t serial_nr;
+ uint16_t warn_type;
+ uint8_t sec_info[ETWS_SEC_INFO_SIZE];
+ } input;
+ /* encoded ETWS primary notification */
+ uint8_t primary[ETWS_PRIM_NOTIF_SIZE];
+
+ /* timer running for the duration of the ETWS Primary Notification (PN) */
+ struct osmo_timer_list timer;
+};
+
struct bts_oml_fail_rep {
struct llist_head list;
time_t time;
@@ -925,21 +806,11 @@ struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net,
extern const struct value_string gsm_chreq_descs[];
extern const struct value_string gsm_pchant_names[];
extern const struct value_string gsm_pchant_descs[];
-extern const struct value_string gsm_pchan_ids[];
const char *gsm_pchan_name(enum gsm_phys_chan_config c);
-static inline const char *gsm_pchan_id(enum gsm_phys_chan_config c)
-{ return get_value_string(gsm_pchan_ids, c); }
enum gsm_phys_chan_config gsm_pchan_parse(const char *name);
-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);
-
-static inline char *gsm_lchan_name(const struct gsm_lchan *lchan)
-{
- return lchan->name;
-}
void gsm_abis_mo_reset(struct gsm_abis_mo *mo);
void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
@@ -952,169 +823,28 @@ 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_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. */
@@ -1123,13 +853,14 @@ struct gsm_tz {
int dst; /* daylight savings */
};
-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 all_allocated {
+ struct osmo_time_cc sdcch;
+ struct osmo_time_cc static_sdcch;
+ struct osmo_time_cc tch;
+ struct osmo_time_cc static_tch;
+};
+struct gsm_network {
struct osmo_plmn_id plmn;
/* bit-mask of permitted encryption algorithms. LSB=A5/0, MSB=A5/7 */
@@ -1150,10 +881,10 @@ struct gsm_network {
struct llist_head bts_list;
struct llist_head bts_rejected;
- /* BTS-based counters when we can't find the actual BTS
- * e.g. when conn->lchan is NULL */
- struct rate_ctr_group *bts_unknown_ctrs;
- struct osmo_stat_item_group *bts_unknown_statg;
+ /* BTS-based counters when we can't find the actual BTS
+ * e.g. when conn->lchan is NULL */
+ struct rate_ctr_group *bts_unknown_ctrs;
+ struct osmo_stat_item_group *bts_unknown_statg;
/* see gsm_network_T_defs */
struct osmo_tdef *T_defs;
@@ -1181,7 +912,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,38 +936,44 @@ 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;
+
+ /* Timer to write each BTS's set of lchan duration counters' state to the stats system. */
+ struct osmo_timer_list bts_store_lchan_durations_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;
+ struct bts_setup_ramp_net bts_setup_ramp;
+
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 chan_counts chan_counts;
+ struct all_allocated all_allocated;
};
struct gsm_audio_support {
@@ -1244,6 +981,8 @@ struct gsm_audio_support {
ver : 7;
};
+int gsm_audio_support_cmp(const struct gsm_audio_support *a, const struct gsm_audio_support *b);
+
extern void talloc_ctx_init(void *ctx_root);
enum gsm_bts_type parse_btstype(const char *arg);
@@ -1272,9 +1011,6 @@ void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr,
/* generic E1 line operations for all ISDN-based BTS. */
extern struct e1inp_line_ops bts_isdn_e1inp_line_ops;
-/* control interface handling */
-int bsc_base_ctrl_cmds_install(void);
-
bool ts_is_usable(const struct gsm_bts_trx_ts *ts);
int gsm_lchan_type_by_pchan(enum gsm_phys_chan_config pchan);
@@ -1285,4 +1021,18 @@ 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);
+/* 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);
+
+int select_best_cipher(uint8_t msc_mask, uint8_t bsc_mask);
+
#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..7d68e6317 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,20 @@ 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_penalty_low_rxqual_ho, 60, \
+ "handover2 ", "penalty-time low-rxqual-ho", "<0-99999>", atoi, "%d", as_is, \
+ HO_CFG_STR_HANDOVER2 \
+ HO_CFG_STR_PENALTY_TIME \
+ "Time to suspend handover back to a cell after bad RxQual caused handover away from it\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..03a8436ad 100644
--- a/include/osmocom/bsc/handover_fsm.h
+++ b/include/osmocom/bsc/handover_fsm.h
@@ -55,9 +55,7 @@ struct handover_rr_detect_data {
const uint8_t *access_delay;
};
-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/handover_vty.h b/include/osmocom/bsc/handover_vty.h
index 6ad5276e2..1dd8bf08a 100644
--- a/include/osmocom/bsc/handover_vty.h
+++ b/include/osmocom/bsc/handover_vty.h
@@ -3,6 +3,6 @@
#include <osmocom/vty/vty.h>
#include <osmocom/bsc/handover_cfg.h>
-void ho_vty_init();
+void ho_vty_init(void);
void ho_vty_write_net(struct vty *vty, struct gsm_network *net);
void ho_vty_write_bts(struct vty *vty, struct gsm_bts *bts);
diff --git a/include/osmocom/bsc/lb.h b/include/osmocom/bsc/lb.h
index e04df7d28..08206d4d8 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_init(void);
+int lb_start_or_stop(void);
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.h b/include/osmocom/bsc/lchan.h
new file mode 100644
index 000000000..4fcfa2026
--- /dev/null
+++ b/include/osmocom/bsc/lchan.h
@@ -0,0 +1,388 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_08.h>
+#include <osmocom/gsm/gsm23003.h>
+
+#include <osmocom/bsc/meas_rep.h>
+
+/* 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;
+};
+
+/* Maximum number of neighbor cells whose average we track */
+#define MAX_NEIGH_MEAS 10
+/* Maximum size of the averaging window for neighbor cells */
+#define MAX_WIN_NEIGH_AVG 10
+/* Maximum number of report history we store */
+#define MAX_MEAS_REP 10
+
+/* processed neighbor measurements for one cell */
+struct neigh_meas_proc {
+ uint16_t arfcn;
+ uint8_t bsic;
+ uint8_t rxlev[MAX_WIN_NEIGH_AVG];
+ unsigned int rxlev_cnt;
+ uint8_t last_seen_nr;
+};
+
+enum channel_rate {
+ CH_RATE_SDCCH,
+ CH_RATE_HALF,
+ 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;
+};
+
+/* Channel Request reason */
+enum gsm_chreq_reason_t {
+ GSM_CHREQ_REASON_EMERG,
+ GSM_CHREQ_REASON_PAG,
+ GSM_CHREQ_REASON_CALL,
+ GSM_CHREQ_REASON_LOCATION_UPD,
+ GSM_CHREQ_REASON_OTHER,
+ 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;
+}
+
+/* State of the SAPIs in the lchan */
+enum lchan_sapi_state {
+ LCHAN_SAPI_S_NONE,
+ LCHAN_SAPI_S_REQ,
+ LCHAN_SAPI_S_ASSIGNED,
+ LCHAN_SAPI_S_REL,
+ LCHAN_SAPI_S_ERROR,
+};
+
+/* is the data link established? who established it? */
+#define LCHAN_SAPI_UNUSED 0
+#define LCHAN_SAPI_MS 1
+#define LCHAN_SAPI_NET 2
+
+#define MAX_A5_KEY_LEN (128/8)
+
+struct gsm_encr {
+ uint8_t alg_a5_n; /* N: 0 (A5/0), 1 (A5/1), ... 7 (A5/7) */
+ uint8_t key_len;
+ uint8_t key[MAX_A5_KEY_LEN];
+ bool kc128_present;
+ uint8_t kc128[16];
+};
+
+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_for activ_for)
+{ return get_value_string(lchan_activate_mode_names, activ_for); }
+
+enum imm_ass_time {
+ IMM_ASS_TIME_POST_CHAN_ACK = 0,
+ IMM_ASS_TIME_PRE_CHAN_ACK,
+ IMM_ASS_TIME_PRE_TS_ACK,
+};
+
+struct lchan_activate_info {
+ enum lchan_activate_for activ_for;
+ /* If activ_for == ACTIVATE_FOR_MS_CHANNEL_REQUEST, the original CHREQ reason. */
+ enum gsm_chreq_reason_t chreq_reason;
+ struct gsm_subscriber_connection *for_conn;
+ struct channel_mode_and_rate ch_mode_rate;
+ struct gsm_encr encr;
+ 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;
+};
+
+/* Measurement pre-processing state */
+struct gsm_power_ctrl_meas_proc_state {
+ /* Number of measurements processed */
+ unsigned int meas_num;
+ /* Algorithm specific data */
+ union {
+ struct {
+ /* Scaled up 100 times average value */
+ int Avg100;
+ } ewma;
+ };
+};
+
+struct lchan_power_ctrl_state {
+ /* Measurement pre-processing state (for dynamic mode) */
+ struct gsm_power_ctrl_meas_proc_state rxlev_meas_proc;
+ struct gsm_power_ctrl_meas_proc_state rxqual_meas_proc;
+ /* Number of SACCH blocks to skip (for dynamic mode) */
+ int skip_block_num;
+};
+
+struct gsm_lchan {
+ /* The TS that we're part of */
+ struct gsm_bts_trx_ts *ts;
+ /* The logical subslot number in the TS */
+ uint8_t nr;
+ char *name;
+
+ char *last_error;
+
+ struct osmo_fsm_inst *fi;
+ struct osmo_fsm_inst *fi_rtp;
+ 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
+ * sent ACKs like Immediate Assignment or BSSMAP Assignment Complete, and if other errors
+ * 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;
+ /* RSL error code, RSL_ERR_* */
+ uint8_t rsl_error_cause;
+
+ /* If a release event is being handled, ignore other ricocheting release events until that
+ * release handling has concluded. */
+ bool in_release_handler;
+ } release;
+
+ /* The logical channel type */
+ enum gsm_chan_t type;
+ /* Power levels for MS and BTS */
+ uint8_t bs_power_db;
+ uint8_t ms_power;
+ /* Encryption information */
+ struct gsm_encr encr;
+
+ /* Established data link layer services */
+ uint8_t sapis[8];
+
+ struct {
+ uint32_t bound_ip; /*< where the BTS receives RTP */
+ uint16_t bound_port;
+ uint32_t connect_ip; /*< where the BTS sends RTP to (MGW) */
+ uint16_t connect_port;
+ uint16_t conn_id;
+ uint8_t rtp_payload;
+ uint8_t rtp_payload2;
+ uint8_t speech_mode;
+
+ /* info we need to postpone the AoIP
+ * assignment completed message */
+ struct {
+ uint8_t rr_cause;
+ bool valid;
+ } ass_compl;
+
+ struct {
+ bool use;
+ uint8_t local_cid;
+ bool remote_cid_present;
+ uint8_t remote_cid;
+ } osmux;
+ } abis_ip;
+
+ /* 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];
+
+ /* cache of last measurement reports on this lchan */
+ struct gsm_meas_rep meas_rep[MAX_MEAS_REP];
+ int meas_rep_idx;
+ int meas_rep_count;
+ uint8_t meas_rep_last_seen_nr;
+
+ /* GSM Random Access data */
+ /* TODO: don't allocate this, rather keep an "is_present" flag */
+ struct gsm48_req_ref *rqd_ref;
+
+ struct gsm_subscriber_connection *conn;
+
+ /* 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;
+ /* Timestamps and markers to track active state duration. */
+ struct timespec active_start;
+ struct timespec active_stored;
+};
+
+#define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0])
+
+void lchan_init(struct gsm_lchan *lchan, struct gsm_bts_trx_ts *ts, unsigned int nr);
+
+void lchan_update_name(struct gsm_lchan *lchan);
+uint64_t gsm_lchan_active_duration_ms(const struct gsm_lchan *lchan);
+
+static inline char *gsm_lchan_name(const struct gsm_lchan *lchan)
+{
+ OSMO_ASSERT(lchan);
+ return lchan->name;
+}
+
+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);
+
+void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm);
+
+#define LOGPLCHAN(lchan, ss, level, fmt, args...) \
+ LOGP(ss, level, "%s (ss=%d,%s) (%s) " fmt, \
+ lchan ? gsm_ts_and_pchan_name(lchan->ts) : "-", \
+ lchan ? lchan->nr : 0, \
+ lchan ? gsm_chan_t_name(lchan->type) : "-", \
+ bsc_subscr_name(lchan && lchan->conn ? lchan->conn->bsub : NULL), \
+ ## args)
diff --git a/include/osmocom/bsc/lchan_fsm.h b/include/osmocom/bsc/lchan_fsm.h
index 9fe7db107..b179a7fbb 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_chan_t_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,
@@ -49,21 +56,21 @@ enum lchan_fsm_event {
LCHAN_EV_REQUEST_MODE_MODIFY,
};
-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 +80,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..397b5e177 100644
--- a/include/osmocom/bsc/lchan_rtp_fsm.h
+++ b/include/osmocom/bsc/lchan_rtp_fsm.h
@@ -1,16 +1,19 @@
/* osmo-bsc API to manage lchans, logical channels in GSM cells. */
#pragma once
+#include <osmocom/bsc/lchan.h>
+
#define LOG_LCHAN_RTP(lchan, level, fmt, args...) do { \
if (lchan->fi_rtp) \
LOGPFSML(lchan->fi_rtp, level, fmt, ## args); \
else \
LOGP(DLMGCP, level, "%s (not initialized) " fmt, gsm_lchan_name(lchan), \
## args); \
- } while(0)
+ } while (0)
struct gsm_lchan;
struct mgcp_conn_peer;
+enum mgcp_codecs;
enum lchan_rtp_fsm_state {
LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE,
@@ -46,3 +49,4 @@ bool lchan_rtp_established(struct gsm_lchan *lchan);
void lchan_forget_mgw_endpoint(struct gsm_lchan *lchan);
void mgcp_pick_codec(struct mgcp_conn_peer *verb_info, const struct gsm_lchan *lchan, bool bss_side);
+bool mgcp_codec_is_picked(const struct mgcp_conn_peer *verb_info, enum mgcp_codecs codec);
diff --git a/include/osmocom/bsc/lchan_select.h b/include/osmocom/bsc/lchan_select.h
index 41e7015cf..8f1dc628a 100644
--- a/include/osmocom/bsc/lchan_select.h
+++ b/include/osmocom/bsc/lchan_select.h
@@ -1,7 +1,28 @@
/* Select a suitable lchan from a given cell. */
#pragma once
-struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type);
+enum lchan_select_reason {
+ SELECT_FOR_MS_CHAN_REQ,
+ SELECT_FOR_ASSIGNMENT,
+ SELECT_FOR_HANDOVER,
+};
+
+extern const struct value_string lchan_select_reason_names[];
+static inline const char *lchan_select_reason_name(enum lchan_select_reason reason)
+{ return get_value_string(lchan_select_reason_names, reason); }
+
+struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts,
+ enum gsm_chan_t type,
+ enum lchan_select_reason reason,
+ void *ctx);
+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);
+ enum gsm48_chan_mode chan_mode,
+ enum channel_rate chan_rate,
+ enum lchan_select_reason reason,
+ void *ctx);
+struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts,
+ enum gsm_chan_t type,
+ enum lchan_select_reason reason,
+ void *ctx, 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..78f938116 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;
@@ -31,6 +31,15 @@ struct lcs_loc_req {
bool cell_id_present;
struct gsm0808_cell_id cell_id;
+ bool client_type_present;
+ enum bssmap_le_lcs_client_type client_type;
+
+ bool priority_present;
+ uint8_t priority;
+
+ bool qos_present;
+ struct osmo_bssmap_le_lcs_qos qos;
+
struct osmo_mobile_identity imsi;
struct osmo_mobile_identity imei;
} 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..bd14bd409 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);
+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(void);
+
+#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..4c34300a1
--- /dev/null
+++ b/include/osmocom/bsc/nm_common_fsm.h
@@ -0,0 +1,125 @@
+/* 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>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+struct gsm_bts;
+
+/* 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_SETUP_RAMP_READY, /* BTS setup ramp allow to continue to configure */
+ NM_EV_FORCE_LOCK, /* Only supported by RadioCarrier so far */
+ NM_EV_FEATURE_NEGOTIATED, /* Sent by BTS to NSVC MO */
+ NM_EV_RSL_CONNECT_ACK, /* Sent by BTS to BBTRANSC MO */
+ NM_EV_RSL_CONNECT_NACK, /* Sent by BTS to BBTRANSC 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;
+
+void nm_obj_fsm_becomes_enabled_disabled(struct gsm_bts *bts, void *obj,
+ enum abis_nm_obj_class obj_class,
+ bool running);
+
+void nm_fsm_dispatch_all_configuring(struct gsm_bts *bts, uint32_t event, void *data);
diff --git a/include/osmocom/bsc/osmo_bsc.h b/include/osmocom/bsc/osmo_bsc.h
index de0d93acd..a1b663d8d 100644
--- a/include/osmocom/bsc/osmo_bsc.h
+++ b/include/osmocom/bsc/osmo_bsc.h
@@ -17,7 +17,7 @@ struct gsm_audio_support;
struct gsm_subscriber_connection;
struct gsm_bts;
-struct bsc_api *osmo_bsc_api();
+struct bsc_api *osmo_bsc_api(void);
int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg);
int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg);
@@ -25,7 +25,8 @@ int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg);
int bsc_handle_udt(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length);
int bsc_handle_dt(struct gsm_subscriber_connection *conn, struct msgb *msg);
-int bsc_ctrl_cmds_install();
+struct gsm_network;
+int bsc_ctrl_cmds_install(struct gsm_network *net);
void bsc_gen_location_state_trap(struct gsm_bts *bts);
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/paging.h b/include/osmocom/bsc/paging.h
index 2d0f8da6a..15eb49e43 100644
--- a/include/osmocom/bsc/paging.h
+++ b/include/osmocom/bsc/paging.h
@@ -33,14 +33,14 @@
struct bsc_msc_data;
#define LOG_PAGING(PARAMS, SUBSYS, LEVEL, fmt, args...) \
- LOGP(SUBSYS, LEVEL, "(msc%d) Paging%s: %s: " fmt, \
+ LOGP(SUBSYS, LEVEL, "(msc=%d) Paging%s: %s: " fmt, \
(PARAMS)->msc ? (PARAMS)->msc->nr : -1, \
(PARAMS)->reason == BSC_PAGING_FOR_LCS ? " for LCS" : "", \
bsc_subscr_name((PARAMS)->bsub), \
##args)
#define LOG_PAGING_BTS(PARAMS, BTS, SUBSYS, LEVEL, fmt, args...) \
- LOG_PAGING(PARAMS, SUBSYS, LEVEL, "(bts%u) " fmt, (BTS) ? (BTS)->nr : 255, ##args)
+ LOG_PAGING(PARAMS, SUBSYS, LEVEL, "(bts=%u) " fmt, (BTS) ? (BTS)->nr : 255, ##args)
#define BSUB_USE_PAGING_START "paging-start"
#define BSUB_USE_PAGING_REQUEST "paging-req"
@@ -53,6 +53,17 @@ enum bsc_paging_reason {
BSC_PAGING_FOR_LCS = 0x2,
};
+/* OS#5552, OS#5553: Maximum allowed scheduling transmit delay in paging
+ * requests to be queued, in seconds. If calculated delay for requests to be
+ * queued goes over this threshold, they are discarded instead of inserted to
+ * the queue. This avoids keeping queueing requests which will be scheduled for
+ * transmission too late.
+ */
+#define PAGING_THRESHOLD_X3113_DEFAULT_SEC 60
+
+#define MAX_PAGING_BLOCKS_CCCH 9
+#define MAX_BS_PA_MFRMS 9
+
struct bsc_paging_params {
enum bsc_paging_reason reason;
struct bsc_msc_data *msc;
@@ -69,19 +80,24 @@ struct bsc_paging_params {
struct gsm_paging_request {
/* list_head for list of all paging requests */
struct llist_head entry;
- /* the subscriber which we're paging. Later gsm_paging_request
- * should probably become a part of the bsc_subsrc struct? */
+ /* the subscriber which we're paging. This struct is included using
+ * bsub_entry field in list bsub->active_paging_requests */
struct bsc_subscr *bsub;
+ struct llist_head bsub_entry;
/* back-pointer to the BTS on which we are paging */
struct gsm_bts *bts;
/* what kind of channel type do we ask the MS to establish */
int chan_type;
+ /* paging group of the subscriber: */
+ uint8_t pgroup;
/* Timer 3113: how long do we try to page? */
struct osmo_timer_list T3113;
/* How often did we ask the BTS to page? */
int attempts;
+ /* Timestamp of last time the subscriber was paged */
+ struct timespec last_attempt_ts;
/* MSC that has issued this paging */
struct bsc_msc_data *msc;
@@ -89,21 +105,61 @@ struct gsm_paging_request {
enum bsc_paging_reason reason;
};
+/*
+ * This keeps track of the paging status of one BTS. It
+ * includes a number of pending requests, a back pointer
+ * to the gsm_bts, a timer and some more state.
+ */
+struct gsm_bts_paging_state {
+ /* pending requests (initial paging request, no retransmits) */
+ struct llist_head initial_req_list;
+ /* Number of requests in initial_req_list */
+ unsigned int initial_req_list_len;
+ /* pending requests (already transmitted at least once) */
+ struct llist_head retrans_req_list;
+ /* Number of requests in pending_requests_len */
+ unsigned int retrans_req_list_len;
+
+ /* Number of requests in initial_req_list, indexed by pgroup. */
+ unsigned int initial_req_pgroup_counts[MAX_PAGING_BLOCKS_CCCH * MAX_BS_PA_MFRMS];
+
+ struct gsm_bts *bts;
+
+ struct osmo_timer_list work_timer;
+ struct osmo_timer_list credit_timer;
+
+ /* Last time paging worker was triggered */
+ struct timespec last_sched_ts;
+
+ /* free chans needed */
+ int free_chans_need;
+
+ /* load */
+ uint16_t available_slots;
+};
+
+void paging_global_init(void);
+
+void paging_init(struct gsm_bts *bts);
+void paging_destructor(struct gsm_bts *bts);
+
/* schedule paging request */
int paging_request_bts(const struct bsc_paging_params *params, struct gsm_bts *bts);
-int paging_request_stop(struct bsc_msc_data **msc_p, enum bsc_paging_reason *reasons_p,
+void paging_request_stop(struct bsc_msc_data **msc_p, enum bsc_paging_reason *reasons_p,
struct gsm_bts *bts, struct bsc_subscr *bsub);
-int paging_request_cancel(struct bsc_subscr *bsub, enum bsc_paging_reason reasons);
+void paging_request_cancel(struct bsc_subscr *bsub, enum bsc_paging_reason reasons);
/* update paging load */
void paging_update_buffer_space(struct gsm_bts *bts, uint16_t);
/* pending paging requests */
-unsigned int paging_pending_requests_nr(struct gsm_bts *bts);
+unsigned int paging_pending_requests_nr(const struct gsm_bts *bts);
void paging_flush_bts(struct gsm_bts *bts, struct bsc_msc_data *msc);
void paging_flush_network(struct gsm_network *net, struct bsc_msc_data *msc);
+uint16_t paging_estimate_available_slots(const struct gsm_bts *bts, unsigned int time_span_s);
+
int bsc_paging_start(struct bsc_paging_params *params);
#endif
diff --git a/include/osmocom/bsc/pcu_if.h b/include/osmocom/bsc/pcu_if.h
index 1f398b4aa..3c34cdf4c 100644
--- a/include/osmocom/bsc/pcu_if.h
+++ b/include/osmocom/bsc/pcu_if.h
@@ -5,6 +5,8 @@
extern int pcu_direct;
+#define PCUIF_HDR_SIZE (sizeof(struct gsm_pcu_if) - sizeof(((struct gsm_pcu_if *)0)->u))
+
struct pcu_sock_state {
struct gsm_network *net;
struct osmo_fd listen_bfd; /* fd for listen socket */
diff --git a/include/osmocom/bsc/pcuif_proto.h b/include/osmocom/bsc/pcuif_proto.h
index 8f7260280..7e13b5c76 100644
--- a/include/osmocom/bsc/pcuif_proto.h
+++ b/include/osmocom/bsc/pcuif_proto.h
@@ -19,10 +19,17 @@
#define PCU_IF_MSG_DATA_CNF_DT 0x11 /* confirm (with direct tlli) */
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
+#define PCU_IF_MSG_E1_CCU_IND 0x33 /* retrieve E1 CCU comm. parameters */
#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 */
@@ -32,11 +39,12 @@
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */
-#define PCU_IF_SAPI_AGCH_DT 0x08 /* assignment on AGCH but with additional TLLI */
+#define PCU_IF_SAPI_PCH_DT 0x08 /* assignment on PCH (confirmed using TLLI) */
/* flags */
#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */
#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */
+#define PCU_IF_FLAG_DT (1 << 2)/* use TLLI for confirmation directly */
#define PCU_IF_FLAG_CS1 (1 << 16)
#define PCU_IF_FLAG_CS2 (1 << 17)
#define PCU_IF_FLAG_CS3 (1 << 18)
@@ -56,6 +64,9 @@
#define PCU_IF_ADDR_TYPE_IPV4 0x04 /* IPv4 address */
#define PCU_IF_ADDR_TYPE_IPV6 0x29 /* IPv6 address */
+#define PCU_IF_NUM_NSVC 2
+#define PCU_IF_NUM_TRX 8
+
enum gsm_pcu_if_text_type {
PCU_VERSION,
PCU_OML_ALERT,
@@ -138,7 +149,7 @@ struct gsm_pcu_if_info_trx {
struct gsm_pcu_if_info_ind {
uint32_t version;
uint32_t flags;
- struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
+ struct gsm_pcu_if_info_trx trx[PCU_IF_NUM_TRX]; /* TRX infos per BTS */
uint8_t bsic;
/* RAI */
uint16_t mcc, mnc;
@@ -167,14 +178,25 @@ struct gsm_pcu_if_info_ind {
uint8_t initial_cs;
uint8_t initial_mcs;
/* NSVC */
- uint16_t nsvci[2];
- uint16_t local_port[2];
- uint16_t remote_port[2];
- uint8_t address_type[2];
+ uint16_t nsvci[PCU_IF_NUM_NSVC];
+ uint16_t local_port[PCU_IF_NUM_NSVC];
+ uint16_t remote_port[PCU_IF_NUM_NSVC];
+ uint8_t address_type[PCU_IF_NUM_NSVC];
union {
struct in_addr v4;
struct in6_addr v6;
- } remote_ip[2];
+ } remote_ip[PCU_IF_NUM_NSVC];
+} __attribute__ ((packed));
+
+/* E1 CCU connection parameters */
+struct gsm_pcu_if_e1_ccu_ind {
+ /* GSM/GPRS air interface */
+ uint8_t trx_nr;
+ uint8_t ts_nr;
+ /* E1 line interface */
+ uint8_t e1_nr;
+ uint8_t e1_ts;
+ uint8_t e1_ts_ss;
} __attribute__ ((packed));
struct gsm_pcu_if_act_req {
@@ -208,6 +230,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 */
@@ -224,10 +286,13 @@ struct gsm_pcu_if {
struct gsm_pcu_if_rach_ind rach_ind;
struct gsm_pcu_if_txt_ind txt_ind;
struct gsm_pcu_if_info_ind info_ind;
+ struct gsm_pcu_if_e1_ccu_ind e1_ccu_ind;
struct gsm_pcu_if_act_req act_req;
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..2eb23e7e2
--- /dev/null
+++ b/include/osmocom/bsc/power_control.h
@@ -0,0 +1,99 @@
+#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);
+
+/* 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);
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..4d5080da2 100644
--- a/include/osmocom/bsc/signal.h
+++ b/include/osmocom/bsc/signal.h
@@ -68,12 +68,15 @@ enum signal_nm {
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 */
- S_NM_STATECHG_OPER, /* Operational State changed*/
- S_NM_STATECHG_ADM, /* Administrative State changed */
+ S_NM_STATECHG, /* NM Oper/Admin/Avail State changed. arg is struct nm_statechg_signal_data*/
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 */
+ S_NM_RUNNING_CHG, /* Object moves from/to NM running state (op=Enabled adm=Unlocked avail=OK) */
};
/* SS_LCHAN signals */
@@ -117,23 +120,34 @@ enum signal_rf {
};
struct ipacc_ack_signal_data {
- struct gsm_bts_trx *trx;
- uint8_t msg_type;
+ /* The BTS which sent the ACK/NACK to us: */
+ struct gsm_bts *bts;
+ /* messge header containing msg_type, obj_class, obj_inst: */
+ struct abis_om_fom_hdr *foh;
};
struct abis_om2k_mo;
+/* data for <SS_NM, S_NM_STATECHG>: */
struct nm_statechg_signal_data {
struct gsm_bts *bts;
uint8_t obj_class;
void *obj;
- struct gsm_nm_state *old_state;
- struct gsm_nm_state *new_state;
+ 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 */
- struct abis_om2k_mo *om2k_mo;
+ /* This pointer is valid for RBS2000 MO */
+ const struct abis_om2k_mo *om2k_mo;
+};
+
+/* data for <SS_NM, S_NM_RUNNING_CHG>: */
+struct nm_running_chg_signal_data {
+ struct gsm_bts *bts;
+ uint8_t obj_class;
+ void *obj;
+ bool running;
};
struct nm_om2k_signal_data {
diff --git a/include/osmocom/bsc/smscb.h b/include/osmocom/bsc/smscb.h
index f48c1a170..b4f2e196f 100644
--- a/include/osmocom/bsc/smscb.h
+++ b/include/osmocom/bsc/smscb.h
@@ -9,6 +9,7 @@
struct bsc_cbc_link;
/* smscb.c */
+void smscb_global_init(void);
void bts_smscb_del(struct bts_smscb_message *smscb, struct bts_smscb_chan_state *cstate,
const char *reason);
const char *bts_smscb_msg2str(const struct bts_smscb_message *smscb);
@@ -18,7 +19,6 @@ int cbsp_tx_restart(struct bsc_cbc_link *cbc, bool is_emerg);
const char *bts_smscb_chan_state_name(const struct bts_smscb_chan_state *cstate);
unsigned int bts_smscb_chan_load_percent(const struct bts_smscb_chan_state *cstate);
unsigned int bts_smscb_chan_page_count(const struct bts_smscb_chan_state *cstate);
-void smscb_vty_init(void);
/* cbch_scheduler.c */
int bts_smscb_gen_sched_arr(struct bts_smscb_chan_state *cstate, struct bts_smscb_page ***arr_out);
@@ -26,7 +26,9 @@ struct bts_smscb_page *bts_smscb_pull_page(struct bts_smscb_chan_state *cstate);
void bts_smscb_page_done(struct bts_smscb_chan_state *cstate, struct bts_smscb_page *page);
int bts_smscb_rx_cbch_load_ind(struct gsm_bts *bts, bool cbch_extended, bool is_overflow,
uint8_t slot_count);
+void bts_cbch_init(struct gsm_bts *bts);
void bts_cbch_timer_schedule(struct gsm_bts *bts);
+void bts_cbch_timer_cb(void *data);
enum bsc_cbc_link_mode {
BSC_CBC_LINK_MODE_DISABLED = 0,
@@ -61,6 +63,11 @@ struct bsc_cbc_link {
struct msgb *msg;
} client;
};
-void cbc_vty_init(void);
int bsc_cbc_link_restart(void);
int cbsp_tx_decoded(struct bsc_cbc_link *cbc, struct osmo_cbsp_decoded *decoded);
+
+void bts_etws_init(struct gsm_bts *bts);
+
+/* smscb_vty.c: */
+void smscb_vty_init(void);
+void cbc_vty_init(void);
diff --git a/include/osmocom/bsc/system_information.h b/include/osmocom/bsc/system_information.h
index 35892d9d6..7b1cff0f2 100644
--- a/include/osmocom/bsc/system_information.h
+++ b/include/osmocom/bsc/system_information.h
@@ -3,16 +3,18 @@
#include <osmocom/gsm/sysinfo.h>
-#include <osmocom/bsc/arfcn_range_encode.h>
+#include <osmocom/gsm/gsm48_arfcn_range_encode.h>
struct gsm_bts;
+int band_compatible(const struct gsm_bts *bts, int arfcn);
+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..cea1e4c4d 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,
@@ -37,17 +37,18 @@ enum ts_fsm_event {
TS_EV_LCHAN_REQUESTED,
TS_EV_LCHAN_UNUSED,
TS_EV_PDCH_ACT_ACK,
- TS_EV_PDCH_ACT_NACK,
- TS_EV_PDCH_DEACT_ACK,
- TS_EV_PDCH_DEACT_NACK,
+ TS_EV_PDCH_ACT_NACK,
+ TS_EV_PDCH_DEACT_ACK,
+ 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..a54b18e44 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/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
deleted file mode 100644
index ca3639715..000000000
--- a/m4/ax_check_compile_flag.m4
+++ /dev/null
@@ -1,74 +0,0 @@
-# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
-#
-# DESCRIPTION
-#
-# Check whether the given FLAG works with the current language's compiler
-# or gives an error. (Warnings, however, are ignored)
-#
-# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
-# success/failure.
-#
-# If EXTRA-FLAGS is defined, it is added to the current language's default
-# flags (e.g. CFLAGS) when the check is done. The check is thus made with
-# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
-# force the compiler to issue an error when a bad flag is given.
-#
-# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
-#
-# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
-# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
-#
-# LICENSE
-#
-# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
-# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify it
-# under the terms of the GNU 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 General Public License along
-# with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# As a special exception, the respective Autoconf Macro's copyright owner
-# gives unlimited permission to copy, distribute and modify the configure
-# scripts that are the output of Autoconf when processing the Macro. You
-# need not follow the terms of the GNU General Public License when using
-# or distributing such scripts, even though portions of the text of the
-# Macro appear in them. The GNU General Public License (GPL) does govern
-# all other use of the material that constitutes the Autoconf Macro.
-#
-# This special exception to the GPL applies to versions of the Autoconf
-# Macro released by the Autoconf Archive. When you make and distribute a
-# modified version of the Autoconf Macro, you may extend this special
-# exception to the GPL to apply to your modified version as well.
-
-#serial 4
-
-AC_DEFUN([AX_CHECK_COMPILE_FLAG],
-[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
-AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
-AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
- ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
- _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
- AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
- [AS_VAR_SET(CACHEVAR,[yes])],
- [AS_VAR_SET(CACHEVAR,[no])])
- _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
-AS_VAR_IF(CACHEVAR,yes,
- [m4_default([$2], :)],
- [m4_default([$3], :)])
-AS_VAR_POPDEF([CACHEVAR])dnl
-])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/src/ipaccess/Makefile.am b/src/ipaccess/Makefile.am
index 717a6a187..9bd648ddf 100644
--- a/src/ipaccess/Makefile.am
+++ b/src/ipaccess/Makefile.am
@@ -9,6 +9,8 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -46,14 +48,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 +58,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..a26bb55d7 100644
--- a/src/ipaccess/abisip-find.c
+++ b/src/ipaccess/abisip-find.c
@@ -53,7 +53,7 @@ static struct {
.format_json = false,
};
-static void print_help()
+static void print_help(void)
{
printf("\n");
printf("Usage: abisip-find [-l] [<interface-name>]\n");
@@ -265,7 +265,7 @@ LLIST_HEAD(base_stations);
void *ctx = NULL;
-void print_timestamp()
+void print_timestamp(void)
{
time_t now = time(NULL);
printf("\n\n----- %s\n", ctime(&now));
@@ -304,7 +304,7 @@ bool base_stations_add(struct base_station *new_bs)
return true;
}
-bool base_stations_timeout()
+bool base_stations_timeout(void)
{
struct base_station *bs, *next_bs;
time_t now = time(NULL);
@@ -323,7 +323,7 @@ bool base_stations_timeout()
return changed;
}
-void base_stations_print()
+void base_stations_print(void)
{
struct base_station *bs;
int count = 0;
@@ -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..3df9f61cb 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;
@@ -131,7 +129,7 @@ static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
{
struct e1inp_line *line;
struct e1inp_ts *sign_ts, *rsl_ts;
- struct e1inp_sign_link *oml_link, *rsl_link;
+ struct e1inp_sign_link *oml_link, *osmo_link, *rsl_link;
line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
if (!line)
@@ -142,23 +140,29 @@ static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
fprintf(stderr, "cannot `ipa' driver, giving up.\n");
return -EINVAL;
}
- line->ops = &ipaccess_e1inp_line_ops;
+ e1inp_line_bind_ops(line, &ipaccess_e1inp_line_ops);
+ e1_set_pcap_fd2(line, -1); /* Disable writing to pcap */
+
+ 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);
+ rsl_ts->driver.ipaccess.fd.fd = -1;
- /* 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);
+ osmo_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OSMO,
+ bts->c0, IPAC_PROTO_OSMO, 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->osmo_link = osmo_link;
+ bts->c0->rsl_link_primary = rsl_link;
/* default port at BTS for incoming connections is 3006 */
if (sin->sin_port == 0)
@@ -192,6 +196,9 @@ static void check_restart_or_exit(struct gsm_bts_trx *trx)
static int ipacc_msg_ack(uint8_t mt, struct gsm_bts_trx *trx)
{
+ if (mt != NM_MT_IPACC_SET_NVATTR_ACK && mt != NM_MT_IPACC_SET_ATTR_ACK)
+ return 0;
+
if (sw_load_state == 1) {
fprintf(stderr, "The new software is activated.\n");
check_restart_or_exit(trx);
@@ -269,6 +276,8 @@ static int nwl_sig_cb(unsigned int subsys, unsigned int signal,
return 0;
}
+static const struct value_string ipa_nvflag_strs[];
+
static int print_attr_rep(struct msgb *mb)
{
/* Parse using nanoBTS own formatting for Get Attribute Response */
@@ -282,22 +291,67 @@ static int print_attr_rep(struct msgb *mb)
char oml_ip[20] = {0};
uint16_t oml_port = 0;
char unit_id[40] = {0};
+ unsigned int indent = 0;
- abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
+ if (abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
abis_nm_tlv_attr_primary_oml(&tp, &ia, &oml_port);
osmo_strlcpy(oml_ip, inet_ntoa(ia), sizeof(oml_ip));
abis_nm_tlv_attr_unit_id(&tp, unit_id, sizeof(unit_id));
- fprintf(stdout, "{ \"primary_oml_ip\": \"%s\", \"primary_oml_port\": %" PRIu16 ", \"unit_id\": \"%s\" }\n",
- oml_ip, oml_port, unit_id);
+#define ENDL(last) \
+ fprintf(stdout, "%s\n", last ? "" : ",")
+#define print_offset(fmt, args...) \
+ fprintf(stdout, "%*s" fmt, indent * 4, "", ## args)
+#define print_field(field, fmt, args...) \
+ print_offset("\"%s\": \"" fmt "\"", field, ## args)
+
+ print_offset("{\n");
+ indent++;
+
+ print_field("primary_oml_ip", "%s", oml_ip); ENDL(false);
+ print_field("primary_oml_port", "%u", oml_port); ENDL(false);
+ print_field("unit_id", "%s", unit_id); ENDL(false);
+
+ uint16_t Fx = (TLVP_VAL(&tp, NM_ATT_IPACC_NV_FLAGS)[2] << 8)
+ | (TLVP_VAL(&tp, NM_ATT_IPACC_NV_FLAGS)[0] << 0);
+ uint16_t Mx = (TLVP_VAL(&tp, NM_ATT_IPACC_NV_FLAGS)[3] << 8)
+ | (TLVP_VAL(&tp, NM_ATT_IPACC_NV_FLAGS)[1] << 0);
+ const struct value_string *nvflag = ipa_nvflag_strs;
+
+ print_offset("\"nv_flags\": {\n");
+ indent++;
+
+ while (nvflag->value && nvflag->str) {
+ const char *val = (Fx & nvflag->value) ? "yes" : "no";
+ if (~Mx & nvflag->value)
+ val = "unknown";
+ print_field(nvflag->str, "%s", val);
+
+ nvflag++;
+
+ if (nvflag->value && nvflag->str)
+ ENDL(false); /* more fields to print */
+ else
+ ENDL(true); /* this was the last field */
+ }
+
+ indent--;
+ print_offset("}\n");
+
+ indent--;
+ print_offset("}\n");
+
return 0;
}
static int nm_state_event(int evt, uint8_t obj_class, void *obj,
- struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
+ const struct gsm_nm_state *old_state, const struct gsm_nm_state *new_state,
struct abis_om_obj_inst *obj_inst);
static int nm_sig_cb(unsigned int subsys, unsigned int signal,
@@ -306,14 +360,23 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
struct ipacc_ack_signal_data *ipacc_data;
struct nm_statechg_signal_data *nsd;
struct msgb *oml_msg;
+ struct gsm_bts_trx *trx;
switch (signal) {
case S_NM_IPACC_NACK:
ipacc_data = signal_data;
- return ipacc_msg_nack(ipacc_data->msg_type);
+ return ipacc_msg_nack(ipacc_data->foh->msg_type);
case S_NM_IPACC_ACK:
ipacc_data = signal_data;
- return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx);
+ switch (ipacc_data->foh->obj_class) {
+ case NM_OC_BASEB_TRANSC:
+ case NM_OC_RADIO_CARRIER:
+ trx = gsm_bts_trx_num(ipacc_data->bts,
+ ipacc_data->foh->obj_inst.trx_nr);
+ return ipacc_msg_ack(ipacc_data->foh->msg_type, trx);
+ default:
+ return 0;
+ }
case S_NM_IPACC_RESTART_ACK:
if (!quiet)
printf("The BTS has acked the restart. Exiting.\n");
@@ -324,11 +387,10 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
printf("The BTS has nacked the restart. Exiting.\n");
exit(0);
break;
- case S_NM_STATECHG_OPER:
- case S_NM_STATECHG_ADM:
+ case S_NM_STATECHG:
nsd = signal_data;
- nm_state_event(signal, nsd->obj_class, nsd->obj, nsd->old_state,
- nsd->new_state, nsd->obj_inst);
+ nm_state_event(signal, nsd->obj_class, nsd->obj, &nsd->old_state,
+ &nsd->new_state, nsd->obj_inst);
break;
case S_NM_GET_ATTR_REP:
fprintf(stderr, "Received SIGNAL S_NM_GET_ATTR_REP\n");
@@ -342,6 +404,32 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
return 0;
}
+/* Callback function to be called every time we receive a signal from INPUT */
+static int inp_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct input_signal_data *isd = signal_data;
+
+ if (subsys != SS_L_INPUT)
+ return -EINVAL;
+
+ fprintf(stderr, "%s(): Input signal '%s' received\n", __func__,
+ get_value_string(e1inp_signal_names, signal));
+
+ switch (signal) {
+ case S_L_INP_TEI_UP:
+ break;
+ case S_L_INP_TEI_DN:
+ fprintf(stderr, "Lost E1 %s link\n", e1inp_signtype_name(isd->link_type));
+ exit(1);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
/* callback function passed to the ABIS OML code */
static int percent;
static int percent_old;
@@ -502,7 +590,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 +605,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)
@@ -542,6 +632,7 @@ static void bootstrap_om(struct gsm_bts_trx *trx)
if (get_attr) {
msgb_put_u8(nmsg_get, NM_ATT_IPACC_PRIM_OML_CFG);
msgb_put_u8(nmsg_get, NM_ATT_IPACC_UNIT_ID);
+ msgb_put_u8(nmsg_get, NM_ATT_IPACC_NV_FLAGS);
}
if (unit_id) {
len = strlen(unit_id);
@@ -632,7 +723,7 @@ out_err:
}
static int nm_state_event(int evt, uint8_t obj_class, void *obj,
- struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
+ const struct gsm_nm_state *old_state, const struct gsm_nm_state *new_state,
struct abis_om_obj_inst *obj_inst)
{
if (obj_class == NM_OC_BASEB_TRANSC) {
@@ -641,9 +732,9 @@ static int nm_state_event(int evt, uint8_t obj_class, void *obj,
bootstrap_om(trx);
found_trx = 1;
}
- } else if (evt == S_NM_STATECHG_OPER &&
+ } else if (evt == S_NM_STATECHG &&
obj_class == NM_OC_RADIO_CARRIER &&
- new_state->availability == 3) {
+ new_state->availability == NM_AVSTATE_OFF_LINE) {
struct gsm_bts_trx *trx = obj;
if (net_listen_testnr)
@@ -926,7 +1017,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,
},
};
@@ -1104,6 +1195,7 @@ int main(int argc, char **argv)
bts->oml_tei = stream_id;
osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
+ osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
osmo_signal_register_handler(SS_IPAC_NWL, nwl_sig_cb, NULL);
ipac_nwl_init();
@@ -1121,7 +1213,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 +1222,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..8549397d2 100644
--- a/src/ipaccess/stubs.c
+++ b/src/ipaccess/stubs.c
@@ -1,6 +1,6 @@
/* Stubs required for linking */
-/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+/* (C) 2018-2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -19,28 +19,10 @@
*
*/
-#include <stdbool.h>
struct gsm_bts;
-struct gsm_bts_trx_ts;
-struct msgb;
-struct bsc_msc_data;
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts)
+int gsm_bts_check_cfg(struct gsm_bts *bts)
{
- /* No TS init required here. */
- return true;
-}
-
-int abis_rsl_rcvmsg(struct msgb *msg)
-{
- /* No RSL handling here */
+ /* No checks required 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..60a76faf0 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -21,51 +21,59 @@ 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.c \
bsc_ctrl_lookup.c \
bsc_init.c \
bsc_rf_ctrl.c \
bsc_rll.c \
bsc_sccp.c \
+ bsc_stats.c \
bsc_subscr_conn_fsm.c \
bsc_subscriber.c \
bsc_vty.c \
bts.c \
bts_trx.c \
+ bts_trx_ctrl.c \
bts_ericsson_rbs2000.c \
bts_init.c \
bts_ipaccess_nanobts.c \
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_ctrl.c \
+ bts_setup_ramp.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 \
handover_logic.c \
handover_vty.c \
lb.c \
+ lchan.c \
lchan_fsm.c \
lchan_rtp_fsm.c \
lchan_select.c \
@@ -75,29 +83,61 @@ 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 \
+ smscb_vty.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) \
+ -lm \
+ $(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..938f45d13 100644
--- a/src/osmo-bsc/abis_nm.c
+++ b/src/osmo-bsc/abis_nm.c
@@ -49,14 +49,19 @@
#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>
+#include <osmocom/bsc/ipaccess.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)
{
@@ -205,7 +210,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb);
static int update_admstate(struct gsm_bts *bts, uint8_t obj_class,
struct abis_om_obj_inst *obj_inst, uint8_t adm_state)
{
- struct gsm_nm_state *nm_state, new_state;
+ struct gsm_nm_state *nm_state;
struct nm_statechg_signal_data nsd;
memset(&nsd, 0, sizeof(nsd));
@@ -217,18 +222,18 @@ static int update_admstate(struct gsm_bts *bts, uint8_t obj_class,
if (!nm_state)
return -1;
- new_state = *nm_state;
- new_state.administrative = adm_state;
-
nsd.bts = bts;
nsd.obj_class = obj_class;
- nsd.old_state = nm_state;
- nsd.new_state = &new_state;
+ nsd.old_state = *nm_state;
+ nsd.new_state = *nm_state;
nsd.obj_inst = obj_inst;
- osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd);
+ nsd.new_state.administrative = adm_state;
+
+ /* Update current state before emitting signal: */
nm_state->administrative = adm_state;
+ osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd);
return 0;
}
@@ -239,72 +244,67 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
struct e1inp_sign_link *sign_link = mb->dst;
struct gsm_bts *bts = sign_link->trx->bts;
struct tlv_parsed tp;
- struct gsm_nm_state *nm_state, new_state;
+ struct gsm_nm_state *nm_state;
+ struct nm_statechg_signal_data nsd;
- memset(&new_state, 0, sizeof(new_state));
+ memset(&nsd, 0, sizeof(nsd));
+ nsd.obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
+ if (!nsd.obj) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "unknown managed object\n");
+ return -EINVAL;
+ }
nm_state = gsm_objclass2nmstate(bts, foh->obj_class, &foh->obj_inst);
if (!nm_state) {
LOGPFOH(DNM, LOGL_ERROR, foh, "unknown managed object\n");
return -EINVAL;
}
- new_state = *nm_state;
+ nsd.obj_class = foh->obj_class;
+ nsd.old_state = *nm_state;
+ nsd.new_state = *nm_state;
+ nsd.obj_inst = &foh->obj_inst;
+ nsd.bts = bts;
+
+ if (abis_nm_tlv_parse(&tp, bts, foh->data, oh->length - sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
DEBUGPFOH(DNM, foh, "STATE CHG: ");
- abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) {
- new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE);
+ nsd.new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE);
DEBUGPC(DNM, "OP_STATE=%s ",
- abis_nm_opstate_name(new_state.operational));
+ abis_nm_opstate_name(nsd.new_state.operational));
}
if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) {
if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0)
- new_state.availability = 0xff;
+ nsd.new_state.availability = NM_AVSTATE_OK;
else
- new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS);
+ nsd.new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS);
DEBUGPC(DNM, "AVAIL=%s(%02x) ",
- abis_nm_avail_name(new_state.availability),
- new_state.availability);
+ abis_nm_avail_name(nsd.new_state.availability),
+ nsd.new_state.availability);
} else
- new_state.availability = 0xff;
+ nsd.new_state.availability = NM_AVSTATE_OK;
if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
- new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
+ nsd.new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
DEBUGPC(DNM, "ADM=%2s ",
get_value_string(abis_nm_adm_state_names,
- new_state.administrative));
+ nsd.new_state.administrative));
}
- if ((new_state.administrative != 0 && nm_state->administrative == 0) ||
- new_state.operational != nm_state->operational ||
- new_state.availability != nm_state->availability) {
+ if ((nsd.new_state.administrative != 0 && nsd.old_state.administrative == 0) ||
+ nsd.new_state.operational != nsd.old_state.operational ||
+ nsd.new_state.availability != nsd.old_state.availability) {
DEBUGPC(DNM, "\n");
- /* Update the operational state of a given object in our in-memory data
+ /* Update the state of a given object in our in-memory data
* structures and send an event to the higher layer */
- struct nm_statechg_signal_data nsd;
- nsd.obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
- nsd.obj_class = foh->obj_class;
- nsd.old_state = nm_state;
- nsd.new_state = &new_state;
- nsd.obj_inst = &foh->obj_inst;
- nsd.bts = bts;
- osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd);
- nm_state->operational = new_state.operational;
- nm_state->availability = new_state.availability;
- if (nm_state->administrative == 0)
- nm_state->administrative = new_state.administrative;
+ *nm_state = nsd.new_state;
+ osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd);
} else {
DEBUGPC(DNM, "(No State change detected)\n");
}
-#if 0
- if (op_state == 1) {
- /* try to enable objects that are disabled */
- abis_nm_opstart(bts, foh->obj_class,
- foh->obj_inst.bts_nr,
- foh->obj_inst.trx_nr,
- foh->obj_inst.ts_nr);
- }
-#endif
return 0;
}
@@ -315,7 +315,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 +345,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;
@@ -370,8 +370,10 @@ struct nm_fail_rep_signal_data *abis_nm_fail_evt_rep_parse(struct msgb *mb, stru
sd = talloc_zero(tall_bsc_ctx, struct nm_fail_rep_signal_data);
OSMO_ASSERT(sd);
- if (abis_nm_tlv_parse(&sd->tp, bts, foh->data, oh->length-sizeof(*foh)) < 0)
+ if (abis_nm_tlv_parse(&sd->tp, bts, foh->data, oh->length - sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
goto fail;
+ }
if (TLVP_PRESENT(&sd->tp, NM_ATT_ADD_TEXT)) {
const uint8_t *val = TLVP_VAL(&sd->tp, NM_ATT_ADD_TEXT);
@@ -525,10 +527,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 +578,45 @@ 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);
+ bts->features_known = true;
+
+ /* Log each BTS feature in the reported vector */
+ for (i = 0; i < len * 8; i++) {
+ if (!osmo_bts_has_feature(&bts->features, i))
+ continue;
+
+ if (i >= _NUM_BTS_FEAT)
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Get Attributes Response: unknown feature 0x%02x is"
+ " supported\n", i);
+ else
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Get Attributes Response: feature '%s' is"
+ " supported\n", osmo_bts_features_name(i));
+ }
- 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));
+ /* Add features from the BTS model: nanobts may support more
+ * features than it reports, since we extend the enum of
+ * features for osmo-bts. */
+ if (bts->type == GSM_BTS_TYPE_NANOBTS) {
+ for (i = 0; i < _NUM_BTS_FEAT; i++) {
+ if (osmo_bts_has_feature(&bts->model->features, i) /* intentional check against bts model */
+ && !osmo_bts_has_feature(&bts->features, i)) {
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Get Attributes Response: feature '%s' is"
+ " assumed to be supported\n", osmo_bts_features_name(i));
+ osmo_bts_set_feature(&bts->features, i);
+ }
}
}
}
@@ -666,23 +686,34 @@ static int parse_attr_resp_info(struct gsm_bts *bts, const struct gsm_bts_trx *t
/* After parsing unreported attribute id list inside Response info,
there's a list of reported attribute ids and their values, in a TLV
list form. */
- abis_nm_tlv_parse(tp, bts, data, data_len);
+ if (abis_nm_tlv_parse(tp, bts, data, data_len) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
+
return parse_attr_resp_info_attr(bts, trx, foh, tp);
}
/* 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));
+ if (abis_nm_tlv_parse(&tp, bts, foh->data, oh->length - sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
/* nanoBTS doesn't send Get Attribute Response Info, uses its own format */
if (bts->type != GSM_BTS_TYPE_NANOBTS)
@@ -690,6 +721,12 @@ static int abis_nm_rx_get_attr_resp(struct msgb *mb, const struct gsm_bts_trx *t
else
rc = parse_attr_resp_info_attr(bts, trx, foh, &tp);
+ if (gsm_bts_check_cfg(bts) != 0) {
+ LOGP(DLINP, LOGL_ERROR, "(bts=%u) BTS config invalid, dropping BTS!\n", bts->nr);
+ ipaccess_drop_oml_deferred(bts);
+ return -EINVAL;
+ }
+
osmo_signal_dispatch(SS_NM, S_NM_GET_ATTR_REP, mb);
return rc;
@@ -718,7 +755,11 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
return ret;
}
- abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh));
+ if (abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length - sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
+
sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG);
sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG);
if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) {
@@ -755,7 +796,11 @@ static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb)
struct tlv_parsed tp;
uint8_t adm_state;
- abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh));
+ if (abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length - sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
+
if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE))
return -EINVAL;
@@ -774,8 +819,12 @@ static int abis_nm_rx_lmt_event(struct msgb *mb)
struct e1inp_sign_link *sign_link = mb->dst;
struct tlv_parsed tp;
+ if (abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length - sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
+
DEBUGPFOH(DNM, foh, "LMT Event ");
- abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh));
if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) &&
TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) {
uint8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION);
@@ -826,6 +875,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 +891,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 +918,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))
@@ -903,9 +976,12 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
struct nm_nack_signal_data nack_data;
struct tlv_parsed tp;
- LOGPFOH(DNM, LOGL_NOTICE, foh, "%s NACK ", abis_nm_nack_name(mt));
+ if (abis_nm_tlv_parse(&tp, bts, foh->data, oh->length - sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
- abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
+ LOGPFOH(DNM, LOGL_NOTICE, foh, "%s NACK ", abis_nm_nack_name(mt));
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
LOGPC(DNM, LOGL_NOTICE, "CAUSE=%s\n",
abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
@@ -947,10 +1023,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 +1046,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 +1078,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 +1233,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 +1331,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;
}
@@ -1300,56 +1379,51 @@ static int sw_activate(struct abis_nm_sw *sw)
return abis_nm_sendmsg(sw->bts, msg);
}
-struct sdp_firmware {
- char magic[4];
- char more_magic[4];
- unsigned int header_length;
- unsigned int file_length;
-} __attribute__ ((packed));
-
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 number is wrong.\n");
return -1;
}
if (firmware_header.more_magic[0] != 0x10 ||
- 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");
+ firmware_header.more_magic[1] != 0x02) {
+ LOGPMO(mo, DNM, LOGL_ERROR, "The more magic number is wrong.\n");
return -1;
}
+ if (firmware_header.more_more_magic != 0x0000) {
+ LOGPMO(mo, DNM, LOGL_ERROR, "The more 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 +1665,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 +1688,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 +1733,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 +1830,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 +1853,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 +1876,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 +1924,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 +2022,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 +2054,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 {
@@ -2792,6 +2865,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
struct tlv_parsed tp;
struct ipacc_ack_signal_data signal;
struct e1inp_sign_link *sign_link = msg->dst;
+ struct gsm_bts *bts = sign_link->trx->bts;
struct gsm_bts_trx *trx;
foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen);
@@ -2801,11 +2875,14 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
return -EINVAL;
}
- abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh));
+ if (abis_nm_tlv_parse(&tp, bts, foh->data, oh->length - sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
/* The message might be received over the main OML link, so we cannot
* just use sign_link->trx. Resolve it by number from the FOM header. */
- trx = gsm_bts_trx_num(sign_link->trx->bts, foh->obj_inst.trx_nr);
+ trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
DEBUGPFOH(DNM, foh, "Rx IPACCESS(0x%02x): %s\n", foh->msg_type,
osmo_hexdump(foh->data, oh->length - sizeof(*foh)));
@@ -2885,17 +2962,15 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
case NM_MT_IPACC_RSL_CONNECT_NACK:
case NM_MT_IPACC_SET_NVATTR_NACK:
case NM_MT_IPACC_GET_NVATTR_NACK:
- if (!trx)
- goto obj_inst_error;
- signal.trx = trx;
- signal.msg_type = foh->msg_type;
+ signal.bts = bts;
+ signal.foh = foh;
osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal);
break;
+ case NM_MT_IPACC_RSL_CONNECT_ACK:
case NM_MT_IPACC_SET_NVATTR_ACK:
- if (!trx)
- goto obj_inst_error;
- signal.trx = trx;
- signal.msg_type = foh->msg_type;
+ case NM_MT_IPACC_SET_ATTR_ACK:
+ signal.bts = bts;
+ signal.foh = foh;
osmo_signal_dispatch(SS_NM, S_NM_IPACC_ACK, &signal);
break;
default:
@@ -2962,40 +3037,48 @@ static void rsl_connect_timeout(void *data)
LOG_TRX(trx, DRSL, LOGL_NOTICE, "RSL connection request timed out\n");
/* Fake an RSL CONNECT NACK message from the BTS. */
- signal.trx = trx;
- signal.msg_type = NM_MT_IPACC_RSL_CONNECT_NACK;
+ struct abis_om_fom_hdr foh = {
+ .msg_type = NM_MT_IPACC_RSL_CONNECT_NACK,
+ .obj_class = NM_OC_BASEB_TRANSC,
+ .obj_inst = {
+ .bts_nr = trx->bts->bts_nr,
+ .trx_nr = trx->nr,
+ .ts_nr = 0xff,
+ },
+ };
+ signal.foh = &foh;
+ signal.bts = trx->bts;
osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal);
}
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);
+ } else {
+ ia = (struct in_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..2a653bf97 100644
--- a/src/osmo-bsc/abis_om2000.c
+++ b/src/osmo-bsc/abis_om2000.c
@@ -44,9 +44,22 @@
#include <osmocom/bsc/abis_om2000.h>
#include <osmocom/bsc/signal.h>
#include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/nm_common_fsm.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/abis/e1_input.h>
+static inline void abis_om2000_fsm_transc_becomes_enabled(struct gsm_bts_trx *trx)
+{
+ nm_obj_fsm_becomes_enabled_disabled(trx->bts, trx, NM_OC_RADIO_CARRIER, true);
+ nm_obj_fsm_becomes_enabled_disabled(trx->bts, &trx->bb_transc, NM_OC_BASEB_TRANSC, true);
+}
+
+static inline void abis_om2000_fsm_transc_becomes_disabled(struct gsm_bts_trx *trx)
+{
+ nm_obj_fsm_becomes_enabled_disabled(trx->bts, trx, NM_OC_RADIO_CARRIER, false);
+ nm_obj_fsm_becomes_enabled_disabled(trx->bts, &trx->bb_transc, NM_OC_BASEB_TRANSC, false);
+}
+
/* FIXME: move to libosmocore */
struct osmo_fsm_inst *osmo_fsm_inst_alloc_child_id(struct osmo_fsm *fsm,
struct osmo_fsm_inst *parent,
@@ -310,6 +323,7 @@ enum abis_om2k_dei {
OM2K_DEI_MAX_ALLOWED_POWER = 0xa9,
OM2K_DEI_MAX_ALLOWED_NUM_TRXCS = 0xaa,
OM2K_DEI_MCTR_FEAT_STATUS_BMAP = 0xab,
+ OM2K_DEI_SEEN_UNKNOWN_D2 = 0xd2,
};
enum abis_om2k_mostate {
@@ -388,6 +402,7 @@ const struct tlv_definition om2k_att_tlvdef = {
[OM2K_DEI_FS_OFFSET] = { TLV_TYPE_FIXED, 5 },
[OM2K_DEI_EXT_COND_MAP_2_EXT] = { TLV_TYPE_FIXED, 4 },
[OM2K_DEI_TSS_MO_STATE] = { TLV_TYPE_FIXED, 4 },
+ [OM2K_DEI_SEEN_UNKNOWN_D2] = { TLV_TYPE_FIXED, 6 },
},
};
@@ -776,6 +791,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 +867,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 +879,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;
@@ -917,7 +934,7 @@ mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
return nm_state;
}
-static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo)
+static void *mo2obj(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
{
struct gsm_bts_trx *trx;
@@ -945,17 +962,11 @@ 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)
+/* Derive an OML Availability state from an OM2000 MO state */
+static enum abis_nm_avail_state abis_nm_av_state_from_om2k_av_state(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;
- struct nm_statechg_signal_data nsd;
bool has_enabled_state;
- if (!nm_state)
- return;
-
switch (mo->class) {
case OM2K_MO_CLS_CF:
case OM2K_MO_CLS_TRXC:
@@ -966,61 +977,132 @@ static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo,
break;
}
- new_state = *nm_state;
switch (mo_state) {
case OM2K_MOSTATE_RESET:
- new_state.availability = NM_AVSTATE_POWER_OFF;
- break;
+ return NM_AVSTATE_POWER_OFF;
case OM2K_MOSTATE_STARTED:
- new_state.availability = has_enabled_state ? NM_AVSTATE_OFF_LINE : NM_AVSTATE_OK;
- break;
+ return has_enabled_state ? NM_AVSTATE_OFF_LINE : NM_AVSTATE_OK;
case OM2K_MOSTATE_ENABLED:
- new_state.availability = NM_AVSTATE_OK;
- break;
+ return NM_AVSTATE_OK;
case OM2K_MOSTATE_DISABLED:
- new_state.availability = NM_AVSTATE_POWER_OFF;
- break;
+ return NM_AVSTATE_POWER_OFF;
default:
- new_state.availability = NM_AVSTATE_DEGRADED;
- break;
+ return NM_AVSTATE_DEGRADED;
}
+}
+
+/* The OM2000 -> 12.21 mapping we do doesn't have a separate bb_transc MO,
+ * for compatibility reasons we pretend to have it anyway. */
+static void update_bb_trxc_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, uint8_t mo_state)
+{
+ struct nm_statechg_signal_data nsd;
+ struct gsm_bts_trx *trx;
+
+ trx = gsm_bts_trx_num(bts, mo->inst);
+ if (!trx)
+ return;
memset(&nsd, 0, sizeof(nsd));
nsd.bts = bts;
nsd.obj = mo2obj(bts, mo);
- nsd.old_state = nm_state;
- nsd.new_state = &new_state;
+ nsd.old_state = trx->bb_transc.mo.nm_state;
+ nsd.new_state = trx->bb_transc.mo.nm_state;
nsd.om2k_mo = mo;
- osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd);
-
- nm_state->availability = new_state.availability;
+ nsd.new_state.availability = abis_nm_av_state_from_om2k_av_state(mo, mo_state);
+ trx->bb_transc.mo.nm_state.availability = nsd.new_state.availability;
+ osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd);
}
-static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo,
- uint8_t op_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;
+ struct nm_statechg_signal_data nsd;
if (!nm_state)
return;
- new_state = *nm_state;
+ memset(&nsd, 0, sizeof(nsd));
+
+ nsd.bts = bts;
+ nsd.obj = mo2obj(bts, mo);
+ nsd.old_state = *nm_state;
+ nsd.new_state = *nm_state;
+ nsd.om2k_mo = mo;
+
+ nsd.new_state.availability = abis_nm_av_state_from_om2k_av_state(mo, mo_state);
+
+ /* Update current state before emitting signal: */
+ nm_state->availability = nsd.new_state.availability;
+ osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd);
+
+ /* When the TRXC MO is updated, also update the BB TRXC accordingly. */
+ if (mo->class == OM2K_MO_CLS_TRXC)
+ update_bb_trxc_mo_state(bts, mo, mo_state);
+}
+
+/* Derive an OML Operational state from an OM2000 OP state */
+static enum abis_nm_op_state abis_nm_op_state_from_om2k_op_state(uint8_t op_state)
+{
switch (op_state) {
case 1:
- new_state.operational = NM_OPSTATE_ENABLED;
- break;
+ return NM_OPSTATE_ENABLED;
case 0:
- new_state.operational = NM_OPSTATE_DISABLED;
- break;
+ return NM_OPSTATE_DISABLED;
default:
- new_state.operational = NM_OPSTATE_NULL;
- break;
+ return NM_OPSTATE_NULL;
}
+}
+
+/* (see comment in update_bb_trxc_mo_state() above) */
+static void update_bb_trxc_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t op_state)
+{
+ struct nm_statechg_signal_data nsd;
+ struct gsm_bts_trx *trx;
+
+ trx = gsm_bts_trx_num(bts, mo->inst);
+ if (!trx)
+ return;
+
+ memset(&nsd, 0, sizeof(nsd));
+
+ nsd.bts = bts;
+ nsd.obj = mo2obj(bts, mo);
+ nsd.old_state = trx->bb_transc.mo.nm_state;
+ nsd.new_state = trx->bb_transc.mo.nm_state;
+ nsd.om2k_mo = mo;
+
+ nsd.new_state.operational = abis_nm_op_state_from_om2k_op_state(op_state);
+ trx->bb_transc.mo.nm_state.operational = nsd.new_state.operational;
+ osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd);
+}
+
+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 nm_statechg_signal_data nsd;
- nm_state->operational = new_state.operational;
+ if (!nm_state)
+ return;
+
+ memset(&nsd, 0, sizeof(nsd));
+
+ nsd.bts = bts;
+ nsd.obj = mo2obj(bts, mo);
+ nsd.old_state = *nm_state;
+ nsd.new_state = *nm_state;
+ nsd.om2k_mo = mo;
+
+ nsd.new_state.operational = abis_nm_op_state_from_om2k_op_state(op_state);
+
+ /* Update current state before emitting signal: */
+ nm_state->operational = nsd.new_state.operational;
+ osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd);
+
+ /* When the TRXC MO is updated, also update the BB TRXC accordingly. */
+ if (mo->class == OM2K_MO_CLS_TRXC)
+ update_bb_trxc_op_state(bts, mo, op_state);
}
static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg)
@@ -1041,8 +1123,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 +1133,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 +1148,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 +1166,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 +1182,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 +1190,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 +1235,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 +1245,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 +1305,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 +1360,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 +1388,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 +1404,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 +1426,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 +1468,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 +1493,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 +1503,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 +1600,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 +1669,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 +1683,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 +1698,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 +1732,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 +1744,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 +1768,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 +1783,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 +1793,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 +1810,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 +1884,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 +1898,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 +1910,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 +1922,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 +1935,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 +1963,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 +1973,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 +1982,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 +1991,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 +2011,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 +2020,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 +2029,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 +2038,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 +2068,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 +2085,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 +2093,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 +2134,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 +2144,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 +2171,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 +2182,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 +2207,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 +2215,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 +2224,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 +2233,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 +2243,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)
@@ -2184,16 +2254,11 @@ static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *d
struct om2k_trx_fsm_priv *otfp = fi->priv;
struct gsm_bts_trx_ts *ts;
- /* notify TS is ready */
- ts = &otfp->trx->ts[otfp->cur_ts_nr];
- osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_READY, NULL);
-
/* next ? */
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 +2277,117 @@ 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;
+ struct nm_statechg_signal_data nsd;
+ struct nm_statechg_signal_data nsd_bb_transc;
+ struct gsm_bts_trx *trx = otfp->trx;
+ unsigned int i;
+
+ memset(&nsd, 0, sizeof(nsd));
+
+ nsd.bts = trx->bts;
+ nsd.obj = trx;
+ nsd.old_state = trx->mo.nm_state;
+ nsd.new_state = trx->mo.nm_state;
+ nsd.om2k_mo = &trx->rbs2000.trxc.om2k_mo.addr;
+
+ /* See e1_config:bts_isdn_sign_link() / OS#4914 */
+ nsd.new_state.administrative = NM_STATE_UNLOCKED;
+ trx->mo.nm_state.administrative = nsd.new_state.administrative;
+ osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd);
+
+ /* The OM2000 -> 12.21 mapping we do doesn't have separate bb_transc MO,
+ * for compatibility reasons we pretend to have it anyway and mirror the
+ * mo state of the TRXC on it. */
+ memset(&nsd_bb_transc, 0, sizeof(nsd));
+ nsd_bb_transc.bts = trx->bts;
+ nsd_bb_transc.obj = &trx->bb_transc;
+ nsd_bb_transc.old_state = trx->bb_transc.mo.nm_state;
+ nsd_bb_transc.new_state = trx->bb_transc.mo.nm_state;
+ nsd_bb_transc.om2k_mo = &trx->rbs2000.trxc.om2k_mo.addr;
+ nsd_bb_transc.new_state.administrative = NM_STATE_UNLOCKED;
+ trx->bb_transc.mo.nm_state.administrative = nsd_bb_transc.new_state.administrative;
+ osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd_bb_transc);
+
+ abis_om2000_fsm_transc_becomes_enabled(trx);
+
+ if (fi->proc.parent)
+ osmo_fsm_inst_dispatch(fi->proc.parent, otfp->done_event, NULL);
+
+ /* Notify the timeslot FSM that all TRX initialization steps are done. */
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++)
+ osmo_fsm_inst_dispatch(trx->ts[i].fi, TS_EV_OML_READY, NULL);
+}
+
+static void om2k_trx_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct om2k_trx_fsm_priv *otfp = fi->priv;
+
+ switch (event) {
+ case OM2K_TRX_EVT_RESET:
+ abis_om2000_fsm_transc_becomes_disabled(otfp->trx);
+ 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 +2403,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 +2472,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 +2491,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 +2502,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 +2514,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 +2524,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 +2541,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 +2554,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 +2565,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 +2575,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 +2590,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 +2598,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 +2649,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 +2706,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 +2760,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 +2882,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 +2905,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 +2913,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 +2954,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 +2984,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;
}
@@ -2830,22 +2997,19 @@ 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));
+ if (tlv_count >= 20) {
+ 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 +3036,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 +3053,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);
@@ -2920,11 +3079,13 @@ int abis_om2k_rcvmsg(struct msgb *msg)
switch (msg_type) {
case OM2K_MSGT_CAL_TIME_REQ:
rc = abis_om2k_cal_time_resp(bts);
- break;
+ /* we receive this from MOs without FSM (https://osmocom.org/issues/4670) */
+ goto no_mo;
case OM2K_MSGT_FAULT_REP:
display_fault_maps(msg->l2h, msgb_l2len(msg), &o2h->mo);
rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK);
- break;
+ /* we receive this from MOs without FSM (https://osmocom.org/issues/4643) */
+ goto no_mo;
case OM2K_MSGT_NEGOT_REQ:
rc = om2k_rx_negot_req(msg);
break;
@@ -2996,26 +3157,24 @@ 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));
- return 0;
+ "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), msgb_hexdump(msg));
+ goto no_mo;
}
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));
- return 0;
+ "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), msgb_hexdump(msg));
+ goto no_mo;
}
/* Dispatch message to that MO */
om2k_mo_fsm_recvmsg(bts, mo, &odm);
+no_mo:
msgb_free(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 +3186,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 +3215,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..7eb3a4392 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -55,8 +55,9 @@
#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>
+#include <osmocom/bsc/lchan.h>
static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,
struct gsm_meas_rep *resp)
@@ -72,26 +73,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 +147,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 +155,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;
+ out[0] = ALG_A5_NR_TO_RSL(lchan->encr.alg_a5_n);
+ switch (out[0]) {
+ 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_a5_n);
+ 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[1], 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[1], 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_a5_n);
+ 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 +219,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 +293,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 +313,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 +322,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 +337,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 +346,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 +357,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 +393,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,33 +418,33 @@ 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:
default:
LOGP(DRSL, LOGL_ERROR,
"unsupported activation lchan->type %u %s\n",
- lchan->type, gsm_lchant_name(lchan->type));
+ lchan->type, gsm_chan_t_name(lchan->type));
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 +460,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 +476,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 +506,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 +602,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 +616,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 +624,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),
@@ -525,11 +661,15 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
msg->l3h = len + 1;
*len = msgb_l3len(msg);
- if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) {
+ if (lchan->encr.alg_a5_n > 0) {
uint8_t encr_info[MAX_A5_KEY_LEN+2];
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 +681,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 +746,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;
@@ -582,16 +765,36 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
(uint8_t *) &cm);
- if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) {
+ if (lchan->encr.alg_a5_n > 0) {
uint8_t encr_info[MAX_A5_KEY_LEN+2];
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);
- msg->dst = lchan->ts->trx->rsl_link;
+ /* 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 = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
@@ -601,11 +804,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 +829,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 +840,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 +862,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);
}
@@ -667,14 +881,14 @@ int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group,
const struct osmo_mobile_identity *mi,
uint8_t chan_needed, bool is_gprs)
{
- struct abis_rsl_dchan_hdr *dh;
+ struct abis_rsl_cchan_hdr *cch;
struct msgb *msg = rsl_msgb_alloc();
uint8_t *l;
int rc;
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_PAGING_CMD);
- dh->chan_nr = RSL_CHAN_PCH_AGCH;
+ cch = (struct abis_rsl_cchan_hdr *) msgb_put(msg, sizeof(*cch));
+ rsl_init_cchan_hdr(cch, RSL_MT_PAGING_CMD);
+ cch->chan_nr = RSL_CHAN_PCH_AGCH;
msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group);
@@ -693,7 +907,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);
}
@@ -713,30 +927,21 @@ int rsl_forward_layer3_info(struct gsm_lchan *lchan, const uint8_t *l3_info, uin
return rsl_data_request(msg, 0);
}
-int imsi_str2bcd(uint8_t *bcd_out, const char *str_in)
-{
- int i, len = strlen(str_in);
-
- for (i = 0; i < len; i++) {
- int num = str_in[i] - 0x30;
- if (num < 0 || num > 9)
- return -1;
- if (i % 2 == 0)
- bcd_out[i/2] = num;
- else
- bcd_out[i/2] |= (num << 4);
- }
-
- return 0;
-}
-
/* Chapter 8.5.6 */
-struct msgb *rsl_imm_assign_cmd_common(struct gsm_bts *bts, uint8_t len, uint8_t *val)
+struct msgb *rsl_imm_assign_cmd_common(const struct gsm_bts *bts, uint8_t len, const uint8_t *val)
{
- struct msgb *msg = rsl_msgb_alloc();
+ struct msgb *msg;
struct abis_rsl_dchan_hdr *dh;
uint8_t buf[GSM_MACBLOCK_LEN];
+ if (len > sizeof(buf)) {
+ LOGP(DRSL, LOGL_ERROR,
+ "Cannot send IMMEDIATE ASSIGNMENT message with excessive length (%u)\n", len);
+ return NULL;
+ }
+
+ msg = rsl_msgb_alloc();
+
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD);
dh->chan_nr = RSL_CHAN_PCH_AGCH;
@@ -753,12 +958,12 @@ 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;
}
/* Chapter 8.5.6 */
-int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val)
+int rsl_imm_assign_cmd(const struct gsm_bts *bts, uint8_t len, const uint8_t *val)
{
struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val);
if (!msg)
@@ -766,13 +971,18 @@ int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val)
return abis_rsl_sendmsg(msg);
}
-/* Chapter 8.5.6 */
-int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len, uint8_t *val)
+/* Chapter 8.5.6 Immediate Assignment Command (with Ericcson vendor specific RSL extension) */
+int rsl_ericsson_imm_assign_cmd(const struct gsm_bts *bts, uint32_t tlli, uint8_t len,
+ const uint8_t *val, uint8_t pag_grp)
{
struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val);
if (!msg)
return 1;
+ /* Append ericsson proprietary paging group IE, this will instruct the BTS to
+ * send this immediate assignment through PCH instead of AGCH. */
+ msgb_tv_put(msg, RSL_IE_ERIC_PAGING_GROUP, pag_grp);
+
/* ericsson can handle a reference at the end of the message which is used in
* the confirm message. The confirm message is only sent if the trailer is present */
msgb_put_u8(msg, RSL_IE_ERIC_MOBILE_ID);
@@ -784,37 +994,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);
- msg->dst = msg->lchan->ts->trx->rsl_link;
+ 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 = rsl_chan_link(msg->lchan);
return abis_rsl_sendmsg(msg);
}
@@ -824,10 +1150,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 +1173,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 +1194,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 +1210,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",
@@ -888,7 +1218,12 @@ static int rsl_rx_chan_act_nack(struct msgb *msg)
return -EINVAL;
}
- rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg) - sizeof(*dh)) < 0) {
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "Failed to parse RSL %s\n",
+ rsl_or_ipac_msg_name(dh->c.msg_type));
+ return -EINVAL;
+ }
+
cause_p = rsl_cause(&tp);
LOG_LCHAN(lchan, LOGL_ERROR, "CHANNEL ACTIVATE NACK%s\n", rsl_cause_name(&tp));
@@ -904,86 +1239,122 @@ 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;
- rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg) - sizeof(*dh)) < 0) {
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "Failed to parse RSL %s\n",
+ rsl_or_ipac_msg_name(dh->c.msg_type));
+ return -EINVAL;
+ }
+
cause_p = rsl_cause(&tp);
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;
-
- 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;
- }
+ struct osmo_strbuf sb = { .buf = buf, .len = len };
- 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 RXLEV=%ddBm\n",
+ mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev));
+ }
}
if (bsub)
@@ -1011,6 +1382,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");
@@ -1020,7 +1392,11 @@ static int rsl_rx_meas_res(struct msgb *msg)
memset(mr, 0, sizeof(*mr));
mr->lchan = msg->lchan;
- rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg) - sizeof(*dh)) < 0) {
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "Failed to parse RSL %s\n",
+ rsl_or_ipac_msg_name(dh->c.msg_type));
+ return -EINVAL;
+ }
if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) ||
!TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) ||
@@ -1044,7 +1420,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 +1439,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 +1461,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;
@@ -1097,7 +1477,11 @@ static int rsl_rx_hando_det(struct msgb *msg)
.msg = msg,
};
- rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg) - sizeof(*dh)) < 0) {
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "Failed to parse RSL %s\n",
+ rsl_or_ipac_msg_name(dh->c.msg_type));
+ return -EINVAL;
+ }
if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY))
d.access_delay = TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY);
@@ -1133,6 +1517,9 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
int rc = 0;
struct e1inp_sign_link *sign_link = msg->dst;
+ if (msgb_l2len(msg) < sizeof(*rslh))
+ return -EINVAL;
+
if (rslh->ie_chan != RSL_IE_CHAN_NR) {
LOGP(DRSL, LOGL_ERROR,
"Rx RSL DCHAN: invalid RSL header, expecting Channel Number IE tag, got 0x%x\n",
@@ -1190,7 +1577,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 +1602,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;
}
@@ -1233,7 +1620,14 @@ static int rsl_rx_error_rep(struct msgb *msg)
struct tlv_parsed tp;
struct e1inp_sign_link *sign_link = msg->dst;
- rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh));
+ if (msgb_l2len(msg) < sizeof(*rslh))
+ return -EINVAL;
+
+ if (rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg) - sizeof(*rslh)) < 0) {
+ LOGP(DRSL, LOGL_ERROR, "%s Failed to parse RSL %s\n",
+ gsm_trx_name(sign_link->trx), rsl_or_ipac_msg_name(rslh->msg_type));
+ return -EINVAL;
+ }
LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT%s\n",
gsm_trx_name(sign_link->trx), rsl_cause_name(&tp));
@@ -1241,6 +1635,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 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;
+ }
+ }
+
+ if (rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg) - sizeof(*rslh)) < 0) {
+ LOGP(DRSL, LOGL_ERROR, "%s Failed to parse RSL %s\n",
+ gsm_trx_name(trx), rsl_or_ipac_msg_name(rslh->msg_type));
+ 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 +1722,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 +1738,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;
@@ -1344,25 +1813,29 @@ struct chan_rqd {
/* Handle packet channel rach requests */
static int rsl_rx_pchan_rqd(struct chan_rqd *rqd)
{
- uint8_t t1, t2, t3;
uint32_t fn;
uint8_t rqd_ta;
uint8_t is_11bit;
+ struct gsm_time gsm_time;
/* Process rach request and forward contained information to PCU */
if (rqd->ref.ra == 0x7F) {
is_11bit = 1;
/* FIXME: Also handle 11 bit rach requests */
- LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",rqd->bts->nr);
+ LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n", rqd->bts->nr);
return -EINVAL;
} else {
is_11bit = 0;
- t1 = rqd->ref.t1;
- t2 = rqd->ref.t2;
- t3 = rqd->ref.t3_low | (rqd->ref.t3_high << 3);
- fn = (51 * ((t3-t2) % 26) + t3 + 51 * 26 * t1);
rqd_ta = rqd->ta;
+
+ gsm_time.t1 = rqd->ref.t1;
+ gsm_time.t2 = rqd->ref.t2;
+ gsm_time.t3 = rqd->ref.t3_low | (rqd->ref.t3_high << 3);
+ fn = gsm_gsmtime2fn(&gsm_time);
+
+ LOG_BTS(rqd->bts, DRSL, LOGL_INFO, "CHAN RQD: fn(t1=%u,t3=%u,t2=%u) = %u\n",
+ gsm_time.t1, gsm_time.t3, gsm_time.t2, fn);
}
return pcu_tx_rach_ind(rqd->bts, rqd_ta, rqd->ref.ra, fn, is_11bit,
@@ -1446,13 +1919,56 @@ 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);
+ LOG_BTS(bts, DRSL, LOGL_INFO, "CHAN RQD: reason: %s (ra=0x%02x, t1=%d, t3=%d, t2=%d, neci=0x%02x, chreq_reason=0x%02x)\n",
+ get_value_string(gsm_chreq_descs, rqd->reason), rqd->ref.ra,
+ rqd->ref.t1, rqd->ref.t3_high << 3 | rqd->ref.t3_low, rqd->ref.t2,
+ bts->network->neci, rqd->reason);
+
+ 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;
+ }
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL]);
+ /* 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 (cfg: network / bts / rach emergency call allowed 0)\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 +1995,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_chan_req_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_chan_req_reverse)
+ lchan_any = lchan;
}
}
}
@@ -1518,12 +2027,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, SELECT_FOR_MS_CHAN_REQ, NULL, 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, SELECT_FOR_MS_CHAN_REQ, NULL, 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 +2042,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,11 +2059,23 @@ 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));
-
- lchan_release(rqd->release_lchan, !!(rqd->release_lchan->conn), true, 0);
+ gsm_lchan_name(release_lchan), osmo_fsm_inst_state_name(release_lchan->fi));
+
+ /* Make sure the Clear Request to the MSC has the proper cause */
+ if (release_lchan->conn)
+ gscon_bssmap_clear(release_lchan->conn, GSM0808_CAUSE_PREEMPTION);
+ /* The gscon FSM would only release the lchan after the MSC responds with a Clear Command.
+ * But we need it released right now. Also with the right RR cause. */
+ lchan_release(release_lchan, !!(release_lchan->conn), true, GSM48_RR_CAUSE_PREMPTIVE_REL,
+ gscon_last_eutran_plmn(release_lchan->conn));
+
+ /* 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, GSM48_RR_CAUSE_PREMPTIVE_REL,
+ gscon_last_eutran_plmn(release_lchan->conn));
} else {
- /* BTS is shutting down, give up... */
+ /* if BTS has shut down, give up... */
if (rqd->release_lchan->ts->fi->state == TS_ST_NOT_INITIALIZED)
return false;
@@ -1575,6 +2097,62 @@ 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 gsm_lchan *lchan = NULL;
+ int free_tchf, free_tchh;
+ bool needs_dyn_switch;
+
+ lchan = lchan_avail_by_type(bts, GSM_LCHAN_SDCCH, SELECT_FOR_MS_CHAN_REQ, NULL, 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;
+
+ free_tchf = bts->chan_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ free_tchh = bts->chan_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_chan_t_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_chan_t_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;
@@ -1588,8 +2166,8 @@ void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts)
/* Handle PDCH related rach requests (in case of BSC-co-located-PCU) */
if (rqd->reason == GSM_CHREQ_REASON_PDCH) {
- rsl_rx_pchan_rqd(rqd);
- return;
+ if (rsl_rx_pchan_rqd(rqd) == 0)
+ goto leave;
}
/* Ensure that emergency calls will get priority over regular calls, however releasing
@@ -1610,36 +2188,44 @@ 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)
- 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);
+ 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,
+ SELECT_FOR_MS_CHAN_REQ,
+ NULL);
+ }
+ /* 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 (bts->chan_alloc_tch_signalling_policy == BTS_TCH_SIGNALLING_ALWAYS ||
+ (bts->chan_alloc_tch_signalling_policy == BTS_TCH_SIGNALLING_VOICE &&
+ gsm_chreq_reason_is_voicecall(rqd->reason)) ||
+ (bts->chan_alloc_tch_signalling_policy == BTS_TCH_SIGNALLING_EMERG &&
+ rqd->reason == GSM_CHREQ_REASON_EMERG)) {
+ 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_chan_t_name(GSM_LCHAN_SDCCH),
+ rqd->ref.ra, gsm_chan_t_name(GSM_LCHAN_TCH_H));
+ lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_H,
+ SELECT_FOR_MS_CHAN_REQ,
+ NULL);
+ }
+ 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_chan_t_name(GSM_LCHAN_SDCCH),
+ rqd->ref.ra, gsm_chan_t_name(GSM_LCHAN_TCH_F));
+ lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_F,
+ SELECT_FOR_MS_CHAN_REQ,
+ NULL);
+ }
}
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_chan_t_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,27 +2237,65 @@ 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);
+
+leave:
llist_del(&rqd->entry);
talloc_free(rqd);
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_SUCCESSFUL_EMERG));
+ break;
+ case GSM_CHREQ_REASON_CALL:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_SUCCESSFUL_CALL));
+ break;
+ case GSM_CHREQ_REASON_LOCATION_UPD:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_SUCCESSFUL_LOCATION_UPD));
+ break;
+ case GSM_CHREQ_REASON_PAG:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_SUCCESSFUL_PAG));
+ break;
+ case GSM_CHREQ_REASON_PDCH:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_SUCCESSFUL_PDCH));
+ break;
+ case GSM_CHREQ_REASON_OTHER:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_SUCCESSFUL_OTHER));
+ break;
+ default:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_SUCCESSFUL_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 +2303,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 +2333,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;
}
@@ -1710,22 +2346,21 @@ static int rsl_rx_ccch_load(struct msgb *msg)
struct ccch_signal_data sd;
sd.bts = sign_link->trx->bts;
- sd.rach_slot_count = -1;
- sd.rach_busy_count = -1;
- sd.rach_access_count = -1;
+ sd.rach_slot_count = UINT16_MAX;
+ sd.rach_busy_count = UINT16_MAX;
+ sd.rach_access_count = UINT16_MAX;
switch (rslh->data[0]) {
case RSL_IE_PAGING_LOAD:
sd.pg_buf_space = rslh->data[1] << 8 | rslh->data[2];
- if (is_ipaccess_bts(sign_link->trx->bts) && sd.pg_buf_space == 0xffff) {
- /* paging load below configured threshold, use 50 as default */
- sd.pg_buf_space = 50;
+ if (is_ipaccess_bts(sign_link->trx->bts) && sd.pg_buf_space == UINT16_MAX) {
+ sd.pg_buf_space = paging_estimate_available_slots(sd.bts, sd.bts->ccch_load_ind_period);
}
paging_update_buffer_space(sign_link->trx->bts, sd.pg_buf_space);
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 +2375,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);
}
@@ -1765,7 +2400,12 @@ static int rsl_rx_cbch_load(struct msgb *msg)
struct tlv_parsed tp;
uint8_t slot_count;
- rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg) - sizeof(*rslh));
+ if (rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg) - sizeof(*rslh)) < 0) {
+ LOGP(DRSL, LOGL_ERROR, "%s Failed to parse RSL %s\n",
+ gsm_trx_name(sign_link->trx), rsl_or_ipac_msg_name(rslh->c.msg_type));
+ return -EINVAL;
+ }
+
if (!TLVP_PRESENT(&tp, RSL_IE_CBCH_LOAD_INFO)) {
LOG_BTS(bts, DRSL, LOGL_ERROR, "CBCH LOAD IND without mandatory CBCH Load Info IE\n");
return -1;
@@ -1810,8 +2450,12 @@ 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;
+ if (msgb_l2len(msg) < sizeof(*rslh))
+ return -EINVAL;
+
msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr,
"Abis RSL rx CCHAN: ");
@@ -1827,7 +2471,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 +2483,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;
}
@@ -1852,9 +2496,14 @@ static int rsl_rx_rll_err_ind(struct msgb *msg)
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
uint8_t rlm_cause;
- rsl_tlv_parse(&tp, rllh->data, msgb_l2len(msg) - sizeof(*rllh));
+ if (rsl_tlv_parse(&tp, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) {
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "Failed to parse RSL %s\n",
+ rsl_or_ipac_msg_name(rllh->c.msg_type));
+ return -EINVAL;
+ }
+
if (!TLVP_PRESENT(&tp, RSL_IE_RLM_CAUSE)) {
- LOG_LCHAN(msg->lchan, LOGL_ERROR, "ERROR INDICATION without mandantory cause.\n");
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "ERROR INDICATION without mandatory cause.\n");
return -1;
}
@@ -1863,7 +2512,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);
@@ -1881,9 +2530,15 @@ static int abis_rsl_rx_rll(struct msgb *msg)
struct e1inp_sign_link *sign_link = msg->dst;
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
int rc = 0;
- uint8_t sapi = rllh->link_id & 0x7;
+ uint8_t sapi;
+ if (msgb_l2len(msg) < sizeof(*rllh))
+ return -1;
+
+ sapi = rllh->link_id & 0x7;
msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr, "Abis RSL rx RLL: ");
+ if (OSMO_UNLIKELY(msg->lchan == NULL))
+ return -1;
switch (rllh->c.msg_type) {
case RSL_MT_DATA_IND:
@@ -1964,7 +2619,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 +2627,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 +2675,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:
@@ -2105,11 +2760,16 @@ static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv, const
port = ntohs(port);
lchan->abis_ip.connect_port = port;
}
+ if (TLVP_PRESENT(tv, RSL_IE_OSMO_OSMUX_CID)) {
+ lchan->abis_ip.osmux.remote_cid_present = true;
+ lchan->abis_ip.osmux.remote_cid = tlvp_val8(tv, RSL_IE_OSMO_OSMUX_CID, 0);
+ }
LOG_LCHAN(lchan, LOGL_DEBUG, "Rx IPACC %s ACK:"
- " BTS=%s:%u conn_id=%u rtp_payload2=0x%02x speech_mode=0x%02x\n",
+ " BTS=%s:%u conn_id=%u rtp_payload2=0x%02x speech_mode=0x%02x osmux_use=%d osmux_loc_cid=%d\n",
label, ip_to_a(lchan->abis_ip.bound_ip), lchan->abis_ip.bound_port,
- lchan->abis_ip.conn_id, lchan->abis_ip.rtp_payload2, lchan->abis_ip.speech_mode);
+ lchan->abis_ip.conn_id, lchan->abis_ip.rtp_payload2, lchan->abis_ip.speech_mode,
+ lchan->abis_ip.osmux.use, lchan->abis_ip.osmux.local_cid);
}
/*! Send Issue IPA RSL CRCX to configure the RTP port of the BTS.
@@ -2117,22 +2777,32 @@ 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);
msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload);
+ if (lchan->abis_ip.osmux.use)
+ msgb_tlv_put(msg, RSL_IE_OSMO_OSMUX_CID, 1, &lchan->abis_ip.osmux.local_cid);
- 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);
+ LOG_LCHAN(lchan, LOGL_DEBUG,
+ "Sending IPACC CRCX to BTS: speech_mode=0x%02x RTP_PAYLOAD=%d osmux_use=%d osmux_loc_cid=%d\n",
+ lchan->abis_ip.speech_mode, lchan->abis_ip.rtp_payload,
+ lchan->abis_ip.osmux.use, lchan->abis_ip.osmux.local_cid);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
@@ -2144,14 +2814,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);
@@ -2162,8 +2838,10 @@ struct msgb *rsl_make_ipacc_mdcx(const struct gsm_lchan *lchan, uint32_t dest_ip
msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload);
if (lchan->abis_ip.rtp_payload2)
msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, lchan->abis_ip.rtp_payload2);
+ if (lchan->abis_ip.osmux.use)
+ msgb_tlv_put(msg, RSL_IE_OSMO_OSMUX_CID, 1, &lchan->abis_ip.osmux.local_cid);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return msg;
}
@@ -2176,6 +2854,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),
@@ -2203,7 +2884,12 @@ static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg)
* address and port number to which it has bound the given logical
* channel */
- rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg) - sizeof(*dh)) < 0) {
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "Failed to parse RSL %s\n",
+ rsl_or_ipac_msg_name(dh->c.msg_type));
+ return -EINVAL;
+ }
+
if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) ||
!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) ||
!TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) {
@@ -2211,6 +2897,15 @@ static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg)
return -EINVAL;
}
+ if (!lchan->abis_ip.osmux.use && TLVP_PRESENT(&tv, RSL_IE_OSMO_OSMUX_CID)) {
+ LOGP(DRSL, LOGL_NOTICE, "Received unexpected IE Osmux CID\n");
+ return -EINVAL;
+ }
+ if (lchan->abis_ip.osmux.use && !TLVP_PRESENT(&tv, RSL_IE_OSMO_OSMUX_CID)) {
+ LOGP(DRSL, LOGL_NOTICE, "Mandatory IE Osmux CID missing\n");
+ return -EINVAL;
+ }
+
ipac_parse_rtp(lchan, &tv, "CRCX");
osmo_fsm_inst_dispatch(lchan->fi_rtp, LCHAN_RTP_EV_IPACC_CRCX_ACK, 0);
@@ -2223,7 +2918,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");
@@ -2248,7 +2943,12 @@ static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg)
* it now tells us the IP address and port number to which it has
* connected the given logical channel */
- rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg) - sizeof(*dh)) < 0) {
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "Failed to parse RSL %s\n",
+ rsl_or_ipac_msg_name(dh->c.msg_type));
+ return -EINVAL;
+ }
+
ipac_parse_rtp(lchan, &tv, "MDCX");
osmo_fsm_inst_dispatch(lchan->fi_rtp, LCHAN_RTP_EV_IPACC_MDCX_ACK, 0);
@@ -2261,7 +2961,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");
@@ -2276,7 +2976,12 @@ static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg)
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tv;
- rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg) - sizeof(*dh)) < 0) {
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "Failed to parse RSL %s\n",
+ rsl_or_ipac_msg_name(dh->c.msg_type));
+ return -EINVAL;
+ }
+
LOG_LCHAN(msg->lchan, LOGL_NOTICE, "Rx IPACC DLCX IND%s\n",
rsl_cause_name(&tv));
@@ -2289,6 +2994,9 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
int rc = 0;
+ if (msgb_l2len(msg) < sizeof(*rllh))
+ return -EINVAL;
+
msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr,
"Abis RSL rx IPACC: ");
@@ -2330,7 +3038,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 +3068,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 +3100,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 +3173,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 +3194,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 +3220,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 +3234,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 +3250,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 +3267,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..80a35766c 100644
--- a/src/osmo-bsc/acc.c
+++ b/src/osmo-bsc/acc.c
@@ -73,9 +73,8 @@ static void acc_mgr_enable_rotation_cond(struct acc_mgr *acc_mgr)
if (!osmo_timer_pending(&acc_mgr->rotate_timer))
osmo_timer_schedule(&acc_mgr->rotate_timer, acc_mgr->rotation_time_sec, 0);
} else {
- /* No rotation needed, disable rotation timer */
- if (osmo_timer_pending(&acc_mgr->rotate_timer))
- osmo_timer_del(&acc_mgr->rotate_timer);
+ /* No rotation needed, disable rotation timer (if pending) */
+ osmo_timer_del(&acc_mgr->rotate_timer);
}
}
@@ -411,122 +410,47 @@ static void do_acc_ramping_step(void *data)
}
/* Implements osmo_signal_cbfn() -- trigger or abort ACC ramping upon changes RF lock state. */
-static int acc_ramp_nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
+static int acc_ramp_nm_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
{
- struct nm_statechg_signal_data *nsd = signal_data;
- struct acc_ramp *acc_ramp = handler_data;
- struct gsm_bts_trx *trx = NULL;
- bool trigger_ramping = false, abort_ramping = false;
-
- /* Handled signals map to an Administrative State Change ACK, or a State Changed Event Report. */
- if (signal != S_NM_STATECHG_ADM && signal != S_NM_STATECHG_OPER)
+ struct nm_running_chg_signal_data *nsd;
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+ if (signal != S_NM_RUNNING_CHG)
return 0;
-
- if (nsd->obj_class != NM_OC_RADIO_CARRIER)
+ nsd = signal_data;
+ bts = nsd->bts;
+ switch (nsd->obj_class) {
+ case NM_OC_RADIO_CARRIER:
+ trx = (struct gsm_bts_trx *)nsd->obj;
+ break;
+ case NM_OC_BASEB_TRANSC:
+ trx = gsm_bts_bb_trx_get_trx((struct gsm_bts_bb_trx *)nsd->obj);
+ break;
+ default:
return 0;
-
- trx = nsd->obj;
-
- LOG_TRX(trx, DRSL, LOGL_DEBUG, "ACC RAMP: administrative state %s -> %s\n",
- get_value_string(abis_nm_adm_state_names, nsd->old_state->administrative),
- get_value_string(abis_nm_adm_state_names, nsd->new_state->administrative));
- LOG_TRX(trx, DRSL, LOGL_DEBUG, "ACC RAMP: operational state %s -> %s\n",
- abis_nm_opstate_name(nsd->old_state->operational),
- abis_nm_opstate_name(nsd->new_state->operational));
+ }
/* We only care about state changes of the first TRX. */
- if (trx->nr != 0)
+ if (trx != trx->bts->c0)
return 0;
- /* RSL must already be up. We cannot send RACH system information to the BTS otherwise. */
- if (trx->rsl_link == NULL) {
- LOG_TRX(trx, DRSL, LOGL_DEBUG,
- "ACC RAMP: ignoring state change because RSL link is down\n");
- return 0;
- }
-
- /* Trigger or abort ACC ramping based on the new state of this TRX. */
- if (nsd->old_state->administrative != nsd->new_state->administrative) {
- switch (nsd->new_state->administrative) {
- case NM_STATE_UNLOCKED:
- if (nsd->old_state->operational != nsd->new_state->operational) {
- /*
- * Administrative and operational state have both changed.
- * Trigger ramping only if TRX 0 will be both enabled and unlocked.
- */
- if (nsd->new_state->operational == NM_OPSTATE_ENABLED)
- trigger_ramping = true;
- else
- LOG_TRX(trx, DRSL, LOGL_DEBUG,
- "ACC RAMP: ignoring state change because TRX is "
- "transitioning into operational state '%s'\n",
- abis_nm_opstate_name(nsd->new_state->operational));
- } else {
- /*
- * Operational state has not changed.
- * Trigger ramping only if TRX 0 is already usable.
- */
- if (trx_is_usable(trx))
- trigger_ramping = true;
- else
- LOG_TRX(trx, DRSL, LOGL_DEBUG, "ACC RAMP: ignoring state change "
- "because TRX is not usable\n");
- }
- break;
- case NM_STATE_LOCKED:
- case NM_STATE_SHUTDOWN:
- abort_ramping = true;
- break;
- case NM_STATE_NULL:
- default:
- LOG_TRX(trx, DRSL, LOGL_ERROR, "ACC RAMP: unrecognized administrative state '0x%x' "
- "reported for TRX 0\n", nsd->new_state->administrative);
- break;
- }
- }
- if (nsd->old_state->operational != nsd->new_state->operational) {
- switch (nsd->new_state->operational) {
- case NM_OPSTATE_ENABLED:
- if (nsd->old_state->administrative != nsd->new_state->administrative) {
- /*
- * Administrative and operational state have both changed.
- * Trigger ramping only if TRX 0 will be both enabled and unlocked.
- */
- if (nsd->new_state->administrative == NM_STATE_UNLOCKED)
- trigger_ramping = true;
- else
- LOG_TRX(trx, DRSL, LOGL_DEBUG, "ACC RAMP: ignoring state change "
- "because TRX is transitioning into administrative state '%s'\n",
- get_value_string(abis_nm_adm_state_names, nsd->new_state->administrative));
- } else {
- /*
- * Administrative state has not changed.
- * Trigger ramping only if TRX 0 is already unlocked.
- */
- if (trx->mo.nm_state.administrative == NM_STATE_UNLOCKED)
- trigger_ramping = true;
- else
- LOG_TRX(trx, DRSL, LOGL_DEBUG, "ACC RAMP: ignoring state change "
- "because TRX is in administrative state '%s'\n",
- get_value_string(abis_nm_adm_state_names, trx->mo.nm_state.administrative));
- }
- break;
- case NM_OPSTATE_DISABLED:
- abort_ramping = true;
- break;
- case NM_OPSTATE_NULL:
- default:
- LOG_TRX(trx, DRSL, LOGL_ERROR, "ACC RAMP: unrecognized operational state '0x%x' "
- "reported for TRX 0\n", nsd->new_state->administrative);
- break;
+ LOG_TRX(trx, DRSL, LOGL_DEBUG, "ACC RAMP: nm_obj=%s running=%u\n",
+ get_value_string(abis_nm_obj_class_names, nsd->obj_class), nsd->running);
+ if (nsd->running) {
+ /* Trigger ramping only if TRX 0 is already usable. That usually
+ * means RCARRIER+BBTRANSC NM objects are running (op=enabled
+ * adm=unlocked) */
+ if (trx_is_usable(trx)) {
+ LOG_BTS(bts, DPAG, LOGL_INFO, "ACC RAMP: C0 becomes available\n");
+ acc_ramp_trigger(&trx->bts->acc_ramp);
+ } else {
+ LOG_TRX(trx, DRSL, LOGL_DEBUG, "ACC RAMP: ignoring state change "
+ "because TRX is not usable\n");
}
+ } else {
+ acc_ramp_abort(&trx->bts->acc_ramp);
}
-
- if (trigger_ramping)
- acc_ramp_trigger(acc_ramp);
- else if (abort_ramping)
- acc_ramp_abort(acc_ramp);
-
return 0;
}
@@ -548,7 +472,6 @@ void acc_ramp_init(struct acc_ramp *acc_ramp, struct gsm_bts *bts)
acc_ramp->chan_load_lower_threshold = ACC_RAMP_CHAN_LOAD_THRESHOLD_LOW;
acc_ramp->chan_load_upper_threshold = ACC_RAMP_CHAN_LOAD_THRESHOLD_UP;
osmo_timer_setup(&acc_ramp->step_timer, do_acc_ramping_step, acc_ramp);
- osmo_signal_register_handler(SS_NM, acc_ramp_nm_sig_cb, acc_ramp);
}
/*!
@@ -641,8 +564,12 @@ void acc_ramp_trigger(struct acc_ramp *acc_ramp)
*/
void acc_ramp_abort(struct acc_ramp *acc_ramp)
{
- if (osmo_timer_pending(&acc_ramp->step_timer))
- osmo_timer_del(&acc_ramp->step_timer);
+ osmo_timer_del(&acc_ramp->step_timer);
acc_mgr_set_len_allowed_ramp(&acc_ramp->bts->acc_mgr, 10);
}
+
+void acc_ramp_global_init(void)
+{
+ osmo_signal_register_handler(SS_NM, acc_ramp_nm_sig_cb, NULL);
+}
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..7f6a7ea85 100644
--- a/src/osmo-bsc/assignment_fsm.c
+++ b/src/osmo-bsc/assignment_fsm.c
@@ -32,15 +32,17 @@
#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/lchan.h>
#include <osmocom/bsc/assignment_fsm.h>
static struct osmo_fsm assignment_fsm;
-struct gsm_subscriber_connection *assignment_fi_conn(struct osmo_fsm_inst *fi)
+static struct gsm_subscriber_connection *assignment_fi_conn(struct osmo_fsm_inst *fi)
{
OSMO_ASSERT(fi);
OSMO_ASSERT(fi->fsm == &assignment_fsm);
@@ -49,10 +51,10 @@ struct gsm_subscriber_connection *assignment_fi_conn(struct osmo_fsm_inst *fi)
}
static const struct osmo_tdef_state_timeout assignment_fsm_timeouts[32] = {
- [ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE] = { .T=10 },
- [ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE] = { .keep_timer=true },
- [ASSIGNMENT_ST_WAIT_LCHAN_ESTABLISHED] = { .keep_timer=true },
- [ASSIGNMENT_ST_WAIT_MGW_ENDPOINT_TO_MSC] = { .T=23042 },
+ [ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE] = { .T = 10 },
+ [ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE] = { .keep_timer = true },
+ [ASSIGNMENT_ST_WAIT_LCHAN_ESTABLISHED] = { .keep_timer = true },
+ [ASSIGNMENT_ST_WAIT_MGW_ENDPOINT_TO_MSC] = { .T = -9 },
};
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
@@ -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,31 @@ 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)); \
+ LOG_ASSIGNMENT(conn, LOGL_DEBUG, "incrementing rate counter: bts%u %s %s\n", \
+ bts->nr, \
+ bts_ctr_description[BTS_##counter##_SIGN].name, \
+ bts_ctr_description[BTS_##counter##_SIGN].description); \
+ 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)); \
+ LOG_ASSIGNMENT(conn, LOGL_DEBUG, "incrementing rate counter: bts%u %s %s\n", \
+ bts->nr, \
+ bts_ctr_description[BTS_##counter##_SPEECH].name, \
+ bts_ctr_description[BTS_##counter##_SPEECH].description); \
+ break; \
+ default: \
+ break; \
+ } \
+ } \
+ } while (0)
#define assignment_count_result(counter) do { \
if (!conn->assignment.result_rate_ctr_done) { \
@@ -94,21 +117,24 @@ 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) {
- gscon_forget_mgw_endpoint_ci(conn, conn->assignment.created_ci_for_msc);
+ /* Store ci pointer locally, because gscon_forget_mgw_endpoint_ci() NULLs
+ * conn->assignment.created_ci_for_msc. */
+ struct osmo_mgcpc_ep_ci *ci = conn->assignment.created_ci_for_msc;
+ gscon_forget_mgw_endpoint_ci(conn, ci);
/* If this is the last endpoint released, the mgw_endpoint_fsm will terminate and tell
* the gscon about it. */
- osmo_mgcpc_ep_ci_dlcx(conn->assignment.created_ci_for_msc);
+ osmo_mgcpc_ep_ci_dlcx(ci);
}
conn->assignment = (struct assignment_fsm_data){
@@ -118,13 +144,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 +185,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),
- gsm_lchant_name(lchan->type));
+ get_value_string(gsm48_chan_mode_names, lchan->current_ch_mode_rate.chan_mode),
+ gsm_chan_t_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,14 +222,14 @@ 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->current_ch_mode_rate.s15_s0;
sc_ptr = &sc;
}
}
resp = gsm0808_create_ass_compl2(lchan->abis_ip.ass_compl.rr_cause,
chosen_channel,
- lchan->encr.alg_id, perm_spch,
+ ALG_A5_NR_TO_BSSAP(lchan->encr.alg_a5_n), perm_spch,
addr_local_p, sc_ptr, NULL, lcls_get_status(conn));
if (!resp) {
@@ -212,7 +242,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,55 +254,75 @@ 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;
- /* New RTP information is now accepted */
+ /* New RTP information is now accepted. If there is no RTP stream, this information is zero / empty. Either way
+ * store the result of this assignment. */
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;
+ assignment_count_result(CTR_ASSIGNMENT_COMPLETED);
+
LOG_ASSIGNMENT(conn, LOGL_DEBUG, "Assignment successful\n");
osmo_fsm_inst_term(conn->assignment.fi, OSMO_FSM_TERM_REGULAR, 0);
-
- assignment_count_result(CTR_ASSIGNMENT_COMPLETED);
}
-static void assignment_fsm_update_id(struct gsm_subscriber_connection *conn)
+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",
- 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);
+ osmo_fsm_inst_update_id_f_sanitize(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_name(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_name(new_lchan->ts->pchan_is),
+ 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 +330,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;
@@ -315,7 +365,7 @@ static bool lchan_type_compat_with_mode(enum gsm_chan_t type, const struct chann
}
}
-void assignment_fsm_init()
+static __attribute__((constructor)) void assignment_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&assignment_fsm) == 0);
}
@@ -324,7 +374,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:
@@ -340,7 +390,7 @@ static int check_requires_voice(bool *requires_voice, enum gsm48_chan_mode chan_
return 0;
}
-/* Check if the incoming assignment requests requires a voice stream or not,
+/* Check if the incoming assignment request requires a voice stream or not,
* we will look at the preferred and the alternate channel mode and also make
* sure that both are consistent. */
static int check_requires_voice_stream(struct gsm_subscriber_connection *conn)
@@ -356,11 +406,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 +418,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 +441,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 +511,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 +518,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 +527,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 +564,60 @@ 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_chan_t_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,
+ SELECT_FOR_ASSIGNMENT, conn->lchan);
+ 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 +626,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 +640,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 +697,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 +790,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 +839,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 +925,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 +951,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 +967,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 +980,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);
@@ -785,7 +988,7 @@ int assignment_fsm_timer_cb(struct osmo_fsm_inst *fi)
return 0;
}
-void assignment_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+static void assignment_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
assignment_reset(conn);
diff --git a/src/osmo-bsc/osmo_bsc_ctrl.c b/src/osmo-bsc/bsc_ctrl.c
index 05bc86c2b..8eb7caf71 100644
--- a/src/osmo-bsc/osmo_bsc_ctrl.c
+++ b/src/osmo-bsc/bsc_ctrl.c
@@ -1,6 +1,9 @@
-/* (C) 2011 by Daniel Willmann <daniel@totalueberwachung.de>
- * (C) 2011 by Holger Hans Peter Freyther
+/*
+ * (C) 2011 by Daniel Willmann <daniel@totalueberwachung.de>
* (C) 2011 by On-Waves
+ * (C) 2011-2015 by Holger Hans Peter Freyther
+ * (C) 2013-2015 by sysmocom s.f.m.c. GmbH
+ *
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -17,235 +20,461 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+#include <errno.h>
+#include <time.h>
+
+#include <osmocom/gsm/ipa.h>
#include <osmocom/ctrl/control_cmd.h>
-#include <osmocom/bsc/ctrl.h>
-#include <osmocom/bsc/debug.h>
+#include <osmocom/ctrl/control_if.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>
-#include <osmocom/bsc/osmo_bsc.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/chan_alloc.h>
#include <osmocom/bsc/osmo_bsc_rf.h>
#include <osmocom/bsc/bsc_msc_data.h>
-#include <osmocom/bsc/signal.h>
-#include <osmocom/bsc/a_reset.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/a_reset.h>
+#include <osmocom/bsc/ctrl.h>
+#include <osmocom/bsc/handover_ctrl.h>
+#include <osmocom/bsc/neighbor_ident.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/signal.h>
+static int verify_net_apply_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ FILE *cfile;
-#include <osmocom/ctrl/control_if.h>
+ if (!cmd->value || cmd->value[0] == '\0')
+ return -1;
-#include <osmocom/gsm/protocol/ipaccess.h>
-#include <osmocom/gsm/ipa.h>
+ cfile = fopen(cmd->value, "r");
+ if (!cfile)
+ return -1;
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
+ fclose(cfile);
-/* Obtain SS7 application server currently handling given MSC (DPC) */
-static struct osmo_ss7_as *msc_get_ss7_as(struct bsc_msc_data *msc)
-{
- struct osmo_ss7_route *rt;
- struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(msc->a.sccp);
- rt = osmo_ss7_route_lookup(ss7, msc->a.msc_addr.pc);
- if (!rt)
- return NULL;
- return rt->dest.as;
+ return 0;
}
-
-static int _ss7_as_send(struct osmo_ss7_as *as, struct msgb *msg)
+static int set_net_apply_config_file(struct ctrl_cmd *cmd, void *_data)
{
- struct osmo_ss7_asp *asp;
- unsigned int i;
+ 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;
+ }
- /* FIXME: unify with xua_as_transmit_msg() and perform proper ASP lookup */
- for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
- asp = as->cfg.asps[i];
- if (!asp)
- continue;
- /* FIXME: deal with multiple ASPs per AS */
- return osmo_ss7_asp_send(asp, msg);
+ 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;
}
- msgb_free(msg);
- return -1;
+
+ rc = neighbors_check_cfg();
+ if (rc) {
+ cmd->reply = talloc_asprintf(cmd, "Errors in neighbor configuration");
+ 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");
-int bsc_sccplite_msc_send(struct bsc_msc_data *msc, struct msgb *msg)
+static int verify_net_write_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
{
- struct osmo_ss7_as *as;
+ 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;
- as = msc_get_ss7_as(msc);
- if (!as) {
- msgb_free(msg);
+ 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)
+{
+ struct gsm_network *net = cmd->node;
+ cmd->reply = talloc_asprintf(cmd, "%s", osmo_mcc_name(net->plmn.mcc));
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ return CTRL_CMD_REPLY;
+}
+static int set_net_mcc(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_network *net = cmd->node;
+ uint16_t mcc;
+ if (osmo_mcc_from_str(cmd->value, &mcc))
return -1;
+ net->plmn.mcc = mcc;
+ return get_net_mcc(cmd, _data);
+}
+static int verify_net_mcc(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (osmo_mcc_from_str(value, NULL))
+ return -1;
+ return 0;
+}
+
+CTRL_CMD_DEFINE(net_mnc, "mnc");
+static int get_net_mnc(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_network *net = cmd->node;
+ cmd->reply = talloc_asprintf(cmd, "%s", osmo_mnc_name(net->plmn.mnc, net->plmn.mnc_3_digits));
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ return CTRL_CMD_REPLY;
+}
+static int set_net_mnc(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_network *net = cmd->node;
+ struct osmo_plmn_id plmn = net->plmn;
+ if (osmo_mnc_from_str(cmd->value, &plmn.mnc, &plmn.mnc_3_digits)) {
+ cmd->reply = "Error while decoding MNC";
+ return CTRL_CMD_ERROR;
}
+ net->plmn = plmn;
+ return get_net_mnc(cmd, _data);
+}
+static int verify_net_mnc(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (osmo_mnc_from_str(value, NULL, NULL))
+ return -1;
+ return 0;
+}
- /* don't attempt to send CTRL on a non-SCCPlite AS */
- if (as->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
- return 0;
+static int set_net_apply_config(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_network *net = cmd->node;
+ struct gsm_bts *bts;
- return _ss7_as_send(as, msg);
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ if (!is_ipaccess_bts(bts))
+ continue;
+
+ /*
+ * The ip.access nanoBTS seems to be unreliable on BSSGP
+ * so let's us just reboot it. For the sysmoBTS we can just
+ * restart the process as all state is gone.
+ */
+ 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);
+ } else
+ ipaccess_drop_oml(bts, "ctrl net.apply-configuration");
+ }
+
+ cmd->reply = "Tried to drop the BTS";
+ return CTRL_CMD_REPLY;
}
-/* Encode a CTRL command and send it to the given ASP
- * \param[in] asp ASP through which we shall send the encoded message
- * \param[in] cmd decoded CTRL command to be encoded and sent. Ownership is *NOT*
- * transferred, to permit caller to send the same CMD to several ASPs.
- * Caller must hence free 'cmd' itself.
- * \returns 0 on success; negative on error */
-static int sccplite_asp_ctrl_cmd_send(struct osmo_ss7_asp *asp, struct ctrl_cmd *cmd)
+CTRL_CMD_DEFINE_WO_NOVRF(net_apply_config, "apply-configuration");
+
+static int verify_net_mcc_mnc_apply(struct ctrl_cmd *cmd, const char *value, void *d)
{
- /* this is basically like libosmoctrl:ctrl_cmd_send(), not for a dedicated
- * CTRL connection but for the CTRL piggy-back on the IPA/SCCPlite link */
- struct msgb *msg;
+ char *tmp, *saveptr, *mcc, *mnc;
+ int rc = 0;
- /* don't attempt to send CTRL on a non-SCCPlite ASP */
- if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
- return 0;
+ tmp = talloc_strdup(cmd, value);
+ if (!tmp)
+ return 1;
- msg = ctrl_cmd_make(cmd);
- if (!msg)
- return -1;
+ mcc = strtok_r(tmp, ",", &saveptr);
+ mnc = strtok_r(NULL, ",", &saveptr);
- ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
- ipa_prepend_header(msg, IPAC_PROTO_OSMO);
+ if (osmo_mcc_from_str(mcc, NULL) || osmo_mnc_from_str(mnc, NULL, NULL))
+ rc = -1;
- return osmo_ss7_asp_send(asp, msg);
+ talloc_free(tmp);
+ return rc;
}
-/* Ownership of 'cmd' is *NOT* transferred, to permit caller to send the same CMD to several ASPs.
- * Caller must hence free 'cmd' itself. */
-static int sccplite_msc_ctrl_cmd_send(struct bsc_msc_data *msc, struct ctrl_cmd *cmd)
+static int set_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data)
{
- struct msgb *msg;
+ struct gsm_network *net = cmd->node;
+ char *tmp, *saveptr, *mcc_str, *mnc_str;
+ struct osmo_plmn_id plmn;
- msg = ctrl_cmd_make(cmd);
- if (!msg)
- return -1;
+ tmp = talloc_strdup(cmd, cmd->value);
+ if (!tmp)
+ goto oom;
- ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
- ipa_prepend_header(msg, IPAC_PROTO_OSMO);
+ mcc_str = strtok_r(tmp, ",", &saveptr);
+ mnc_str = strtok_r(NULL, ",", &saveptr);
- return bsc_sccplite_msc_send(msc, msg);
+ if (osmo_mcc_from_str(mcc_str, &plmn.mcc)) {
+ cmd->reply = "Error while decoding MCC";
+ talloc_free(tmp);
+ return CTRL_CMD_ERROR;
+ }
+
+ if (osmo_mnc_from_str(mnc_str, &plmn.mnc, &plmn.mnc_3_digits)) {
+ cmd->reply = "Error while decoding MNC";
+ talloc_free(tmp);
+ return CTRL_CMD_ERROR;
+ }
+
+ talloc_free(tmp);
+
+ if (!osmo_plmn_cmp(&net->plmn, &plmn)) {
+ cmd->reply = "Nothing changed";
+ return CTRL_CMD_REPLY;
+ }
+
+ net->plmn = plmn;
+
+ return set_net_apply_config(cmd, data);
+
+oom:
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
}
+CTRL_CMD_DEFINE_WO(net_mcc_mnc_apply, "mcc-mnc-apply");
-/* receive + process a CTRL command from the piggy-back on the IPA/SCCPlite link.
- * Transfers msg ownership. */
-int bsc_sccplite_rx_ctrl(struct osmo_ss7_asp *asp, struct msgb *msg)
+static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data)
{
- struct ctrl_cmd *cmd;
- bool parse_failed;
- int rc;
+ struct gsm_network *net = cmd->node;
+ struct gsm_bts *bts;
+ const char *policy_name;
- /* caller has already ensured ipaccess_head + ipaccess_head_ext */
- OSMO_ASSERT(msg->l2h);
+ policy_name = osmo_bsc_rf_get_policy_name(net->rf_ctrl->policy);
- /* prase raw (ASCII) CTRL command into ctrl_cmd */
- cmd = ctrl_cmd_parse3(asp, msg, &parse_failed);
- OSMO_ASSERT(cmd);
- msgb_free(msg);
- if (cmd->type == CTRL_TYPE_ERROR && parse_failed)
- goto send_reply;
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ struct gsm_bts_trx *trx;
- /* handle the CTRL command */
- ctrl_cmd_handle(bsc_gsmnet->ctrl, cmd, bsc_gsmnet);
+ /* Exclude the BTS from the global lock */
+ if (bts->excl_from_rf_lock)
+ continue;
-send_reply:
- rc = sccplite_asp_ctrl_cmd_send(asp, cmd);
- talloc_free(cmd);
- return rc;
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx->mo.nm_state.availability == NM_AVSTATE_OK &&
+ trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) {
+ cmd->reply = talloc_asprintf(cmd,
+ "state=on,policy=%s,bts=%u,trx=%u",
+ policy_name, bts->nr, trx->nr);
+ return CTRL_CMD_REPLY;
+ }
+ }
+ }
+
+ cmd->reply = talloc_asprintf(cmd, "state=off,policy=%s",
+ policy_name);
+ return CTRL_CMD_REPLY;
}
+#define TIME_FORMAT_RFC2822 "%a, %d %b %Y %T %z"
-void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_data *msc_data)
+static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data)
{
- struct ctrl_cmd *trap;
- struct ctrl_handle *ctrl;
+ int locked = atoi(cmd->value);
+ struct gsm_network *net = cmd->node;
+ time_t now = time(NULL);
+ char now_buf[64];
+ struct osmo_bsc_rf *rf;
+
+ if (!net) {
+ cmd->reply = "net not found.";
+ return CTRL_CMD_ERROR;
+ }
- ctrl = msc_data->network->ctrl;
+ rf = net->rf_ctrl;
- trap = ctrl_cmd_trap(cmd);
- if (!trap) {
+ if (!rf) {
+ cmd->reply = "RF Ctrl is not enabled in the BSC Configuration";
+ return CTRL_CMD_ERROR;
+ }
- LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n");
- return;
+ talloc_free(rf->last_rf_lock_ctrl_command);
+ strftime(now_buf, sizeof(now_buf), TIME_FORMAT_RFC2822, gmtime(&now));
+ rf->last_rf_lock_ctrl_command =
+ talloc_asprintf(rf, "rf_locked %u (%s)", locked, now_buf);
+
+ osmo_bsc_rf_schedule_lock(rf, locked == 1 ? '0' : '1');
+
+ cmd->reply = talloc_asprintf(cmd, "%u", locked);
+ if (!cmd->reply) {
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
}
- ctrl_cmd_send_to_all(ctrl, trap);
- sccplite_msc_ctrl_cmd_send(msc_data, trap);
+ return CTRL_CMD_REPLY;
+}
- talloc_free(trap);
+static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ int locked = atoi(cmd->value);
+
+ if ((locked != 0) && (locked != 1))
+ return 1;
+
+ return 0;
}
+CTRL_CMD_DEFINE(net_rf_lock, "rf_locked");
-CTRL_CMD_DEFINE_RO(msc_connection_status, "connection_status");
-static int get_msc_connection_status(struct ctrl_cmd *cmd, void *data)
+static int get_net_bts_num(struct ctrl_cmd *cmd, void *data)
{
- struct bsc_msc_data *msc = (struct bsc_msc_data *)cmd->node;
+ struct gsm_network *net = cmd->node;
- if (msc == NULL) {
- cmd->reply = "msc not found";
+ cmd->reply = talloc_asprintf(cmd, "%u", net->num_bts);
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(net_bts_num, "number-of-bts");
+
+/* 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;
}
- if (a_reset_conn_ready(msc))
- cmd->reply = "connected";
- else
- cmd->reply = "disconnected";
return CTRL_CMD_REPLY;
}
+CTRL_CMD_DEFINE_RO(net_rf_states, "rf_states");
-/* Backwards compat. */
-CTRL_CMD_DEFINE_RO(msc0_connection_status, "msc_connection_status");
-
-static int get_msc0_connection_status(struct ctrl_cmd *cmd, void *data)
+CTRL_CMD_DEFINE(net_timezone, "timezone");
+static int get_net_timezone(struct ctrl_cmd *cmd, void *data)
{
- struct bsc_msc_data *msc = osmo_msc_data_find(bsc_gsmnet, 0);
- void *old_node = cmd->node;
- int rc;
+ struct gsm_network *net = (struct gsm_network *)cmd->node;
- cmd->node = msc;
- rc = get_msc_connection_status(cmd, data);
- cmd->node = old_node;
+ struct gsm_tz *tz = &net->tz;
+ if (tz->override)
+ cmd->reply = talloc_asprintf(cmd, "%d,%d,%d",
+ tz->hr, tz->mn, tz->dst);
+ else
+ cmd->reply = talloc_asprintf(cmd, "off");
- return rc;
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
}
-static int msc_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
+static int set_net_timezone(struct ctrl_cmd *cmd, void *data)
{
- struct ctrl_cmd *cmd;
- struct gsm_network *gsmnet = (struct gsm_network *)handler_data;
- struct bsc_msc_data *msc = (struct bsc_msc_data *)signal_data;
+ char *saveptr, *hourstr, *minstr, *dststr, *tmp = 0;
+ int override = 0;
+ struct gsm_network *net = (struct gsm_network *)cmd->node;
+ struct gsm_tz *tz = &net->tz;
- if (signal == S_MSC_LOST) {
- LOGP(DCTRL, LOGL_DEBUG, "MSC connection lost, sending TRAP.\n");
- } else if (signal == S_MSC_CONNECTED) {
- LOGP(DCTRL, LOGL_DEBUG, "MSC connection (re)established, sending TRAP.\n");
- } else {
- return 0;
- }
+ tmp = talloc_strdup(cmd, cmd->value);
+ if (!tmp)
+ goto oom;
- cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n");
- return 0;
+ hourstr = strtok_r(tmp, ",", &saveptr);
+ minstr = strtok_r(NULL, ",", &saveptr);
+ dststr = strtok_r(NULL, ",", &saveptr);
+
+ if (hourstr != NULL) {
+ override = strcasecmp(hourstr, "off") != 0;
+ if (override) {
+ tz->hr = atol(hourstr);
+ tz->mn = minstr ? atol(minstr) : 0;
+ tz->dst = dststr ? atol(dststr) : 0;
+ }
}
- cmd->id = "0";
- cmd->variable = talloc_asprintf(cmd, "msc.%d.connection_status", msc->nr);
- cmd->node = msc;
+ tz->override = override;
- get_msc_connection_status(cmd, NULL);
- ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
+ talloc_free(tmp);
+ tmp = NULL;
- if (msc->nr == 0) {
- /* Backwards compat. */
- cmd->variable = "msc_connection_status";
- ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
+ return get_net_timezone(cmd, data);
+
+oom:
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+}
+
+static int verify_net_timezone(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ char *saveptr, *hourstr, *minstr, *dststr, *tmp;
+ int override, tz_hours, tz_mins, tz_dst;
+
+ tmp = talloc_strdup(cmd, value);
+ if (!tmp)
+ return 1;
+
+ hourstr = strtok_r(tmp, ",", &saveptr);
+ minstr = strtok_r(NULL, ",", &saveptr);
+ dststr = strtok_r(NULL, ",", &saveptr);
+
+ if (hourstr == NULL)
+ goto err;
+
+ override = strcasecmp(hourstr, "off") != 0;
+
+ if (!override) {
+ talloc_free(tmp);
+ return 0;
}
- talloc_free(cmd);
+ if (minstr == NULL || dststr == NULL)
+ goto err;
+
+ tz_hours = atol(hourstr);
+ tz_mins = atol(minstr);
+ tz_dst = atol(dststr);
+
+ talloc_free(tmp);
+ tmp = NULL;
+
+ if ((tz_hours < -19) || (tz_hours > 19) ||
+ (tz_mins < 0) || (tz_mins >= 60) || (tz_mins % 15 != 0) ||
+ (tz_dst < 0) || (tz_dst > 2))
+ goto err;
return 0;
+
+err:
+ talloc_free(tmp);
+ cmd->reply = talloc_strdup(cmd, "The format is <hours>,<mins>,<dst> or 'off' where -19 <= hours <= 19, mins in {0, 15, 30, 45}, and 0 <= dst <= 2");
+ return 1;
}
CTRL_CMD_DEFINE_RO(bts_connection_status, "bts_connection_status");
@@ -307,309 +536,231 @@ static int bts_connection_status_trap_cb(unsigned int subsys, unsigned int signa
return 0;
}
-static int get_bts_loc(struct ctrl_cmd *cmd, void *data);
-
-static void generate_location_state_trap(struct gsm_bts *bts, struct bsc_msc_data *msc)
+CTRL_CMD_DEFINE_RO(msc_connection_status, "connection_status");
+static int get_msc_connection_status(struct ctrl_cmd *cmd, void *data)
{
- struct ctrl_cmd *cmd;
- const char *oper, *admin, *policy;
+ struct bsc_msc_data *msc = (struct bsc_msc_data *)cmd->node;
- cmd = ctrl_cmd_create(msc, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
- return;
+ if (msc == NULL) {
+ cmd->reply = "msc not found";
+ return CTRL_CMD_ERROR;
}
-
- cmd->id = "0";
- cmd->variable = talloc_asprintf(cmd, "bts.%i.location-state", bts->nr);
-
- /* Prepare the location reply */
- cmd->node = bts;
- get_bts_loc(cmd, NULL);
-
- oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts));
- admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts));
- policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts));
-
- cmd->reply = talloc_asprintf_append(cmd->reply,
- ",%s,%s,%s,%s,%s",
- oper, admin, policy,
- osmo_mcc_name(bts->network->plmn.mcc),
- osmo_mnc_name(bts->network->plmn.mnc,
- bts->network->plmn.mnc_3_digits));
-
- osmo_bsc_send_trap(cmd, msc);
- talloc_free(cmd);
+ if (a_reset_conn_ready(msc))
+ cmd->reply = "connected";
+ else
+ cmd->reply = "disconnected";
+ return CTRL_CMD_REPLY;
}
-void bsc_gen_location_state_trap(struct gsm_bts *bts)
-{
- struct bsc_msc_data *msc;
-
- llist_for_each_entry(msc, &bts->network->mscs, entry)
- generate_location_state_trap(bts, msc);
-}
+/* Backwards compat. */
+CTRL_CMD_DEFINE_RO(msc0_connection_status, "msc_connection_status");
-static int location_equal(struct bts_location *a, struct bts_location *b)
+static int get_msc0_connection_status(struct ctrl_cmd *cmd, void *data)
{
- return ((a->tstamp == b->tstamp) && (a->valid == b->valid) && (a->lat == b->lat) &&
- (a->lon == b->lon) && (a->height == b->height));
-}
+ struct bsc_msc_data *msc = osmo_msc_data_find(bsc_gsmnet, 0);
+ void *old_node = cmd->node;
+ int rc;
-static void cleanup_locations(struct llist_head *locations)
-{
- struct bts_location *myloc, *tmp;
- int invalpos = 0, i = 0;
+ cmd->node = msc;
+ rc = get_msc_connection_status(cmd, data);
+ cmd->node = old_node;
- LOGP(DCTRL, LOGL_DEBUG, "Checking position list.\n");
- llist_for_each_entry_safe(myloc, tmp, locations, list) {
- i++;
- if (i > 3) {
- LOGP(DCTRL, LOGL_DEBUG, "Deleting old position.\n");
- llist_del(&myloc->list);
- talloc_free(myloc);
- } else if (myloc->valid == BTS_LOC_FIX_INVALID) {
- /* Only capture the newest of subsequent invalid positions */
- invalpos++;
- if (invalpos > 1) {
- LOGP(DCTRL, LOGL_DEBUG, "Deleting subsequent invalid position.\n");
- invalpos--;
- i--;
- llist_del(&myloc->list);
- talloc_free(myloc);
- }
- } else {
- invalpos = 0;
- }
- }
- LOGP(DCTRL, LOGL_DEBUG, "Found %i positions.\n", i);
+ return rc;
}
-CTRL_CMD_DEFINE(bts_loc, "location");
-static int get_bts_loc(struct ctrl_cmd *cmd, void *data)
+static int msc_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
{
- struct bts_location *curloc;
- struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
- if (!bts) {
- cmd->reply = "bts not found.";
- return CTRL_CMD_ERROR;
- }
+ struct ctrl_cmd *cmd;
+ struct gsm_network *gsmnet = (struct gsm_network *)handler_data;
+ struct bsc_msc_data *msc = (struct bsc_msc_data *)signal_data;
- if (llist_empty(&bts->loc_list)) {
- cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0");
- return CTRL_CMD_REPLY;
+ if (signal == S_MSC_LOST) {
+ LOGP(DCTRL, LOGL_DEBUG, "MSC connection lost, sending TRAP.\n");
+ } else if (signal == S_MSC_CONNECTED) {
+ LOGP(DCTRL, LOGL_DEBUG, "MSC connection (re)established, sending TRAP.\n");
} else {
- curloc = llist_entry(bts->loc_list.next, struct bts_location, list);
+ return 0;
}
- cmd->reply = talloc_asprintf(cmd, "%lu,%s,%f,%f,%f", curloc->tstamp,
- get_value_string(bts_loc_fix_names, curloc->valid), curloc->lat, curloc->lon, curloc->height);
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
+ cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
+ if (!cmd) {
+ LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n");
+ return 0;
}
- return CTRL_CMD_REPLY;
-}
+ cmd->id = "0";
+ cmd->variable = talloc_asprintf(cmd, "msc.%d.connection_status", msc->nr);
+ cmd->node = msc;
-static int set_bts_loc(struct ctrl_cmd *cmd, void *data)
-{
- char *saveptr, *lat, *lon, *height, *tstamp, *valid, *tmp;
- struct bts_location *curloc, *lastloc;
- int ret;
- struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
- if (!bts) {
- cmd->reply = "bts not found.";
- return CTRL_CMD_ERROR;
- }
+ get_msc_connection_status(cmd, NULL);
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- goto oom;
+ ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
- curloc = talloc_zero(tall_bsc_ctx, struct bts_location);
- if (!curloc) {
- talloc_free(tmp);
- goto oom;
+ if (msc->nr == 0) {
+ /* Backwards compat. */
+ cmd->variable = "msc_connection_status";
+ ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
}
- INIT_LLIST_HEAD(&curloc->list);
-
-
- tstamp = strtok_r(tmp, ",", &saveptr);
- valid = strtok_r(NULL, ",", &saveptr);
- lat = strtok_r(NULL, ",", &saveptr);
- lon = strtok_r(NULL, ",", &saveptr);
- height = strtok_r(NULL, "\0", &saveptr);
-
- curloc->tstamp = atol(tstamp);
- curloc->valid = get_string_value(bts_loc_fix_names, valid);
- curloc->lat = atof(lat);
- curloc->lon = atof(lon);
- curloc->height = atof(height);
- talloc_free(tmp);
- lastloc = llist_entry(bts->loc_list.next, struct bts_location, list);
+ talloc_free(cmd);
- /* Add location to the end of the list */
- llist_add(&curloc->list, &bts->loc_list);
+ return 0;
+}
- ret = get_bts_loc(cmd, data);
+static int msc_signal_handler(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct msc_signal_data *msc;
+ struct gsm_network *net;
+ struct gsm_bts *bts;
- if (!location_equal(curloc, lastloc))
- bsc_gen_location_state_trap(bts);
+ if (subsys != SS_MSC)
+ return 0;
+ if (signal != S_MSC_AUTHENTICATED)
+ return 0;
- cleanup_locations(&bts->loc_list);
+ msc = signal_data;
- return ret;
+ net = msc->data->network;
+ llist_for_each_entry(bts, &net->bts_list, list)
+ ctrl_generate_bts_location_state_trap(bts, msc->data);
-oom:
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
+ return 0;
}
-static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data)
+/* Obtain SS7 application server currently handling given MSC (DPC) */
+static struct osmo_ss7_as *msc_get_ss7_as(struct bsc_msc_data *msc)
{
- char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp;
- time_t tstamp;
- int valid;
- double lat, lon, height __attribute__((unused));
-
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
-
- tstampstr = strtok_r(tmp, ",", &saveptr);
- validstr = strtok_r(NULL, ",", &saveptr);
- latstr = strtok_r(NULL, ",", &saveptr);
- lonstr = strtok_r(NULL, ",", &saveptr);
- heightstr = strtok_r(NULL, "\0", &saveptr);
-
- if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) ||
- (lonstr == NULL) || (heightstr == NULL))
- goto err;
+ struct osmo_ss7_route *rt;
+ struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(msc->a.sccp);
+ rt = osmo_ss7_route_lookup(ss7, msc->a.msc_addr.pc);
+ if (!rt)
+ return NULL;
+ return rt->dest.as;
+}
- tstamp = atol(tstampstr);
- valid = get_string_value(bts_loc_fix_names, validstr);
- lat = atof(latstr);
- lon = atof(lonstr);
- height = atof(heightstr);
- talloc_free(tmp);
- tmp = NULL;
+static int _ss7_as_send(struct osmo_ss7_as *as, struct msgb *msg)
+{
+ struct osmo_ss7_asp *asp;
+ unsigned int i;
- if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) ||
- (lon < -180) || (lon > 180) || (valid < 0)) {
- goto err;
+ /* FIXME: unify with xua_as_transmit_msg() and perform proper ASP lookup */
+ for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
+ asp = as->cfg.asps[i];
+ if (!asp)
+ continue;
+ /* FIXME: deal with multiple ASPs per AS */
+ return osmo_ss7_asp_send(asp, msg);
}
-
- return 0;
-
-err:
- talloc_free(tmp);
- cmd->reply = talloc_strdup(cmd, "The format is <unixtime>,(invalid|fix2d|fix3d),<lat>,<lon>,<height>");
- return 1;
+ msgb_free(msg);
+ return -1;
}
-CTRL_CMD_DEFINE(net_timezone, "timezone");
-static int get_net_timezone(struct ctrl_cmd *cmd, void *data)
+int bsc_sccplite_msc_send(struct bsc_msc_data *msc, struct msgb *msg)
{
- struct gsm_network *net = (struct gsm_network*)cmd->node;
-
- struct gsm_tz *tz = &net->tz;
- if (tz->override)
- cmd->reply = talloc_asprintf(cmd, "%d,%d,%d",
- tz->hr, tz->mn, tz->dst);
- else
- cmd->reply = talloc_asprintf(cmd, "off");
+ struct osmo_ss7_as *as;
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
+ as = msc_get_ss7_as(msc);
+ if (!as) {
+ msgb_free(msg);
+ return -1;
}
- return CTRL_CMD_REPLY;
+ /* don't attempt to send CTRL on a non-SCCPlite AS */
+ if (as->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
+ return 0;
+
+ return _ss7_as_send(as, msg);
}
-static int set_net_timezone(struct ctrl_cmd *cmd, void *data)
+/* Encode a CTRL command and send it to the given ASP
+ * \param[in] asp ASP through which we shall send the encoded message
+ * \param[in] cmd decoded CTRL command to be encoded and sent. Ownership is *NOT*
+ * transferred, to permit caller to send the same CMD to several ASPs.
+ * Caller must hence free 'cmd' itself.
+ * \returns 0 on success; negative on error */
+static int sccplite_asp_ctrl_cmd_send(struct osmo_ss7_asp *asp, struct ctrl_cmd *cmd)
{
- char *saveptr, *hourstr, *minstr, *dststr, *tmp = 0;
- int override = 0;
- struct gsm_network *net = (struct gsm_network*)cmd->node;
- struct gsm_tz *tz = &net->tz;
+ /* this is basically like libosmoctrl:ctrl_cmd_send(), not for a dedicated
+ * CTRL connection but for the CTRL piggy-back on the IPA/SCCPlite link */
+ struct msgb *msg;
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- goto oom;
+ /* don't attempt to send CTRL on a non-SCCPlite ASP */
+ if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
+ return 0;
- hourstr = strtok_r(tmp, ",", &saveptr);
- minstr = strtok_r(NULL, ",", &saveptr);
- dststr = strtok_r(NULL, ",", &saveptr);
+ msg = ctrl_cmd_make(cmd);
+ if (!msg)
+ return -1;
- if (hourstr != NULL) {
- override = strcasecmp(hourstr, "off") != 0;
- if (override) {
- tz->hr = atol(hourstr);
- tz->mn = minstr ? atol(minstr) : 0;
- tz->dst = dststr ? atol(dststr) : 0;
- }
- }
+ ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
+ ipa_prepend_header(msg, IPAC_PROTO_OSMO);
- tz->override = override;
+ return osmo_ss7_asp_send(asp, msg);
+}
+/* Ownership of 'cmd' is *NOT* transferred, to permit caller to send the same CMD to several ASPs.
+ * Caller must hence free 'cmd' itself. */
+static int sccplite_msc_ctrl_cmd_send(struct bsc_msc_data *msc, struct ctrl_cmd *cmd)
+{
+ struct msgb *msg;
- talloc_free(tmp);
- tmp = NULL;
+ msg = ctrl_cmd_make(cmd);
+ if (!msg)
+ return -1;
- return get_net_timezone(cmd, data);
+ ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
+ ipa_prepend_header(msg, IPAC_PROTO_OSMO);
-oom:
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
+ return bsc_sccplite_msc_send(msc, msg);
}
-static int verify_net_timezone(struct ctrl_cmd *cmd, const char *value, void *data)
+/* receive + process a CTRL command from the piggy-back on the IPA/SCCPlite link.
+ * Transfers msg ownership. */
+int bsc_sccplite_rx_ctrl(struct osmo_ss7_asp *asp, struct msgb *msg)
{
- char *saveptr, *hourstr, *minstr, *dststr, *tmp;
- int override, tz_hours, tz_mins, tz_dst;
+ struct ctrl_cmd *cmd;
+ bool parse_failed;
+ int rc;
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
+ /* caller has already ensured ipaccess_head + ipaccess_head_ext */
+ OSMO_ASSERT(msg->l2h);
- hourstr = strtok_r(tmp, ",", &saveptr);
- minstr = strtok_r(NULL, ",", &saveptr);
- dststr = strtok_r(NULL, ",", &saveptr);
+ /* prase raw (ASCII) CTRL command into ctrl_cmd */
+ cmd = ctrl_cmd_parse3(asp, msg, &parse_failed);
+ OSMO_ASSERT(cmd);
+ msgb_free(msg);
+ if (cmd->type == CTRL_TYPE_ERROR && parse_failed)
+ goto send_reply;
- if (hourstr == NULL)
- goto err;
+ /* handle the CTRL command */
+ ctrl_cmd_handle(bsc_gsmnet->ctrl, cmd, bsc_gsmnet);
- override = strcasecmp(hourstr, "off") != 0;
+send_reply:
+ rc = sccplite_asp_ctrl_cmd_send(asp, cmd);
+ talloc_free(cmd);
+ return rc;
+}
- if (!override) {
- talloc_free(tmp);
- return 0;
- }
- if (minstr == NULL || dststr == NULL)
- goto err;
+void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_data *msc_data)
+{
+ struct ctrl_cmd *trap;
+ struct ctrl_handle *ctrl;
- tz_hours = atol(hourstr);
- tz_mins = atol(minstr);
- tz_dst = atol(dststr);
+ ctrl = msc_data->network->ctrl;
- talloc_free(tmp);
- tmp = NULL;
+ trap = ctrl_cmd_trap(cmd);
+ if (!trap) {
- if ((tz_hours < -19) || (tz_hours > 19) ||
- (tz_mins < 0) || (tz_mins >= 60) || (tz_mins % 15 != 0) ||
- (tz_dst < 0) || (tz_dst > 2))
- goto err;
+ LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n");
+ return;
+ }
- return 0;
+ ctrl_cmd_send_to_all(ctrl, trap);
+ sccplite_msc_ctrl_cmd_send(msc_data, trap);
-err:
- talloc_free(tmp);
- cmd->reply = talloc_strdup(cmd, "The format is <hours>,<mins>,<dst> or 'off' where -19 <= hours <= 19, mins in {0, 15, 30, 45}, and 0 <= dst <= 2");
- return 1;
+ talloc_free(trap);
}
CTRL_CMD_DEFINE_WO_NOVRF(net_notification, "notification");
@@ -669,63 +820,47 @@ static int set_net_inform_msc(struct ctrl_cmd *cmd, void *data)
return CTRL_CMD_HANDLED;
}
-static int msc_signal_handler(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
+static int bsc_base_ctrl_cmds_install(struct gsm_network *net)
{
- struct msc_signal_data *msc;
- struct gsm_network *net;
- struct gsm_bts *bts;
+ 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_ROOT, &cmd_net_timezone);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_bts_connection_status);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_msc0_connection_status);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_notification);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_inform_msc);
- if (subsys != SS_MSC)
- return 0;
- if (signal != S_MSC_AUTHENTICATED)
- return 0;
+ rc = ctrl_cmd_install(CTRL_NODE_MSC, &cmd_msc_connection_status);
- msc = signal_data;
+ rc |= osmo_signal_register_handler(SS_L_INPUT, &bts_connection_status_trap_cb, net);
+ rc |= osmo_signal_register_handler(SS_MSC, &msc_connection_status_trap_cb, net);
+ rc |= osmo_signal_register_handler(SS_MSC, msc_signal_handler, NULL);
- net = msc->data->network;
- llist_for_each_entry(bts, &net->bts_list, list)
- generate_location_state_trap(bts, msc->data);
-
- return 0;
+ return rc;
}
+
int bsc_ctrl_cmds_install(struct gsm_network *net)
{
int rc;
- rc = bsc_base_ctrl_cmds_install();
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_timezone);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_MSC, &cmd_msc_connection_status);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_msc0_connection_status);
- if (rc)
- goto end;
- rc = osmo_signal_register_handler(SS_MSC, &msc_connection_status_trap_cb, net);
+ rc = bsc_base_ctrl_cmds_install(net);
if (rc)
goto end;
- rc = osmo_signal_register_handler(SS_MSC, msc_signal_handler, NULL);
+ rc = bsc_ho_ctrl_cmds_install(net);
if (rc)
goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_bts_connection_status);
+ rc = bsc_bts_ctrl_cmds_install();
if (rc)
goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_notification);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_inform_msc);
- if (rc)
- goto end;
- rc = osmo_signal_register_handler(SS_L_INPUT, &bts_connection_status_trap_cb, net);
-
end:
return rc;
}
diff --git a/src/osmo-bsc/bsc_ctrl_commands.c b/src/osmo-bsc/bsc_ctrl_commands.c
deleted file mode 100644
index 9383167fb..000000000
--- a/src/osmo-bsc/bsc_ctrl_commands.c
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * (C) 2013-2015 by Holger Hans Peter Freyther
- * (C) 2013-2015 by sysmocom s.f.m.c. GmbH
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-#include <errno.h>
-#include <time.h>
-
-#include <osmocom/ctrl/control_cmd.h>
-#include <osmocom/gsm/gsm48.h>
-#include <osmocom/bsc/ipaccess.h>
-#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/bsc/abis_nm.h>
-#include <osmocom/bsc/debug.h>
-#include <osmocom/bsc/chan_alloc.h>
-#include <osmocom/bsc/osmo_bsc_rf.h>
-#include <osmocom/bsc/bsc_msc_data.h>
-#include <osmocom/bsc/bts.h>
-
-CTRL_CMD_DEFINE(net_mcc, "mcc");
-static int get_net_mcc(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_network *net = cmd->node;
- cmd->reply = talloc_asprintf(cmd, "%s", osmo_mcc_name(net->plmn.mcc));
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
- return CTRL_CMD_REPLY;
-}
-static int set_net_mcc(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_network *net = cmd->node;
- uint16_t mcc;
- if (osmo_mcc_from_str(cmd->value, &mcc))
- return -1;
- net->plmn.mcc = mcc;
- return get_net_mcc(cmd, _data);
-}
-static int verify_net_mcc(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- if (osmo_mcc_from_str(value, NULL))
- return -1;
- return 0;
-}
-
-CTRL_CMD_DEFINE(net_mnc, "mnc");
-static int get_net_mnc(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_network *net = cmd->node;
- cmd->reply = talloc_asprintf(cmd, "%s", osmo_mnc_name(net->plmn.mnc, net->plmn.mnc_3_digits));
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
- return CTRL_CMD_REPLY;
-}
-static int set_net_mnc(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_network *net = cmd->node;
- struct osmo_plmn_id plmn = net->plmn;
- if (osmo_mnc_from_str(cmd->value, &plmn.mnc, &plmn.mnc_3_digits)) {
- cmd->reply = "Error while decoding MNC";
- return CTRL_CMD_ERROR;
- }
- net->plmn = plmn;
- return get_net_mnc(cmd, _data);
-}
-static int verify_net_mnc(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- if (osmo_mnc_from_str(value, NULL, NULL))
- return -1;
- return 0;
-}
-
-static int set_net_apply_config(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- if (!is_ipaccess_bts(bts))
- continue;
-
- /*
- * The ip.access nanoBTS seems to be unrelaible on BSSGP
- * 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) {
- struct gsm_bts_trx *trx;
- llist_for_each_entry_reverse(trx, &bts->trx_list, list)
- abis_nm_ipaccess_restart(trx);
- } else
- ipaccess_drop_oml(bts, "ctrl net.apply-configuration");
- }
-
- cmd->reply = "Tried to drop the BTS";
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_WO_NOVRF(net_apply_config, "apply-configuration");
-
-static int verify_net_mcc_mnc_apply(struct ctrl_cmd *cmd, const char *value, void *d)
-{
- char *tmp, *saveptr, *mcc, *mnc;
- int rc = 0;
-
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
-
- mcc = strtok_r(tmp, ",", &saveptr);
- mnc = strtok_r(NULL, ",", &saveptr);
-
- if (osmo_mcc_from_str(mcc, NULL) || osmo_mnc_from_str(mnc, NULL, NULL))
- rc = -1;
-
- talloc_free(tmp);
- return rc;
-}
-
-static int set_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- char *tmp, *saveptr, *mcc_str, *mnc_str;
- struct osmo_plmn_id plmn;
-
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- goto oom;
-
- mcc_str = strtok_r(tmp, ",", &saveptr);
- mnc_str = strtok_r(NULL, ",", &saveptr);
-
- if (osmo_mcc_from_str(mcc_str, &plmn.mcc)) {
- cmd->reply = "Error while decoding MCC";
- talloc_free(tmp);
- return CTRL_CMD_ERROR;
- }
-
- if (osmo_mnc_from_str(mnc_str, &plmn.mnc, &plmn.mnc_3_digits)) {
- cmd->reply = "Error while decoding MNC";
- talloc_free(tmp);
- return CTRL_CMD_ERROR;
- }
-
- talloc_free(tmp);
-
- if (!osmo_plmn_cmp(&net->plmn, &plmn)) {
- cmd->reply = "Nothing changed";
- return CTRL_CMD_REPLY;
- }
-
- net->plmn = plmn;
-
- return set_net_apply_config(cmd, data);
-
-oom:
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
-}
-CTRL_CMD_DEFINE_WO(net_mcc_mnc_apply, "mcc-mnc-apply");
-
-/* BTS related commands below */
-CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535);
-CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535);
-
-static int set_bts_apply_config(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
-
- if (!is_ipaccess_bts(bts)) {
- cmd->reply = "BTS is not IP based";
- return CTRL_CMD_ERROR;
- }
-
- ipaccess_drop_oml(bts, "ctrl bts.apply-configuration");
- cmd->reply = "Tried to drop the BTS";
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_WO_NOVRF(bts_apply_config, "apply-configuration");
-
-static int set_bts_si(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
- int rc;
-
- rc = gsm_bts_set_system_infos(bts);
- if (rc != 0) {
- cmd->reply = "Failed to generate SI";
- return CTRL_CMD_ERROR;
- }
-
- cmd->reply = "Generated new System Information";
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_WO_NOVRF(bts_si, "send-new-system-informations");
-
-static int get_bts_chan_load(struct ctrl_cmd *cmd, void *data)
-{
- int i;
- struct pchan_load pl;
- struct gsm_bts *bts;
- const char *space = "";
-
- bts = cmd->node;
- memset(&pl, 0, sizeof(pl));
- bts_chan_load(&pl, bts);
-
- cmd->reply = talloc_strdup(cmd, "");
-
- for (i = 0; i < ARRAY_SIZE(pl.pchan); ++i) {
- const struct load_counter *lc = &pl.pchan[i];
-
- /* These can never have user load */
- if (i == GSM_PCHAN_NONE)
- continue;
- if (i == GSM_PCHAN_CCCH)
- continue;
- if (i == GSM_PCHAN_PDCH)
- continue;
- if (i == GSM_PCHAN_UNKNOWN)
- continue;
-
- cmd->reply = talloc_asprintf_append(cmd->reply,
- "%s%s,%u,%u",
- space, gsm_pchan_name(i), lc->used, lc->total);
- if (!cmd->reply)
- goto error;
- space = " ";
- }
-
- return CTRL_CMD_REPLY;
-
-error:
- cmd->reply = "Memory allocation failure";
- return CTRL_CMD_ERROR;
-}
-
-CTRL_CMD_DEFINE_RO(bts_chan_load, "channel-load");
-
-static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data)
-{
- const struct gsm_bts *bts = cmd->node;
-
- cmd->reply = get_model_oml_status(bts);
-
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_RO(bts_oml_conn, "oml-connection-state");
-
-static int get_bts_oml_up(struct ctrl_cmd *cmd, void *data)
-{
- const struct gsm_bts *bts = cmd->node;
-
- cmd->reply = talloc_asprintf(cmd, "%llu", bts_uptime(bts));
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_RO(bts_oml_up, "oml-uptime");
-
-static int verify_bts_gprs_mode(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- int valid;
- enum bts_gprs_mode mode;
- struct gsm_bts *bts = cmd->node;
-
- mode = bts_gprs_mode_parse(value, &valid);
- if (!valid) {
- cmd->reply = "Mode is not known";
- return 1;
- }
-
- if (!bts_gprs_mode_is_compat(bts, mode)) {
- cmd->reply = "bts does not support this mode";
- return 1;
- }
-
- return 0;
-}
-
-static int get_bts_gprs_mode(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
-
- cmd->reply = talloc_strdup(cmd, bts_gprs_mode_name(bts->gprs.mode));
- return CTRL_CMD_REPLY;
-}
-
-static int set_bts_gprs_mode(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
-
- bts->gprs.mode = bts_gprs_mode_parse(cmd->value, NULL);
- return get_bts_gprs_mode(cmd, data);
-}
-
-CTRL_CMD_DEFINE(bts_gprs_mode, "gprs-mode");
-
-static int get_bts_rf_state(struct ctrl_cmd *cmd, void *data)
-{
- const char *oper, *admin, *policy;
- struct gsm_bts *bts = cmd->node;
-
- if (!bts) {
- cmd->reply = "bts not found.";
- return CTRL_CMD_ERROR;
- }
-
- oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts));
- admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts));
- policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts));
-
- cmd->reply = talloc_asprintf(cmd, "%s,%s,%s", oper, admin, policy);
- if (!cmd->reply) {
- cmd->reply = "OOM.";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state");
-
-static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- struct gsm_bts *bts;
- const char *policy_name;
-
- policy_name = osmo_bsc_rf_get_policy_name(net->rf_ctrl->policy);
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- struct gsm_bts_trx *trx;
-
- /* Exclude the BTS from the global lock */
- if (bts->excl_from_rf_lock)
- continue;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->mo.nm_state.availability == NM_AVSTATE_OK &&
- trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) {
- cmd->reply = talloc_asprintf(cmd,
- "state=on,policy=%s,bts=%u,trx=%u",
- policy_name, bts->nr, trx->nr);
- return CTRL_CMD_REPLY;
- }
- }
- }
-
- cmd->reply = talloc_asprintf(cmd, "state=off,policy=%s",
- policy_name);
- return CTRL_CMD_REPLY;
-}
-
-#define TIME_FORMAT_RFC2822 "%a, %d %b %Y %T %z"
-
-static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data)
-{
- int locked = atoi(cmd->value);
- struct gsm_network *net = cmd->node;
- time_t now = time(NULL);
- char now_buf[64];
- struct osmo_bsc_rf *rf;
-
- if (!net) {
- cmd->reply = "net not found.";
- return CTRL_CMD_ERROR;
- }
-
- rf = net->rf_ctrl;
-
- if (!rf) {
- cmd->reply = "RF Ctrl is not enabled in the BSC Configuration";
- return CTRL_CMD_ERROR;
- }
-
- talloc_free(rf->last_rf_lock_ctrl_command);
- strftime(now_buf, sizeof(now_buf), TIME_FORMAT_RFC2822, gmtime(&now));
- rf->last_rf_lock_ctrl_command =
- talloc_asprintf(rf, "rf_locked %u (%s)", locked, now_buf);
-
- osmo_bsc_rf_schedule_lock(rf, locked == 1 ? '0' : '1');
-
- cmd->reply = talloc_asprintf(cmd, "%u", locked);
- if (!cmd->reply) {
- cmd->reply = "OOM.";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- int locked = atoi(cmd->value);
-
- if ((locked != 0) && (locked != 1))
- return 1;
-
- return 0;
-}
-CTRL_CMD_DEFINE(net_rf_lock, "rf_locked");
-
-static int get_net_bts_num(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
-
- cmd->reply = talloc_asprintf(cmd, "%u", net->num_bts);
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(net_bts_num, "number-of-bts");
-
-/* TRX related commands below here */
-CTRL_HELPER_GET_INT(trx_max_power, struct gsm_bts_trx, max_power_red);
-static int verify_trx_max_power(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- int tmp = atoi(value);
-
- if (tmp < 0 || tmp > 22) {
- cmd->reply = "Value must be between 0 and 22";
- return -1;
- }
-
- if (tmp & 1) {
- cmd->reply = "Value must be even";
- return -1;
- }
-
- return 0;
-}
-CTRL_CMD_DEFINE_RANGE(trx_arfcn, "arfcn", struct gsm_bts_trx, arfcn, 0, 1023);
-
-static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_bts_trx *trx = cmd->node;
- int old_power;
-
- /* remember the old value, set the new one */
- old_power = trx->max_power_red;
- trx->max_power_red = atoi(cmd->value);
-
- /* Maybe update the value */
- if (old_power != trx->max_power_red) {
- LOGP(DCTRL, LOGL_NOTICE,
- "%s updating max_pwr_red(%d)\n",
- gsm_trx_name(trx), trx->max_power_red);
- abis_nm_update_max_power_red(trx);
- }
-
- return get_trx_max_power(cmd, _data);
-}
-CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction");
-
-int bsc_base_ctrl_cmds_install(void)
-{
- int rc = 0;
- 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_BTS, &cmd_bts_lac);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn);
- 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_TRX, &cmd_trx_max_power);
- rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn);
-
- return rc;
-}
diff --git a/src/osmo-bsc/bsc_ctrl_lookup.c b/src/osmo-bsc/bsc_ctrl_lookup.c
index 4328d76eb..145cda320 100644
--- a/src/osmo-bsc/bsc_ctrl_lookup.c
+++ b/src/osmo-bsc/bsc_ctrl_lookup.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
@@ -115,10 +111,7 @@ err_index:
return -ERANGE;
}
-struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net,
- const char *bind_addr, uint16_t port)
+struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net, uint16_t port)
{
- return ctrl_interface_setup_dynip2(net, bind_addr, port,
- bsc_ctrl_node_lookup,
- _LAST_CTRL_NODE_BSC);
+ return ctrl_interface_setup2(net, port, bsc_ctrl_node_lookup, _LAST_CTRL_NODE_BSC);
}
diff --git a/src/osmo-bsc/bsc_init.c b/src/osmo-bsc/bsc_init.c
index 7e839532a..c381c0f09 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,30 @@ 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 void bsc_store_bts_lchan_durations(void *data)
+{
+ struct gsm_network *net = data;
+ struct gsm_bts *bts;
+
+ llist_for_each_entry(bts, &net->bts_list, list)
+ bts_store_lchan_durations(bts);
+
+ /* Keep this timer ticking. */
+ osmo_timer_schedule(&net->bts_store_lchan_durations_timer, BTS_STORE_LCHAN_DURATIONS_INTERVAL, 0);
+}
+
static struct gsm_network *bsc_network_init(void *ctx)
{
struct gsm_network *net = gsm_network_init(ctx);
@@ -100,7 +115,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 +132,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 +188,14 @@ 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);
+
+ /* Init lchan duration tracking timer. */
+ osmo_timer_setup(&net->bts_store_lchan_durations_timer, bsc_store_bts_lchan_durations, net);
+ osmo_timer_schedule(&net->bts_store_lchan_durations_timer, BTS_STORE_LCHAN_DURATIONS_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 +204,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..ce307207c
--- /dev/null
+++ b/src/osmo-bsc/bsc_stats.c
@@ -0,0 +1,236 @@
+/* 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_EXPIRED] = {"paging:expired", "Paging Request expired because of timeout T3113"},
+ [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++;
+
+ all_allocated_update_bts(bts);
+ }
+
+ 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);
+
+ /* This is optional, just running this to catch bugs in chan_counts accounting. If there is a bug, there will be
+ * a DLGLOBAL ERROR logged, and the error gets fixed. */
+ chan_counts_bsc_verify();
+}
+
+static void all_allocated_update(struct all_allocated *all_allocated, const struct chan_counts *c)
+{
+ osmo_time_cc_set_flag(&all_allocated->sdcch,
+ c->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_SDCCH]
+ && !c->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_SDCCH]);
+
+ osmo_time_cc_set_flag(&all_allocated->static_sdcch,
+ c->val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_SDCCH]
+ && !c->val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_SDCCH]);
+
+ osmo_time_cc_set_flag(&all_allocated->tch,
+ (c->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_F]
+ + c->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_H])
+ && !(c->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F]
+ + c->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H]));
+
+ osmo_time_cc_set_flag(&all_allocated->static_tch,
+ (c->val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_F]
+ + c->val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_MAX_TOTAL][GSM_LCHAN_TCH_H])
+ && !(c->val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F]
+ + c->val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H]));
+}
+
+void all_allocated_update_bts(struct gsm_bts *bts)
+{
+ all_allocated_update(&bts->all_allocated, &bts->chan_counts);
+}
+
+void all_allocated_update_bsc(void)
+{
+ struct gsm_network *net = bsc_gsmnet;
+ all_allocated_update(&net->all_allocated, &net->chan_counts);
+}
diff --git a/src/osmo-bsc/bsc_subscr_conn_fsm.c b/src/osmo-bsc/bsc_subscr_conn_fsm.c
index ed08e86ad..ebfbe82a2 100644
--- a/src/osmo-bsc/bsc_subscr_conn_fsm.c
+++ b/src/osmo-bsc/bsc_subscr_conn_fsm.c
@@ -32,6 +32,7 @@
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/lchan_rtp_fsm.h>
+#include <osmocom/bsc/lchan.h>
#include <osmocom/bsc/bsc_subscriber.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
#include <osmocom/bsc/osmo_bsc_lcls.h>
@@ -45,6 +46,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>
@@ -59,6 +61,8 @@
enum gscon_fsm_states {
ST_INIT,
+ /* wait for initial BSSMAP after the MSC opened a new SCCP connection */
+ ST_WAIT_INITIAL_USER_DATA,
/* waiting for CC from MSC */
ST_WAIT_CC,
/* active connection */
@@ -66,11 +70,13 @@ enum gscon_fsm_states {
ST_ASSIGNMENT,
ST_HANDOVER,
/* BSSMAP CLEAR has been received */
- ST_CLEARING,
+ ST_WAIT_CLEAR_CMD,
+ ST_WAIT_SCCP_RLSD,
};
static const struct value_string gscon_fsm_event_names[] = {
{GSCON_EV_A_CONN_IND, "MT-CONNECT.ind"},
+ {GSCON_EV_A_INITIAL_USER_DATA, "A_INITIAL_USER_DATA"},
{GSCON_EV_MO_COMPL_L3, "MO_COMPL_L3"},
{GSCON_EV_A_CONN_CFM, "MO-CONNECT.cfm"},
{GSCON_EV_A_CLEAR_CMD, "CLEAR_CMD"},
@@ -93,8 +99,10 @@ static const struct value_string gscon_fsm_event_names[] = {
};
struct osmo_tdef_state_timeout conn_fsm_timeouts[32] = {
+ [ST_WAIT_INITIAL_USER_DATA] = { .T = -25 },
[ST_WAIT_CC] = { .T = -3210 },
- [ST_CLEARING] = { .T = -4 },
+ [ST_WAIT_CLEAR_CMD] = { .T = -4 },
+ [ST_WAIT_SCCP_RLSD] = { .T = -4 },
};
/* Transition to a state, using the T timer defined in conn_fsm_timeouts.
@@ -137,34 +145,79 @@ int gscon_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg)
return rc;
}
-static void gscon_bssmap_clear(struct gsm_subscriber_connection *conn,
- enum gsm0808_cause cause)
+void gscon_bssmap_clear(struct gsm_subscriber_connection *conn, enum gsm0808_cause cause)
{
+ /* already clearing? */
+ switch (conn->fi->state) {
+ case ST_WAIT_CLEAR_CMD:
+ case ST_WAIT_SCCP_RLSD:
+ return;
+ default:
+ break;
+ }
+
+ conn->clear_cause = cause;
+ conn_fsm_state_chg(ST_WAIT_CLEAR_CMD);
+}
+static void gscon_fsm_wait_clear_cmd_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
struct msgb *resp;
int rc;
-
- if (conn->rx_clear_command) {
- LOGPFSML(conn->fi, LOGL_DEBUG, "Not sending BSSMAP CLEAR REQUEST, already got CLEAR COMMAND from MSC\n");
- return;
- }
+ struct gsm_subscriber_connection *conn = fi->priv;
+ enum gsm0808_cause cause = conn->clear_cause;
if (!conn->sccp.msc) {
- LOGPFSML(conn->fi, LOGL_ERROR, "Unable to deliver BSSMAP Clear Request message, no MSC for this conn\n");
- return;
+ LOGPFSML(fi, LOGL_ERROR, "Unable to deliver BSSMAP Clear Request message, no MSC for this conn\n");
+ goto nothing_sent;
}
- LOGPFSML(conn->fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST(%s) to MSC\n", gsm0808_cause_name(cause));
+ LOGPFSML(fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST(%s) to MSC\n", gsm0808_cause_name(cause));
resp = gsm0808_create_clear_rqst(cause);
if (!resp) {
- LOGPFSML(conn->fi, LOGL_ERROR, "Unable to compose BSSMAP Clear Request message\n");
- return;
+ LOGPFSML(fi, LOGL_ERROR, "Unable to compose BSSMAP Clear Request message\n");
+ goto nothing_sent;
}
- 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)
+ if (rc < 0) {
LOGPFSML(conn->fi, LOGL_ERROR, "Unable to deliver BSSMAP Clear Request message\n");
+ goto nothing_sent;
+ }
+ return;
+
+nothing_sent:
+ /* Normally, we request a CLEAR from the MSC and terminate as soon as the CLEAR COMMAND has been issued by the
+ * MSC. But if we are trying to clear without being able to send anything to the MSC, we might as well shut down
+ * the conn right away now. */
+ conn_fsm_state_chg(ST_WAIT_SCCP_RLSD);
+}
+
+void gscon_fsm_wait_sccp_rlsd_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ /* According to 3GPP 48.008 3.1.9.1. "The BSS need not wait for the radio channel
+ * release to be completed or for the guard timer to expire before returning the
+ * CLEAR COMPLETE message" */
+ if (!gscon_sigtran_send(conn, gsm0808_create_clear_complete()))
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_CLEAR_COMPLETE));
+
+ /* Give the handover_fsm a chance to book this as handover success before tearing down everything,
+ * making it look like a sudden death failure. */
+ if (conn->ho.fi)
+ osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_CONN_RELEASING, NULL);
+
+ if (conn->lcs.loc_req)
+ osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_CONN_CLEAR, NULL);
+
+ gscon_release_lchans(conn, true, bsc_gsm48_rr_cause_from_gsm0808_cause(conn->clear_cause));
+ osmo_mgcpc_ep_clear(conn->user_plane.mgw_endpoint);
+
+ /* If there is no SCCP connection at all, then no need to wait for an SCCP RLSD. */
+ if (!conn->sccp.msc || conn->sccp.state != SUBSCR_SCCP_ST_CONNECTED)
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
}
/* forward MO DTAP from RSL side to BSSAP side */
@@ -176,7 +229,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 +248,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)
@@ -208,89 +262,133 @@ void gscon_release_lchans(struct gsm_subscriber_connection *conn, bool do_rr_rel
gscon_release_lchan(conn, conn->lchan, do_rr_release, false, cause_rr);
}
-static void handle_bssap_n_connect(struct osmo_fsm_inst *fi, struct osmo_scu_prim *scu_prim)
+static int validate_initial_user_data(struct osmo_fsm_inst *fi, struct msgb *msg)
{
- struct gsm_subscriber_connection *conn = fi->priv;
- struct msgb *msg = scu_prim->oph.msg;
struct bssmap_header *bs;
- uint8_t bssmap_type;
+ enum BSS_MAP_MSG_TYPE bssmap_type;
msg->l3h = msgb_l2(msg);
if (!msgb_l3(msg)) {
LOGPFSML(fi, LOGL_ERROR, "internal error: no l3 in msg\n");
- goto refuse;
+ return -EINVAL;
}
if (msgb_l3len(msg) < sizeof(*bs)) {
- LOGPFSML(fi, LOGL_NOTICE, "message too short for BSSMAP header (%u < %zu)\n",
+ LOGPFSML(fi, LOGL_ERROR, "message too short for BSSMAP header (%u < %zu)\n",
msgb_l3len(msg), sizeof(*bs));
- goto refuse;
+ return -EINVAL;
}
bs = (struct bssmap_header*)msgb_l3(msg);
if (msgb_l3len(msg) < (bs->length + sizeof(*bs))) {
- LOGPFSML(fi, LOGL_NOTICE,
+ LOGPFSML(fi, LOGL_ERROR,
"message too short for length indicated in BSSMAP header (%u < %u)\n",
msgb_l3len(msg), bs->length);
- goto refuse;
+ return -EINVAL;
}
switch (bs->type) {
case BSSAP_MSG_BSS_MANAGEMENT:
break;
default:
- LOGPFSML(fi, LOGL_NOTICE,
- "message type not allowed for N-CONNECT: %s\n", gsm0808_bssap_name(bs->type));
- goto refuse;
+ LOGPFSML(fi, LOGL_ERROR,
+ "message type not allowed for initial BSSMAP: %s\n", gsm0808_bssap_name(bs->type));
+ return -EINVAL;
}
msg->l4h = &msg->l3h[sizeof(*bs)];
- bssmap_type = msg->l4h[0];
-
- LOGPFSML(fi, LOGL_DEBUG, "Rx N-CONNECT: %s: %s\n", gsm0808_bssap_name(bs->type),
- gsm0808_bssmap_name(bssmap_type));
+ /* Validate initial message type. See also BSC_Tests.TC_outbound_connect. */
+ bssmap_type = msg->l4h[0];
switch (bssmap_type) {
case BSS_MAP_MSG_HANDOVER_RQST:
case BSS_MAP_MSG_PERFORM_LOCATION_RQST:
- break;
+ return 0;
default:
- LOGPFSML(fi, LOGL_NOTICE, "No support for N-CONNECT: %s: %s\n",
+ LOGPFSML(fi, LOGL_ERROR, "No support for initial BSSMAP: %s: %s\n",
gsm0808_bssap_name(bs->type), gsm0808_bssmap_name(bssmap_type));
- goto refuse;
+ return -EINVAL;
}
+}
- /* First off, accept the new conn. */
- if (osmo_sccp_tx_conn_resp(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
- &scu_prim->u.connect.called_addr, NULL, 0)) {
- LOGPFSML(fi, LOGL_ERROR, "Cannot send SCCP CONN RESP\n");
- goto refuse;
- }
+static void handle_initial_user_data(struct osmo_fsm_inst *fi, struct msgb *msg)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct bssmap_header *bs;
+ enum BSS_MAP_MSG_TYPE bssmap_type;
- /* Make sure the conn FSM will osmo_sccp_tx_disconn() on term */
- conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED;
+ /* validate_initial_user_data() must be called before this */
+ OSMO_ASSERT(msgb_l4(msg));
+
+ bs = msgb_l3(msg);
+ bssmap_type = msg->l4h[0];
+
+ /* FIXME: Extract optional IMSI and update FSM using osmo_fsm_inst_set_id() (OS#2969) */
+
+ LOGPFSML(fi, LOGL_DEBUG, "Rx initial BSSMAP: %s: %s\n", gsm0808_bssap_name(bs->type),
+ gsm0808_bssmap_name(bssmap_type));
switch (bssmap_type) {
case BSS_MAP_MSG_HANDOVER_RQST:
- /* Inter-BSC MT Handover Request, another BSS is handovering to us. */
+ rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_RX_DT1_HANDOVER_RQST]);
+ /* Inter-BSC incoming Handover Request, another BSS is handovering to us. */
handover_start_inter_bsc_in(conn, msg);
return;
case BSS_MAP_MSG_PERFORM_LOCATION_RQST:
+ rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_REQUEST]);
/* Location Services: MSC asks for location of an IDLE subscriber */
conn_fsm_state_chg(ST_ACTIVE);
lcs_loc_req_start(conn, msg);
return;
default:
- OSMO_ASSERT(false);
+ LOGPFSML(fi, LOGL_ERROR, "No support for initial BSSMAP: %s: %s\n",
+ gsm0808_bssap_name(bs->type), gsm0808_bssmap_name(bssmap_type));
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ return;
}
+}
-refuse:
- osmo_sccp_tx_disconn(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
- &scu_prim->u.connect.called_addr, 0);
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+static void handle_sccp_n_connect(struct osmo_fsm_inst *fi, struct osmo_scu_prim *scu_prim)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct msgb *msg = scu_prim->oph.msg;
+
+ /* Make sure the conn FSM will osmo_sccp_tx_disconn() on term */
+ conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED;
+
+ msg->l3h = msgb_l2(msg);
+
+ /* If (BSSMAP) user data is included, validate it before accepting the connection */
+ if (msgb_l3(msg) && msgb_l3len(msg)) {
+ if (validate_initial_user_data(fi, msg)) {
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ return;
+ }
+ }
+
+ /* accept the new conn. */
+ if (osmo_sccp_tx_conn_resp(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
+ &scu_prim->u.connect.called_addr, NULL, 0)) {
+ LOGPFSML(fi, LOGL_ERROR, "Cannot send SCCP CONN RESP\n");
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ return;
+ }
+
+ /* The initial user data may already be included in this N-Connect, or it may come later in a separate message.
+ * If it is already included, also go to ST_WAIT_INITIAL_USER_DATA now, so that we don't have to tend to two
+ * separate code paths doing the same thing (handling of HANDOVER_END). */
+ OSMO_ASSERT(conn_fsm_state_chg(ST_WAIT_INITIAL_USER_DATA) == 0);
+
+ /* It is usually a bad idea to continue using a fi after a state change, because the fi might terminate during
+ * the state change. In this case it is certain that the fi stays around for the initial user data. */
+ if (msgb_l3(msg) && msgb_l3len(msg)) {
+ handle_initial_user_data(fi, msg);
+ } else {
+ LOGPFSML(fi, LOGL_DEBUG, "N-Connect does not contain user data (no BSSMAP message included)\n");
+ }
}
static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -299,7 +397,6 @@ static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
struct osmo_scu_prim *scu_prim = NULL;
struct msgb *msg = NULL;
int rc;
- enum handover_result ho_result;
switch (event) {
case GSCON_EV_MO_COMPL_L3:
@@ -327,11 +424,28 @@ static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
return;
}
- /* FIXME: Extract optional IMSI and update FSM using osmo_fsm_inst_set_id()
- * related: OS2969 (same as above) */
+ handle_sccp_n_connect(fi, scu_prim);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void gscon_fsm_wait_initial_user_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct msgb *msg = data;
+ enum handover_result ho_result;
- handle_bssap_n_connect(fi, scu_prim);
+ switch (event) {
+ case GSCON_EV_A_INITIAL_USER_DATA:
+ if (validate_initial_user_data(fi, msg)) {
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ return;
+ }
+ handle_initial_user_data(fi, msg);
break;
+
case GSCON_EV_HANDOVER_END:
ho_result = HO_RESULT_ERROR;
if (data)
@@ -348,12 +462,10 @@ static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
return;
}
LOG_HO(conn, LOGL_ERROR,
- "Conn is in state %s, the only accepted handover kind is inter-BSC MT\n",
+ "Conn is in state %s, the only accepted handover kind is inter-BSC incoming handover\n",
osmo_fsm_inst_state_name(conn->fi));
}
gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
- if (conn->fi->state != ST_CLEARING)
- osmo_fsm_inst_state_chg(fi, ST_CLEARING, 60, -4);
return;
default:
OSMO_ASSERT(false);
@@ -373,7 +485,6 @@ static void gscon_fsm_wait_cc(struct osmo_fsm_inst *fi, uint32_t event, void *da
confirmed connection, then instead simply drop the connection */
LOGPFSML(fi, LOGL_INFO,
"Connection confirmed but lchan was dropped previously, clearing conn\n");
- osmo_fsm_inst_state_chg(conn->fi, ST_CLEARING, 60, -4);
gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
break;
}
@@ -510,6 +621,62 @@ static bool same_mgw_info(const struct mgcp_conn_peer *a, const struct mgcp_conn
return true;
}
+static struct mgcp_client *select_mgw(struct gsm_subscriber_connection *conn, struct gsm_lchan *for_lchan)
+{
+ struct mgcp_client_pool_member *mgcp_pmemb;
+ struct mgcp_client *mgcp_client;
+ struct gsm_bts *bts = for_lchan->ts->trx->bts;
+
+ /* If BTS is not pinned to a given MGW, let regular allocation which
+ * spreads load over available MGWs: */
+ if (bts->mgw_pool_target == -1)
+ goto pick_any;
+
+ /* BTS is pinned to an MGW, retrieve pointer to it: */
+ mgcp_pmemb = mgcp_client_pool_find_member_by_nr(conn->network->mgw.mgw_pool, bts->mgw_pool_target);
+ if (!mgcp_pmemb) {
+ if (!bts->mgw_pool_target_strict) {
+ LOGPFSML(conn->fi, LOGL_NOTICE,
+ "mgw pool-target %u not found! selecting another one.\n", bts->mgw_pool_target);
+ goto pick_any;
+ } else {
+ LOGPFSML(conn->fi, LOGL_ERROR, "mgw pool-target %u not found!\n", bts->mgw_pool_target);
+ return NULL;
+ }
+ }
+ if (mgcp_client_pool_member_is_blocked(mgcp_pmemb)) {
+ if (!bts->mgw_pool_target_strict) {
+ LOGPFSML(conn->fi, LOGL_NOTICE,
+ "mgw pool-target %u is administratively blocked! selecting another one.\n",
+ bts->mgw_pool_target);
+ goto pick_any;
+ } else {
+ LOGPFSML(conn->fi, LOGL_ERROR, "mgw pool-target %u is administratively blocked!\n",
+ bts->mgw_pool_target);
+ return NULL;
+ }
+ }
+
+ mgcp_client = mgcp_client_pool_member_get(mgcp_pmemb);
+ if (!mgcp_client) {
+ if (!bts->mgw_pool_target_strict) {
+ LOGPFSML(conn->fi, LOGL_NOTICE,
+ "mgw pool-target %u is not connected! selecting another one.\n",
+ bts->mgw_pool_target);
+ goto pick_any;
+ } else {
+ LOGPFSML(conn->fi, LOGL_ERROR, "mgw pool-target %u is not connected!\n",
+ bts->mgw_pool_target);
+ return NULL;
+ }
+ }
+ return mgcp_client;
+
+pick_any:
+ mgcp_client = mgcp_client_pool_get(conn->network->mgw.mgw_pool);
+ return mgcp_client;
+}
+
/* Make sure a conn->user_plane.mgw_endpoint is allocated with the proper mgw endpoint name. For
* SCCPlite, pass in msc_assigned_cic the CIC received upon BSSMAP Assignment Command or BSSMAP Handover
* Request form the MSC (which is only stored in conn->user_plane after success). Ignored for AoIP. */
@@ -517,39 +684,57 @@ 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) {
+ LOG_LCHAN(for_lchan, LOGL_ERROR, "no conn!\n");
+ return 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 = select_mgw(conn, for_lchan);
+ if (!mgcp_client) {
+ LOGPFSML(conn->fi, LOGL_ERROR,
+ "cannot ensure MGW endpoint -- no MGW configured, check configuration!\n");
+ 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,
+ uint8_t i460_bit_offs;
+ if (for_lchan->ts->e1_link.e1_ts_ss == E1_SUBSLOT_FULL)
+ i460_bit_offs = 0;
+ else
+ i460_bit_offs = for_lchan->ts->e1_link.e1_ts_ss * 2;
+
+ 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);
+ i460_bit_offs);
}
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 +784,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 +802,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;
}
@@ -651,15 +838,25 @@ bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn,
static const struct osmo_fsm_state gscon_fsm_states[] = {
[ST_INIT] = {
.name = "INIT",
- .in_event_mask = S(GSCON_EV_MO_COMPL_L3) | S(GSCON_EV_A_CONN_IND)
- | S(GSCON_EV_HANDOVER_END),
- .out_state_mask = S(ST_WAIT_CC) | S(ST_ACTIVE) | S(ST_CLEARING),
+ .in_event_mask = S(GSCON_EV_MO_COMPL_L3) | S(GSCON_EV_A_CONN_IND),
+ .out_state_mask = 0
+ | S(ST_WAIT_INITIAL_USER_DATA)
+ | S(ST_WAIT_CC) | S(ST_ACTIVE) | S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD),
.action = gscon_fsm_init,
+ },
+ [ST_WAIT_INITIAL_USER_DATA] = {
+ .name = "WAIT_INITIAL_USER_DATA",
+ .in_event_mask = 0
+ | S(GSCON_EV_A_INITIAL_USER_DATA)
+ | S(GSCON_EV_HANDOVER_END)
+ ,
+ .out_state_mask = S(ST_WAIT_CC) | S(ST_ACTIVE) | S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD),
+ .action = gscon_fsm_wait_initial_user_data,
},
[ST_WAIT_CC] = {
.name = "WAIT_CC",
.in_event_mask = S(GSCON_EV_A_CONN_CFM),
- .out_state_mask = S(ST_ACTIVE) | S(ST_CLEARING),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD),
.action = gscon_fsm_wait_cc,
},
[ST_ACTIVE] = {
@@ -669,26 +866,32 @@ static const struct osmo_fsm_state gscon_fsm_states[] = {
| S(GSCON_EV_LCS_LOC_REQ_END)
| S(GSCON_EV_MO_COMPL_L3)
,
- .out_state_mask = S(ST_CLEARING) | S(ST_ASSIGNMENT) |
+ .out_state_mask = S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD) | S(ST_ASSIGNMENT) |
S(ST_HANDOVER),
.action = gscon_fsm_active,
},
[ST_ASSIGNMENT] = {
.name = "ASSIGNMENT",
.in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_ASSIGNMENT_END),
- .out_state_mask = S(ST_ACTIVE) | S(ST_CLEARING),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD),
.action = gscon_fsm_assignment,
},
[ST_HANDOVER] = {
.name = "HANDOVER",
.in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_HANDOVER_END),
- .out_state_mask = S(ST_ACTIVE) | S(ST_CLEARING),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_CLEAR_CMD) | S(ST_WAIT_SCCP_RLSD),
.action = gscon_fsm_handover,
},
- [ST_CLEARING] = {
- .name = "CLEARING",
- /* dead end state */
- },
+ [ST_WAIT_CLEAR_CMD] = {
+ .name = "WAIT_CLEAR_CMD",
+ .onenter = gscon_fsm_wait_clear_cmd_onenter,
+ .out_state_mask = S(ST_WAIT_SCCP_RLSD),
+ },
+ [ST_WAIT_SCCP_RLSD] = {
+ .name = "WAIT_SCCP_RLSD",
+ .onenter = gscon_fsm_wait_sccp_rlsd_onenter,
+ .in_event_mask = S(GSCON_EV_HANDOVER_END),
+ },
};
void gscon_change_primary_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan *new_lchan)
@@ -696,9 +899,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;
@@ -727,19 +940,15 @@ void gscon_lchan_releasing(struct gsm_subscriber_connection *conn, struct gsm_lc
lchan_forget_conn(conn->lchan);
conn->lchan = NULL;
}
- /* If the conn has no lchan anymore, it was released by the BTS and needs to Clear towards MSC. */
- if (!conn->lchan) {
+ /* If the conn has no lchan anymore, it was released by the BTS and needs to Clear towards MSC.
+ * However, if a Location Request is still busy, do not send Clear Request. */
+ if (!conn->lchan && !conn->lcs.loc_req) {
switch (conn->fi->state) {
case ST_WAIT_CC:
/* The SCCP connection was not yet confirmed by a CC, the BSSAP is not fully established
yet so we cannot release it. First wait for the CC, and release in gscon_fsm_wait_cc(). */
break;
default:
- /* Ensure that the FSM is in ST_CLEARING. */
- osmo_fsm_inst_state_chg(conn->fi, ST_CLEARING, 60, -4);
- /* fall thru, omit an error log if already in ST_CLEARING */
- case ST_CLEARING:
- /* Request a Clear Command from the MSC. */
gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
break;
}
@@ -778,8 +987,7 @@ void gscon_forget_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan
osmo_fsm_inst_name(conn->fi), detach_label);
}
- if ((conn->fi && conn->fi->state != ST_CLEARING)
- && !conn->lchan
+ if (!conn->lchan
&& !conn->ho.new_lchan
&& !conn->assignment.new_lchan
&& !conn->lcs.loc_req)
@@ -788,6 +996,16 @@ 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);
+
+ /* Be sure that the endpoint CI we are maintaining in user_plane
+ * is also removed from the other locations as well. */
+ gscon_forget_mgw_endpoint_ci(conn, conn->user_plane.mgw_endpoint_ci_msc);
+
conn->user_plane.mgw_endpoint = NULL;
conn->user_plane.mgw_endpoint_ci_msc = NULL;
conn->ho.created_ci_for_msc = NULL;
@@ -803,50 +1021,27 @@ void gscon_forget_mgw_endpoint_ci(struct gsm_subscriber_connection *conn, struct
if (conn->user_plane.mgw_endpoint_ci_msc == ci)
conn->user_plane.mgw_endpoint_ci_msc = NULL;
+
+ if (conn->assignment.created_ci_for_msc == ci)
+ conn->assignment.created_ci_for_msc = NULL;
}
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 struct tlv_parsed *tp;
+ struct osmo_mobile_identity mi_imsi;
/* Regular allstate event processing */
switch (event) {
case GSCON_EV_A_CLEAR_CMD:
- conn->rx_clear_command = true;
-
- /* Give the handover_fsm a chance to book this as handover success before tearing down everything,
- * making it look like a sudden death failure. */
- if (conn->ho.fi)
- osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_CONN_RELEASING, NULL);
-
- if (conn->lcs.loc_req)
- osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_CONN_CLEAR, NULL);
-
OSMO_ASSERT(data);
- ccd = data;
- if (conn->lchan)
- conn->lchan->release.is_csfb = ccd->is_csfb;
- /* 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));
- /* FIXME: Release all terestrial resources in ST_CLEARING */
- /* According to 3GPP 48.008 3.1.9.1. "The BSS need not wait for the radio channel
- * release to be completed or for the guard timer to expire before returning the
- * CLEAR COMPLETE message" */
-
- /* Close MGCP connections */
- osmo_mgcpc_ep_clear(conn->user_plane.mgw_endpoint);
-
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_CLEAR_COMPLETE]);
- gscon_sigtran_send(conn, gsm0808_create_clear_complete());
+ conn->clear_cause = *(const enum gsm0808_cause *)data;
+ conn_fsm_state_chg(ST_WAIT_SCCP_RLSD);
break;
case GSCON_EV_A_DISC_IND:
- /* MSC or SIGTRAN network has hard-released SCCP connection,
- * terminate the FSM now. */
+ /* MSC or SIGTRAN network has hard-released SCCP connection, terminate the FSM now.
+ * Cleanup is done in gscon_pre_term() and gscon_cleanup(). */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, data);
break;
case GSCON_EV_FORGET_MGW_ENDPOINT:
@@ -859,7 +1054,9 @@ static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *d
conn->lchan->release.rr_cause =
bsc_gsm48_rr_cause_from_rsl_cause(conn->lchan->release.rsl_error_cause);
}
- gscon_bssmap_clear(conn, GSM0808_CAUSE_RADIO_INTERFACE_FAILURE);
+ /* Request BSSMAP Clear, but do not abort an ongoing Location Request */
+ if (!conn->lcs.loc_req)
+ gscon_bssmap_clear(conn, GSM0808_CAUSE_RADIO_INTERFACE_FAILURE);
break;
case GSCON_EV_MGW_MDCX_RESP_MSC:
LOGPFSML(fi, LOGL_DEBUG, "Rx MDCX of MSC side (LCLS?)\n");
@@ -868,14 +1065,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 +1125,21 @@ static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau
static void gscon_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct gsm_subscriber_connection *conn = fi->priv;
+ struct mgcp_client *mgcp_client;
+
+ /* Put MGCP client back into MGW pool */
+ mgcp_client = osmo_mgcpc_ep_client(conn->user_plane.mgw_endpoint);
+ mgcp_client_pool_put(mgcp_client);
osmo_mgcpc_ep_clear(conn->user_plane.mgw_endpoint);
+ conn->user_plane.mgw_endpoint = NULL;
+ conn->user_plane.mgw_endpoint_ci_msc = NULL;
+
+ if (conn->ho.fi)
+ osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_CONN_RELEASING, NULL);
+
+ if (conn->lcs.loc_req)
+ osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_CONN_CLEAR, NULL);
if (conn->lcls.fi) {
/* request termination of LCLS FSM */
@@ -926,14 +1148,12 @@ static void gscon_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
}
LOGPFSML(fi, LOGL_DEBUG, "Releasing all lchans (if any) because this conn is terminating\n");
- /* when things go smoothly, the lchan should have been released before FSM instance termination. So if this is
- * necessary it's probably "abnormal". */
- gscon_release_lchans(conn, true, GSM48_RR_CAUSE_ABNORMAL_UNSPEC);
+ gscon_release_lchans(conn, true, bsc_gsm48_rr_cause_from_gsm0808_cause(conn->clear_cause));
/* 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)
@@ -958,8 +1178,9 @@ static int gscon_timer_cb(struct osmo_fsm_inst *fi)
case -4:
/* The MSC has sent a BSSMAP Clear Command, we acknowledged that, but the conn was never
* disconnected. */
- LOGPFSML(fi, LOGL_ERROR, "Long after a BSSMAP Clear Command, the conn is still not"
- " released. For sanity, discarding this conn now.\n");
+ LOGPFSML(fi, LOGL_ERROR, "Long after expecting %s, the conn is still not"
+ " released. For sanity, discarding this conn now.\n",
+ fi->state == ST_WAIT_CLEAR_CMD ? "BSSMAP Clear Command" : "SCCP RLSD");
a_reset_conn_fail(conn->sccp.msc);
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
break;
@@ -987,7 +1208,7 @@ static struct osmo_fsm gscon_fsm = {
.event_names = gscon_fsm_event_names,
};
-void bsc_subscr_conn_fsm_init()
+static __attribute__((constructor)) void bsc_subscr_conn_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&gscon_fsm) == 0);
OSMO_ASSERT(osmo_fsm_register(&lcls_fsm) == 0);
@@ -1004,9 +1225,12 @@ 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;
+ /* Default clear cause (on RR translates to GSM48_RR_CAUSE_ABNORMAL_UNSPEC) */
+ conn->clear_cause = GSM0808_CAUSE_EQUIPMENT_FAILURE;
+
/* don't allocate from 'conn' context, as gscon_cleanup() will call talloc_free(conn) before
* libosmocore will call talloc_free(conn->fi), i.e. avoid use-after-free during cleanup */
conn->fi = osmo_fsm_inst_alloc(&gscon_fsm, net, conn, LOGL_DEBUG, NULL);
@@ -1089,19 +1313,23 @@ static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, en
switch (rllr_ind) {
case BSC_RLLR_IND_EST_CONF:
- rsl_data_request(msg, OBSC_LINKID_CB(msg));
+ rsl_data_request(msg, link_id);
break;
case BSC_RLLR_IND_REL_IND:
- bsc_sapi_n_reject(lchan->conn, OBSC_LINKID_CB(msg),
+ bsc_sapi_n_reject(lchan->conn, RSL_LINK_ID2DLCI(link_id),
GSM0808_CAUSE_MS_NOT_EQUIPPED);
msgb_free(msg);
break;
case BSC_RLLR_IND_ERR_IND:
case BSC_RLLR_IND_TIMEOUT:
- bsc_sapi_n_reject(lchan->conn, OBSC_LINKID_CB(msg),
+ bsc_sapi_n_reject(lchan->conn, RSL_LINK_ID2DLCI(link_id),
GSM0808_CAUSE_BSS_NOT_EQUIPPED);
msgb_free(msg);
break;
+ default:
+ LOGPLCHAN(lchan, DRLL, LOGL_NOTICE, "Received unknown rllr_ind %u\n", rllr_ind);
+ msgb_free(msg);
+ break;
}
}
@@ -1123,7 +1351,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) {
@@ -1139,7 +1366,7 @@ static void gsm0808_send_rsl_dtap(struct gsm_subscriber_connection *conn,
rc = rll_establish(msg->lchan, sapi, rll_ind_cb, msg);
if (rc) {
msgb_free(msg);
- bsc_sapi_n_reject(conn, link_id, GSM0808_CAUSE_BSS_NOT_EQUIPPED);
+ bsc_sapi_n_reject(conn, RSL_LINK_ID2DLCI(link_id), GSM0808_CAUSE_BSS_NOT_EQUIPPED);
goto failed_to_send;
}
return;
@@ -1154,7 +1381,6 @@ static void gsm0808_send_rsl_dtap(struct gsm_subscriber_connection *conn,
failed_to_send:
LOGPFSML(conn->fi, LOGL_ERROR, "Tx BSSMAP CLEAR REQUEST to MSC\n");
gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
- osmo_fsm_inst_state_chg(conn->fi, ST_ACTIVE, 0, 0);
}
void gscon_submit_rsl_dtap(struct gsm_subscriber_connection *conn,
diff --git a/src/osmo-bsc/bsc_subscriber.c b/src/osmo-bsc/bsc_subscriber.c
index 4a4829866..9f4e82be1 100644
--- a/src/osmo-bsc/bsc_subscriber.c
+++ b/src/osmo-bsc/bsc_subscriber.c
@@ -30,6 +30,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/bsc/bsc_subscriber.h>
+#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/debug.h>
static void bsc_subscr_free(struct bsc_subscr *bsub);
@@ -77,6 +78,7 @@ static struct bsc_subscr *bsc_subscr_alloc(struct llist_head *list)
.talloc_object = bsub,
.use_cb = bsub_use_cb,
};
+ INIT_LLIST_HEAD(&bsub->active_paging_requests);
llist_add_tail(&bsub->entry, list);
@@ -101,6 +103,24 @@ struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list,
return NULL;
}
+struct bsc_subscr *bsc_subscr_find_by_imei(struct llist_head *list,
+ const char *imei,
+ const char *use_token)
+{
+ struct bsc_subscr *bsub;
+
+ if (!imei || !*imei)
+ return NULL;
+
+ llist_for_each_entry(bsub, list, entry) {
+ if (!strcmp(bsub->imei, imei)) {
+ bsc_subscr_get(bsub, use_token);
+ return bsub;
+ }
+ }
+ return NULL;
+}
+
struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list,
uint32_t tmsi,
const char *use_token)
@@ -127,6 +147,8 @@ struct bsc_subscr *bsc_subscr_find_by_mi(struct llist_head *list, const struct o
switch (mi->type) {
case GSM_MI_TYPE_IMSI:
return bsc_subscr_find_by_imsi(list, mi->imsi, use_token);
+ case GSM_MI_TYPE_IMEI:
+ return bsc_subscr_find_by_imei(list, mi->imei, use_token);
case GSM_MI_TYPE_TMSI:
return bsc_subscr_find_by_tmsi(list, mi->tmsi, use_token);
default:
@@ -141,6 +163,13 @@ void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi)
osmo_strlcpy(bsub->imsi, imsi, sizeof(bsub->imsi));
}
+void bsc_subscr_set_imei(struct bsc_subscr *bsub, const char *imei)
+{
+ if (!bsub)
+ return;
+ osmo_strlcpy(bsub->imei, imei, sizeof(bsub->imei));
+}
+
struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list,
const char *imsi,
const char *use_token)
@@ -157,6 +186,22 @@ struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list,
return bsub;
}
+struct bsc_subscr *bsc_subscr_find_or_create_by_imei(struct llist_head *list,
+ const char *imei,
+ const char *use_token)
+{
+ struct bsc_subscr *bsub;
+ bsub = bsc_subscr_find_by_imei(list, imei, use_token);
+ if (bsub)
+ return bsub;
+ bsub = bsc_subscr_alloc(list);
+ if (!bsub)
+ return NULL;
+ bsc_subscr_set_imei(bsub, imei);
+ bsc_subscr_get(bsub, use_token);
+ return bsub;
+}
+
struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list,
uint32_t tmsi,
const char *use_token)
@@ -181,6 +226,8 @@ struct bsc_subscr *bsc_subscr_find_or_create_by_mi(struct llist_head *list, cons
switch (mi->type) {
case GSM_MI_TYPE_IMSI:
return bsc_subscr_find_or_create_by_imsi(list, mi->imsi, use_token);
+ case GSM_MI_TYPE_IMEI:
+ return bsc_subscr_find_or_create_by_imei(list, mi->imei, use_token);
case GSM_MI_TYPE_TMSI:
return bsc_subscr_find_or_create_by_tmsi(list, mi->tmsi, use_token);
default:
@@ -198,6 +245,8 @@ static int bsc_subscr_name_buf(char *buf, size_t buflen, struct bsc_subscr *bsub
}
if (bsub->imsi[0])
OSMO_STRBUF_PRINTF(sb, "-IMSI-%s", bsub->imsi);
+ else if (bsub->imei[0])
+ OSMO_STRBUF_PRINTF(sb, "-IMEI-%s", bsub->imei);
if (bsub->tmsi != GSM_RESERVED_TMSI)
OSMO_STRBUF_PRINTF(sb, "-TMSI-0x%08x", bsub->tmsi);
return sb.chars_needed;
@@ -222,6 +271,7 @@ const char *bsc_subscr_id(struct bsc_subscr *bsub)
static void bsc_subscr_free(struct bsc_subscr *bsub)
{
+ OSMO_ASSERT(llist_empty(&bsub->active_paging_requests));
llist_del(&bsub->entry);
talloc_free(bsub);
}
@@ -246,3 +296,41 @@ void log_set_filter_bsc_subscr(struct log_target *target,
} else
target->filter_map &= ~(1 << LOG_FLT_BSC_SUBSCR);
}
+
+void bsc_subscr_add_active_paging_request(struct bsc_subscr *bsub, struct gsm_paging_request *req)
+{
+ bsub->active_paging_requests_len++;
+ bsc_subscr_get(bsub, BSUB_USE_PAGING_REQUEST);
+ llist_add_tail(&req->bsub_entry, &bsub->active_paging_requests);
+}
+
+void bsc_subscr_remove_active_paging_request(struct bsc_subscr *bsub, struct gsm_paging_request *req)
+{
+ llist_del(&req->bsub_entry);
+ bsub->active_paging_requests_len--;
+ bsc_subscr_put(bsub, BSUB_USE_PAGING_REQUEST);
+}
+
+void bsc_subscr_remove_active_paging_request_all(struct bsc_subscr *bsub)
+{
+ /* Avoid accessing bsub after reaching 0 active_paging_request_len,
+ * since it could be freed during put(): */
+ unsigned remaining = bsub->active_paging_requests_len;
+ while (remaining > 0) {
+ struct gsm_paging_request *req;
+ req = llist_first_entry(&bsub->active_paging_requests,
+ struct gsm_paging_request, bsub_entry);
+ bsc_subscr_remove_active_paging_request(bsub, req);
+ remaining--;
+ }
+}
+
+struct gsm_paging_request *bsc_subscr_find_req_by_bts(const struct bsc_subscr *bsub, const struct gsm_bts *bts)
+{
+ struct gsm_paging_request *req;
+ llist_for_each_entry(req, &bsub->active_paging_requests, bsub_entry) {
+ if (req->bts == bts)
+ return req;
+ }
+ return NULL;
+}
diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c
index cd7d0e001..20d57db99 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,47 @@
#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/bts_setup_ramp.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 <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/lchan.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 +81,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 +101,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 +142,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 +228,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)
-{
- unsigned int i;
- bool no_features = true;
- vty_out(vty, " Features:%s", VTY_NEWLINE);
-
- for (i = 0; i < _NUM_BTS_FEAT; i++) {
- if (osmo_bts_has_feature(&bts->features, i)) {
- vty_out(vty, " %03u ", i);
- vty_out(vty, "%-40s%s", osmo_bts_feature_name(i), VTY_NEWLINE);
- no_features = false;
- }
- }
-
- if (no_features)
- vty_out(vty, " (not available)%s", VTY_NEWLINE);
-}
-
-static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
-{
- struct pchan_load pl;
- unsigned long long sec;
- struct gsm_bts_trx *trx;
- int ts_hopping_total;
- int ts_non_hopping_total;
-
- vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, "
- "BSIC %u (NCC=%u, BCC=%u) and %u TRX%s",
- bts->nr, btstype2str(bts->type), gsm_band_name(bts->band),
- bts->cell_identity,
- bts->location_area_code, bts->bsic,
- bts->bsic >> 3, bts->bsic & 7,
- bts->num_trx, VTY_NEWLINE);
- vty_out(vty, " Description: %s%s",
- bts->description ? bts->description : "(null)", VTY_NEWLINE);
-
- vty_out(vty, " ARFCNs:");
- ts_hopping_total = 0;
- ts_non_hopping_total = 0;
- llist_for_each_entry(trx, &bts->trx_list, list) {
- int ts_nr;
- int ts_hopping = 0;
- int ts_non_hopping = 0;
- for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
- if (ts->hopping.enabled)
- ts_hopping++;
- else
- ts_non_hopping++;
- }
-
- if (ts_non_hopping)
- vty_out(vty, " %u", trx->arfcn);
- ts_hopping_total += ts_hopping;
- ts_non_hopping_total += ts_non_hopping;
- }
- if (ts_hopping_total) {
- if (ts_non_hopping_total)
- vty_out(vty, " / Hopping on %d of %d timeslots",
- ts_hopping_total, ts_hopping_total + ts_non_hopping_total);
- else
- vty_out(vty, " Hopping on all %d timeslots", ts_hopping_total);
- }
- vty_out(vty, "%s", VTY_NEWLINE);
-
- if (strnlen(bts->pcu_version, MAX_VERSION_LENGTH))
- vty_out(vty, " PCU version %s connected%s", bts->pcu_version,
- VTY_NEWLINE);
- vty_out(vty, " MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE);
- vty_out(vty, " Minimum Rx Level for Access: %i dBm%s",
- rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min),
- VTY_NEWLINE);
- vty_out(vty, " Cell Reselection Hysteresis: %u dBm%s",
- bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
- vty_out(vty, " Access Control Class rotation allow mask: 0x%" PRIx16 "%s",
- bts->acc_mgr.allowed_subset_mask, VTY_NEWLINE);
- vty_out(vty, " Access Control Class ramping: %senabled%s",
- acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "not ", VTY_NEWLINE);
- if (acc_ramp_is_enabled(&bts->acc_ramp)) {
- vty_out(vty, " Access Control Class ramping step interval: %u seconds%s",
- acc_ramp_get_step_interval(&bts->acc_ramp), VTY_NEWLINE);
- vty_out(vty, " Access Control Class channel load thresholds: (%" PRIu8 ", %" PRIu8 ")%s",
- bts->acc_ramp.chan_load_lower_threshold,
- bts->acc_ramp.chan_load_upper_threshold, VTY_NEWLINE);
- vty_out(vty, " enabling %u Access Control Class%s per ramping step%s",
- acc_ramp_get_step_size(&bts->acc_ramp),
- acc_ramp_get_step_size(&bts->acc_ramp) > 1 ? "es" : "", VTY_NEWLINE);
- }
- vty_out(vty, " RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer,
- VTY_NEWLINE);
- vty_out(vty, " RACH Max transmissions: %u%s",
- rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
- VTY_NEWLINE);
- if (bts->si_common.rach_control.cell_bar)
- vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE);
- if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
- vty_out(vty, " Uplink DTX: %s%s",
- (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ?
- "enabled" : "forced", VTY_NEWLINE);
- else
- vty_out(vty, " Uplink DTX: not enabled%s", VTY_NEWLINE);
- vty_out(vty, " Downlink DTX: %senabled%s", bts->dtxd ? "" : "not ",
- VTY_NEWLINE);
- vty_out(vty, " Channel Description Attachment: %s%s",
- (bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE);
- vty_out(vty, " Channel Description BS-PA-MFRMS: %u%s",
- bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE);
- vty_out(vty, " Channel Description BS-AG_BLKS-RES: %u%s",
- bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE);
- vty_out(vty, " System Information present: 0x%08x, static: 0x%08x%s",
- bts->si_valid, bts->si_mode_static, VTY_NEWLINE);
- vty_out(vty, " Early Classmark Sending: 2G %s, 3G %s%s%s",
- bts->early_classmark_allowed ? "allowed" : "forbidden",
- bts->early_classmark_allowed_3g ? "allowed" : "forbidden",
- bts->early_classmark_allowed_3g && !bts->early_classmark_allowed ?
- " (forbidden by 2G bit)" : "",
- VTY_NEWLINE);
- if (bts->pcu_sock_path)
- vty_out(vty, " PCU Socket Path: %s%s", bts->pcu_sock_path, VTY_NEWLINE);
- if (is_ipaccess_bts(bts))
- vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s",
- bts->ip_access.site_id, bts->ip_access.bts_id,
- bts->oml_tei, VTY_NEWLINE);
- else if (bts->type == GSM_BTS_TYPE_NOKIA_SITE)
- vty_out(vty, " Skip Reset: %d%s",
- bts->nokia.skip_reset, VTY_NEWLINE);
- vty_out(vty, " NM State: ");
- net_dump_nmstate(vty, &bts->mo.nm_state);
- vty_out(vty, " Site Mgr NM State: ");
- net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state);
-
- if (bts->gprs.mode != BTS_GPRS_NONE) {
- vty_out(vty, " GPRS NSE: ");
- net_dump_nmstate(vty, &bts->gprs.nse.mo.nm_state);
- vty_out(vty, " GPRS CELL: ");
- net_dump_nmstate(vty, &bts->gprs.cell.mo.nm_state);
- vty_out(vty, " GPRS NSVC0: ");
- net_dump_nmstate(vty, &bts->gprs.nsvc[0].mo.nm_state);
- vty_out(vty, " GPRS NSVC1: ");
- net_dump_nmstate(vty, &bts->gprs.nsvc[1].mo.nm_state);
- } else
- vty_out(vty, " GPRS: not configured%s", VTY_NEWLINE);
-
- vty_out(vty, " Paging: %u pending requests, %u free slots%s",
- paging_pending_requests_nr(bts),
- bts->paging.available_slots, VTY_NEWLINE);
- if (is_ipaccess_bts(bts)) {
- vty_out(vty, " OML Link: ");
- e1isl_dump_vty_tcp(vty, bts->oml_link);
- vty_out(vty, " OML Link state: %s", get_model_oml_status(bts));
- sec = bts_uptime(bts);
- if (sec)
- vty_out(vty, " %llu days %llu hours %llu min. %llu sec.",
- OSMO_SEC2DAY(sec), OSMO_SEC2HRS(sec), OSMO_SEC2MIN(sec), sec % 60);
- vty_out(vty, "%s", VTY_NEWLINE);
- } else {
- vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
- e1isl_dump_vty(vty, bts->oml_link);
- }
-
- vty_out(vty, " Neighbor Cells: ");
- switch (bts->neigh_list_manual_mode) {
- default:
- case NL_MODE_AUTOMATIC:
- vty_out(vty, "Automatic");
- /* generate_bcch_chan_list() should populate si_common.neigh_list */
- break;
- case NL_MODE_MANUAL:
- vty_out(vty, "Manual");
- break;
- case NL_MODE_MANUAL_SI5SEP:
- vty_out(vty, "Manual/separate SI5");
- break;
- }
- vty_out(vty, ", ARFCNs:");
- vty_out_neigh_list(vty, &bts->si_common.neigh_list);
- if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) {
- vty_out(vty, " SI5:");
- vty_out_neigh_list(vty, &bts->si_common.si5_neigh_list);
- }
- vty_out(vty, "%s", VTY_NEWLINE);
-
- /* FIXME: chan_desc */
- memset(&pl, 0, sizeof(pl));
- bts_chan_load(&pl, bts);
- vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE);
- dump_pchan_load_vty(vty, " ", &pl);
-
- bts_dump_vty_cbch(vty, &bts->cbch_basic);
- bts_dump_vty_cbch(vty, &bts->cbch_extended);
-
- vty_out(vty, " Channel Requests : %"PRIu64" total, %"PRIu64" no channel%s",
- bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL].current,
- bts->bts_ctrs->ctr[BTS_CTR_CHREQ_NO_CHANNEL].current,
- VTY_NEWLINE);
- vty_out(vty, " Channel Failures : %"PRIu64" rf_failures, %"PRIu64" rll failures%s",
- bts->bts_ctrs->ctr[BTS_CTR_CHAN_RF_FAIL].current,
- bts->bts_ctrs->ctr[BTS_CTR_CHAN_RLL_ERR].current,
- VTY_NEWLINE);
- vty_out(vty, " BTS failures : %"PRIu64" OML, %"PRIu64" RSL%s",
- bts->bts_ctrs->ctr[BTS_CTR_BTS_OML_FAIL].current,
- bts->bts_ctrs->ctr[BTS_CTR_BTS_RSL_FAIL].current,
- VTY_NEWLINE);
-
- vty_out_stat_item_group(vty, " ", bts->bts_statg);
-
- bts_dump_vty_features(vty, bts);
-}
-
DEFUN(show_bts, show_bts_cmd, "show bts [<0-255>]",
SHOW_STR "Display information about a BTS\n"
"BTS number\n")
@@ -654,479 +341,6 @@ DEFUN(show_rejected_bts, show_rejected_bts_cmd, "show rejected-bts",
return CMD_SUCCESS;
}
-/* utility functions */
-static void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line,
- const char *ts, const char *ss)
-{
- e1_link->e1_nr = atoi(line);
- e1_link->e1_ts = atoi(ts);
- if (!strcmp(ss, "full"))
- e1_link->e1_ts_ss = 255;
- else
- e1_link->e1_ts_ss = atoi(ss);
-}
-
-static void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link,
- const char *prefix)
-{
- if (!e1_link->e1_ts)
- return;
-
- if (e1_link->e1_ts_ss == 255)
- vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s",
- prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE);
- else
- vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s",
- prefix, e1_link->e1_nr, e1_link->e1_ts,
- e1_link->e1_ts_ss, VTY_NEWLINE);
-}
-
-
-static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts)
-{
- vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE);
- if (ts->tsc != -1)
- vty_out(vty, " training_sequence_code %u%s", ts->tsc, VTY_NEWLINE);
- if (ts->pchan_from_config != GSM_PCHAN_NONE)
- vty_out(vty, " phys_chan_config %s%s",
- gsm_pchan_name(ts->pchan_from_config), VTY_NEWLINE);
- vty_out(vty, " hopping enabled %u%s",
- ts->hopping.enabled, VTY_NEWLINE);
- if (ts->hopping.enabled) {
- unsigned int i;
- vty_out(vty, " hopping sequence-number %u%s",
- ts->hopping.hsn, VTY_NEWLINE);
- vty_out(vty, " hopping maio %u%s",
- ts->hopping.maio, VTY_NEWLINE);
- for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) {
- if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i))
- continue;
- vty_out(vty, " hopping arfcn add %u%s",
- i, VTY_NEWLINE);
- }
- }
- config_write_e1_link(vty, &ts->e1_link, " ");
-
- if (ts->trx->bts->model->config_write_ts)
- ts->trx->bts->model->config_write_ts(vty, ts);
-}
-
-static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx)
-{
- int i;
-
- vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
- if (trx->description)
- vty_out(vty, " description %s%s", trx->description,
- VTY_NEWLINE);
- vty_out(vty, " rf_locked %u%s",
- trx->mo.nm_state.administrative == NM_STATE_LOCKED ? 1 : 0,
- VTY_NEWLINE);
- vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE);
- vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE);
- vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE);
- config_write_e1_link(vty, &trx->rsl_e1_link, " rsl ");
- vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE);
-
- if (trx->bts->model->config_write_trx)
- trx->bts->model->config_write_trx(vty, trx);
-
- for (i = 0; i < TRX_NR_TS; i++)
- config_write_ts_single(vty, &trx->ts[i]);
-}
-
-static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts)
-{
- unsigned int i;
- vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode),
- VTY_NEWLINE);
- if (bts->gprs.mode == BTS_GPRS_NONE)
- return;
-
- vty_out(vty, " gprs routing area %u%s", bts->gprs.rac,
- VTY_NEWLINE);
- vty_out(vty, " gprs network-control-order nc%u%s",
- bts->gprs.net_ctrl_ord, VTY_NEWLINE);
- if (!bts->gprs.ctrl_ack_type_use_block)
- vty_out(vty, " gprs control-ack-type-rach%s", VTY_NEWLINE);
- vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci,
- VTY_NEWLINE);
- for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++)
- vty_out(vty, " gprs cell timer %s %u%s",
- get_value_string(gprs_bssgp_cfg_strs, i),
- bts->gprs.cell.timer[i], VTY_NEWLINE);
- vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei,
- VTY_NEWLINE);
- for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++)
- vty_out(vty, " gprs ns timer %s %u%s",
- get_value_string(gprs_ns_timer_strs, i),
- bts->gprs.nse.timer[i], VTY_NEWLINE);
- for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
- struct gsm_bts_gprs_nsvc *nsvc =
- &bts->gprs.nsvc[i];
- struct osmo_sockaddr_str remote = {};
- uint16_t port;
-
- vty_out(vty, " gprs nsvc %u nsvci %u%s", i,
- nsvc->nsvci, VTY_NEWLINE);
-
- vty_out(vty, " gprs nsvc %u local udp port %u%s", i,
- nsvc->local_port, VTY_NEWLINE);
-
- if (osmo_sockaddr_str_from_sockaddr(&remote, &nsvc->remote.u.sas) ||
- remote.af != AF_UNSPEC) {
- vty_out(vty, " gprs nsvc %u remote ip %s%s", i,
- remote.ip, VTY_NEWLINE);
- }
-
- /* Can't use remote.port because it's only valid when family != AF_UNSPEC, but the
- * port can be even configured when the IP isn't */
- port = osmo_htons(nsvc->remote.u.sin.sin_port);
- if (port)
- vty_out(vty, " gprs nsvc %u remote udp port %u%s", i,
- port, VTY_NEWLINE);
- }
-
- /* EGPRS specific parameters */
- if (bts->gprs.mode == BTS_GPRS_EGPRS) {
- if (bts->gprs.egprs_pkt_chan_request)
- vty_out(vty, " gprs egprs-packet-channel-request%s", VTY_NEWLINE);
- }
-}
-
-/* Write the model data if there is one */
-static void config_write_bts_model(struct vty *vty, struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
-
- if (!bts->model)
- return;
-
- if (bts->model->config_write_bts)
- bts->model->config_write_bts(vty, bts);
-
- llist_for_each_entry(trx, &bts->trx_list, list)
- config_write_trx_single(vty, trx);
-}
-
-static void write_amr_modes(struct vty *vty, const char *prefix,
- const char *name, struct amr_mode *modes, int num)
-{
- int i;
-
- vty_out(vty, " %s threshold %s", prefix, name);
- for (i = 0; i < num - 1; i++)
- vty_out(vty, " %d", modes[i].threshold);
- vty_out(vty, "%s", VTY_NEWLINE);
- vty_out(vty, " %s hysteresis %s", prefix, name);
- for (i = 0; i < num - 1; i++)
- vty_out(vty, " %d", modes[i].hysteresis);
- vty_out(vty, "%s", VTY_NEWLINE);
-}
-
-static void config_write_bts_amr(struct vty *vty, struct gsm_bts *bts,
- struct amr_multirate_conf *mr, int full)
-{
- struct gsm48_multi_rate_conf *mr_conf;
- const char *prefix = (full) ? "amr tch-f" : "amr tch-h";
- int i, num;
-
- if (!(mr->gsm48_ie[1]))
- return;
-
- mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
-
- num = 0;
- vty_out(vty, " %s modes", prefix);
- for (i = 0; i < ((full) ? 8 : 6); i++) {
- if ((mr->gsm48_ie[1] & (1 << i))) {
- vty_out(vty, " %d", i);
- num++;
- }
- }
- vty_out(vty, "%s", VTY_NEWLINE);
- if (num > 4)
- num = 4;
- if (num > 1) {
- write_amr_modes(vty, prefix, "ms", mr->ms_mode, num);
- write_amr_modes(vty, prefix, "bts", mr->bts_mode, num);
- }
- vty_out(vty, " %s start-mode ", prefix);
- if (mr_conf->icmi) {
- num = 0;
- for (i = 0; i < ((full) ? 8 : 6) && num < 4; i++) {
- if ((mr->gsm48_ie[1] & (1 << i)))
- num++;
- if (mr_conf->smod == num - 1) {
- vty_out(vty, "%d%s", num, VTY_NEWLINE);
- break;
- }
- }
- } else
- vty_out(vty, "auto%s", VTY_NEWLINE);
-}
-
-static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
-{
- int i;
- uint8_t tmp;
-
- vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE);
- vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE);
- if (bts->description)
- vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE);
- vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE);
- vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE);
- vty_out(vty, " location_area_code %u%s", bts->location_area_code,
- VTY_NEWLINE);
- if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
- vty_out(vty, " dtx uplink%s%s",
- (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? "" : " force",
- VTY_NEWLINE);
- if (bts->dtxd)
- vty_out(vty, " dtx downlink%s", VTY_NEWLINE);
- vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE);
- vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE);
- vty_out(vty, " cell reselection hysteresis %u%s",
- bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
- vty_out(vty, " rxlev access min %u%s",
- bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE);
-
- if (bts->si_common.cell_ro_sel_par.present) {
- struct gsm48_si_selection_params *sp;
- sp = &bts->si_common.cell_ro_sel_par;
-
- if (sp->cbq)
- vty_out(vty, " cell bar qualify %u%s",
- sp->cbq, VTY_NEWLINE);
-
- if (sp->cell_resel_off)
- vty_out(vty, " cell reselection offset %u%s",
- sp->cell_resel_off*2, VTY_NEWLINE);
-
- if (sp->temp_offs == 7)
- vty_out(vty, " temporary offset infinite%s",
- VTY_NEWLINE);
- else if (sp->temp_offs)
- vty_out(vty, " temporary offset %u%s",
- sp->temp_offs*10, VTY_NEWLINE);
-
- if (sp->penalty_time == 31)
- vty_out(vty, " penalty time reserved%s",
- VTY_NEWLINE);
- else if (sp->penalty_time)
- vty_out(vty, " penalty time %u%s",
- (sp->penalty_time*20)+20, VTY_NEWLINE);
- }
-
- if (gsm_bts_get_radio_link_timeout(bts) < 0)
- vty_out(vty, " radio-link-timeout infinite%s", VTY_NEWLINE);
- else
- vty_out(vty, " radio-link-timeout %d%s",
- gsm_bts_get_radio_link_timeout(bts), VTY_NEWLINE);
-
- vty_out(vty, " channel allocator %s%s",
- bts->chan_alloc_reverse ? "descending" : "ascending",
- VTY_NEWLINE);
- vty_out(vty, " rach tx integer %u%s",
- bts->si_common.rach_control.tx_integer, VTY_NEWLINE);
- vty_out(vty, " rach max transmission %u%s",
- rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
- VTY_NEWLINE);
-
- vty_out(vty, " channel-description attach %u%s",
- bts->si_common.chan_desc.att, VTY_NEWLINE);
- vty_out(vty, " channel-description bs-pa-mfrms %u%s",
- bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE);
- vty_out(vty, " channel-description bs-ag-blks-res %u%s",
- bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE);
-
- if (bts->ccch_load_ind_thresh != 10)
- vty_out(vty, " ccch load-indication-threshold %u%s",
- bts->ccch_load_ind_thresh, VTY_NEWLINE);
- if (bts->rach_b_thresh != -1)
- vty_out(vty, " rach nm busy threshold %u%s",
- bts->rach_b_thresh, VTY_NEWLINE);
- if (bts->rach_ldavg_slots != -1)
- vty_out(vty, " rach nm load average %u%s",
- bts->rach_ldavg_slots, VTY_NEWLINE);
- if (bts->si_common.rach_control.cell_bar)
- vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
- if ((bts->si_common.rach_control.t2 & 0x4) == 0)
- vty_out(vty, " rach emergency call allowed 1%s", VTY_NEWLINE);
- if ((bts->si_common.rach_control.t3) != 0)
- for (i = 0; i < 8; i++)
- if (bts->si_common.rach_control.t3 & (0x1 << i))
- vty_out(vty, " rach access-control-class %d barred%s", i, VTY_NEWLINE);
- if ((bts->si_common.rach_control.t2 & 0xfb) != 0)
- for (i = 0; i < 8; i++)
- if ((i != 2) && (bts->si_common.rach_control.t2 & (0x1 << i)))
- vty_out(vty, " rach access-control-class %d barred%s", i+8, VTY_NEWLINE);
- if (bts->acc_mgr.len_allowed_adm < 10)
- vty_out(vty, " access-control-class-rotate %" PRIu8 "%s", bts->acc_mgr.len_allowed_adm, VTY_NEWLINE);
- if (bts->acc_mgr.rotation_time_sec != ACC_MGR_QUANTUM_DEFAULT)
- vty_out(vty, " access-control-class-rotate-quantum %" PRIu32 "%s", bts->acc_mgr.rotation_time_sec, VTY_NEWLINE);
- vty_out(vty, " %saccess-control-class-ramping%s", acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "no ", VTY_NEWLINE);
- if (acc_ramp_is_enabled(&bts->acc_ramp)) {
- vty_out(vty, " access-control-class-ramping-step-interval %u%s",
- acc_ramp_get_step_interval(&bts->acc_ramp), VTY_NEWLINE);
- vty_out(vty, " access-control-class-ramping-step-size %u%s", acc_ramp_get_step_size(&bts->acc_ramp),
- VTY_NEWLINE);
- vty_out(vty, " access-control-class-ramping-chan-load %u %u%s",
- bts->acc_ramp.chan_load_lower_threshold, bts->acc_ramp.chan_load_upper_threshold, VTY_NEWLINE);
- }
- if (!bts->si_unused_send_empty)
- vty_out(vty, " no system-information unused-send-empty%s", VTY_NEWLINE);
- for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) {
- if (bts->si_mode_static & (1 << i)) {
- vty_out(vty, " system-information %s mode static%s",
- get_value_string(osmo_sitype_strs, i), VTY_NEWLINE);
- vty_out(vty, " system-information %s static %s%s",
- get_value_string(osmo_sitype_strs, i),
- osmo_hexdump_nospc(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN),
- VTY_NEWLINE);
- }
- }
- vty_out(vty, " early-classmark-sending %s%s",
- bts->early_classmark_allowed ? "allowed" : "forbidden", VTY_NEWLINE);
- vty_out(vty, " early-classmark-sending-3g %s%s",
- bts->early_classmark_allowed_3g ? "allowed" : "forbidden", VTY_NEWLINE);
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- vty_out(vty, " ipa unit-id %u %u%s",
- bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
- if (bts->ip_access.rsl_ip) {
- struct in_addr ia;
- ia.s_addr = htonl(bts->ip_access.rsl_ip);
- vty_out(vty, " ipa rsl-ip %s%s", inet_ntoa(ia),
- VTY_NEWLINE);
- }
- vty_out(vty, " oml ipa stream-id %u line %u%s",
- bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE);
- break;
- case GSM_BTS_TYPE_NOKIA_SITE:
- vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE);
- vty_out(vty, " nokia_site no-local-rel-conf %d%s",
- bts->nokia.no_loc_rel_cnf, VTY_NEWLINE);
- vty_out(vty, " nokia_site bts-reset-timer %d%s", bts->nokia.bts_reset_timer_cnf, VTY_NEWLINE);
- /* fall through: Nokia requires "oml e1" parameters also */
- default:
- config_write_e1_link(vty, &bts->oml_e1_link, " oml ");
- vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE);
- break;
- }
-
- /* if we have a limit, write it */
- if (bts->paging.free_chans_need >= 0)
- vty_out(vty, " paging free %d%s", bts->paging.free_chans_need, VTY_NEWLINE);
-
- vty_out(vty, " neighbor-list mode %s%s",
- get_value_string(bts_neigh_mode_strs, bts->neigh_list_manual_mode), VTY_NEWLINE);
- if (bts->neigh_list_manual_mode != NL_MODE_AUTOMATIC) {
- for (i = 0; i < 1024; i++) {
- if (bitvec_get_bit_pos(&bts->si_common.neigh_list, i))
- vty_out(vty, " neighbor-list add arfcn %u%s",
- i, VTY_NEWLINE);
- }
- }
- if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) {
- for (i = 0; i < 1024; i++) {
- if (bitvec_get_bit_pos(&bts->si_common.si5_neigh_list, i))
- vty_out(vty, " si5 neighbor-list add arfcn %u%s",
- i, VTY_NEWLINE);
- }
- }
-
- for (i = 0; i < MAX_EARFCN_LIST; i++) {
- struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- if (e->arfcn[i] != OSMO_EARFCN_INVALID) {
- vty_out(vty, " si2quater neighbor-list add earfcn %u "
- "thresh-hi %u", e->arfcn[i], e->thresh_hi);
-
- vty_out(vty, " thresh-lo %u",
- e->thresh_lo_valid ? e->thresh_lo : 32);
-
- vty_out(vty, " prio %u",
- e->prio_valid ? e->prio : 8);
-
- vty_out(vty, " qrxlv %u",
- e->qrxlm_valid ? e->qrxlm : 32);
-
- tmp = e->meas_bw[i];
- vty_out(vty, " meas %u",
- (tmp != OSMO_EARFCN_MEAS_INVALID) ? tmp : 8);
-
- vty_out(vty, "%s", VTY_NEWLINE);
- }
- }
-
- for (i = 0; i < bts->si_common.uarfcn_length; i++) {
- vty_out(vty, " si2quater neighbor-list add uarfcn %u %u %u%s",
- bts->si_common.data.uarfcn_list[i],
- bts->si_common.data.scramble_list[i] & ~(1 << 9),
- (bts->si_common.data.scramble_list[i] >> 9) & 1,
- VTY_NEWLINE);
- }
-
- neighbor_ident_vty_write(vty, " ", bts);
-
- vty_out(vty, " codec-support fr");
- if (bts->codec.hr)
- vty_out(vty, " hr");
- if (bts->codec.efr)
- vty_out(vty, " efr");
- if (bts->codec.amr)
- vty_out(vty, " amr");
- vty_out(vty, "%s", VTY_NEWLINE);
-
- config_write_bts_amr(vty, bts, &bts->mr_full, 1);
- config_write_bts_amr(vty, bts, &bts->mr_half, 0);
-
- config_write_bts_gprs(vty, bts);
-
- if (bts->excl_from_rf_lock)
- vty_out(vty, " rf-lock-exclude%s", VTY_NEWLINE);
-
- if (bts->force_combined_si_set)
- vty_out(vty, " %sforce-combined-si%s",
- bts->force_combined_si ? "" : "no ", VTY_NEWLINE);
-
- for (i = 0; i < ARRAY_SIZE(bts->depends_on); ++i) {
- int j;
-
- if (bts->depends_on[i] == 0)
- continue;
-
- for (j = 0; j < sizeof(bts->depends_on[i]) * 8; ++j) {
- int bts_nr;
-
- if ((bts->depends_on[i] & (1<<j)) == 0)
- continue;
-
- bts_nr = (i * sizeof(bts->depends_on[i]) * 8) + j;
- vty_out(vty, " depends-on-bts %d%s", bts_nr, VTY_NEWLINE);
- }
- }
- if (bts->pcu_sock_path)
- vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE);
-
- ho_vty_write_bts(vty, bts);
-
- config_write_bts_model(vty, bts);
-}
-
-static int config_write_bts(struct vty *v)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(v);
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &gsmnet->bts_list, list)
- config_write_bts_single(v, bts);
-
- return CMD_SUCCESS;
-}
-
static int config_write_net(struct vty *vty)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
@@ -1192,38 +406,10 @@ static int config_write_net(struct vty *vty)
vty_out(vty, "%s", VTY_NEWLINE);
}
- return CMD_SUCCESS;
-}
+ neighbor_ident_vty_write_network(vty, " ");
+ mgcp_client_pool_config_write(vty, " ");
-static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx, bool print_rsl, bool show_connected)
-{
- if (show_connected && !trx->rsl_link)
- return;
-
- if (!show_connected && trx->rsl_link)
- return;
-
- vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s",
- trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE);
- vty_out(vty, "Description: %s%s",
- trx->description ? trx->description : "(null)", VTY_NEWLINE);
- vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, "
- "resulting BS power: %d dBm%s",
- trx->nominal_power, trx->max_power_red,
- trx->nominal_power - trx->max_power_red, VTY_NEWLINE);
- vty_out(vty, " Radio Carrier NM State: ");
- net_dump_nmstate(vty, &trx->mo.nm_state);
- if (print_rsl)
- vty_out(vty, " RSL State: %s%s", trx->rsl_link? "connected" : "disconnected", VTY_NEWLINE);
- vty_out(vty, " Baseband Transceiver NM State: ");
- net_dump_nmstate(vty, &trx->bb_transc.mo.nm_state);
- if (is_ipaccess_bts(trx->bts)) {
- vty_out(vty, " ip.access stream ID: 0x%02x ", trx->rsl_tei);
- e1isl_dump_vty_tcp(vty, trx->rsl_link);
- } else {
- vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
- e1isl_dump_vty(vty, trx->rsl_link);
- }
+ return CMD_SUCCESS;
}
static void trx_dump_vty_all(struct vty *vty, struct gsm_bts_trx *trx)
@@ -1282,56 +468,6 @@ DEFUN(show_trx,
return CMD_SUCCESS;
}
-/* call vty_out() to print a string like " as TCH/H" for dynamic timeslots.
- * Don't do anything if the ts is not dynamic. */
-static void vty_out_dyn_ts_status(struct vty *vty, struct gsm_bts_trx_ts *ts)
-{
- enum gsm_phys_chan_config target;
- if (ts_is_pchan_switching(ts, &target)) {
- vty_out(vty, " switching %s -> %s", gsm_pchan_name(ts->pchan_is),
- gsm_pchan_name(target));
- } else if (ts->pchan_is != ts->pchan_on_init) {
- vty_out(vty, " as %s", gsm_pchan_name(ts->pchan_is));
- }
-}
-
-static void vty_out_dyn_ts_details(struct vty *vty, struct gsm_bts_trx_ts *ts)
-{
- /* show dyn TS details, if applicable */
- switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- vty_out(vty, " Osmocom Dyn TS:");
- vty_out_dyn_ts_status(vty, ts);
- vty_out(vty, VTY_NEWLINE);
- break;
- case GSM_PCHAN_TCH_F_PDCH:
- vty_out(vty, " IPACC Dyn PDCH TS:");
- vty_out_dyn_ts_status(vty, ts);
- vty_out(vty, VTY_NEWLINE);
- break;
- default:
- /* no dyn ts */
- break;
- }
-}
-
-static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts)
-{
- vty_out(vty, "BTS %u, TRX %u, Timeslot %u, phys cfg %s (active %s)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr,
- gsm_pchan_name(ts->pchan_on_init),
- gsm_pchan_name(ts->pchan_is));
- if (ts->pchan_is != ts->pchan_on_init)
- vty_out(vty, " (%s mode)", gsm_pchan_name(ts->pchan_is));
- vty_out(vty, ", TSC %u%s NM State: ", gsm_ts_tsc(ts), VTY_NEWLINE);
- vty_out_dyn_ts_details(vty, ts);
- net_dump_nmstate(vty, &ts->mo.nm_state);
- if (!is_ipaccess_bts(ts->trx->bts))
- vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s",
- ts->e1_link.e1_nr, ts->e1_link.e1_ts,
- ts->e1_link.e1_ts_ss, VTY_NEWLINE);
-}
-
DEFUN(show_ts,
show_ts_cmd,
"show timeslot [<0-255>] [<0-255>] [<0-7>]",
@@ -1408,7 +544,7 @@ DEFUN(show_ts,
return CMD_SUCCESS;
}
-static void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub)
+void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub)
{
if (strlen(bsub->imsi))
vty_out(vty, " IMSI: %s%s", bsub->imsi, VTY_NEWLINE);
@@ -1418,39 +554,6 @@ static void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub)
vty_out(vty, " Use count: %s%s", osmo_use_count_to_str_c(OTC_SELECT, &bsub->use_count), VTY_NEWLINE);
}
-static void meas_rep_dump_uni_vty(struct vty *vty,
- struct gsm_meas_rep_unidir *mru,
- const char *prefix,
- const char *dir)
-{
- vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ",
- prefix, dir, rxlev2dbm(mru->full.rx_lev),
- dir, rxlev2dbm(mru->sub.rx_lev));
- vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s",
- dir, mru->full.rx_qual, dir, mru->sub.rx_qual,
- VTY_NEWLINE);
-}
-
-static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr,
- const char *prefix)
-{
- vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE);
- vty_out(vty, "%s Flags: %s%s%s%s%s", prefix,
- mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "",
- mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "",
- mr->flags & MEAS_REP_F_FPC ? "FPC " : "",
- mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ",
- VTY_NEWLINE);
- if (mr->flags & MEAS_REP_F_MS_TO)
- vty_out(vty, "%s MS Timing Offset: %d%s", prefix, mr->ms_timing_offset, VTY_NEWLINE);
- if (mr->flags & MEAS_REP_F_MS_L1)
- vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s",
- prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE);
- if (mr->flags & MEAS_REP_F_DL_VALID)
- meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl");
- meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul");
-}
-
static inline void print_all_trx_ext(struct vty *vty, bool show_connected)
{
struct gsm_network *net = gsmnet_from_vty(vty);
@@ -1479,88 +582,13 @@ DEFUN(show_trx_con,
return CMD_SUCCESS;
}
-static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan)
-{
- int idx;
-
- vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s",
- lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
- lchan->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE);
- vty_out_dyn_ts_details(vty, lchan->ts);
- vty_out(vty, " Connection: %u, State: %s%s%s%s",
- lchan->conn ? 1: 0, lchan_state_name(lchan),
- lchan->fi && lchan->fi->state == LCHAN_ST_BORKEN ? " Error reason: " : "",
- lchan->fi && lchan->fi->state == LCHAN_ST_BORKEN ? lchan->last_error : "",
- VTY_NEWLINE);
- vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s",
- lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red
- - lchan->bs_power*2,
- ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
- VTY_NEWLINE);
- vty_out(vty, " Channel Mode / Codec: %s%s",
- gsm48_chan_mode_name(lchan->tch_mode),
- VTY_NEWLINE);
- if (lchan->conn && lchan->conn->bsub) {
- vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
- bsc_subscr_dump_vty(vty, lchan->conn->bsub);
- } else
- vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
- if (is_ipaccess_bts(lchan->ts->trx->bts)) {
- struct in_addr ia;
- if (lchan->abis_ip.bound_ip) {
- ia.s_addr = htonl(lchan->abis_ip.bound_ip);
- vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s",
- inet_ntoa(ia), lchan->abis_ip.bound_port,
- lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id,
- VTY_NEWLINE);
- }
- if (lchan->abis_ip.connect_ip) {
- ia.s_addr = htonl(lchan->abis_ip.connect_ip);
- vty_out(vty, " Conn. IP: %s Port %u RTP_TYPE=%u SPEECH_MODE=0x%02x%s",
- inet_ntoa(ia), lchan->abis_ip.connect_port,
- lchan->abis_ip.rtp_payload, lchan->abis_ip.speech_mode,
- VTY_NEWLINE);
- }
-
- }
-
- /* we want to report the last measurement report */
- idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
- lchan->meas_rep_idx, 1);
- meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " ");
-}
-
-static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan)
-{
- struct gsm_meas_rep *mr;
- int idx;
-
- /* we want to report the last measurement report */
- idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
- lchan->meas_rep_idx, 1);
- mr = &lchan->meas_rep[idx];
-
- vty_out(vty, "BTS %u, TRX %u, Timeslot %u %s",
- lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
- gsm_pchan_name(lchan->ts->pchan_on_init));
- vty_out_dyn_ts_status(vty, lchan->ts);
- vty_out(vty, ", Lchan %u, Type %s, State %s - "
- "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s",
- lchan->nr,
- gsm_lchant_name(lchan->type), lchan_state_name(lchan),
- mr->ms_l1.pwr,
- rxlev2dbm(mr->dl.full.rx_lev),
- rxlev2dbm(mr->ul.full.rx_lev),
- VTY_NEWLINE);
-}
-
static int dump_lchan_trx_ts(struct gsm_bts_trx_ts *ts, struct vty *vty,
void (*dump_cb)(struct vty *, struct gsm_lchan *),
bool all)
{
struct gsm_lchan *lchan;
- ts_for_each_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
if (lchan_state_is(lchan, LCHAN_ST_UNUSED) && all == false)
continue;
dump_cb(vty, lchan);
@@ -1741,26 +769,38 @@ DEFUN(show_subscr_conn,
return CMD_SUCCESS;
}
-static int trigger_ho_or_as(struct vty *vty, struct gsm_lchan *from_lchan, struct gsm_bts *to_bts)
+static int trigger_as(struct vty *vty, struct gsm_lchan *from_lchan, struct gsm_lchan *to_lchan)
{
- if (!to_bts || from_lchan->ts->trx->bts == to_bts) {
- LOGP(DHO, LOGL_NOTICE, "%s Manually triggering Assignment from VTY\n",
- gsm_lchan_name(from_lchan));
- to_bts = from_lchan->ts->trx->bts;
- } else
- LOGP(DHO, LOGL_NOTICE, "%s (ARFCN %u) --> BTS %u Manually triggering Handover from VTY\n",
- gsm_lchan_name(from_lchan), from_lchan->ts->trx->arfcn, to_bts->nr);
- {
- struct handover_out_req req = {
- .from_hodec_id = HODEC_USER,
- .old_lchan = from_lchan,
- .target_nik = *bts_ident_key(to_bts),
- };
- handover_request(&req);
+ LOG_LCHAN(from_lchan, LOGL_NOTICE, "Manually triggering Assignment from VTY\n");
+ if (!to_lchan) {
+ struct gsm_bts *bts = from_lchan->ts->trx->bts;
+ to_lchan = lchan_select_by_type(bts, from_lchan->type,
+ SELECT_FOR_ASSIGNMENT,
+ from_lchan);
+ vty_out(vty, "Error: cannot find free lchan of type %s%s",
+ gsm_chan_t_name(from_lchan->type), VTY_NEWLINE);
+ }
+ if (reassignment_request_to_lchan(ASSIGN_FOR_VTY, from_lchan, to_lchan, -1, -1)) {
+ vty_out(vty, "Error: not allowed to start assignment for %s%s",
+ gsm_lchan_name(from_lchan), VTY_NEWLINE);
+ return CMD_WARNING;
}
return CMD_SUCCESS;
}
+static int trigger_ho(struct vty *vty, struct gsm_lchan *from_lchan, struct gsm_bts *to_bts)
+{
+ struct handover_out_req req = {
+ .from_hodec_id = HODEC_USER,
+ .old_lchan = from_lchan,
+ };
+ bts_cell_ab(&req.target_cell_ab, to_bts);
+ LOGP(DHO, LOGL_NOTICE, "%s (ARFCN %u) --> BTS %u Manually triggering Handover from VTY\n",
+ gsm_lchan_name(from_lchan), from_lchan->ts->trx->arfcn, to_bts->nr);
+ handover_request(&req);
+ return CMD_SUCCESS;
+}
+
static int ho_or_as(struct vty *vty, const char *argv[], int argc)
{
struct gsm_network *net = gsmnet_from_vty(vty);
@@ -1771,11 +811,10 @@ static int ho_or_as(struct vty *vty, const char *argv[], int argc)
unsigned int trx_nr = atoi(argv[1]);
unsigned int ts_nr = atoi(argv[2]);
unsigned int ss_nr = atoi(argv[3]);
- unsigned int bts_nr_new;
const char *action;
if (argc > 4) {
- bts_nr_new = atoi(argv[4]);
+ unsigned int bts_nr_new = atoi(argv[4]);
/* Lookup the BTS where we want to handover to */
llist_for_each_entry(bts, &net->bts_list, list) {
@@ -1804,7 +843,10 @@ static int ho_or_as(struct vty *vty, const char *argv[], int argc)
conn->lchan->ts->nr == ts_nr && conn->lchan->nr == ss_nr) {
vty_out(vty, "starting %s for lchan %s...%s", action, conn->lchan->name, VTY_NEWLINE);
lchan_dump_full_vty(vty, conn->lchan);
- return trigger_ho_or_as(vty, conn->lchan, new_bts);
+ if (new_bts)
+ return trigger_ho(vty, conn->lchan, new_bts);
+ else
+ return trigger_as(vty, conn->lchan, NULL);
}
}
@@ -1814,6 +856,28 @@ static int ho_or_as(struct vty *vty, const char *argv[], int argc)
return CMD_WARNING;
}
+/* tsc_set and tsc: -1 to automatically determine which TSC Set / which TSC to use. */
+static int trigger_vamos_mode_modify(struct vty *vty, struct gsm_lchan *lchan, bool vamos, int tsc_set, int tsc)
+{
+ struct lchan_modify_info info = {
+ .modify_for = MODIFY_FOR_VTY,
+ .ch_mode_rate = lchan->current_ch_mode_rate,
+ .requires_voice_stream = (lchan->fi_rtp != NULL),
+ .vamos = vamos,
+ .tsc_set = {
+ .present = (tsc_set >= 0),
+ .val = tsc_set,
+ },
+ .tsc = {
+ .present = (tsc >= 0),
+ .val = tsc,
+ },
+ };
+
+ lchan_mode_modify(lchan, &info);
+ return CMD_SUCCESS;
+}
+
#define MANUAL_HANDOVER_STR "Manually trigger handover (for debugging)\n"
#define MANUAL_ASSIGNMENT_STR "Manually trigger assignment (for debugging)\n"
@@ -1855,7 +919,7 @@ static struct gsm_lchan *find_used_voice_lchan(struct vty *vty, int random_idx)
if (ts->fi->state != TS_ST_IN_USE)
continue;
- ts_for_each_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
if (lchan_state_is(lchan, LCHAN_ST_ESTABLISHED)
&& (lchan->type == GSM_LCHAN_TCH_F
|| lchan->type == GSM_LCHAN_TCH_H)) {
@@ -1897,18 +961,20 @@ static struct gsm_bts *find_other_bts_with_free_slots(struct vty *vty, struct gs
continue;
llist_for_each_entry(trx, &bts->trx_list, list) {
- struct gsm_lchan *lchan = lchan_select_by_type(bts, free_type);
+ struct gsm_lchan *lchan = lchan_select_by_type(bts, free_type,
+ SELECT_FOR_HANDOVER,
+ NULL);
if (!lchan)
continue;
vty_out(vty, "Found unused %s slot: %s%s",
- gsm_lchant_name(free_type), gsm_lchan_name(lchan), VTY_NEWLINE);
+ gsm_chan_t_name(free_type), gsm_lchan_name(lchan), VTY_NEWLINE);
lchan_dump_full_vty(vty, lchan);
return bts;
}
}
vty_out(vty, "%% Cannot find any BTS (other than BTS %u) with free %s lchan%s",
- not_this_bts? not_this_bts->nr : 255, gsm_lchant_name(free_type), VTY_NEWLINE);
+ not_this_bts ? not_this_bts->nr : 255, gsm_chan_t_name(free_type), VTY_NEWLINE);
return NULL;
}
@@ -1929,7 +995,7 @@ DEFUN(handover_any, handover_any_cmd,
if (!to_bts)
return CMD_WARNING;
- return trigger_ho_or_as(vty, from_lchan, to_bts);
+ return trigger_ho(vty, from_lchan, to_bts);
}
DEFUN(assignment_any, assignment_any_cmd,
@@ -1944,18 +1010,19 @@ DEFUN(assignment_any, assignment_any_cmd,
if (!from_lchan)
return CMD_WARNING;
- return trigger_ho_or_as(vty, from_lchan, NULL);
+ return trigger_as(vty, from_lchan, NULL);
}
DEFUN(handover_any_to_arfcn_bsic, handover_any_to_arfcn_bsic_cmd,
- "handover any to " NEIGHBOR_IDENT_VTY_KEY_PARAMS,
+ "handover any to " CELL_AB_VTY_PARAMS,
MANUAL_HANDOVER_STR
"Pick any actively used TCH/F or TCH/H lchan to handover to another cell."
" This is likely to fail outside of a lab setup where you are certain that"
" all MS are able to see the target cell.\n"
"'to'\n"
- NEIGHBOR_IDENT_VTY_KEY_DOC)
+ CELL_AB_VTY_DOC)
{
+ struct cell_ab ab = {};
struct handover_out_req req;
struct gsm_lchan *from_lchan;
@@ -1968,12 +1035,8 @@ DEFUN(handover_any_to_arfcn_bsic, handover_any_to_arfcn_bsic_cmd,
.old_lchan = from_lchan,
};
- if (!neighbor_ident_bts_parse_key_params(vty, from_lchan->ts->trx->bts,
- argv, &req.target_nik)) {
- vty_out(vty, "%% BTS %u does not know about this neighbor%s",
- from_lchan->ts->trx->bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
+ neighbor_ident_vty_parse_arfcn_bsic(&ab, argv);
+ req.target_cell_ab = ab;
handover_request(&req);
return CMD_SUCCESS;
@@ -1981,7 +1044,7 @@ DEFUN(handover_any_to_arfcn_bsic, handover_any_to_arfcn_bsic_cmd,
static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag)
{
- vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE);
+ vty_out(vty, "Paging on BTS %u (%u request timeouts)%s", pag->bts->nr, pag->attempts, VTY_NEWLINE);
bsc_subscr_dump_vty(vty, pag->bsub);
}
@@ -1989,10 +1052,9 @@ static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts)
{
struct gsm_paging_request *pag;
- if (!bts->paging.bts)
- return;
-
- llist_for_each_entry(pag, &bts->paging.pending_requests, entry)
+ llist_for_each_entry(pag, &bts->paging.initial_req_list, entry)
+ paging_dump_vty(vty, pag);
+ llist_for_each_entry(pag, &bts->paging.retrans_req_list, entry)
paging_dump_vty(vty, pag);
}
@@ -2057,11 +1119,12 @@ DEFUN(show_paging_group,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_neci,
- cfg_net_neci_cmd,
- "neci (0|1)",
- "New Establish Cause Indication\n"
- "Don't set the NECI bit\n" "Set the NECI bit\n")
+DEFUN_USRATTR(cfg_net_neci,
+ cfg_net_neci_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "neci (0|1)",
+ "New Establish Cause Indication\n"
+ "Don't set the NECI bit\n" "Set the NECI bit\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
@@ -2070,13 +1133,14 @@ DEFUN(cfg_net_neci,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_pag_any_tch,
- cfg_net_pag_any_tch_cmd,
- "paging any use tch (0|1)",
- "Assign a TCH when receiving a Paging Any request\n"
- "Any Channel\n" "Use\n" "TCH\n"
- "Do not use TCH for Paging Request Any\n"
- "Do use TCH for Paging Request Any\n")
+DEFUN_USRATTR(cfg_net_pag_any_tch,
+ cfg_net_pag_any_tch_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "paging any use tch (0|1)",
+ "Assign a TCH when receiving a Paging Any request\n"
+ "Any Channel\n" "Use\n" "TCH\n"
+ "Do not use TCH for Paging Request Any\n"
+ "Do use TCH for Paging Request Any\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->pag_any_tch = atoi(argv[0]);
@@ -2094,7 +1158,7 @@ DEFUN_DEPRECATED(cfg_net_dtx,
return CMD_SUCCESS;
}
-#define NRI_STR "Mapping of Network Resource Indicators to this MSC, for MSC pooling\n"
+#define NRI_STR "Mapping of Network Resource Indicators, for MSC pooling\n"
#define NULL_NRI_STR "Define NULL-NRI values that cause re-assignment of an MS to a different MSC, for MSC pooling.\n"
#define NRI_FIRST_LAST_STR "First value of the NRI value range, should not surpass the configured 'nri bitlen'.\n" \
"Last value of the NRI value range, should not surpass the configured 'nri bitlen' and be larger than the" \
@@ -2106,22 +1170,25 @@ DEFUN_DEPRECATED(cfg_net_dtx,
LOGP(DMSC, LOGL_ERROR, "msc %d: " FORMAT "\n", MSC->nr, ##args); \
} while (0)
-DEFUN(cfg_net_nri_bitlen,
- cfg_net_nri_bitlen_cmd,
- "nri bitlen <1-15>",
- NRI_STR
- "Set number of bits that an NRI has, to extract from TMSI identities (always starting just after the TMSI's most significant octet).\n"
- "bit count (default: " OSMO_STRINGIFY_VAL(NRI_BITLEN_DEFAULT) ")\n")
+DEFUN_ATTR(cfg_net_nri_bitlen,
+ cfg_net_nri_bitlen_cmd,
+ "nri bitlen <1-15>",
+ NRI_STR
+ "Set number of bits that an NRI has, to extract from TMSI identities (always starting just after the TMSI's most significant octet).\n"
+ "bit count (default: " OSMO_STRINGIFY_VAL(OSMO_NRI_BITLEN_DEFAULT) ")\n",
+ CMD_ATTR_IMMEDIATE)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
gsmnet->nri_bitlen = atoi(argv[0]);
return CMD_SUCCESS;
}
-DEFUN(cfg_net_nri_null_add, cfg_net_nri_null_add_cmd,
- "nri null add <0-32767> [<0-32767>]",
- NRI_STR NULL_NRI_STR "Add NULL-NRI value (or range)\n"
- NRI_FIRST_LAST_STR)
+DEFUN_ATTR(cfg_net_nri_null_add,
+ cfg_net_nri_null_add_cmd,
+ "nri null add <0-32767> [<0-32767>]",
+ NRI_STR NULL_NRI_STR "Add NULL-NRI value (or range)\n"
+ NRI_FIRST_LAST_STR,
+ CMD_ATTR_IMMEDIATE)
{
int rc;
const char *message;
@@ -2135,10 +1202,12 @@ DEFUN(cfg_net_nri_null_add, cfg_net_nri_null_add_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_nri_null_del, cfg_net_nri_null_del_cmd,
- "nri null del <0-32767> [<0-32767>]",
- NRI_STR NULL_NRI_STR "Remove NRI value or range from the NRI mapping for this MSC\n"
- NRI_FIRST_LAST_STR)
+DEFUN_ATTR(cfg_net_nri_null_del,
+ cfg_net_nri_null_del_cmd,
+ "nri null del <0-32767> [<0-32767>]",
+ NRI_STR NULL_NRI_STR "Remove NRI value or range from the NRI mapping\n"
+ NRI_FIRST_LAST_STR,
+ CMD_ATTR_IMMEDIATE)
{
int rc;
const char *message;
@@ -2152,2592 +1221,6 @@ DEFUN(cfg_net_nri_null_del, cfg_net_nri_null_del_cmd,
return CMD_SUCCESS;
}
-/* per-BTS configuration */
-DEFUN(cfg_bts,
- cfg_bts_cmd,
- "bts <0-255>",
- "Select a BTS to configure\n"
- BTS_NR_STR)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- int bts_nr = atoi(argv[0]);
- struct gsm_bts *bts;
-
- if (bts_nr > gsmnet->num_bts) {
- vty_out(vty, "%% The next unused BTS number is %u%s",
- gsmnet->num_bts, VTY_NEWLINE);
- return CMD_WARNING;
- } else if (bts_nr == gsmnet->num_bts) {
- /* allocate a new one */
- bts = bsc_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN,
- HARDCODED_BSIC);
- osmo_stat_item_inc(gsmnet->bsc_statg->items[BSC_STAT_NUM_BTS_TOTAL], 1);
- } else
- bts = gsm_bts_num(gsmnet, bts_nr);
-
- if (!bts) {
- vty_out(vty, "%% Unable to allocate BTS %u%s",
- gsmnet->num_bts, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty->index = bts;
- vty->index_sub = &bts->description;
- vty->node = BTS_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_type,
- cfg_bts_type_cmd,
- "type TYPE", /* dynamically created */
- "Set the BTS type\n" "Type\n")
-{
- struct gsm_bts *bts = vty->index;
- int rc;
-
- rc = gsm_set_bts_type(bts, str2btstype(argv[0]));
- if (rc < 0)
- return CMD_WARNING;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_band,
- cfg_bts_band_cmd,
- "band BAND",
- "Set the frequency band of this BTS\n" "Frequency band\n")
-{
- struct gsm_bts *bts = vty->index;
- int band = gsm_band_parse(argv[0]);
-
- if (band < 0) {
- vty_out(vty, "%% BAND %d is not a valid GSM band%s",
- band, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->band = band;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_dtxu, cfg_bts_dtxu_cmd, "dtx uplink [force]",
- "Configure discontinuous transmission\n"
- "Enable Uplink DTX for this BTS\n"
- "MS 'shall' use DTXu instead of 'may' use (might not be supported by "
- "older phones).\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->dtxu = (argc > 0) ? GSM48_DTX_SHALL_BE_USED : GSM48_DTX_MAY_BE_USED;
- if (!is_ipaccess_bts(bts))
- vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration "
- "neither supported nor tested!%s", VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_dtxu, cfg_bts_no_dtxu_cmd, "no dtx uplink",
- NO_STR
- "Configure discontinuous transmission\n"
- "Disable Uplink DTX for this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_dtxd, cfg_bts_dtxd_cmd, "dtx downlink",
- "Configure discontinuous transmission\n"
- "Enable Downlink DTX for this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->dtxd = true;
- if (!is_ipaccess_bts(bts))
- vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration "
- "neither supported nor tested!%s", VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_dtxd, cfg_bts_no_dtxd_cmd, "no dtx downlink",
- NO_STR
- "Configure discontinuous transmission\n"
- "Disable Downlink DTX for this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->dtxd = false;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_ci,
- cfg_bts_ci_cmd,
- "cell_identity <0-65535>",
- "Set the Cell identity of this BTS\n" "Cell Identity\n")
-{
- struct gsm_bts *bts = vty->index;
- int ci = atoi(argv[0]);
-
- if (ci < 0 || ci > 0xffff) {
- vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s",
- ci, VTY_NEWLINE);
- return CMD_WARNING;
- }
- bts->cell_identity = ci;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_lac,
- cfg_bts_lac_cmd,
- "location_area_code <0-65535>",
- "Set the Location Area Code (LAC) of this BTS\n" "LAC\n")
-{
- struct gsm_bts *bts = vty->index;
- int lac = atoi(argv[0]);
-
- if (lac < 0 || lac > 0xffff) {
- vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s",
- lac, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) {
- vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s",
- lac, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->location_area_code = lac;
-
- return CMD_SUCCESS;
-}
-
-
-/* compatibility wrapper for old config files */
-DEFUN_HIDDEN(cfg_bts_tsc,
- cfg_bts_tsc_cmd,
- "training_sequence_code <0-7>",
- "Set the Training Sequence Code (TSC) of this BTS\n" "TSC\n")
-{
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_bsic,
- cfg_bts_bsic_cmd,
- "base_station_id_code <0-63>",
- "Set the Base Station Identity Code (BSIC) of this BTS\n"
- "BSIC of this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
- int bsic = atoi(argv[0]);
-
- if (bsic < 0 || bsic > 0x3f) {
- vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s",
- bsic, VTY_NEWLINE);
- return CMD_WARNING;
- }
- bts->bsic = bsic;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_unit_id,
- cfg_bts_unit_id_cmd,
- "ipa unit-id <0-65534> <0-255>",
- "Abis/IP specific options\n"
- "Set the IPA BTS Unit ID\n"
- "Unit ID (Site)\n"
- "Unit ID (BTS)\n")
-{
- struct gsm_bts *bts = vty->index;
- int site_id = atoi(argv[0]);
- int bts_id = atoi(argv[1]);
-
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->ip_access.site_id = site_id;
- bts->ip_access.bts_id = bts_id;
-
- return CMD_SUCCESS;
-}
-
-DEFUN_DEPRECATED(cfg_bts_unit_id,
- cfg_bts_deprecated_unit_id_cmd,
- "ip.access unit_id <0-65534> <0-255>",
- "Abis/IP specific options\n"
- "Set the IPA BTS Unit ID\n"
- "Unit ID (Site)\n"
- "Unit ID (BTS)\n");
-
-DEFUN(cfg_bts_rsl_ip,
- cfg_bts_rsl_ip_cmd,
- "ipa rsl-ip A.B.C.D",
- "Abis/IP specific options\n"
- "Set the IPA RSL IP Address of the BSC\n"
- "Destination IP address for RSL connection\n")
-{
- struct gsm_bts *bts = vty->index;
- struct in_addr ia;
-
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- inet_aton(argv[0], &ia);
- bts->ip_access.rsl_ip = ntohl(ia.s_addr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN_DEPRECATED(cfg_bts_rsl_ip,
- cfg_bts_deprecated_rsl_ip_cmd,
- "ip.access rsl-ip A.B.C.D",
- "Abis/IP specific options\n"
- "Set the IPA RSL IP Address of the BSC\n"
- "Destination IP address for RSL connection\n");
-
-#define NOKIA_STR "Nokia *Site related commands\n"
-
-DEFUN(cfg_bts_nokia_site_skip_reset,
- cfg_bts_nokia_site_skip_reset_cmd,
- "nokia_site skip-reset (0|1)",
- NOKIA_STR
- "Skip the reset step during bootstrap process of this BTS\n"
- "Do NOT skip the reset\n" "Skip the reset\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->type != GSM_BTS_TYPE_NOKIA_SITE) {
- vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->nokia.skip_reset = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_nokia_site_no_loc_rel_cnf,
- cfg_bts_nokia_site_no_loc_rel_cnf_cmd,
- "nokia_site no-local-rel-conf (0|1)",
- NOKIA_STR
- "Do not wait for RELease CONFirm message when releasing channel locally\n"
- "Wait for RELease CONFirm\n" "Do not wait for RELease CONFirm\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!is_nokia_bts(bts)) {
- vty_out(vty, "%% BTS is not of Nokia *Site type%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->nokia.no_loc_rel_cnf = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_nokia_site_bts_reset_timer_cnf,
- cfg_bts_nokia_site_bts_reset_timer_cnf_cmd,
- "nokia_site bts-reset-timer <15-100>",
- NOKIA_STR
- "The amount of time (in sec.) between BTS_RESET is sent,\n"
- "and the BTS is being bootstrapped.\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!is_nokia_bts(bts)) {
- vty_out(vty, "%% BTS is not of Nokia *Site type%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->nokia.bts_reset_timer_cnf = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-#define OML_STR "Organization & Maintenance Link\n"
-#define IPA_STR "A-bis/IP Specific Options\n"
-
-DEFUN(cfg_bts_stream_id,
- cfg_bts_stream_id_cmd,
- "oml ipa stream-id <0-255> line E1_LINE",
- OML_STR IPA_STR
- "Set the ipa Stream ID of the OML link of this BTS\n"
- "Stream Identifier\n" "Virtual E1 Line Number\n" "Virtual E1 Line Number\n")
-{
- struct gsm_bts *bts = vty->index;
- int stream_id = atoi(argv[0]), linenr = atoi(argv[1]);
-
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->oml_tei = stream_id;
- /* This is used by e1inp_bind_ops callback for each BTS model. */
- bts->oml_e1_link.e1_nr = linenr;
-
- return CMD_SUCCESS;
-}
-
-DEFUN_DEPRECATED(cfg_bts_stream_id,
- cfg_bts_deprecated_stream_id_cmd,
- "oml ip.access stream_id <0-255> line E1_LINE",
- OML_STR IPA_STR
- "Set the ip.access Stream ID of the OML link of this BTS\n"
- "Stream Identifier\n" "Virtual E1 Line Number\n" "Virtual E1 Line Number\n");
-
-#define OML_E1_STR OML_STR "OML E1/T1 Configuration\n"
-
-DEFUN(cfg_bts_oml_e1,
- cfg_bts_oml_e1_cmd,
- "oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
- OML_E1_STR
- "E1/T1 line number to be used for OML\n"
- "E1/T1 line number to be used for OML\n"
- "E1/T1 timeslot to be used for OML\n"
- "E1/T1 timeslot to be used for OML\n"
- "E1/T1 sub-slot to be used for OML\n"
- "Use E1/T1 sub-slot 0\n"
- "Use E1/T1 sub-slot 1\n"
- "Use E1/T1 sub-slot 2\n"
- "Use E1/T1 sub-slot 3\n"
- "Use full E1 slot 3\n"
- )
-{
- struct gsm_bts *bts = vty->index;
-
- parse_e1_link(&bts->oml_e1_link, argv[0], argv[1], argv[2]);
-
- return CMD_SUCCESS;
-}
-
-
-DEFUN(cfg_bts_oml_e1_tei,
- cfg_bts_oml_e1_tei_cmd,
- "oml e1 tei <0-63>",
- OML_E1_STR
- "Set the TEI to be used for OML\n"
- "TEI Number\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->oml_tei = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd,
- "channel allocator (ascending|descending)",
- "Channel Allocator\n" "Channel Allocator\n"
- "Allocate Timeslots and Transceivers in ascending order\n"
- "Allocate Timeslots and Transceivers in descending order\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!strcmp(argv[0], "ascending"))
- bts->chan_alloc_reverse = 0;
- else
- bts->chan_alloc_reverse = 1;
-
- return CMD_SUCCESS;
-}
-
-#define RACH_STR "Random Access Control Channel\n"
-
-DEFUN(cfg_bts_rach_tx_integer,
- cfg_bts_rach_tx_integer_cmd,
- "rach tx integer <0-15>",
- RACH_STR
- "Set the raw tx integer value in RACH Control parameters IE\n"
- "Set the raw tx integer value in RACH Control parameters IE\n"
- "Raw tx integer value in RACH Control parameters IE\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rach_max_trans,
- cfg_bts_rach_max_trans_cmd,
- "rach max transmission (1|2|4|7)",
- RACH_STR
- "Set the maximum number of RACH burst transmissions\n"
- "Set the maximum number of RACH burst transmissions\n"
- "Maximum number of 1 RACH burst transmissions\n"
- "Maximum number of 2 RACH burst transmissions\n"
- "Maximum number of 4 RACH burst transmissions\n"
- "Maximum number of 7 RACH burst transmissions\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0]));
- return CMD_SUCCESS;
-}
-
-#define CD_STR "Channel Description\n"
-
-DEFUN(cfg_bts_chan_desc_att,
- cfg_bts_chan_desc_att_cmd,
- "channel-description attach (0|1)",
- CD_STR
- "Set if attachment is required\n"
- "Attachment is NOT required\n"
- "Attachment is required (standard)\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->si_common.chan_desc.att = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-ALIAS_DEPRECATED(cfg_bts_chan_desc_att,
- cfg_bts_chan_dscr_att_cmd,
- "channel-descrption attach (0|1)",
- CD_STR
- "Set if attachment is required\n"
- "Attachment is NOT required\n"
- "Attachment is required (standard)\n");
-
-DEFUN(cfg_bts_chan_desc_bs_pa_mfrms,
- cfg_bts_chan_desc_bs_pa_mfrms_cmd,
- "channel-description bs-pa-mfrms <2-9>",
- CD_STR
- "Set number of multiframe periods for paging groups\n"
- "Number of multiframe periods for paging groups\n")
-{
- struct gsm_bts *bts = vty->index;
- int bs_pa_mfrms = atoi(argv[0]);
-
- bts->si_common.chan_desc.bs_pa_mfrms = bs_pa_mfrms - 2;
- return CMD_SUCCESS;
-}
-ALIAS_DEPRECATED(cfg_bts_chan_desc_bs_pa_mfrms,
- cfg_bts_chan_dscr_bs_pa_mfrms_cmd,
- "channel-descrption bs-pa-mfrms <2-9>",
- CD_STR
- "Set number of multiframe periods for paging groups\n"
- "Number of multiframe periods for paging groups\n");
-
-DEFUN(cfg_bts_chan_desc_bs_ag_blks_res,
- cfg_bts_chan_desc_bs_ag_blks_res_cmd,
- "channel-description bs-ag-blks-res <0-7>",
- CD_STR
- "Set number of blocks reserved for access grant\n"
- "Number of blocks reserved for access grant\n")
-{
- struct gsm_bts *bts = vty->index;
- int bs_ag_blks_res = atoi(argv[0]);
-
- bts->si_common.chan_desc.bs_ag_blks_res = bs_ag_blks_res;
- return CMD_SUCCESS;
-}
-ALIAS_DEPRECATED(cfg_bts_chan_desc_bs_ag_blks_res,
- cfg_bts_chan_dscr_bs_ag_blks_res_cmd,
- "channel-descrption bs-ag-blks-res <0-7>",
- CD_STR
- "Set number of blocks reserved for access grant\n"
- "Number of blocks reserved for access grant\n");
-
-#define CCCH_STR "Common Control Channel\n"
-
-DEFUN(cfg_bts_ccch_load_ind_thresh,
- cfg_bts_ccch_load_ind_thresh_cmd,
- "ccch load-indication-threshold <0-100>",
- CCCH_STR
- "Percentage of CCCH load at which BTS sends RSL CCCH LOAD IND\n"
- "CCCH Load Threshold in percent (Default: 10)\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->ccch_load_ind_thresh = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-#define NM_STR "Network Management\n"
-
-DEFUN(cfg_bts_rach_nm_b_thresh,
- cfg_bts_rach_nm_b_thresh_cmd,
- "rach nm busy threshold <0-255>",
- RACH_STR NM_STR
- "Set the NM Busy Threshold\n"
- "Set the NM Busy Threshold\n"
- "NM Busy Threshold in dB\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->rach_b_thresh = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rach_nm_ldavg,
- cfg_bts_rach_nm_ldavg_cmd,
- "rach nm load average <0-65535>",
- RACH_STR NM_STR
- "Set the NM Loadaverage Slots value\n"
- "Set the NM Loadaverage Slots value\n"
- "NM Loadaverage Slots value\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->rach_ldavg_slots = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd,
- "cell barred (0|1)",
- "Should this cell be barred from access?\n"
- "Should this cell be barred from access?\n"
- "Cell should NOT be barred\n"
- "Cell should be barred\n")
-
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.rach_control.cell_bar = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rach_ec_allowed, cfg_bts_rach_ec_allowed_cmd,
- "rach emergency call allowed (0|1)",
- RACH_STR
- "Should this cell allow emergency calls?\n"
- "Should this cell allow emergency calls?\n"
- "Should this cell allow emergency calls?\n"
- "Do NOT allow emergency calls\n"
- "Allow emergency calls\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (atoi(argv[0]) == 0)
- bts->si_common.rach_control.t2 |= 0x4;
- else
- bts->si_common.rach_control.t2 &= ~0x4;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rach_ac_class, cfg_bts_rach_ac_class_cmd,
- "rach access-control-class (0|1|2|3|4|5|6|7|8|9|11|12|13|14|15) (barred|allowed)",
- RACH_STR
- "Set access control class\n"
- "Access control class 0\n"
- "Access control class 1\n"
- "Access control class 2\n"
- "Access control class 3\n"
- "Access control class 4\n"
- "Access control class 5\n"
- "Access control class 6\n"
- "Access control class 7\n"
- "Access control class 8\n"
- "Access control class 9\n"
- "Access control class 11 for PLMN use\n"
- "Access control class 12 for security services\n"
- "Access control class 13 for public utilities (e.g. water/gas suppliers)\n"
- "Access control class 14 for emergency services\n"
- "Access control class 15 for PLMN staff\n"
- "barred to use access control class\n"
- "allowed to use access control class\n")
-{
- struct gsm_bts *bts = vty->index;
-
- uint8_t control_class;
- uint8_t allowed = 0;
-
- if (strcmp(argv[1], "allowed") == 0)
- allowed = 1;
-
- control_class = atoi(argv[0]);
- if (control_class < 8)
- if (allowed)
- bts->si_common.rach_control.t3 &= ~(0x1 << control_class);
- else
- bts->si_common.rach_control.t3 |= (0x1 << control_class);
- else
- if (allowed)
- bts->si_common.rach_control.t2 &= ~(0x1 << (control_class - 8));
- else
- bts->si_common.rach_control.t2 |= (0x1 << (control_class - 8));
-
- if (control_class < 10)
- acc_mgr_perm_subset_changed(&bts->acc_mgr, &bts->si_common.rach_control);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd,
- "ms max power <0-40>",
- "MS Options\n"
- "Maximum transmit power of the MS\n"
- "Maximum transmit power of the MS\n"
- "Maximum transmit power of the MS in dBm\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->ms_max_power = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-#define CELL_STR "Cell Parameters\n"
-
-DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd,
- "cell reselection hysteresis <0-14>",
- CELL_STR "Cell re-selection parameters\n"
- "Cell Re-Selection Hysteresis in dB\n"
- "Cell Re-Selection Hysteresis in dB\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd,
- "rxlev access min <0-63>",
- "Minimum RxLev needed for cell access\n"
- "Minimum RxLev needed for cell access\n"
- "Minimum RxLev needed for cell access\n"
- "Minimum RxLev needed for cell access (better than -110dBm)\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_cell_bar_qualify, cfg_bts_cell_bar_qualify_cmd,
- "cell bar qualify (0|1)",
- CELL_STR "Cell Bar Qualify\n" "Cell Bar Qualify\n"
- "Set CBQ to 0\n" "Set CBQ to 1\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.cbq = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_cell_resel_ofs, cfg_bts_cell_resel_ofs_cmd,
- "cell reselection offset <0-126>",
- CELL_STR "Cell Re-Selection Parameters\n"
- "Cell Re-Selection Offset (CRO) in dB\n"
- "Cell Re-Selection Offset (CRO) in dB\n"
- )
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.cell_resel_off = atoi(argv[0])/2;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_temp_ofs, cfg_bts_temp_ofs_cmd,
- "temporary offset <0-60>",
- "Cell selection temporary negative offset\n"
- "Cell selection temporary negative offset\n"
- "Cell selection temporary negative offset in dB\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.temp_offs = atoi(argv[0])/10;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_temp_ofs_inf, cfg_bts_temp_ofs_inf_cmd,
- "temporary offset infinite",
- "Cell selection temporary negative offset\n"
- "Cell selection temporary negative offset\n"
- "Sets cell selection temporary negative offset to infinity\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.temp_offs = 7;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_penalty_time, cfg_bts_penalty_time_cmd,
- "penalty time <20-620>",
- "Cell selection penalty time\n"
- "Cell selection penalty time\n"
- "Cell selection penalty time in seconds (by 20s increments)\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.penalty_time = (atoi(argv[0])-20)/20;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_penalty_time_rsvd, cfg_bts_penalty_time_rsvd_cmd,
- "penalty time reserved",
- "Cell selection penalty time\n"
- "Cell selection penalty time\n"
- "Set cell selection penalty time to reserved value 31, "
- "(indicate that CELL_RESELECT_OFFSET is subtracted from C2 "
- "and TEMPORARY_OFFSET is ignored)\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.penalty_time = 31;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_radio_link_timeout, cfg_bts_radio_link_timeout_cmd,
- "radio-link-timeout <4-64>",
- "Radio link timeout criterion (BTS side)\n"
- "Radio link timeout value (lost SACCH block)\n")
-{
- struct gsm_bts *bts = vty->index;
-
- gsm_bts_set_radio_link_timeout(bts, atoi(argv[0]));
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_radio_link_timeout_inf, cfg_bts_radio_link_timeout_inf_cmd,
- "radio-link-timeout infinite",
- "Radio link timeout criterion (BTS side)\n"
- "Infinite Radio link timeout value (use only for BTS RF testing)\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->type != GSM_BTS_TYPE_OSMOBTS) {
- vty_out(vty, "%% infinite radio link timeout not supported by this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty_out(vty, "%% INFINITE RADIO LINK TIMEOUT, USE ONLY FOR BTS RF TESTING%s", VTY_NEWLINE);
- gsm_bts_set_radio_link_timeout(bts, -1);
-
- return CMD_SUCCESS;
-}
-
-#define GPRS_TEXT "GPRS Packet Network\n"
-
-#define GPRS_CHECK_ENABLED(bts) \
- do { \
- if (bts->gprs.mode == BTS_GPRS_NONE) { \
- vty_out(vty, "%% GPRS is not enabled on BTS %u%s", \
- bts->nr, VTY_NEWLINE); \
- return CMD_WARNING; \
- } \
- } while (0)
-
-DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd,
- "gprs cell bvci <2-65535>",
- GPRS_TEXT
- "GPRS Cell Settings\n"
- "GPRS BSSGP VC Identifier\n"
- "GPRS BSSGP VC Identifier\n")
-{
- /* ETSI TS 101 343: values 0 and 1 are reserved for signalling and PTM */
- struct gsm_bts *bts = vty->index;
-
- GPRS_CHECK_ENABLED(bts);
-
- bts->gprs.cell.bvci = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd,
- "gprs nsei <0-65535>",
- GPRS_TEXT
- "GPRS NS Entity Identifier\n"
- "GPRS NS Entity Identifier\n")
-{
- struct gsm_bts *bts = vty->index;
-
- GPRS_CHECK_ENABLED(bts);
-
- bts->gprs.nse.nsei = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-#define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \
- "NSVC Logical Number\n"
-
-DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd,
- "gprs nsvc <0-1> nsvci <0-65535>",
- GPRS_TEXT NSVC_TEXT
- "NS Virtual Connection Identifier\n"
- "GPRS NS VC Identifier\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = atoi(argv[0]);
-
- GPRS_CHECK_ENABLED(bts);
-
- bts->gprs.nsvc[idx].nsvci = atoi(argv[1]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd,
- "gprs nsvc <0-1> local udp port <0-65535>",
- GPRS_TEXT NSVC_TEXT
- "GPRS NS Local UDP Port\n"
- "GPRS NS Local UDP Port\n"
- "GPRS NS Local UDP Port\n"
- "GPRS NS Local UDP Port Number\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = atoi(argv[0]);
-
- GPRS_CHECK_ENABLED(bts);
-
- bts->gprs.nsvc[idx].local_port = atoi(argv[1]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd,
- "gprs nsvc <0-1> remote udp port <0-65535>",
- GPRS_TEXT NSVC_TEXT
- "GPRS NS Remote UDP Port\n"
- "GPRS NS Remote UDP Port\n"
- "GPRS NS Remote UDP Port\n"
- "GPRS NS Remote UDP Port Number\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = atoi(argv[0]);
-
- GPRS_CHECK_ENABLED(bts);
-
- /* sockaddr_in and sockaddr_in6 have the port at the same position */
- bts->gprs.nsvc[idx].remote.u.sin.sin_port = htons(atoi(argv[1]));
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd,
- "gprs nsvc <0-1> remote ip " VTY_IPV46_CMD,
- GPRS_TEXT NSVC_TEXT
- "GPRS NS Remote IP Address\n"
- "GPRS NS Remote IP Address\n"
- "GPRS NS Remote IPv4 Address\n"
- "GPRS NS Remote IPv6 Address\n")
-{
- struct gsm_bts *bts = vty->index;
- struct osmo_sockaddr_str remote;
- int idx = atoi(argv[0]);
- int ret;
-
- GPRS_CHECK_ENABLED(bts);
-
- ret = osmo_sockaddr_str_from_str2(&remote, argv[1]);
- if (ret) {
- vty_out(vty, "%% Invalid IP address %s%s", argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* Can't use osmo_sockaddr_str_to_sockaddr() because the port would be overriden */
- bts->gprs.nsvc[idx].remote.u.sas.ss_family = remote.af;
- switch (remote.af) {
- case AF_INET:
- osmo_sockaddr_str_to_in_addr(&remote, &bts->gprs.nsvc[idx].remote.u.sin.sin_addr);
- break;
- case AF_INET6:
- osmo_sockaddr_str_to_in6_addr(&remote, &bts->gprs.nsvc[idx].remote.u.sin6.sin6_addr);
- break;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd,
- "paging free <-1-1024>",
- "Paging options\n"
- "Only page when having a certain amount of free slots\n"
- "amount of required free paging slots. -1 to disable\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->paging.free_chans_need = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd,
- "gprs ns timer " NS_TIMERS " <0-255>",
- GPRS_TEXT "Network Service\n"
- "Network Service Timer\n"
- NS_TIMERS_HELP "Timer Value\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
- int val = atoi(argv[1]);
-
- GPRS_CHECK_ENABLED(bts);
-
- if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer))
- return CMD_WARNING;
-
- bts->gprs.nse.timer[idx] = val;
-
- return CMD_SUCCESS;
-}
-
-#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)"
-#define BSSGP_TIMERS_HELP \
- "Tbvc-block timeout\n" \
- "Tbvc-block retries\n" \
- "Tbvc-unblock retries\n" \
- "Tbvcc-reset timeout\n" \
- "Tbvc-reset retries\n" \
- "Tbvc-suspend timeout\n" \
- "Tbvc-suspend retries\n" \
- "Tbvc-resume timeout\n" \
- "Tbvc-resume retries\n" \
- "Tbvc-capa-update timeout\n" \
- "Tbvc-capa-update retries\n"
-
-DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd,
- "gprs cell timer " BSSGP_TIMERS " <0-255>",
- GPRS_TEXT "Cell / BSSGP\n"
- "Cell/BSSGP Timer\n"
- BSSGP_TIMERS_HELP "Timer Value\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]);
- int val = atoi(argv[1]);
-
- GPRS_CHECK_ENABLED(bts);
-
- if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer))
- return CMD_WARNING;
-
- bts->gprs.cell.timer[idx] = val;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd,
- "gprs routing area <0-255>",
- GPRS_TEXT
- "GPRS Routing Area Code\n"
- "GPRS Routing Area Code\n"
- "GPRS Routing Area Code\n")
-{
- struct gsm_bts *bts = vty->index;
-
- GPRS_CHECK_ENABLED(bts);
-
- bts->gprs.rac = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_ctrl_ack, cfg_bts_gprs_ctrl_ack_cmd,
- "gprs control-ack-type-rach", GPRS_TEXT
- "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to "
- "four access bursts format instead of default RLC/MAC control block\n")
-{
- struct gsm_bts *bts = vty->index;
-
- GPRS_CHECK_ENABLED(bts);
-
- bts->gprs.ctrl_ack_type_use_block = false;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_no_bts_gprs_ctrl_ack, cfg_no_bts_gprs_ctrl_ack_cmd,
- "no gprs control-ack-type-rach", NO_STR GPRS_TEXT
- "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to "
- "default RLC/MAC control block\n")
-{
- struct gsm_bts *bts = vty->index;
-
- GPRS_CHECK_ENABLED(bts);
-
- bts->gprs.ctrl_ack_type_use_block = true;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_net_ctrl_ord, cfg_bts_gprs_net_ctrl_ord_cmd,
- "gprs network-control-order (nc0|nc1|nc2)",
- GPRS_TEXT
- "GPRS Network Control Order\n"
- "MS controlled cell re-selection, no measurement reporting\n"
- "MS controlled cell re-selection, MS sends measurement reports\n"
- "Network controlled cell re-selection, MS sends measurement reports\n")
-{
- struct gsm_bts *bts = vty->index;
-
- GPRS_CHECK_ENABLED(bts);
-
- bts->gprs.net_ctrl_ord = atoi(argv[0] + 2);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd,
- "gprs mode (none|gprs|egprs)",
- GPRS_TEXT
- "GPRS Mode for this BTS\n"
- "GPRS Disabled on this BTS\n"
- "GPRS Enabled on this BTS\n"
- "EGPRS (EDGE) Enabled on this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
- enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0], NULL);
-
- if (!bts_gprs_mode_is_compat(bts, mode)) {
- vty_out(vty, "%% This BTS type does not support %s%s", argv[0],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.mode = mode;
-
- return CMD_SUCCESS;
-}
-
-DEFUN_DEPRECATED(cfg_bts_gprs_11bit_rach_support_for_egprs,
- cfg_bts_gprs_11bit_rach_support_for_egprs_cmd,
- "gprs 11bit_rach_support_for_egprs (0|1)",
- GPRS_TEXT "EGPRS Packet Channel Request support\n"
- "Disable EGPRS Packet Channel Request support\n"
- "Enable EGPRS Packet Channel Request support\n")
-{
- struct gsm_bts *bts = vty->index;
-
- vty_out(vty, "%% 'gprs 11bit_rach_support_for_egprs' is now deprecated: "
- "use '[no] gprs egprs-packet-channel-request' instead%s", VTY_NEWLINE);
-
- bts->gprs.egprs_pkt_chan_request = (argv[0][0] == '1');
-
- if (bts->gprs.mode == BTS_GPRS_NONE && bts->gprs.egprs_pkt_chan_request) {
- vty_out(vty, "%% (E)GPRS is not enabled (see 'gprs mode')%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (bts->gprs.mode != BTS_GPRS_EGPRS) {
- vty_out(vty, "%% EGPRS Packet Channel Request support requires "
- "EGPRS mode to be enabled (see 'gprs mode')%s", VTY_NEWLINE);
- /* Do not return here, keep the old behaviour. */
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_egprs_pkt_chan_req,
- cfg_bts_gprs_egprs_pkt_chan_req_cmd,
- "gprs egprs-packet-channel-request",
- GPRS_TEXT "EGPRS Packet Channel Request support")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->gprs.mode != BTS_GPRS_EGPRS) {
- vty_out(vty, "%% EGPRS Packet Channel Request support requires "
- "EGPRS mode to be enabled (see 'gprs mode')%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.egprs_pkt_chan_request = true;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_gprs_egprs_pkt_chan_req,
- cfg_bts_no_gprs_egprs_pkt_chan_req_cmd,
- "no gprs egprs-packet-channel-request",
- NO_STR GPRS_TEXT "EGPRS Packet Channel Request support")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->gprs.mode != BTS_GPRS_EGPRS) {
- vty_out(vty, "%% EGPRS Packet Channel Request support requires "
- "EGPRS mode to be enabled (see 'gprs mode')%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.egprs_pkt_chan_request = false;
- return CMD_SUCCESS;
-}
-
-#define SI_TEXT "System Information Messages\n"
-#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)"
-#define SI_TYPE_HELP "System Information Type 1\n" \
- "System Information Type 2\n" \
- "System Information Type 3\n" \
- "System Information Type 4\n" \
- "System Information Type 5\n" \
- "System Information Type 6\n" \
- "System Information Type 7\n" \
- "System Information Type 8\n" \
- "System Information Type 9\n" \
- "System Information Type 10\n" \
- "System Information Type 13\n" \
- "System Information Type 16\n" \
- "System Information Type 17\n" \
- "System Information Type 18\n" \
- "System Information Type 19\n" \
- "System Information Type 20\n" \
- "System Information Type 2bis\n" \
- "System Information Type 2ter\n" \
- "System Information Type 2quater\n" \
- "System Information Type 5bis\n" \
- "System Information Type 5ter\n"
-
-DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd,
- "system-information " SI_TYPE_TEXT " mode (static|computed)",
- SI_TEXT SI_TYPE_HELP
- "System Information Mode\n"
- "Static user-specified\n"
- "Dynamic, BSC-computed\n")
-{
- struct gsm_bts *bts = vty->index;
- int type;
-
- type = get_string_value(osmo_sitype_strs, argv[0]);
- if (type < 0) {
- vty_out(vty, "%% Error SI Type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[1], "static"))
- bts->si_mode_static |= (1 << type);
- else
- bts->si_mode_static &= ~(1 << type);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd,
- "system-information " SI_TYPE_TEXT " static HEXSTRING",
- SI_TEXT SI_TYPE_HELP
- "Static System Information filling\n"
- "Static user-specified SI content in HEX notation\n")
-{
- struct gsm_bts *bts = vty->index;
- int rc, type;
-
- type = get_string_value(osmo_sitype_strs, argv[0]);
- if (type < 0) {
- vty_out(vty, "%% Error SI Type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!(bts->si_mode_static & (1 << type))) {
- vty_out(vty, "%% SI Type %s is not configured in static mode%s",
- get_value_string(osmo_sitype_strs, type), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* Fill buffer with padding pattern */
- memset(GSM_BTS_SI(bts, type), 0x2b, GSM_MACBLOCK_LEN);
-
- /* Parse the user-specified SI in hex format, [partially] overwriting padding */
- rc = osmo_hexparse(argv[1], GSM_BTS_SI(bts, type), GSM_MACBLOCK_LEN);
- if (rc < 0 || rc > GSM_MACBLOCK_LEN) {
- vty_out(vty, "%% Error parsing HEXSTRING%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* Mark this SI as present */
- bts->si_valid |= (1 << type);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_si_unused_send_empty, cfg_bts_si_unused_send_empty_cmd,
- "system-information unused-send-empty",
- SI_TEXT
- "Send BCCH Info with empty 'Full BCCH Info' TLV to notify disabled SI. "
- "Some nanoBTS fw versions are known to fail upon receival of these messages.\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_unused_send_empty = true;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_si_unused_send_empty, cfg_bts_no_si_unused_send_empty_cmd,
- "no system-information unused-send-empty",
- NO_STR SI_TEXT
- "Avoid sending BCCH Info with empty 'Full BCCH Info' TLV to notify disabled SI. "
- "Some nanoBTS fw versions are known to fail upon receival of these messages.\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!is_ipaccess_bts(bts) || is_sysmobts_v2(bts)) {
- vty_out(vty, "%% This command is only intended for ipaccess nanoBTS. See OS#3707.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->si_unused_send_empty = false;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_early_cm, cfg_bts_early_cm_cmd,
- "early-classmark-sending (allowed|forbidden)",
- "Early Classmark Sending\n"
- "Early Classmark Sending is allowed\n"
- "Early Classmark Sending is forbidden\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!strcmp(argv[0], "allowed"))
- bts->early_classmark_allowed = true;
- else
- bts->early_classmark_allowed = false;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_early_cm_3g, cfg_bts_early_cm_3g_cmd,
- "early-classmark-sending-3g (allowed|forbidden)",
- "3G Early Classmark Sending\n"
- "3G Early Classmark Sending is allowed\n"
- "3G Early Classmark Sending is forbidden\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!strcmp(argv[0], "allowed"))
- bts->early_classmark_allowed_3g = true;
- else
- bts->early_classmark_allowed_3g = false;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd,
- "neighbor-list mode (automatic|manual|manual-si5)",
- "Neighbor List\n" "Mode of Neighbor List generation\n"
- "Automatically from all BTS in this BSC\n" "Manual\n"
- "Manual with different lists for SI2 and SI5\n")
-{
- struct gsm_bts *bts = vty->index;
- int mode = get_string_value(bts_neigh_mode_strs, argv[0]);
-
- 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;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd,
- "neighbor-list (add|del) arfcn <0-1023>",
- "Neighbor List\n" "Add to manual neighbor list\n"
- "Delete from manual neighbor list\n" "ARFCN of neighbor\n"
- "ARFCN of neighbor\n")
-{
- struct gsm_bts *bts = vty->index;
- struct bitvec *bv = &bts->si_common.neigh_list;
- uint16_t arfcn = atoi(argv[1]);
- enum gsm_band unused;
-
- if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) {
- vty_out(vty, "%% Cannot configure neighbor list in "
- "automatic mode%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
- vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[0], "add"))
- bitvec_set_bit_pos(bv, arfcn, 1);
- else
- bitvec_set_bit_pos(bv, arfcn, 0);
-
- return CMD_SUCCESS;
-}
-
-/* help text should be kept in sync with EARFCN_*_INVALID defines */
-DEFUN(cfg_bts_si2quater_neigh_add, cfg_bts_si2quater_neigh_add_cmd,
- "si2quater neighbor-list add earfcn <0-65535> thresh-hi <0-31> "
- "thresh-lo <0-32> prio <0-8> qrxlv <0-32> meas <0-8>",
- "SI2quater Neighbor List\n" "SI2quater Neighbor List\n"
- "Add to manual SI2quater neighbor list\n"
- "EARFCN of neighbor\n" "EARFCN of neighbor\n"
- "threshold high bits\n" "threshold high bits\n"
- "threshold low bits\n" "threshold low bits (32 means NA)\n"
- "priority\n" "priority (8 means NA)\n"
- "QRXLEVMIN\n" "QRXLEVMIN (32 means NA)\n"
- "measurement bandwidth\n" "measurement bandwidth (8 means NA)\n")
-{
- struct gsm_bts *bts = vty->index;
- struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- uint16_t arfcn = atoi(argv[0]);
- uint8_t thresh_hi = atoi(argv[1]), thresh_lo = atoi(argv[2]),
- prio = atoi(argv[3]), qrx = atoi(argv[4]), meas = atoi(argv[5]);
- int r = bts_earfcn_add(bts, arfcn, thresh_hi, thresh_lo, prio, qrx, meas);
-
- switch (r) {
- case 1:
- vty_out(vty, "%% Warning: multiple threshold-high are not supported, overriding with %u%s",
- thresh_hi, VTY_NEWLINE);
- break;
- case EARFCN_THRESH_LOW_INVALID:
- vty_out(vty, "%% Warning: multiple threshold-low are not supported, overriding with %u%s",
- thresh_lo, VTY_NEWLINE);
- break;
- case EARFCN_QRXLV_INVALID + 1:
- vty_out(vty, "%% Warning: multiple QRXLEVMIN are not supported, overriding with %u%s",
- qrx, VTY_NEWLINE);
- break;
- case EARFCN_PRIO_INVALID:
- vty_out(vty, "%% Warning: multiple priorities are not supported, overriding with %u%s",
- prio, VTY_NEWLINE);
- break;
- default:
- if (r < 0) {
- vty_out(vty, "%% Unable to add ARFCN %u: %s%s", arfcn, strerror(-r), VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- if (si2q_num(bts) <= SI2Q_MAX_NUM)
- return CMD_SUCCESS;
-
- vty_out(vty, "%% Warning: not enough space in SI2quater (%u/%u used) for a given EARFCN %u%s",
- bts->si2q_count, SI2Q_MAX_NUM, arfcn, VTY_NEWLINE);
- osmo_earfcn_del(e, arfcn);
-
- return CMD_WARNING;
-}
-
-DEFUN(cfg_bts_si2quater_neigh_del, cfg_bts_si2quater_neigh_del_cmd,
- "si2quater neighbor-list del earfcn <0-65535>",
- "SI2quater Neighbor List\n"
- "SI2quater Neighbor List\n"
- "Delete from SI2quater manual neighbor list\n"
- "EARFCN of neighbor\n"
- "EARFCN\n")
-{
- struct gsm_bts *bts = vty->index;
- struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- uint16_t arfcn = atoi(argv[0]);
- int r = osmo_earfcn_del(e, arfcn);
- if (r < 0) {
- vty_out(vty, "%% Unable to delete arfcn %u: %s%s", arfcn,
- strerror(-r), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_si2quater_uarfcn_add, cfg_bts_si2quater_uarfcn_add_cmd,
- "si2quater neighbor-list add uarfcn <0-16383> <0-511> <0-1>",
- "SI2quater Neighbor List\n"
- "SI2quater Neighbor List\n" "Add to manual SI2quater neighbor list\n"
- "UARFCN of neighbor\n" "UARFCN of neighbor\n" "scrambling code\n"
- "diversity bit\n")
-{
- struct gsm_bts *bts = vty->index;
- uint16_t arfcn = atoi(argv[0]), scramble = atoi(argv[1]);
-
- switch(bts_uarfcn_add(bts, arfcn, scramble, atoi(argv[2]))) {
- case -ENOMEM:
- vty_out(vty, "%% Unable to add UARFCN: max number of UARFCNs (%u) reached%s",
- MAX_EARFCN_LIST, VTY_NEWLINE);
- return CMD_WARNING;
- case -ENOSPC:
- vty_out(vty, "%% Warning: not enough space in SI2quater for a given UARFCN (%u, %u)%s",
- arfcn, scramble, VTY_NEWLINE);
- return CMD_WARNING;
- case -EADDRINUSE:
- vty_out(vty, "%% Unable to add UARFCN: (%u, %u) is already added%s",
- arfcn, scramble, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_si2quater_uarfcn_del, cfg_bts_si2quater_uarfcn_del_cmd,
- "si2quater neighbor-list del uarfcn <0-16383> <0-511>",
- "SI2quater Neighbor List\n"
- "SI2quater Neighbor List\n"
- "Delete from SI2quater manual neighbor list\n"
- "UARFCN of neighbor\n"
- "UARFCN\n"
- "scrambling code\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts_uarfcn_del(bts, atoi(argv[0]), atoi(argv[1])) < 0) {
- vty_out(vty, "%% Unable to delete uarfcn: pair not found%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd,
- "si5 neighbor-list (add|del) arfcn <0-1023>",
- "SI5 Neighbor List\n"
- "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n"
- "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n"
- "ARFCN of neighbor\n")
-{
- enum gsm_band unused;
- struct gsm_bts *bts = vty->index;
- struct bitvec *bv = &bts->si_common.si5_neigh_list;
- uint16_t arfcn = atoi(argv[1]);
-
- if (!bts->neigh_list_manual_mode) {
- vty_out(vty, "%% Cannot configure neighbor list in "
- "automatic mode%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
- vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[0], "add"))
- bitvec_set_bit_pos(bv, arfcn, 1);
- else
- bitvec_set_bit_pos(bv, arfcn, 0);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_pcu_sock, cfg_bts_pcu_sock_cmd,
- "pcu-socket PATH",
- "PCU Socket Path for using OsmoPCU co-located with BSC (legacy BTS)\n"
- "Path in the file system for the unix-domain PCU socket\n")
-{
- struct gsm_bts *bts = vty->index;
- int rc;
-
- osmo_talloc_replace_string(bts, &bts->pcu_sock_path, argv[0]);
- pcu_sock_exit(bts);
- rc = pcu_sock_init(bts->pcu_sock_path, bts);
- if (rc < 0) {
- vty_out(vty, "%% Error creating PCU socket `%s' for BTS %u%s",
- bts->pcu_sock_path, bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_acc_rotate,
- cfg_bts_acc_rotate_cmd,
- "access-control-class-rotate <0-10>",
- "Enable Access Control Class allowed subset rotation\n"
- "Size of the rotating allowed ACC 0-9 subset (default=10, no subset)\n")
-{
- struct gsm_bts *bts = vty->index;
- int len_allowed_adm = atoi(argv[0]);
- acc_mgr_set_len_allowed_adm(&bts->acc_mgr, len_allowed_adm);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_acc_rotate_quantum,
- cfg_bts_acc_rotate_quantum_cmd,
- "access-control-class-rotate-quantum <1-65535>",
- "Time between rotation of ACC 0-9 generated subsets\n"
- "Time in seconds (default=" OSMO_STRINGIFY_VAL(ACC_MGR_QUANTUM_DEFAULT) ")\n")
-{
- struct gsm_bts *bts = vty->index;
- uint32_t rotation_time_sec = (uint32_t)atoi(argv[0]);
- acc_mgr_set_rotation_time(&bts->acc_mgr, rotation_time_sec);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_acc_ramping,
- cfg_bts_acc_ramping_cmd,
- "access-control-class-ramping",
- "Enable Access Control Class ramping\n")
-{
- struct gsm_bts *bts = vty->index;
- struct gsm_bts_trx *trx;
-
- if (!acc_ramp_is_enabled(&bts->acc_ramp)) {
- acc_ramp_set_enabled(&bts->acc_ramp, true);
- /* Start ramping if at least one TRX is usable */
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx_is_usable(trx)) {
- acc_ramp_trigger(&bts->acc_ramp);
- break;
- }
- }
- }
-
- /*
- * ACC ramping takes effect either when the BTS reconnects RSL,
- * or when RF administrative state changes to 'unlocked'.
- */
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_acc_ramping, cfg_bts_no_acc_ramping_cmd,
- "no access-control-class-ramping",
- NO_STR
- "Disable Access Control Class ramping\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (acc_ramp_is_enabled(&bts->acc_ramp)) {
- acc_ramp_abort(&bts->acc_ramp);
- acc_ramp_set_enabled(&bts->acc_ramp, false);
- if (gsm_bts_set_system_infos(bts) != 0) {
- vty_out(vty, "%% Filed to (re)generate System Information "
- "messages, check the logs%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_acc_ramping_step_interval,
- cfg_bts_acc_ramping_step_interval_cmd,
- "access-control-class-ramping-step-interval (<"
- OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_INTERVAL_MIN) "-"
- OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_INTERVAL_MAX) ">|dynamic)",
- "Configure Access Control Class ramping step interval\n"
- "Set a fixed step interval (in seconds)\n"
- "Use dynamic step interval based on BTS channel load (deprecated, don't use, ignored)\n")
-{
- struct gsm_bts *bts = vty->index;
- bool dynamic = (strcmp(argv[0], "dynamic") == 0);
- int error;
-
- if (dynamic) {
- vty_out(vty, "%% access-control-class-ramping-step-interval 'dynamic' value is deprecated, ignoring it%s", VTY_NEWLINE);
- return CMD_SUCCESS;
- }
-
- error = acc_ramp_set_step_interval(&bts->acc_ramp, atoi(argv[0]));
- if (error != 0) {
- if (error == -ERANGE)
- vty_out(vty, "%% Unable to set ACC ramp step interval: value out of range%s", VTY_NEWLINE);
- else
- vty_out(vty, "%% Unable to set ACC ramp step interval: unknown error%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_acc_ramping_step_size,
- cfg_bts_acc_ramping_step_size_cmd,
- "access-control-class-ramping-step-size (<"
- OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_SIZE_MIN) "-"
- OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_SIZE_MAX) ">)",
- "Configure Access Control Class ramping step size\n"
- "Set the number of Access Control Classes to enable per ramping step\n")
-{
- struct gsm_bts *bts = vty->index;
- int error;
-
- error = acc_ramp_set_step_size(&bts->acc_ramp, atoi(argv[0]));
- if (error != 0) {
- if (error == -ERANGE)
- vty_out(vty, "%% Unable to set ACC ramp step size: value out of range%s", VTY_NEWLINE);
- else
- vty_out(vty, "%% Unable to set ACC ramp step size: unknown error%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_acc_ramping_chan_load,
- cfg_bts_acc_ramping_chan_load_cmd,
- "access-control-class-ramping-chan-load <0-100> <0-100>",
- "Configure Access Control Class ramping channel load thresholds\n"
- "Lower Channel load threshold (%) below which subset size of allowed broadcast ACCs can be increased\n"
- "Upper channel load threshold (%) above which subset size of allowed broadcast ACCs can be decreased\n")
-{
- struct gsm_bts *bts = vty->index;
- int rc;
-
- rc = acc_ramp_set_chan_load_thresholds(&bts->acc_ramp, atoi(argv[0]), atoi(argv[1]));
- if (rc < 0) {
- vty_out(vty, "%% Unable to set ACC channel load thresholds%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-#define EXCL_RFLOCK_STR "Exclude this BTS from the global RF Lock\n"
-
-DEFUN(cfg_bts_excl_rf_lock,
- cfg_bts_excl_rf_lock_cmd,
- "rf-lock-exclude",
- EXCL_RFLOCK_STR)
-{
- struct gsm_bts *bts = vty->index;
- bts->excl_from_rf_lock = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_excl_rf_lock,
- cfg_bts_no_excl_rf_lock_cmd,
- "no rf-lock-exclude",
- NO_STR EXCL_RFLOCK_STR)
-{
- struct gsm_bts *bts = vty->index;
- bts->excl_from_rf_lock = 0;
- return CMD_SUCCESS;
-}
-
-#define FORCE_COMB_SI_STR "Force the generation of a single SI (no ter/bis)\n"
-
-DEFUN(cfg_bts_force_comb_si,
- cfg_bts_force_comb_si_cmd,
- "force-combined-si",
- FORCE_COMB_SI_STR)
-{
- struct gsm_bts *bts = vty->index;
- bts->force_combined_si = 1;
- bts->force_combined_si_set = true;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_force_comb_si,
- cfg_bts_no_force_comb_si_cmd,
- "no force-combined-si",
- NO_STR FORCE_COMB_SI_STR)
-{
- struct gsm_bts *bts = vty->index;
- bts->force_combined_si = 0;
- bts->force_combined_si_set = true;
- return CMD_SUCCESS;
-}
-
-static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[])
-{
- struct gsm_bts *bts = vty->index;
- struct bts_codec_conf *codec = &bts->codec;
- int i;
-
- codec->hr = 0;
- codec->efr = 0;
- codec->amr = 0;
- for (i = 0; i < argc; i++) {
- if (!strcmp(argv[i], "hr"))
- codec->hr = 1;
- if (!strcmp(argv[i], "efr"))
- codec->efr = 1;
- if (!strcmp(argv[i], "amr"))
- codec->amr = 1;
- }
-}
-
-#define CODEC_PAR_STR " (hr|efr|amr)"
-#define CODEC_HELP_STR "Half Rate\n" \
- "Enhanced Full Rate\nAdaptive Multirate\n"
-
-DEFUN(cfg_bts_codec0, cfg_bts_codec0_cmd,
- "codec-support fr",
- "Codec Support settings\nFullrate\n")
-{
- _get_codec_from_arg(vty, 0, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd,
- "codec-support fr" CODEC_PAR_STR,
- "Codec Support settings\nFullrate\n"
- CODEC_HELP_STR)
-{
- _get_codec_from_arg(vty, 1, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd,
- "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR,
- "Codec Support settings\nFullrate\n"
- CODEC_HELP_STR CODEC_HELP_STR)
-{
- _get_codec_from_arg(vty, 2, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd,
- "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR,
- "Codec Support settings\nFullrate\n"
- CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR)
-{
- _get_codec_from_arg(vty, 3, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd,
- "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR,
- "Codec Support settings\nFullrate\n"
- CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR)
-{
- _get_codec_from_arg(vty, 4, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_depends_on, cfg_bts_depends_on_cmd,
- "depends-on-bts <0-255>",
- "This BTS can only be started if another one is up\n"
- BTS_NR_STR)
-{
- struct gsm_bts *bts = vty->index;
- struct gsm_bts *other_bts;
- int dep = atoi(argv[0]);
-
-
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "%% This feature is only available for IP systems.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- other_bts = gsm_bts_num(bts->network, dep);
- if (!other_bts || !is_ipaccess_bts(other_bts)) {
- vty_out(vty, "%% This feature is only available for IP systems.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (dep >= bts->nr) {
- vty_out(vty, "%% Need to depend on an already declared unit.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts_depend_mark(bts, dep);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_depends_on, cfg_bts_no_depends_on_cmd,
- "no depends-on-bts <0-255>",
- NO_STR "This BTS can only be started if another one is up\n"
- BTS_NR_STR)
-{
- struct gsm_bts *bts = vty->index;
- int dep = atoi(argv[0]);
-
- bts_depend_clear(bts, dep);
- return CMD_SUCCESS;
-}
-
-#define AMR_TEXT "Adaptive Multi Rate settings\n"
-#define AMR_MODE_TEXT "Codec modes to use with AMR codec\n"
-#define AMR_START_TEXT "Initial codec to use with AMR\n" \
- "Automatically\nFirst codec\nSecond codec\nThird codec\nFourth codec\n"
-#define AMR_TH_TEXT "AMR threshold between codecs\nMS side\nBTS side\n"
-#define AMR_HY_TEXT "AMR hysteresis between codecs\nMS side\nBTS side\n"
-
-static int get_amr_from_arg(struct vty *vty, int argc, const char *argv[], int full)
-{
- struct gsm_bts *bts = vty->index;
- struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
- struct gsm48_multi_rate_conf *mr_conf =
- (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
- int i;
- int mode;
- int mode_prev = -1;
-
- /* Check if mode parameters are in order */
- for (i = 0; i < argc; i++) {
- mode = atoi(argv[i]);
- if (mode_prev > mode) {
- vty_out(vty, "%% Modes must be listed in order%s",
- VTY_NEWLINE);
- return -1;
- }
-
- if (mode_prev == mode) {
- vty_out(vty, "%% Modes must be unique %s", VTY_NEWLINE);
- return -2;
- }
- mode_prev = mode;
- }
-
- /* Prepare the multirate configuration IE */
- mr->gsm48_ie[1] = 0;
- for (i = 0; i < argc; i++)
- mr->gsm48_ie[1] |= 1 << atoi(argv[i]);
- mr_conf->icmi = 0;
-
- /* Store actual mode identifier values */
- for (i = 0; i < argc; i++) {
- mr->ms_mode[i].mode = atoi(argv[i]);
- mr->bts_mode[i].mode = atoi(argv[i]);
- }
- mr->num_modes = argc;
-
- /* Trim excess threshold and hysteresis values from previous config */
- for (i = argc - 1; i < 4; i++) {
- mr->ms_mode[i].threshold = 0;
- mr->bts_mode[i].threshold = 0;
- mr->ms_mode[i].hysteresis = 0;
- mr->bts_mode[i].hysteresis = 0;
- }
- return 0;
-}
-
-static void get_amr_th_from_arg(struct vty *vty, int argc, const char *argv[], int full)
-{
- struct gsm_bts *bts = vty->index;
- struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
- struct amr_mode *modes;
- int i;
-
- modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode;
- for (i = 0; i < argc - 1; i++)
- modes[i].threshold = atoi(argv[i + 1]);
-}
-
-static void get_amr_hy_from_arg(struct vty *vty, int argc, const char *argv[], int full)
-{
- struct gsm_bts *bts = vty->index;
- struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
- struct amr_mode *modes;
- int i;
-
- modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode;
- for (i = 0; i < argc - 1; i++)
- modes[i].hysteresis = atoi(argv[i + 1]);
-}
-
-static void get_amr_start_from_arg(struct vty *vty, const char *argv[], int full)
-{
- struct gsm_bts *bts = vty->index;
- struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
- struct gsm48_multi_rate_conf *mr_conf =
- (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
- int num = 0, i;
-
- for (i = 0; i < ((full) ? 8 : 6); i++) {
- if ((mr->gsm48_ie[1] & (1 << i))) {
- num++;
- }
- }
-
- if (argv[0][0] == 'a' || num == 0)
- mr_conf->icmi = 0;
- else {
- mr_conf->icmi = 1;
- if (num < atoi(argv[0]))
- mr_conf->smod = num - 1;
- else
- mr_conf->smod = atoi(argv[0]) - 1;
- }
-}
-
-/* Give the current amr configuration a final consistency check by feeding the
- * the configuration into the gsm48 multirate IE generator function */
-static int check_amr_config(struct vty *vty)
-{
- int rc = 0;
- struct amr_multirate_conf *mr;
- const struct gsm48_multi_rate_conf *mr_conf;
- struct gsm_bts *bts = vty->index;
- int vty_rc = CMD_SUCCESS;
-
- mr = &bts->mr_full;
- mr_conf = (struct gsm48_multi_rate_conf*) mr->gsm48_ie;
- rc = gsm48_multirate_config(NULL, mr_conf, mr->ms_mode, mr->num_modes);
- if (rc != 0) {
- vty_out(vty,
- "%% Invalid AMR multirate configuration (tch-f, ms) - check parameters%s",
- VTY_NEWLINE);
- vty_rc = CMD_WARNING;
- }
-
- rc = gsm48_multirate_config(NULL, mr_conf, mr->bts_mode, mr->num_modes);
- if (rc != 0) {
- vty_out(vty,
- "%% Invalid AMR multirate configuration (tch-f, bts) - check parameters%s",
- VTY_NEWLINE);
- vty_rc = CMD_WARNING;
- }
-
- mr = &bts->mr_half;
- mr_conf = (struct gsm48_multi_rate_conf*) mr->gsm48_ie;
- rc = gsm48_multirate_config(NULL, mr_conf, mr->ms_mode, mr->num_modes);
- if (rc != 0) {
- vty_out(vty,
- "%% Invalid AMR multirate configuration (tch-h, ms) - check parameters%s",
- VTY_NEWLINE);
- vty_rc = CMD_WARNING;
- }
-
- rc = gsm48_multirate_config(NULL, mr_conf, mr->bts_mode, mr->num_modes);
- if (rc != 0) {
- vty_out(vty,
- "%% Invalid AMR multirate configuration (tch-h, bts) - check parameters%s",
- VTY_NEWLINE);
- vty_rc = CMD_WARNING;
- }
-
- return vty_rc;
-}
-
-#define AMR_TCHF_PAR_STR " (0|1|2|3|4|5|6|7)"
-#define AMR_TCHF_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" \
- "10,2k\n12,2k\n"
-
-#define AMR_TCHH_PAR_STR " (0|1|2|3|4|5)"
-#define AMR_TCHH_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n"
-
-#define AMR_TH_HELP_STR "Threshold between codec 1 and 2\n"
-#define AMR_HY_HELP_STR "Hysteresis between codec 1 and 2\n"
-
-DEFUN(cfg_bts_amr_fr_modes1, cfg_bts_amr_fr_modes1_cmd,
- "amr tch-f modes" AMR_TCHF_PAR_STR,
- AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
- AMR_TCHF_HELP_STR)
-{
- if (get_amr_from_arg(vty, 1, argv, 1))
- return CMD_WARNING;
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_fr_modes2, cfg_bts_amr_fr_modes2_cmd,
- "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR,
- AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
- AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR)
-{
- if (get_amr_from_arg(vty, 2, argv, 1))
- return CMD_WARNING;
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_fr_modes3, cfg_bts_amr_fr_modes3_cmd,
- "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR,
- AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
- AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR)
-{
- if (get_amr_from_arg(vty, 3, argv, 1))
- return CMD_WARNING;
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_fr_modes4, cfg_bts_amr_fr_modes4_cmd,
- "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR,
- AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
- AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR)
-{
- if (get_amr_from_arg(vty, 4, argv, 1))
- return CMD_WARNING;
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_fr_start_mode, cfg_bts_amr_fr_start_mode_cmd,
- "amr tch-f start-mode (auto|1|2|3|4)",
- AMR_TEXT "Full Rate\n" AMR_START_TEXT)
-{
- get_amr_start_from_arg(vty, argv, 1);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_fr_thres1, cfg_bts_amr_fr_thres1_cmd,
- "amr tch-f threshold (ms|bts) <0-63>",
- AMR_TEXT "Full Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 2, argv, 1);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_fr_thres2, cfg_bts_amr_fr_thres2_cmd,
- "amr tch-f threshold (ms|bts) <0-63> <0-63>",
- AMR_TEXT "Full Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 3, argv, 1);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_fr_thres3, cfg_bts_amr_fr_thres3_cmd,
- "amr tch-f threshold (ms|bts) <0-63> <0-63> <0-63>",
- AMR_TEXT "Full Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 4, argv, 1);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_fr_hyst1, cfg_bts_amr_fr_hyst1_cmd,
- "amr tch-f hysteresis (ms|bts) <0-15>",
- AMR_TEXT "Full Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 2, argv, 1);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_fr_hyst2, cfg_bts_amr_fr_hyst2_cmd,
- "amr tch-f hysteresis (ms|bts) <0-15> <0-15>",
- AMR_TEXT "Full Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 3, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_hyst3, cfg_bts_amr_fr_hyst3_cmd,
- "amr tch-f hysteresis (ms|bts) <0-15> <0-15> <0-15>",
- AMR_TEXT "Full Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 4, argv, 1);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_modes1, cfg_bts_amr_hr_modes1_cmd,
- "amr tch-h modes" AMR_TCHH_PAR_STR,
- AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
- AMR_TCHH_HELP_STR)
-{
- if (get_amr_from_arg(vty, 1, argv, 0))
- return CMD_WARNING;
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_modes2, cfg_bts_amr_hr_modes2_cmd,
- "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR,
- AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
- AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR)
-{
- if (get_amr_from_arg(vty, 2, argv, 0))
- return CMD_WARNING;
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_modes3, cfg_bts_amr_hr_modes3_cmd,
- "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR,
- AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
- AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR)
-{
- if (get_amr_from_arg(vty, 3, argv, 0))
- return CMD_WARNING;
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_modes4, cfg_bts_amr_hr_modes4_cmd,
- "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR,
- AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
- AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR)
-{
- if (get_amr_from_arg(vty, 4, argv, 0))
- return CMD_WARNING;
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_start_mode, cfg_bts_amr_hr_start_mode_cmd,
- "amr tch-h start-mode (auto|1|2|3|4)",
- AMR_TEXT "Half Rate\n" AMR_START_TEXT)
-{
- get_amr_start_from_arg(vty, argv, 0);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_thres1, cfg_bts_amr_hr_thres1_cmd,
- "amr tch-h threshold (ms|bts) <0-63>",
- AMR_TEXT "Half Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 2, argv, 0);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_thres2, cfg_bts_amr_hr_thres2_cmd,
- "amr tch-h threshold (ms|bts) <0-63> <0-63>",
- AMR_TEXT "Half Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 3, argv, 0);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_thres3, cfg_bts_amr_hr_thres3_cmd,
- "amr tch-h threshold (ms|bts) <0-63> <0-63> <0-63>",
- AMR_TEXT "Half Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 4, argv, 0);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_hyst1, cfg_bts_amr_hr_hyst1_cmd,
- "amr tch-h hysteresis (ms|bts) <0-15>",
- AMR_TEXT "Half Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 2, argv, 0);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_hyst2, cfg_bts_amr_hr_hyst2_cmd,
- "amr tch-h hysteresis (ms|bts) <0-15> <0-15>",
- AMR_TEXT "Half Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 3, argv, 0);
- return check_amr_config(vty);
-}
-
-DEFUN(cfg_bts_amr_hr_hyst3, cfg_bts_amr_hr_hyst3_cmd,
- "amr tch-h hysteresis (ms|bts) <0-15> <0-15> <0-15>",
- AMR_TEXT "Half Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 4, argv, 0);
- return check_amr_config(vty);
-}
-
-#define TNUM_STR "T-number, optionally preceded by 't' or 'T'\n"
-DEFUN(cfg_bts_t3113_dynamic, cfg_bts_t3113_dynamic_cmd,
- "timer-dynamic TNNNN",
- "Calculate T3113 dynamically based on channel config and load\n"
- TNUM_STR)
-{
- struct osmo_tdef *d;
- struct gsm_bts *bts = vty->index;
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- d = osmo_tdef_vty_parse_T_arg(vty, gsmnet->T_defs, argv[0]);
- if (!d)
- return CMD_WARNING;
-
- switch (d->T) {
- case 3113:
- bts->T3113_dynamic = true;
- break;
- default:
- vty_out(vty, "%% T%d cannot be set to dynamic%s", d->T, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_t3113_dynamic, cfg_bts_no_t3113_dynamic_cmd,
- "no timer-dynamic TNNNN",
- NO_STR
- "Set given timer to non-dynamic and use the default or user provided fixed value\n"
- TNUM_STR)
-{
- struct osmo_tdef *d;
- struct gsm_bts *bts = vty->index;
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- d = osmo_tdef_vty_parse_T_arg(vty, gsmnet->T_defs, argv[0]);
- if (!d)
- return CMD_WARNING;
-
- switch (d->T) {
- case 3113:
- bts->T3113_dynamic = false;
- break;
- default:
- vty_out(vty, "%% T%d already is non-dynamic%s", d->T, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-#define TRX_TEXT "Radio Transceiver\n"
-
-/* per TRX configuration */
-DEFUN(cfg_trx,
- cfg_trx_cmd,
- "trx <0-255>",
- TRX_TEXT
- "Select a TRX to configure\n")
-{
- int trx_nr = atoi(argv[0]);
- struct gsm_bts *bts = vty->index;
- struct gsm_bts_trx *trx;
-
- if (trx_nr > bts->num_trx) {
- vty_out(vty, "%% The next unused TRX number in this BTS is %u%s",
- bts->num_trx, VTY_NEWLINE);
- return CMD_WARNING;
- } else if (trx_nr == bts->num_trx) {
- /* we need to allocate a new one */
- trx = gsm_bts_trx_alloc(bts);
- } else
- trx = gsm_bts_trx_num(bts, trx_nr);
-
- if (!trx)
- return CMD_WARNING;
-
- vty->index = trx;
- vty->index_sub = &trx->description;
- vty->node = TRX_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_arfcn,
- cfg_trx_arfcn_cmd,
- "arfcn <0-1023>",
- "Set the ARFCN for this TRX\n"
- "Absolute Radio Frequency Channel Number\n")
-{
- enum gsm_band unused;
- struct gsm_bts_trx *trx = vty->index;
- int arfcn = atoi(argv[0]);
-
- if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
- vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* FIXME: check if this ARFCN is supported by this TRX */
-
- trx->arfcn = arfcn;
-
- /* FIXME: patch ARFCN into SYSTEM INFORMATION */
- /* FIXME: use OML layer to update the ARFCN */
- /* FIXME: use RSL layer to update SYSTEM INFORMATION */
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_nominal_power,
- cfg_trx_nominal_power_cmd,
- "nominal power <-20-100>",
- "Nominal TRX RF Power in dBm\n"
- "Nominal TRX RF Power in dBm\n"
- "Nominal TRX RF Power in dBm\n")
-{
- struct gsm_bts_trx *trx = vty->index;
-
- trx->nominal_power = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_max_power_red,
- cfg_trx_max_power_red_cmd,
- "max_power_red <0-100>",
- "Reduction of maximum BS RF Power (relative to nominal power)\n"
- "Reduction of maximum BS RF Power in dB\n")
-{
- int maxpwr_r = atoi(argv[0]);
- struct gsm_bts_trx *trx = vty->index;
- int upper_limit = 24; /* default 12.21 max power red. */
-
- /* FIXME: check if our BTS type supports more than 12 */
- if (maxpwr_r < 0 || maxpwr_r > upper_limit) {
- vty_out(vty, "%% Power %d dB is not in the valid range%s",
- maxpwr_r, VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (maxpwr_r & 1) {
- vty_out(vty, "%% Power %d dB is not an even value%s",
- maxpwr_r, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- trx->max_power_red = maxpwr_r;
-
- /* FIXME: make sure we update this using OML */
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_rsl_e1,
- cfg_trx_rsl_e1_cmd,
- "rsl e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
- "RSL Parameters\n"
- "E1/T1 interface to be used for RSL\n"
- "E1/T1 interface to be used for RSL\n"
- "E1/T1 Line Number to be used for RSL\n"
- "E1/T1 Timeslot to be used for RSL\n"
- "E1/T1 Timeslot to be used for RSL\n"
- "E1/T1 Sub-slot to be used for RSL\n"
- "E1/T1 Sub-slot 0 is to be used for RSL\n"
- "E1/T1 Sub-slot 1 is to be used for RSL\n"
- "E1/T1 Sub-slot 2 is to be used for RSL\n"
- "E1/T1 Sub-slot 3 is to be used for RSL\n"
- "E1/T1 full timeslot is to be used for RSL\n")
-{
- struct gsm_bts_trx *trx = vty->index;
-
- parse_e1_link(&trx->rsl_e1_link, argv[0], argv[1], argv[2]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_rsl_e1_tei,
- cfg_trx_rsl_e1_tei_cmd,
- "rsl e1 tei <0-63>",
- "RSL Parameters\n"
- "Set the TEI to be used for RSL\n"
- "Set the TEI to be used for RSL\n"
- "TEI to be used for RSL\n")
-{
- struct gsm_bts_trx *trx = vty->index;
-
- trx->rsl_tei = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_rf_locked,
- cfg_trx_rf_locked_cmd,
- "rf_locked (0|1)",
- "Set or unset the RF Locking (Turn off RF of the TRX)\n"
- "TRX is NOT RF locked (active)\n"
- "TRX is RF locked (turned off)\n")
-{
- int locked = atoi(argv[0]);
- struct gsm_bts_trx *trx = vty->index;
-
- gsm_trx_lock_rf(trx, locked, "vty");
- return CMD_SUCCESS;
-}
-
-/* per TS configuration */
-DEFUN(cfg_ts,
- cfg_ts_cmd,
- "timeslot <0-7>",
- "Select a Timeslot to configure\n"
- "Timeslot number\n")
-{
- int ts_nr = atoi(argv[0]);
- struct gsm_bts_trx *trx = vty->index;
- struct gsm_bts_trx_ts *ts;
-
- if (ts_nr >= TRX_NR_TS) {
- vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s",
- TRX_NR_TS, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- ts = &trx->ts[ts_nr];
-
- vty->index = ts;
- vty->node = TS_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_pchan,
- cfg_ts_pchan_cmd,
- "phys_chan_config PCHAN", /* dynamically generated! */
- "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
- int pchanc;
-
- pchanc = gsm_pchan_parse(argv[0]);
- if (pchanc < 0)
- return CMD_WARNING;
-
- ts->pchan_from_config = pchanc;
-
- return CMD_SUCCESS;
-}
-
-/* used for backwards compatibility with old config files that still
- * have uppercase pchan type names */
-DEFUN_HIDDEN(cfg_ts_pchan_compat,
- cfg_ts_pchan_compat_cmd,
- "phys_chan_config PCHAN",
- "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
- int pchanc;
-
- pchanc = gsm_pchan_parse(argv[0]);
- if (pchanc < 0) {
- vty_out(vty, "Unknown physical channel name '%s'%s", argv[0], VTY_NEWLINE);
- return CMD_ERR_NO_MATCH;
- }
-
- ts->pchan_from_config = pchanc;
-
- return CMD_SUCCESS;
-}
-
-
-
-DEFUN(cfg_ts_tsc,
- cfg_ts_tsc_cmd,
- "training_sequence_code <0-7>",
- "Training Sequence Code of the Timeslot\n" "TSC\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- if (!osmo_bts_has_feature(&ts->trx->bts->model->features, BTS_FEAT_MULTI_TSC)) {
- vty_out(vty, "%% This BTS does not support a TSC != BCC, "
- "falling back to BCC%s", VTY_NEWLINE);
- ts->tsc = -1;
- return CMD_WARNING;
- }
-
- ts->tsc = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-#define HOPPING_STR "Configure frequency hopping\n"
-
-DEFUN(cfg_ts_hopping,
- cfg_ts_hopping_cmd,
- "hopping enabled (0|1)",
- HOPPING_STR "Enable or disable frequency hopping\n"
- "Disable frequency hopping\n" "Enable frequency hopping\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
- int enabled = atoi(argv[0]);
-
- if (enabled && !osmo_bts_has_feature(&ts->trx->bts->model->features, BTS_FEAT_HOPPING)) {
- vty_out(vty, "%% BTS model does not seem to support freq. hopping%s", VTY_NEWLINE);
- /* Allow enabling frequency hopping anyway, because the BTS might not have
- * connected yet (thus not sent the feature vector), so we cannot know for
- * sure. Jet print a warning and let it go. */
- }
-
- ts->hopping.enabled = enabled;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_hsn,
- cfg_ts_hsn_cmd,
- "hopping sequence-number <0-63>",
- HOPPING_STR
- "Which hopping sequence to use for this channel\n"
- "Hopping Sequence Number (HSN)\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- ts->hopping.hsn = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_maio,
- cfg_ts_maio_cmd,
- "hopping maio <0-63>",
- HOPPING_STR
- "Which hopping MAIO to use for this channel\n"
- "Mobile Allocation Index Offset (MAIO)\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- ts->hopping.maio = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_arfcn_add,
- cfg_ts_arfcn_add_cmd,
- "hopping arfcn add <0-1023>",
- HOPPING_STR "Configure hopping ARFCN list\n"
- "Add an entry to the hopping ARFCN list\n" "ARFCN\n")
-{
- enum gsm_band unused;
- struct gsm_bts_trx_ts *ts = vty->index;
- int arfcn = atoi(argv[0]);
-
- if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
- vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_arfcn_del,
- cfg_ts_arfcn_del_cmd,
- "hopping arfcn del <0-1023>",
- HOPPING_STR "Configure hopping ARFCN list\n"
- "Delete an entry to the hopping ARFCN list\n" "ARFCN\n")
-{
- enum gsm_band unused;
- struct gsm_bts_trx_ts *ts = vty->index;
- int arfcn = atoi(argv[0]);
-
- if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
- vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_arfcn_del_all,
- cfg_ts_arfcn_del_all_cmd,
- "hopping arfcn del-all",
- HOPPING_STR "Configure hopping ARFCN list\n"
- "Delete all previously configured entries\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- bitvec_zero(&ts->hopping.arfcns);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_e1_subslot,
- cfg_ts_e1_subslot_cmd,
- "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
- "E1/T1 channel connected to this on-air timeslot\n"
- "E1/T1 channel connected to this on-air timeslot\n"
- "E1/T1 line connected to this on-air timeslot\n"
- "E1/T1 timeslot connected to this on-air timeslot\n"
- "E1/T1 timeslot connected to this on-air timeslot\n"
- "E1/T1 sub-slot connected to this on-air timeslot\n"
- "E1/T1 sub-slot 0 connected to this on-air timeslot\n"
- "E1/T1 sub-slot 1 connected to this on-air timeslot\n"
- "E1/T1 sub-slot 2 connected to this on-air timeslot\n"
- "E1/T1 sub-slot 3 connected to this on-air timeslot\n"
- "Full E1/T1 timeslot connected to this on-air timeslot\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]);
-
- return CMD_SUCCESS;
-}
-
int print_counter(struct rate_ctr_group *bsc_ctrs, struct rate_ctr *ctr, const struct rate_ctr_desc *desc, void *data)
{
struct vty *vty = data;
@@ -4825,7 +1308,7 @@ DEFUN(restart_bts, restart_bts_cmd,
return CMD_WARNING;
}
- if (!is_ipaccess_bts(bts) || is_sysmobts_v2(bts)) {
+ if (!is_ipaccess_bts(bts) || is_osmobts(bts)) {
vty_out(vty, "%% This command only works for ipaccess nanoBTS.%s",
VTY_NEWLINE);
return CMD_WARNING;
@@ -4838,7 +1321,8 @@ DEFUN(restart_bts, restart_bts_cmd,
return CMD_SUCCESS;
}
-DEFUN(bts_resend, bts_resend_cmd,
+DEFUN(bts_resend_sysinfo,
+ bts_resend_sysinfo_cmd,
"bts <0-255> resend-system-information",
"BTS Specific Commands\n" BTS_NR_STR
"Re-generate + re-send BCCH SYSTEM INFORMATION\n")
@@ -4871,8 +1355,87 @@ DEFUN(bts_resend, bts_resend_cmd,
return CMD_SUCCESS;
}
+DEFUN(bts_resend_power_ctrl_params,
+ bts_resend_power_ctrl_params_cmd,
+ "bts <0-255> resend-power-control-defaults",
+ "BTS Specific Commands\n" BTS_NR_STR
+ "Re-generate + re-send default MS/BS Power control parameters\n")
+{
+ const struct gsm_bts_trx *trx;
+ const struct gsm_bts *bts;
+ int bts_nr = atoi(argv[0]);
+
+ bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
+ if (!bts) {
+ vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bts->ms_power_ctrl.mode != GSM_PWR_CTRL_MODE_DYN_BTS) {
+ vty_out(vty, "%% Not Sending default MS/BS Power control parameters "
+ "because BTS%d is not using dyn-bts mode%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bts->model->power_ctrl_send_def_params == NULL) {
+ vty_out(vty, "%% Sending default MS/BS Power control parameters "
+ "for BTS%d is not implemented%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (bts->model->power_ctrl_send_def_params(trx) != 0) {
+ vty_out(vty, "%% Failed to send default MS/BS Power control parameters "
+ "to BTS%d/TRX%d%s", bts_nr, trx->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(bts_c0_power_red,
+ bts_c0_power_red_cmd,
+ "bts <0-255> c0-power-reduction <0-6>",
+ "BTS Specific Commands\n" BTS_NR_STR
+ "BCCH carrier power reduction operation\n"
+ "Power reduction value (in dB, even numbers only)\n")
+{
+ int bts_nr = atoi(argv[0]);
+ int red = atoi(argv[1]);
+ struct gsm_bts *bts;
+ int rc;
+
+ bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
+ if (!bts) {
+ vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (red % 2 != 0) {
+ vty_out(vty, "%% Incorrect BCCH power reduction value, "
+ "an even number is expected%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ rc = gsm_bts_set_c0_power_red(bts, red);
+ if (rc == -ENOTSUP) {
+ vty_out(vty, "%% BCCH carrier power reduction operation mode "
+ "is not supported for BTS%u%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ } else if (rc != 0) {
+ vty_out(vty, "%% Failed to %sable BCCH carrier power reduction "
+ "operation mode for BTS%u%s", red ? "en" : "dis",
+ bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
-DEFUN(smscb_cmd, smscb_cmd_cmd,
+/* this command is now hidden, as it's a low-level debug hack, and people should
+ * instead use osmo-cbc these days */
+DEFUN_HIDDEN(smscb_cmd, smscb_cmd_cmd,
"bts <0-255> smscb-command (normal|schedule|default) <1-4> HEXSTRING",
"BTS related commands\n" BTS_NR_STR
"SMS Cell Broadcast\n"
@@ -4936,39 +1499,12 @@ DEFUN(smscb_cmd, smscb_cmd_cmd,
return CMD_WARNING;
}
+ /* SDCCH4 might not be correct here if the CBCH is on a SDCCH8? */
rsl_sms_cb_command(bts, RSL_CHAN_SDCCH4_ACCH, cb_cmd, false, buf, rc);
return CMD_SUCCESS;
}
-/* 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;
-}
-
DEFUN(pdch_act, pdch_act_cmd,
"bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)",
BTS_NR_TRX_TS_STR2
@@ -4991,9 +1527,9 @@ DEFUN(pdch_act, pdch_act_cmd,
return CMD_WARNING;
}
- if (ts->pchan_on_init != GSM_PCHAN_TCH_F_TCH_H_PDCH
+ if (ts->pchan_on_init != GSM_PCHAN_OSMO_DYN
&& ts->pchan_on_init != GSM_PCHAN_TCH_F_PDCH) {
- vty_out(vty, "%% Timeslot %u is not dynamic TCH/F_TCH/H_PDCH or TCH/F_PDCH%s",
+ vty_out(vty, "%% Timeslot %u is not dynamic TCH/F_TCH/H_SDCCH8_PDCH or TCH/F_PDCH%s",
ts->nr, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -5025,15 +1561,24 @@ DEFUN(pdch_act, pdch_act_cmd,
/* Activate / Deactivate a single lchan with a specific codec mode */
static int lchan_act_single(struct vty *vty, struct gsm_lchan *lchan, const char *codec_str, int amr_mode, int activate)
{
- struct lchan_activate_info info = { };
+ struct lchan_activate_info info = {0};
uint16_t amr_modes[8] =
{ GSM0808_SC_CFG_AMR_4_75, GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20, GSM0808_SC_CFG_AMR_5_90,
GSM0808_SC_CFG_AMR_6_70, GSM0808_SC_CFG_AMR_7_40, GSM0808_SC_CFG_AMR_7_95, GSM0808_SC_CFG_AMR_10_2,
GSM0808_SC_CFG_AMR_12_2 };
if (activate) {
+ if (!codec_str) {
+ vty_out(vty, "%% Error: need a channel type argument to activate%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
LOG_LCHAN(lchan, LOGL_NOTICE, "attempt from VTY to activate lchan %s with codec %s\n",
gsm_lchan_name(lchan), codec_str);
+ if (!lchan->fi) {
+ vty_out(vty, "%% Cannot activate: Channel not initialized%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
int lchan_t;
if (lchan->fi->state != LCHAN_ST_UNUSED) {
@@ -5046,10 +1591,10 @@ static int lchan_act_single(struct vty *vty, struct gsm_lchan *lchan, const char
if (lchan_t < 0) {
if (lchan->ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH && !strcmp(codec_str, "fr"))
lchan_t = GSM_LCHAN_TCH_F;
- else if (lchan->ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH && !strcmp(codec_str, "hr"))
+ else if (lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN && !strcmp(codec_str, "hr"))
lchan_t = GSM_LCHAN_TCH_H;
else if ((lchan->ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH
- || lchan->ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH)
+ || lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN)
&& !strcmp(codec_str, "fr"))
lchan_t = GSM_LCHAN_TCH_F;
else {
@@ -5060,42 +1605,40 @@ static int lchan_act_single(struct vty *vty, struct gsm_lchan *lchan, const char
}
/* configure the lchan */
- lchan->type = lchan_t;
+ lchan_select_set_type(lchan, lchan_t);
if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr")) {
- info = (struct lchan_activate_info) {
- .activ_for = FOR_VTY,
- .chan_mode = GSM48_CMODE_SPEECH_V1,
- .requires_voice_stream = false,
- };
+ info.ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1;
} else if (!strcmp(codec_str, "efr")) {
- info = (struct lchan_activate_info) {
- .activ_for = FOR_VTY,
- .chan_mode = GSM48_CMODE_SPEECH_EFR,
- .s15_s0 = amr_modes[amr_mode],
- .requires_voice_stream = false,
- };
+ info.ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_EFR;
} else if (!strcmp(codec_str, "amr")) {
if (amr_mode == -1) {
vty_out(vty, "%% AMR requires specification of AMR mode%s", VTY_NEWLINE);
return CMD_WARNING;
}
- info = (struct lchan_activate_info) {
- .activ_for = FOR_VTY,
- .chan_mode = GSM48_CMODE_SPEECH_AMR,
- .s15_s0 = amr_modes[amr_mode],
- .requires_voice_stream = false,
- };
+ info.ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_AMR;
+ info.ch_mode_rate.s15_s0 = amr_modes[amr_mode];
} else if (!strcmp(codec_str, "sig")) {
- info = (struct lchan_activate_info) {
- .activ_for = FOR_VTY,
- .chan_mode = GSM48_CMODE_SIGN,
- .requires_voice_stream = false,
- };
+ info.ch_mode_rate.chan_mode = GSM48_CMODE_SIGN;
} else {
vty_out(vty, "%% Invalid channel mode specified!%s", VTY_NEWLINE);
return CMD_WARNING;
}
+ info.activ_for = ACTIVATE_FOR_VTY;
+ info.requires_voice_stream = false;
+ info.ch_mode_rate.chan_rate = chan_t_to_chan_rate(lchan_t);
+
+ if (activate == 2 || lchan->vamos.is_secondary) {
+ info.vamos = true;
+ if (lchan->vamos.is_secondary) {
+ info.tsc_set.present = true;
+ info.tsc_set.val = 1;
+ }
+ info.tsc.present = true;
+ info.tsc.val = 0;
+ info.ch_mode_rate.chan_mode = gsm48_chan_mode_to_vamos(info.ch_mode_rate.chan_mode);
+ }
+
vty_out(vty, "%% activating lchan %s as %s%s", gsm_lchan_name(lchan), gsm_chan_t_name(lchan->type),
VTY_NEWLINE);
lchan_activate(lchan, &info);
@@ -5107,7 +1650,8 @@ static int lchan_act_single(struct vty *vty, struct gsm_lchan *lchan, const char
}
vty_out(vty, "%% Asking for release of %s in state %s%s", gsm_lchan_name(lchan),
osmo_fsm_inst_state_name(lchan->fi), VTY_NEWLINE);
- lchan_release(lchan, !!(lchan->conn), false, 0);
+ lchan_release(lchan, !!(lchan->conn), false, 0,
+ gscon_last_eutran_plmn(lchan->conn));
}
return CMD_SUCCESS;
@@ -5124,7 +1668,7 @@ static int lchan_act_trx(struct vty *vty, struct gsm_bts_trx *trx, int activate)
for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
ts = &trx->ts[ts_nr];
- ts_for_each_potential_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
switch (ts->pchan_on_init) {
case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
@@ -5135,7 +1679,7 @@ static int lchan_act_trx(struct vty *vty, struct gsm_bts_trx *trx, int activate)
break;
case GSM_PCHAN_TCH_F:
case GSM_PCHAN_TCH_F_PDCH:
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
codec_str = "fr";
break;
case GSM_PCHAN_TCH_H:
@@ -5148,9 +1692,9 @@ static int lchan_act_trx(struct vty *vty, struct gsm_bts_trx *trx, int activate)
if (codec_str && skip_next == false) {
lchan_act_single(vty, lchan, codec_str, -1, activate);
- /* We use GSM_PCHAN_TCH_F_TCH_H_PDCH slots as TCH_F for this test, so we
+ /* We use GSM_PCHAN_OSMO_DYN slots as TCH_F for this test, so we
* must not use the TCH_H reserved lchan in subslot 1. */
- if (ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH)
+ if (ts->pchan_on_init == GSM_PCHAN_OSMO_DYN)
skip_next = true;
}
else {
@@ -5163,41 +1707,83 @@ static int lchan_act_trx(struct vty *vty, struct gsm_bts_trx *trx, int activate)
return CMD_SUCCESS;
}
-/* Debug/Measurement command to activate a given logical channel
- * manually in a given mode/codec. This is useful for receiver
- * performance testing (FER/RBER/...) */
-DEFUN(lchan_act, lchan_act_cmd,
- "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> (activate|deactivate) (hr|fr|efr|amr|sig) [<0-7>]",
- BTS_NR_TRX_TS_SS_STR2
- "Manual Channel Activation (e.g. for BER test)\n"
- "Manual Channel Deactivation (e.g. for BER test)\n"
- "Half-Rate v1\n" "Full-Rate\n" "Enhanced Full Rate\n" "Adaptive Multi-Rate\n" "Signalling\n" "AMR Mode\n")
+static int lchan_act_deact(struct vty *vty, const char **argv, int argc)
{
struct gsm_bts_trx_ts *ts;
+ struct gsm_bts *bts;
struct gsm_lchan *lchan;
- int ss_nr = atoi(argv[3]);
- const char *act_str = argv[4];
- const char *codec_str = argv[5];
+ bool vamos = (strcmp(argv[3], "vamos-sub-slot") == 0);
+ int ss_nr = atoi(argv[4]);
+ const char *act_str = NULL;
+ const char *codec_str = NULL;
int activate;
int amr_mode = -1;
+ if (argc > 5)
+ act_str = argv[5];
if (argc > 6)
- amr_mode = atoi(argv[6]);
+ codec_str = argv[6];
+ if (argc > 7)
+ amr_mode = atoi(argv[7]);
ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
if (!ts)
return CMD_WARNING;
+ if (ss_nr >= ts->max_primary_lchans) {
+ vty_out(vty, "Invalid sub-slot number %d for this timeslot type: %s (%u)%s", ss_nr,
+ gsm_pchan_name(ts->pchan_on_init), ts->max_primary_lchans, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts = ts->trx->bts;
+ if (vamos && bts->features_known && !osmo_bts_has_feature(&bts->features, BTS_FEAT_VAMOS)) {
+ vty_out(vty, "BTS does not support VAMOS%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (vamos)
+ ss_nr += ts->max_primary_lchans;
+
lchan = &ts->lchan[ss_nr];
- if (!strcmp(act_str, "activate"))
+ if (!act_str)
+ activate = 0;
+ else if (!strcmp(act_str, "activate"))
activate = 1;
+ else if (!strcmp(act_str, "activate-vamos"))
+ activate = 2;
else
- activate = 0;
+ return CMD_WARNING;
return lchan_act_single(vty, lchan, codec_str, amr_mode, activate);
}
+/* Debug/Measurement command to activate a given logical channel
+ * manually in a given mode/codec. This is useful for receiver
+ * performance testing (FER/RBER/...) */
+DEFUN(lchan_act, lchan_act_cmd,
+ "bts <0-255> trx <0-255> timeslot <0-7> (sub-slot|vamos-sub-slot) <0-7> (activate|activate-vamos) (hr|fr|efr|amr|sig) [<0-7>]",
+ BTS_NR_TRX_TS_STR2
+ "Primary sub-slot\n" "VAMOS secondary shadow subslot, range <0-1>, only valid for TCH type timeslots\n"
+ SS_NR_STR
+ "Manual Channel Activation (e.g. for BER test)\n"
+ "Manual Channel Activation, in VAMOS mode\n"
+ "Half-Rate v1\n" "Full-Rate\n" "Enhanced Full Rate\n" "Adaptive Multi-Rate\n" "Signalling\n" "AMR Mode\n")
+{
+ return lchan_act_deact(vty, argv, argc);
+}
+
+DEFUN(lchan_deact, lchan_deact_cmd,
+ "bts <0-255> trx <0-255> timeslot <0-7> (sub-slot|vamos-sub-slot) <0-7> deactivate",
+ BTS_NR_TRX_TS_STR2
+ "Primary sub-slot\n" "VAMOS secondary shadow subslot, range <0-1>, only valid for TCH type timeslots\n"
+ SS_NR_STR
+ "Manual Channel Deactivation (e.g. for BER test)\n")
+{
+ return lchan_act_deact(vty, argv, argc);
+}
+
#define ACTIVATE_ALL_LCHANS_STR "Manual Channel Activation of all logical channels (e.g. for BER test)\n"
#define DEACTIVATE_ALL_LCHANS_STR "Manual Channel Deactivation of all logical channels (e.g. for BER test)\n"
@@ -5211,9 +1797,7 @@ DEFUN_HIDDEN(lchan_act_bts, lchan_act_all_cmd,
struct gsm_network *net = gsmnet_from_vty(vty);
const char *act_str = argv[0];
int activate;
- int bts_nr;
struct gsm_bts *bts;
- int trx_nr;
struct gsm_bts_trx *trx;
if (!strcmp(act_str, "activate-all-lchan"))
@@ -5221,14 +1805,17 @@ DEFUN_HIDDEN(lchan_act_bts, lchan_act_all_cmd,
else
activate = 0;
- for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
- bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
- for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
- trx = gsm_bts_trx_num(bts, trx_nr);
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ llist_for_each_entry(trx, &bts->trx_list, list)
lchan_act_trx(vty, trx, activate);
- }
}
+ vty_out(vty, "%% All channels have been %s on all BTS/TRX, please "
+ "make sure that the radio link timeout is set to %s%s",
+ activate ? "activated" : "deactivated",
+ activate ? "'infinite'" : "its old value (e.g. 'oml')",
+ VTY_NEWLINE);
+
return CMD_SUCCESS;
}
@@ -5263,6 +1850,12 @@ DEFUN_HIDDEN(lchan_act_all_bts, lchan_act_all_bts_cmd,
lchan_act_trx(vty, trx, activate);
}
+ vty_out(vty, "%% All channels have been %s on all TRX of BTS%d, please "
+ "make sure that the radio link timeout is set to %s%s",
+ activate ? "activated" : "deactivated", bts_nr,
+ activate ? "'infinite'" : "its old value (e.g. 'oml')",
+ VTY_NEWLINE);
+
return CMD_SUCCESS;
}
@@ -5301,9 +1894,102 @@ DEFUN_HIDDEN(lchan_act_all_trx, lchan_act_all_trx_cmd,
lchan_act_trx(vty, trx, activate);
+ vty_out(vty, "%% All channels have been %s on BTS%d/TRX%d, please "
+ "make sure that the radio link timeout is set to %s%s",
+ activate ? "activated" : "deactivated", bts_nr, trx_nr,
+ activate ? "'infinite'" : "its old value (e.g. 'oml')",
+ VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(lchan_set_mspower, lchan_set_mspower_cmd,
+ "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> ms-power <0-40> [verify]\n",
+ BTS_NR_TRX_TS_SS_STR2
+ "Manually force MS Uplink Power Level in dBm on the lchan (for testing)\n"
+ "Set transmit power of the MS in dBm\n"
+ "Check requested level against BAND and UE Power Class.\n")
+{
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+ struct gsm_lchan *lchan;
+ int bts_nr = atoi(argv[0]);
+ int trx_nr = atoi(argv[1]);
+ int ss_nr = atoi(argv[3]);
+ bool verify = (argc > 5);
+
+ bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
+ if (!bts) {
+ vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ trx = gsm_bts_trx_num(bts, trx_nr);
+ if (!trx) {
+ vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
+ if (!ts) {
+ vty_out(vty, "%% No such TS (%d)%s", atoi(argv[2]), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (ss_nr >= ts->max_primary_lchans) {
+ vty_out(vty, "%% Invalid sub-slot number for this timeslot type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ lchan = &ts->lchan[ss_nr];
+ if (!lchan->fi)
+ return CMD_WARNING;
+
+ if (verify) {
+ lchan_update_ms_power_ctrl_level(lchan, atoi(argv[4]));
+ return CMD_SUCCESS;
+ }
+ lchan->ms_power = ms_pwr_ctl_lvl(ts->trx->bts->band, atoi(argv[4]));
+ rsl_chan_ms_power_ctrl(lchan);
return CMD_SUCCESS;
}
+DEFUN(vamos_modify_lchan, vamos_modify_lchan_cmd,
+ "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> modify (vamos|non-vamos) " TSC_ARGS_OPT,
+ BTS_NR_TRX_TS_SS_STR2
+ "Manually send Channel Mode Modify (for debugging)\n"
+ "Enable VAMOS channel mode\n" "Disable VAMOS channel mode\n"
+ TSC_ARGS_DOC)
+{
+ struct gsm_bts_trx_ts *ts;
+ struct gsm_bts *bts;
+ struct gsm_lchan *lchan;
+ int ss_nr = atoi(argv[3]);
+ const char *vamos_str = argv[4];
+ /* argv[5] is the "tsc" string from TSC_ARGS_OPT */
+ int tsc_set = (argc > 6) ? atoi(argv[6]) : -1;
+ int tsc = (argc > 7) ? atoi(argv[7]) : -1;
+
+ ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
+ if (!ts)
+ return CMD_WARNING;
+
+ if (ss_nr >= ts->max_primary_lchans) {
+ vty_out(vty, "%% Invalid sub-slot number for this timeslot type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts = ts->trx->bts;
+ if (bts->features_known && !osmo_bts_has_feature(&bts->features, BTS_FEAT_VAMOS)) {
+ vty_out(vty, "%% BTS does not support VAMOS%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ lchan = &ts->lchan[ss_nr];
+
+ return trigger_vamos_mode_modify(vty, lchan, strcmp(vamos_str, "vamos") == 0, tsc_set, tsc);
+}
+
/* Debug command to send lchans from state LCHAN_ST_UNUSED to state
* LCHAN_ST_BORKEN and vice versa. */
DEFUN_HIDDEN(lchan_set_borken, lchan_set_borken_cmd,
@@ -5336,7 +2022,7 @@ DEFUN_HIDDEN(lchan_set_borken, lchan_set_borken_cmd,
}
} else {
if (lchan->fi->state == LCHAN_ST_BORKEN) {
- rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_LCHAN_BORKEN_EV_VTY]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(lchan->ts->trx->bts->bts_ctrs, BTS_CTR_LCHAN_BORKEN_EV_VTY));
osmo_fsm_inst_state_chg(lchan->fi, LCHAN_ST_UNUSED, 0, 0);
} else {
vty_out(vty,
@@ -5374,9 +2060,9 @@ DEFUN(lchan_mdcx, lchan_mdcx_cmd,
return CMD_WARNING;
}
- if (ss_nr >= pchan_subslots(ts->pchan_is)) {
+ if (ss_nr >= ts->max_primary_lchans) {
vty_out(vty, "%% subslot index %d too large for physical channel %s (%u slots)%s",
- ss_nr, gsm_pchan_name(ts->pchan_is), pchan_subslots(ts->pchan_is),
+ ss_nr, gsm_pchan_name(ts->pchan_is), ts->max_primary_lchans,
VTY_NEWLINE);
return CMD_WARNING;
}
@@ -5389,6 +2075,98 @@ DEFUN(lchan_mdcx, lchan_mdcx_cmd,
return CMD_SUCCESS;
}
+DEFUN(lchan_reassign, lchan_reassign_cmd,
+ "bts <0-255> trx <0-255> timeslot <0-7> (sub-slot|vamos-sub-slot) <0-7> "
+ "reassign-to trx <0-255> timeslot <0-7> (sub-slot|vamos-sub-slot) <0-7> "
+ TSC_ARGS_OPT,
+ BTS_NR_TRX_TS_STR2
+ "Primary sub-slot\n" "VAMOS secondary shadow subslot, range <0-1>, only valid for TCH type timeslots\n"
+ SS_NR_STR
+ "Trigger Assignment to an unused lchan on the same cell\n"
+ "Target TRX\nTRX nr\nTarget timeslot\ntimeslot nr\n"
+ "Primary sub-slot\n" "VAMOS secondary shadow subslot, range <0-1>, only valid for TCH type timeslots\n"
+ SS_NR_STR
+ TSC_ARGS_DOC)
+{
+ const char *bts_str = argv[0];
+ const char *from_trx_str = argv[1];
+ const char *from_ts_str = argv[2];
+ bool from_vamos = (strcmp(argv[3], "vamos-sub-slot") == 0);
+ int from_ss_nr = atoi(argv[4]);
+ const char *to_trx_str = argv[5];
+ const char *to_ts_str = argv[6];
+ bool to_vamos = (strcmp(argv[7], "vamos-sub-slot") == 0);
+ int to_ss_nr = atoi(argv[8]);
+ int tsc_set = (argc > 10) ? atoi(argv[10]) : -1;
+ int tsc = (argc > 11) ? atoi(argv[11]) : -1;
+
+ struct gsm_bts_trx_ts *from_ts;
+ struct gsm_bts_trx_ts *to_ts;
+ struct gsm_lchan *from_lchan;
+ struct gsm_lchan *to_lchan;
+
+ from_ts = vty_get_ts(vty, bts_str, from_trx_str, from_ts_str);
+ if (!from_ts)
+ return CMD_WARNING;
+ to_ts = vty_get_ts(vty, bts_str, to_trx_str, to_ts_str);
+ if (!to_ts)
+ return CMD_WARNING;
+
+ if (!ts_is_capable_of_pchan(to_ts, from_ts->pchan_is)) {
+ vty_out(vty, "cannot re-assign, target timeslot has mismatching physical channel config: %s -> %s%s",
+ gsm_pchan_name(from_ts->pchan_is), gsm_pchan_name(to_ts->pchan_on_init), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (from_ss_nr >= from_ts->max_primary_lchans) {
+ vty_out(vty, "cannot re-assign, invalid source subslot number: %d%s",
+ from_ss_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (to_ss_nr >= to_ts->max_primary_lchans) {
+ vty_out(vty, "cannot re-assign, invalid target subslot number: %d%s",
+ to_ss_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (from_vamos)
+ from_ss_nr += from_ts->max_primary_lchans;
+ from_lchan = &from_ts->lchan[from_ss_nr];
+
+ if (to_vamos)
+ to_ss_nr += to_ts->max_primary_lchans;
+ to_lchan = &to_ts->lchan[to_ss_nr];
+
+ if (!lchan_state_is(from_lchan, LCHAN_ST_ESTABLISHED)) {
+ vty_out(vty, "cannot re-assign, source lchan is not in ESTABLISHED state%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (!to_lchan->fi) {
+ vty_out(vty, "cannot re-assign, target lchan is not initialized%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (!lchan_state_is(to_lchan, LCHAN_ST_UNUSED)) {
+ vty_out(vty, "cannot re-assign, target lchan is already in use%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Set lchan type, so that activation will work out. */
+ lchan_select_set_type(to_lchan, chan_mode_to_chan_type(from_lchan->current_ch_mode_rate.chan_mode,
+ from_lchan->current_ch_mode_rate.chan_rate));
+
+ LOG_LCHAN(from_lchan, LOGL_NOTICE, "VTY requests re-assignment of this lchan to %s%s\n",
+ gsm_lchan_name(to_lchan), to_lchan->vamos.is_secondary ? " (to VAMOS mode)" : "");
+ LOG_LCHAN(to_lchan, LOGL_NOTICE, "VTY requests re-assignment of %s to this lchan%s TSC %d/%d\n",
+ gsm_lchan_name(from_lchan), to_lchan->vamos.is_secondary ? " (to VAMOS mode)" : "",
+ tsc_set, tsc);
+ if (reassignment_request_to_lchan(ASSIGN_FOR_VTY, from_lchan, to_lchan, tsc_set, tsc)) {
+ vty_out(vty, "failed to request re-assignment%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
DEFUN(ctrl_trap, ctrl_trap_cmd,
"ctrl-interface generate-trap TRAP VALUE",
"Commands related to the CTRL Interface\n"
@@ -5407,9 +2185,10 @@ DEFUN(ctrl_trap, ctrl_trap_cmd,
#define NAME_CMD_STR "Name Commands\n"
#define NAME_STR "Name to use\n"
-DEFUN(cfg_net,
- cfg_net_cmd,
- "network", NETWORK_STR)
+DEFUN_ATTR(cfg_net,
+ cfg_net_cmd,
+ "network", NETWORK_STR,
+ CMD_ATTR_IMMEDIATE)
{
vty->index = gsmnet_from_vty(vty);
vty->node = GSMNET_NODE;
@@ -5417,13 +2196,14 @@ DEFUN(cfg_net,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_ncc,
- cfg_net_ncc_cmd,
- "network country code <1-999>",
- "Set the GSM network country code\n"
- "Country commands\n"
- CODE_CMD_STR
- "Network Country Code to use\n")
+DEFUN_USRATTR(cfg_net_ncc,
+ cfg_net_ncc_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "network country code <1-999>",
+ "Set the GSM network country code\n"
+ "Country commands\n"
+ CODE_CMD_STR
+ "Network Country Code to use\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
uint16_t mcc;
@@ -5438,13 +2218,14 @@ DEFUN(cfg_net_ncc,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_mnc,
- cfg_net_mnc_cmd,
- "mobile network code <0-999>",
- "Set the GSM mobile network code\n"
- "Network Commands\n"
- CODE_CMD_STR
- "Mobile Network Code to use\n")
+DEFUN_USRATTR(cfg_net_mnc,
+ cfg_net_mnc_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "mobile network code <0-999>",
+ "Set the GSM mobile network code\n"
+ "Network Commands\n"
+ CODE_CMD_STR
+ "Mobile Network Code to use\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
uint16_t mnc;
@@ -5461,15 +2242,17 @@ DEFUN(cfg_net_mnc,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_encryption,
- cfg_net_encryption_cmd,
- "encryption a5 <0-3> [<0-3>] [<0-3>] [<0-3>]",
- "Encryption options\n"
- "GSM A5 Air Interface Encryption\n"
- "A5/n Algorithm Number\n"
- "A5/n Algorithm Number\n"
- "A5/n Algorithm Number\n"
- "A5/n Algorithm Number\n")
+DEFUN_USRATTR(cfg_net_encryption,
+ cfg_net_encryption_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "encryption a5 <0-4> [<0-4>] [<0-4>] [<0-4>] [<0-4>]",
+ "Encryption options\n"
+ "GSM A5 Air Interface Encryption\n"
+ "A5/n Algorithm Number\n"
+ "A5/n Algorithm Number\n"
+ "A5/n Algorithm Number\n"
+ "A5/n Algorithm Number\n"
+ "A5/n Algorithm Number\n")
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
unsigned int i;
@@ -5495,18 +2278,18 @@ DEFUN_DEPRECATED(cfg_net_dyn_ts_allow_tch_f,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_timezone,
- cfg_net_timezone_cmd,
- "timezone <-19-19> (0|15|30|45)",
- "Set the Timezone Offset of the network\n"
- "Timezone offset (hours)\n"
- "Timezone offset (00 minutes)\n"
- "Timezone offset (15 minutes)\n"
- "Timezone offset (30 minutes)\n"
- "Timezone offset (45 minutes)\n"
- )
+DEFUN_ATTR(cfg_net_timezone,
+ cfg_net_timezone_cmd,
+ "timezone <-19-19> (0|15|30|45)",
+ "Set the Timezone Offset of the network\n"
+ "Timezone offset (hours)\n"
+ "Timezone offset (00 minutes)\n"
+ "Timezone offset (15 minutes)\n"
+ "Timezone offset (30 minutes)\n"
+ "Timezone offset (45 minutes)\n",
+ CMD_ATTR_IMMEDIATE)
{
- struct gsm_network *net = vty->index;
+ struct gsm_network *net = gsmnet_from_vty(vty);
int tzhr = atoi(argv[0]);
int tzmn = atoi(argv[1]);
@@ -5518,19 +2301,19 @@ DEFUN(cfg_net_timezone,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_timezone_dst,
- cfg_net_timezone_dst_cmd,
- "timezone <-19-19> (0|15|30|45) <0-2>",
- "Set the Timezone Offset of the network\n"
- "Timezone offset (hours)\n"
- "Timezone offset (00 minutes)\n"
- "Timezone offset (15 minutes)\n"
- "Timezone offset (30 minutes)\n"
- "Timezone offset (45 minutes)\n"
- "DST offset (hours)\n"
- )
+DEFUN_ATTR(cfg_net_timezone_dst,
+ cfg_net_timezone_dst_cmd,
+ "timezone <-19-19> (0|15|30|45) <0-2>",
+ "Set the Timezone Offset of the network\n"
+ "Timezone offset (hours)\n"
+ "Timezone offset (00 minutes)\n"
+ "Timezone offset (15 minutes)\n"
+ "Timezone offset (30 minutes)\n"
+ "Timezone offset (45 minutes)\n"
+ "DST offset (hours)\n",
+ CMD_ATTR_IMMEDIATE)
{
- struct gsm_network *net = vty->index;
+ struct gsm_network *net = gsmnet_from_vty(vty);
int tzhr = atoi(argv[0]);
int tzmn = atoi(argv[1]);
int tzdst = atoi(argv[2]);
@@ -5543,27 +2326,30 @@ DEFUN(cfg_net_timezone_dst,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_no_timezone,
- cfg_net_no_timezone_cmd,
- "no timezone",
- NO_STR
- "Disable network timezone override, use system tz\n")
+DEFUN_ATTR(cfg_net_no_timezone,
+ cfg_net_no_timezone_cmd,
+ "no timezone",
+ NO_STR
+ "Disable network timezone override, use system tz\n",
+ CMD_ATTR_IMMEDIATE)
{
- struct gsm_network *net = vty->index;
+ struct gsm_network *net = gsmnet_from_vty(vty);
net->tz.override = 0;
return CMD_SUCCESS;
}
-DEFUN(cfg_net_per_loc_upd, cfg_net_per_loc_upd_cmd,
- "periodic location update <6-1530>",
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval in Minutes\n")
+DEFUN_USRATTR(cfg_net_per_loc_upd,
+ cfg_net_per_loc_upd_cmd,
+ BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK,
+ "periodic location update <6-1530>",
+ "Periodic Location Updating Interval\n"
+ "Periodic Location Updating Interval\n"
+ "Periodic Location Updating Interval\n"
+ "Periodic Location Updating Interval in Minutes\n")
{
- struct gsm_network *net = vty->index;
+ struct gsm_network *net = gsmnet_from_vty(vty);
struct osmo_tdef *d = osmo_tdef_get_entry(net->T_defs, 3212);
OSMO_ASSERT(d);
@@ -5572,14 +2358,16 @@ DEFUN(cfg_net_per_loc_upd, cfg_net_per_loc_upd_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_no_per_loc_upd, cfg_net_no_per_loc_upd_cmd,
- "no periodic location update",
- NO_STR
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval\n")
+DEFUN_USRATTR(cfg_net_no_per_loc_upd,
+ cfg_net_no_per_loc_upd_cmd,
+ BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK,
+ "no periodic location update",
+ NO_STR
+ "Periodic Location Updating Interval\n"
+ "Periodic Location Updating Interval\n"
+ "Periodic Location Updating Interval\n")
{
- struct gsm_network *net = vty->index;
+ struct gsm_network *net = gsmnet_from_vty(vty);
struct osmo_tdef *d = osmo_tdef_get_entry(net->T_defs, 3212);
OSMO_ASSERT(d);
@@ -5590,9 +2378,10 @@ DEFUN(cfg_net_no_per_loc_upd, cfg_net_no_per_loc_upd_cmd,
#define MEAS_FEED_STR "Measurement Report export\n"
-DEFUN(cfg_net_meas_feed_dest, cfg_net_meas_feed_dest_cmd,
- "meas-feed destination ADDR <0-65535>",
- MEAS_FEED_STR "Where to forward Measurement Report feeds\n" "address or hostname\n" "port number\n")
+DEFUN_ATTR(cfg_net_meas_feed_dest, cfg_net_meas_feed_dest_cmd,
+ "meas-feed destination ADDR <0-65535>",
+ MEAS_FEED_STR "Where to forward Measurement Report feeds\n" "address or hostname\n" "port number\n",
+ CMD_ATTR_IMMEDIATE)
{
int rc;
const char *host = argv[0];
@@ -5605,9 +2394,10 @@ DEFUN(cfg_net_meas_feed_dest, cfg_net_meas_feed_dest_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_meas_feed_scenario, cfg_net_meas_feed_scenario_cmd,
- "meas-feed scenario NAME",
- MEAS_FEED_STR "Set a name to include in the Measurement Report feeds\n" "Name string, up to 31 characters\n")
+DEFUN_ATTR(cfg_net_meas_feed_scenario, cfg_net_meas_feed_scenario_cmd,
+ "meas-feed scenario NAME",
+ MEAS_FEED_STR "Set a name to include in the Measurement Report feeds\n" "Name string, up to 31 characters\n",
+ CMD_ATTR_IMMEDIATE)
{
meas_feed_scenario_set(argv[0]);
@@ -5689,8 +2479,10 @@ static struct cmd_node msc_node = {
#define MSC_NR_RANGE "<0-1000>"
-DEFUN(cfg_net_msc, cfg_net_msc_cmd,
- "msc [" MSC_NR_RANGE "]", "Configure MSC details\n" "MSC connection to configure\n")
+DEFUN_ATTR(cfg_net_msc,
+ cfg_net_msc_cmd,
+ "msc [" MSC_NR_RANGE "]", "Configure MSC details\n" "MSC connection to configure\n",
+ CMD_ATTR_IMMEDIATE)
{
int index = argc == 1 ? atoi(argv[0]) : 0;
struct bsc_msc_data *msc;
@@ -5706,8 +2498,10 @@ DEFUN(cfg_net_msc, cfg_net_msc_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_bsc, cfg_net_bsc_cmd,
- "bsc", "Configure BSC\n")
+DEFUN_ATTR(cfg_net_bsc,
+ cfg_net_bsc_cmd,
+ "bsc", "Configure BSC\n",
+ CMD_ATTR_IMMEDIATE)
{
vty->node = BSC_NODE;
return CMD_SUCCESS;
@@ -5733,7 +2527,7 @@ static void write_msc_amr_options(struct vty *vty, struct bsc_msc_data *msc)
if (msc->amr_octet_aligned)
vty_out(vty, " amr-payload octet-aligned%s", VTY_NEWLINE);
else
- vty_out(vty, " amr-payload bandwith-efficient%s", VTY_NEWLINE);
+ vty_out(vty, " amr-payload bandwidth-efficient%s", VTY_NEWLINE);
}
static void msc_write_nri(struct vty *vty, struct bsc_msc_data *msc, bool verbose);
@@ -5747,12 +2541,6 @@ static void write_msc(struct vty *vty, struct bsc_msc_data *msc)
if (msc->core_plmn.mcc != GSM_MCC_MNC_INVALID)
vty_out(vty, " core-mobile-country-code %s%s",
osmo_mcc_name(msc->core_plmn.mcc), VTY_NEWLINE);
- if (msc->core_lac != -1)
- vty_out(vty, " core-location-area-code %d%s",
- msc->core_lac, VTY_NEWLINE);
- if (msc->core_ci != -1)
- vty_out(vty, " core-cell-identity %d%s",
- msc->core_ci, VTY_NEWLINE);
if (msc->audio_length != 0) {
int i;
@@ -5762,10 +2550,10 @@ static void write_msc(struct vty *vty, struct bsc_msc_data *msc)
if (i != 0)
vty_out(vty, " ");
- if (msc->audio_support[i]->hr)
- vty_out(vty, "hr%.1u", msc->audio_support[i]->ver);
+ if (msc->audio_support[i].hr)
+ vty_out(vty, "hr%u", msc->audio_support[i].ver);
else
- vty_out(vty, "fr%.1u", msc->audio_support[i]->ver);
+ vty_out(vty, "fr%u", msc->audio_support[i].ver);
}
vty_out(vty, "%s", VTY_NEWLINE);
@@ -5838,13 +2626,25 @@ static int config_write_bsc(struct vty *vty)
vty_out(vty, " bsc-auto-rf-off %d%s",
bsc_gsmnet->auto_off_timeout, VTY_NEWLINE);
+ if (bsc_gsmnet->bts_setup_ramp.enabled)
+ vty_out(vty, " bts-setup-ramping%s", VTY_NEWLINE);
+
+ if (bsc_gsmnet->bts_setup_ramp.step_size > 0)
+ vty_out(vty, " bts-setup-ramping-step-size %d%s",
+ bsc_gsmnet->bts_setup_ramp.step_size, VTY_NEWLINE);
+
+ if (bsc_gsmnet->bts_setup_ramp.step_interval > 0)
+ vty_out(vty, " bts-setup-ramping-step-interval %d%s",
+ bsc_gsmnet->bts_setup_ramp.step_interval, VTY_NEWLINE);
+
return CMD_SUCCESS;
}
-DEFUN(cfg_net_bsc_ncc,
- cfg_net_bsc_ncc_cmd,
- "core-mobile-network-code <1-999>",
- "Use this network code for the core network\n" "MNC value\n")
+DEFUN_ATTR(cfg_net_msc_ncc,
+ cfg_net_msc_ncc_cmd,
+ "core-mobile-network-code <1-999>",
+ "Use this network code for the core network\n" "MNC value\n",
+ CMD_ATTR_IMMEDIATE)
{
struct bsc_msc_data *data = bsc_msc_data(vty);
uint16_t mnc;
@@ -5859,10 +2659,11 @@ DEFUN(cfg_net_bsc_ncc,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_bsc_mcc,
- cfg_net_bsc_mcc_cmd,
- "core-mobile-country-code <1-999>",
- "Use this country code for the core network\n" "MCC value\n")
+DEFUN_ATTR(cfg_net_msc_mcc,
+ cfg_net_msc_mcc_cmd,
+ "core-mobile-country-code <1-999>",
+ "Use this country code for the core network\n" "MCC value\n",
+ CMD_ATTR_IMMEDIATE)
{
uint16_t mcc;
struct bsc_msc_data *data = bsc_msc_data(vty);
@@ -5874,28 +2675,26 @@ DEFUN(cfg_net_bsc_mcc,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_bsc_lac,
- cfg_net_bsc_lac_cmd,
- "core-location-area-code <0-65535>",
- "Use this location area code for the core network\n" "LAC value\n")
+DEFUN_DEPRECATED(cfg_net_msc_lac,
+ cfg_net_msc_lac_cmd,
+ "core-location-area-code <0-65535>",
+ "Legacy configuration that no longer has any effect\n-\n")
{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->core_lac = atoi(argv[0]);
+ vty_out(vty, "%% Deprecated 'core-location-area-code' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
-DEFUN(cfg_net_bsc_ci,
- cfg_net_bsc_ci_cmd,
- "core-cell-identity <0-65535>",
- "Use this cell identity for the core network\n" "CI value\n")
+DEFUN_DEPRECATED(cfg_net_msc_ci,
+ cfg_net_msc_ci_cmd,
+ "core-cell-identity <0-65535>",
+ "Legacy configuration that no longer has any effect\n-\n")
{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->core_ci = atoi(argv[0]);
+ vty_out(vty, "%% Deprecated 'core-cell-identity' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
-DEFUN_DEPRECATED(cfg_net_bsc_rtp_base,
- cfg_net_bsc_rtp_base_cmd,
+DEFUN_DEPRECATED(cfg_net_msc_rtp_base,
+ cfg_net_msc_rtp_base_cmd,
"ip.access rtp-base <1-65000>",
"deprecated\n" "deprecated, RTP is handled by the MGW\n" "deprecated\n")
{
@@ -5903,52 +2702,66 @@ DEFUN_DEPRECATED(cfg_net_bsc_rtp_base,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_bsc_codec_list,
- cfg_net_bsc_codec_list_cmd,
- "codec-list .LIST",
- "Set the allowed audio codecs\n"
- "List of audio codecs, e.g. fr3 fr1 hr3\n")
+DEFUN_USRATTR(cfg_net_msc_codec_list,
+ cfg_net_msc_codec_list_cmd,
+ BSC_VTY_ATTR_NEW_LCHAN,
+ "codec-list .LIST",
+ "Set the allowed audio codecs and their order of preference\n"
+ "List of audio codecs in order of preference, e.g. 'codec-list fr3 fr2 fr1 hr3 hr1'."
+ " (fr3: AMR-FR, hr3: AMR-HR, fr2: GSM-EFR, fr1: GSM-FR, hr1: GSM-HR)\n")
{
struct bsc_msc_data *data = bsc_msc_data(vty);
+ struct gsm_audio_support tmp[ARRAY_SIZE(data->audio_support)];
int i;
- /* free the old list... if it exists */
- if (data->audio_support) {
- talloc_free(data->audio_support);
- data->audio_support = NULL;
- data->audio_length = 0;
+ /* Nr of arguments must fit in the array */
+ if (argc > ARRAY_SIZE(data->audio_support)) {
+ vty_out(vty, "Too many items in 'msc' / 'codec-list': %d. There can be at most %zu entries.%s",
+ argc, ARRAY_SIZE(data->audio_support), VTY_NEWLINE);
+ return CMD_ERR_EXEED_ARGC_MAX;
}
- /* create a new array */
- data->audio_support =
- talloc_zero_array(bsc_gsmnet, struct gsm_audio_support *, argc);
- data->audio_length = argc;
+ /* check all given arguments first */
+ for (i = 0; i < argc; i++) {
+ int j;
- for (i = 0; i < argc; ++i) {
/* check for hrX or frX */
if (strlen(argv[i]) != 3
|| argv[i][1] != 'r'
|| (argv[i][0] != 'h' && argv[i][0] != 'f')
- || argv[i][2] < 0x30
- || argv[i][2] > 0x39)
- goto error;
-
- data->audio_support[i] = talloc_zero(data->audio_support,
- struct gsm_audio_support);
- data->audio_support[i]->ver = atoi(argv[i] + 2);
+ || argv[i][2] < '0'
+ || argv[i][2] > '9') {
+ vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s", argv[i], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ /* store in tmp[] first, to not overwrite data->audio_support[] in case of error */
+ tmp[i].ver = atoi(argv[i] + 2);
if (strncmp("hr", argv[i], 2) == 0)
- data->audio_support[i]->hr = 1;
+ tmp[i].hr = 1;
else if (strncmp("fr", argv[i], 2) == 0)
- data->audio_support[i]->hr = 0;
+ tmp[i].hr = 0;
+
+ /* forbid invalid versions */
+ if (tmp[i].ver < 1 || tmp[i].ver > 7
+ || (tmp[i].hr && tmp[i].ver == 2)) {
+ vty_out(vty, "'%s' is not a valid codec version%s", argv[i], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* prevent duplicate entries */
+ for (j = 0; j < i; j++) {
+ if (gsm_audio_support_cmp(&tmp[j], &tmp[i]) == 0) {
+ vty_out(vty, "duplicate entry in 'msc' / 'codec-support': %s%s", argv[i], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
}
- return CMD_SUCCESS;
+ memcpy(data->audio_support, tmp, sizeof(data->audio_support));
+ data->audio_length = argc;
-error:
- vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s",
- argv[i], VTY_NEWLINE);
- return CMD_ERR_INCOMPLETE;
+ return CMD_SUCCESS;
}
#define LEGACY_STR "This command has no effect, it is kept to support legacy config files\n"
@@ -6005,11 +2818,12 @@ DEFUN_DEPRECATED(cfg_net_msc_type,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_msc_emerg,
- cfg_net_msc_emerg_cmd,
- "allow-emergency (allow|deny)",
- "Allow CM ServiceRequests with type emergency\n"
- "Allow\n" "Deny\n")
+DEFUN_ATTR(cfg_net_msc_emerg,
+ cfg_net_msc_emerg_cmd,
+ "allow-emergency (allow|deny)",
+ "Allow CM ServiceRequests with type emergency\n"
+ "Allow\n" "Deny\n",
+ CMD_ATTR_IMMEDIATE)
{
struct bsc_msc_data *data = bsc_msc_data(vty);
data->allow_emerg = strcmp("allow", argv[0]) == 0;
@@ -6018,8 +2832,8 @@ DEFUN(cfg_net_msc_emerg,
#define AMR_CONF_STR "AMR Multirate Configuration\n"
#define AMR_COMMAND(name) \
- DEFUN(cfg_net_msc_amr_##name, \
- cfg_net_msc_amr_##name##_cmd, \
+ DEFUN_USRATTR(cfg_net_msc_amr_##name, \
+ cfg_net_msc_amr_##name##_cmd,BSC_VTY_ATTR_NEW_LCHAN, \
"amr-config " #name "k (allowed|forbidden)", \
AMR_CONF_STR "Bitrate\n" "Allowed\n" "Forbidden\n") \
{ \
@@ -6131,25 +2945,27 @@ DEFUN(cfg_msc_cs7_asp_proto,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_msc_lcls_mode,
- cfg_net_msc_lcls_mode_cmd,
- "lcls-mode (disabled|mgw-loop|bts-loop)",
- "Configure 3GPP LCLS (Local Call, Local Switch)\n"
- "Disable LCLS for all calls of this MSC\n"
- "Enable LCLS with looping traffic in MGW\n"
- "Enable LCLS with looping traffic between BTS\n")
+DEFUN_USRATTR(cfg_net_msc_lcls_mode,
+ cfg_net_msc_lcls_mode_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "lcls-mode (disabled|mgw-loop|bts-loop)",
+ "Configure 3GPP LCLS (Local Call, Local Switch)\n"
+ "Disable LCLS for all calls of this MSC\n"
+ "Enable LCLS with looping traffic in MGW\n"
+ "Enable LCLS with looping traffic between BTS\n")
{
struct bsc_msc_data *data = bsc_msc_data(vty);
data->lcls_mode = get_string_value(bsc_lcls_mode_names, argv[0]);
return CMD_SUCCESS;
}
-DEFUN(cfg_net_msc_lcls_mismtch,
- cfg_net_msc_lcls_mismtch_cmd,
- "lcls-codec-mismatch (allowed|forbidden)",
- "Allow 3GPP LCLS (Local Call, Local Switch) when call legs use different codec/rate\n"
- "Allow LCLS only only for calls that use the same codec/rate on both legs\n"
- "Do not Allow LCLS for calls that use a different codec/rate on both legs\n")
+DEFUN_USRATTR(cfg_net_msc_lcls_mismtch,
+ cfg_net_msc_lcls_mismtch_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "lcls-codec-mismatch (allowed|forbidden)",
+ "Allow 3GPP LCLS (Local Call, Local Switch) when call legs use different codec/rate\n"
+ "Allow LCLS only only for calls that use the same codec/rate on both legs\n"
+ "Do not Allow LCLS for calls that use a different codec/rate on both legs\n")
{
struct bsc_msc_data *data = bsc_msc_data(vty);
@@ -6161,15 +2977,16 @@ DEFUN(cfg_net_msc_lcls_mismtch,
return CMD_SUCCESS;
}
-DEFUN(cfg_msc_mgw_x_osmo_ign,
- cfg_msc_mgw_x_osmo_ign_cmd,
- "mgw x-osmo-ign call-id",
- MGCP_CLIENT_MGW_STR
- "Set a (non-standard) X-Osmo-IGN header in all CRCX messages for RTP streams"
- " associated with this MSC, useful for A/SCCPlite MSCs, since osmo-bsc cannot know"
- " the MSC's chosen CallID. This is enabled by default for A/SCCPlite connections,"
- " disabled by default for all others.\n"
- "Send 'X-Osmo-IGN: C' to ignore CallID mismatches. See OsmoMGW.\n")
+DEFUN_USRATTR(cfg_msc_mgw_x_osmo_ign,
+ cfg_msc_mgw_x_osmo_ign_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "mgw x-osmo-ign call-id",
+ MGCP_CLIENT_MGW_STR
+ "Set a (non-standard) X-Osmo-IGN header in all CRCX messages for RTP streams"
+ " associated with this MSC, useful for A/SCCPlite MSCs, since osmo-bsc cannot know"
+ " the MSC's chosen CallID. This is enabled by default for A/SCCPlite connections,"
+ " disabled by default for all others.\n"
+ "Send 'X-Osmo-IGN: C' to ignore CallID mismatches. See OsmoMGW.\n")
{
struct bsc_msc_data *msc = bsc_msc_data(vty);
msc->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
@@ -6177,12 +2994,13 @@ DEFUN(cfg_msc_mgw_x_osmo_ign,
return CMD_SUCCESS;
}
-DEFUN(cfg_msc_no_mgw_x_osmo_ign,
- cfg_msc_no_mgw_x_osmo_ign_cmd,
- "no mgw x-osmo-ign",
- NO_STR
- MGCP_CLIENT_MGW_STR
- "Do not send X-Osmo-IGN MGCP header to this MSC\n")
+DEFUN_USRATTR(cfg_msc_no_mgw_x_osmo_ign,
+ cfg_msc_no_mgw_x_osmo_ign_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no mgw x-osmo-ign",
+ NO_STR
+ MGCP_CLIENT_MGW_STR
+ "Do not send X-Osmo-IGN MGCP header to this MSC\n")
{
struct bsc_msc_data *msc = bsc_msc_data(vty);
msc->x_osmo_ign = 0;
@@ -6191,10 +3009,11 @@ DEFUN(cfg_msc_no_mgw_x_osmo_ign,
}
#define OSMUX_STR "RTP multiplexing\n"
-DEFUN(cfg_msc_osmux,
- cfg_msc_osmux_cmd,
- "osmux (on|off|only)",
- OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only use OSMUX\n")
+DEFUN_USRATTR(cfg_msc_osmux,
+ cfg_msc_osmux_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "osmux (on|off|only)",
+ OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only use OSMUX\n")
{
struct bsc_msc_data *msc = bsc_msc_data(vty);
if (strcmp(argv[0], "off") == 0)
@@ -6212,10 +3031,11 @@ ALIAS_DEPRECATED(deprecated_ussd_text,
"mid-call-text .TEXT",
LEGACY_STR LEGACY_STR);
-DEFUN(cfg_net_bsc_mid_call_timeout,
- cfg_net_bsc_mid_call_timeout_cmd,
- "mid-call-timeout NR",
- "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n")
+DEFUN_ATTR(cfg_net_bsc_mid_call_timeout,
+ cfg_net_bsc_mid_call_timeout_cmd,
+ "mid-call-timeout NR",
+ "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n",
+ CMD_ATTR_IMMEDIATE)
{
bsc_gsmnet->mid_call_timeout = atoi(argv[0]);
return CMD_SUCCESS;
@@ -6230,24 +3050,108 @@ DEFUN(cfg_net_rf_socket,
return CMD_SUCCESS;
}
-DEFUN(cfg_net_rf_off_time,
- cfg_net_rf_off_time_cmd,
- "bsc-auto-rf-off <1-65000>",
- "Disable RF on MSC Connection\n" "Timeout\n")
+DEFUN_ATTR(cfg_net_rf_off_time,
+ cfg_net_rf_off_time_cmd,
+ "bsc-auto-rf-off <1-65000>",
+ "Disable RF on MSC Connection\n" "Timeout\n",
+ CMD_ATTR_IMMEDIATE)
{
bsc_gsmnet->auto_off_timeout = atoi(argv[0]);
return CMD_SUCCESS;
}
-DEFUN(cfg_net_no_rf_off_time,
- cfg_net_no_rf_off_time_cmd,
- "no bsc-auto-rf-off",
- NO_STR "Disable RF on MSC Connection\n")
+DEFUN_ATTR(cfg_net_no_rf_off_time,
+ cfg_net_no_rf_off_time_cmd,
+ "no bsc-auto-rf-off",
+ NO_STR "Disable RF on MSC Connection\n",
+ CMD_ATTR_IMMEDIATE)
{
bsc_gsmnet->auto_off_timeout = -1;
return CMD_SUCCESS;
}
+DEFUN_ATTR(cfg_bsc_bts_setup_ramping,
+ cfg_bsc_bts_setup_ramping_cmd,
+ "bts-setup-ramping",
+ "Enable BTS setup ramping to limit the amount of BTS to configure within a time window.\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ bts_setup_ramp_enable(net);
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bsc_no_bts_setup_ramping,
+ cfg_bsc_no_bts_setup_ramping_cmd,
+ "no bts-setup-ramping",
+ NO_STR
+ "Disable BTS ramping and configure all waiting BTS.\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ bts_setup_ramp_disable(net);
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bsc_bts_ramping_step_interval,
+ cfg_bsc_bts_setup_ramping_step_interval_cmd,
+ "bts-setup-ramping-step-interval <0-65535>",
+ "Configure the BTS setup ramping step interval. The time between ramping steps.\n"
+ "Set a step interval (in seconds)\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ int interval_size = atoi(argv[0]);
+ bts_setup_ramp_set_step_interval(net, interval_size);
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bsc_bts_ramping_step_size,
+ cfg_bsc_bts_setup_ramping_step_size_cmd,
+ "bts-setup-ramping-step-size <0-65535>",
+ "Configure the BTS setup ramping step size. The amount of BTS to allow to configure within a ramping interval\n"
+ "Amount of BTS to setup while a step size\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ int seconds = atoi(argv[0]);
+ bts_setup_ramp_set_step_size(net, seconds);
+ return CMD_SUCCESS;
+}
+
+DEFUN(bts_unblock_setup_ramping,
+ bts_unblock_setup_ramping_cmd,
+ "bts <0-255> unblock-setup-ramping",
+ "BTS Specific Commands\n" BTS_NR_STR
+ "Unblock and allow to configure a BTS if kept back by BTS ramping\n")
+{
+ struct gsm_network *gsmnet;
+ struct gsm_bts *bts;
+ unsigned int bts_nr;
+
+ gsmnet = gsmnet_from_vty(vty);
+
+ bts_nr = atoi(argv[0]);
+ if (bts_nr >= gsmnet->num_bts) {
+ vty_out(vty, "%% BTS number must be between 0 and %d. It was %d.%s",
+ gsmnet->num_bts, bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts = gsm_bts_num(gsmnet, bts_nr);
+ if (!bts) {
+ vty_out(vty, "%% BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bts_setup_ramp_unblock_bts(bts)) {
+ vty_out(vty, "%% The BTS is not blocked by BTS ramping.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN(show_statistics,
show_statistics_cmd,
"show statistics",
@@ -6411,27 +3315,40 @@ DEFUN_DEPRECATED(cfg_net_msc_dest, cfg_net_msc_dest_cmd,
ALIAS_DEPRECATED(cfg_net_msc_dest, cfg_net_msc_no_dest_cmd,
"no dest A.B.C.D <1-65000> <0-255>", NO_STR LEGACY_STR "-\n" "-\n" "-\n");
-DEFUN(cfg_net_msc_amr_octet_align,
- cfg_net_msc_amr_octet_align_cmd,
- "amr-payload (octet-aligned|bandwith-efficient",
- "Set AMR payload framing mode\n"
- "payload fields aligned on octet boundaries\n"
- "payload fields packed (AoIP)\n")
+DEFUN_USRATTR(cfg_net_msc_amr_octet_align,
+ cfg_net_msc_amr_octet_align_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr-payload (octet-aligned|bandwidth-efficient)",
+ "Set AMR payload framing mode\n"
+ "payload fields aligned on octet boundaries\n"
+ "payload fields packed (AoIP)\n")
{
struct bsc_msc_data *data = bsc_msc_data(vty);
if (strcmp(argv[0], "octet-aligned") == 0)
data->amr_octet_aligned = true;
- else if (strcmp(argv[0], "bandwith-efficient") == 0)
+ else if (strcmp(argv[0], "bandwidth-efficient") == 0)
+ data->amr_octet_aligned = false;
+ else {
data->amr_octet_aligned = false;
+ vty_out(vty, "%% Command 'amr-payload': Option 'bandwith-efficient' "
+ "containing typo is deprecated, use 'bandwidth-efficient' instead!%s",
+ VTY_NEWLINE);
+ }
return CMD_SUCCESS;
}
+ALIAS_DEPRECATED(cfg_net_msc_amr_octet_align,
+ cfg_net_msc_amr_octet_align_deprecated_cmd,
+ "amr-payload bandwith-efficient",
+ "Set AMR payload framing mode\n"
+ "payload fields packed (AoIP)\n");
-DEFUN(cfg_msc_nri_add, cfg_msc_nri_add_cmd,
- "nri add <0-32767> [<0-32767>]",
- NRI_STR "Add NRI value or range to the NRI mapping for this MSC\n"
- NRI_FIRST_LAST_STR)
+DEFUN_ATTR(cfg_msc_nri_add, cfg_msc_nri_add_cmd,
+ "nri add <0-32767> [<0-32767>]",
+ NRI_STR "Add NRI value or range to the NRI mapping for this MSC\n"
+ NRI_FIRST_LAST_STR,
+ CMD_ATTR_IMMEDIATE)
{
struct bsc_msc_data *msc = bsc_msc_data(vty);
struct bsc_msc_data *other_msc;
@@ -6466,10 +3383,11 @@ DEFUN(cfg_msc_nri_add, cfg_msc_nri_add_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_msc_nri_del, cfg_msc_nri_del_cmd,
- "nri del <0-32767> [<0-32767>]",
- NRI_STR "Remove NRI value or range from the NRI mapping for this MSC\n"
- NRI_FIRST_LAST_STR)
+DEFUN_ATTR(cfg_msc_nri_del, cfg_msc_nri_del_cmd,
+ "nri del <0-32767> [<0-32767>]",
+ NRI_STR "Remove NRI value or range from the NRI mapping for this MSC\n"
+ NRI_FIRST_LAST_STR,
+ CMD_ATTR_IMMEDIATE)
{
struct bsc_msc_data *msc = bsc_msc_data(vty);
int rc;
@@ -6484,22 +3402,24 @@ DEFUN(cfg_msc_nri_del, cfg_msc_nri_del_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_msc_allow_attach, cfg_msc_allow_attach_cmd,
- "allow-attach",
- "Allow this MSC to attach new subscribers (default).\n")
+DEFUN_ATTR(cfg_msc_allow_attach, cfg_msc_allow_attach_cmd,
+ "allow-attach",
+ "Allow this MSC to attach new subscribers (default).\n",
+ CMD_ATTR_IMMEDIATE)
{
struct bsc_msc_data *msc = bsc_msc_data(vty);
msc->allow_attach = true;
return CMD_SUCCESS;
}
-DEFUN(cfg_msc_no_allow_attach, cfg_msc_no_allow_attach_cmd,
- "no allow-attach",
- NO_STR
- "Do not assign new subscribers to this MSC."
- " Useful if an MSC in an MSC pool is configured to off-load subscribers."
- " The MSC will still be operational for already IMSI-Attached subscribers,"
- " but the NAS node selection function will skip this MSC for new subscribers\n")
+DEFUN_ATTR(cfg_msc_no_allow_attach, cfg_msc_no_allow_attach_cmd,
+ "no allow-attach",
+ NO_STR
+ "Do not assign new subscribers to this MSC."
+ " Useful if an MSC in an MSC pool is configured to off-load subscribers."
+ " The MSC will still be operational for already IMSI-Attached subscribers,"
+ " but the NAS node selection function will skip this MSC for new subscribers\n",
+ CMD_ATTR_IMMEDIATE)
{
struct bsc_msc_data *msc = bsc_msc_data(vty);
msc->allow_attach = false;
@@ -6572,30 +3492,30 @@ DEFUN_HIDDEN(mscpool_roundrobin_next, mscpool_roundrobin_next_cmd,
return CMD_SUCCESS;
}
-int bsc_vty_init(struct gsm_network *network)
+DEFUN(msc_bssmap_reset, msc_bssmap_reset_cmd,
+ "msc " MSC_NR_RANGE " bssmap reset",
+ "Query or manipulate a specific A-interface link\n"
+ "MSC nr\n"
+ "Query or manipulate BSSMAP layer of A-interface\n"
+ "Flip this MSC to disconnected state and re-send BSSMAP RESET\n")
{
- cfg_ts_pchan_cmd.string =
- vty_cmd_string_from_valstr(tall_bsc_ctx,
- gsm_pchant_names,
- "phys_chan_config (", "|", ")",
- VTY_DO_LOWER);
- cfg_ts_pchan_cmd.doc =
- vty_cmd_string_from_valstr(tall_bsc_ctx,
- gsm_pchant_descs,
- "Physical Channel Combination\n",
- "\n", "", 0);
-
- cfg_bts_type_cmd.string =
- vty_cmd_string_from_valstr(tall_bsc_ctx,
- bts_type_names,
- "type (", "|", ")",
- VTY_DO_LOWER);
- cfg_bts_type_cmd.doc =
- vty_cmd_string_from_valstr(tall_bsc_ctx,
- bts_type_descs,
- "BTS Vendor/Type\n",
- "\n", "", 0);
+ int msc_nr = atoi(argv[0]);
+ struct bsc_msc_data *msc;
+
+ msc = osmo_msc_data_find(bsc_gsmnet, msc_nr);
+
+ if (!msc) {
+ vty_out(vty, "%% No such MSC: nr %d\n", msc_nr);
+ return CMD_WARNING;
+ }
+
+ LOGP(DMSC, LOGL_NOTICE, "(msc%d) VTY requests BSSMAP RESET\n", msc_nr);
+ bssmap_reset_resend_reset(msc->a.bssmap_reset);
+ return CMD_SUCCESS;
+}
+int bsc_vty_init(struct gsm_network *network)
+{
OSMO_ASSERT(vty_global_gsm_network == NULL);
vty_global_gsm_network = network;
@@ -6652,167 +3572,26 @@ int bsc_vty_init(struct gsm_network *network)
install_element(GSMNET_NODE, &cfg_net_nri_null_add_cmd);
install_element(GSMNET_NODE, &cfg_net_nri_null_del_cmd);
- install_element(GSMNET_NODE, &cfg_bts_cmd);
- install_node(&bts_node, config_write_bts);
- install_element(BTS_NODE, &cfg_bts_type_cmd);
- install_element(BTS_NODE, &cfg_description_cmd);
- install_element(BTS_NODE, &cfg_no_description_cmd);
- install_element(BTS_NODE, &cfg_bts_band_cmd);
- install_element(BTS_NODE, &cfg_bts_ci_cmd);
- install_element(BTS_NODE, &cfg_bts_dtxu_cmd);
- install_element(BTS_NODE, &cfg_bts_dtxd_cmd);
- install_element(BTS_NODE, &cfg_bts_no_dtxu_cmd);
- install_element(BTS_NODE, &cfg_bts_no_dtxd_cmd);
- install_element(BTS_NODE, &cfg_bts_lac_cmd);
- install_element(BTS_NODE, &cfg_bts_tsc_cmd);
- install_element(BTS_NODE, &cfg_bts_bsic_cmd);
- install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
- install_element(BTS_NODE, &cfg_bts_deprecated_unit_id_cmd);
- install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd);
- install_element(BTS_NODE, &cfg_bts_deprecated_rsl_ip_cmd);
- install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd);
- install_element(BTS_NODE, &cfg_bts_nokia_site_no_loc_rel_cnf_cmd);
- install_element(BTS_NODE, &cfg_bts_nokia_site_bts_reset_timer_cnf_cmd);
- install_element(BTS_NODE, &cfg_bts_stream_id_cmd);
- install_element(BTS_NODE, &cfg_bts_deprecated_stream_id_cmd);
- install_element(BTS_NODE, &cfg_bts_oml_e1_cmd);
- install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd);
- install_element(BTS_NODE, &cfg_bts_challoc_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd);
- install_element(BTS_NODE, &cfg_bts_chan_desc_att_cmd);
- install_element(BTS_NODE, &cfg_bts_chan_dscr_att_cmd);
- install_element(BTS_NODE, &cfg_bts_chan_desc_bs_pa_mfrms_cmd);
- install_element(BTS_NODE, &cfg_bts_chan_dscr_bs_pa_mfrms_cmd);
- install_element(BTS_NODE, &cfg_bts_chan_desc_bs_ag_blks_res_cmd);
- install_element(BTS_NODE, &cfg_bts_chan_dscr_bs_ag_blks_res_cmd);
- install_element(BTS_NODE, &cfg_bts_ccch_load_ind_thresh_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd);
- install_element(BTS_NODE, &cfg_bts_cell_barred_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_ac_class_cmd);
- install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd);
- install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd);
- install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd);
- install_element(BTS_NODE, &cfg_bts_cell_bar_qualify_cmd);
- install_element(BTS_NODE, &cfg_bts_cell_resel_ofs_cmd);
- install_element(BTS_NODE, &cfg_bts_temp_ofs_cmd);
- install_element(BTS_NODE, &cfg_bts_temp_ofs_inf_cmd);
- install_element(BTS_NODE, &cfg_bts_penalty_time_cmd);
- install_element(BTS_NODE, &cfg_bts_penalty_time_rsvd_cmd);
- install_element(BTS_NODE, &cfg_bts_radio_link_timeout_cmd);
- install_element(BTS_NODE, &cfg_bts_radio_link_timeout_inf_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_11bit_rach_support_for_egprs_cmd);
- install_element(BTS_NODE, &cfg_bts_no_gprs_egprs_pkt_chan_req_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_egprs_pkt_chan_req_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_ctrl_ack_cmd);
- install_element(BTS_NODE, &cfg_no_bts_gprs_ctrl_ack_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd);
- install_element(BTS_NODE, &cfg_bts_pag_free_cmd);
- install_element(BTS_NODE, &cfg_bts_si_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_si_static_cmd);
- install_element(BTS_NODE, &cfg_bts_si_unused_send_empty_cmd);
- install_element(BTS_NODE, &cfg_bts_no_si_unused_send_empty_cmd);
- install_element(BTS_NODE, &cfg_bts_early_cm_cmd);
- install_element(BTS_NODE, &cfg_bts_early_cm_3g_cmd);
- install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_neigh_cmd);
- install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd);
- install_element(BTS_NODE, &cfg_bts_si2quater_neigh_add_cmd);
- install_element(BTS_NODE, &cfg_bts_si2quater_neigh_del_cmd);
- install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_add_cmd);
- install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_del_cmd);
- install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd);
- install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd);
- install_element(BTS_NODE, &cfg_bts_force_comb_si_cmd);
- install_element(BTS_NODE, &cfg_bts_no_force_comb_si_cmd);
- install_element(BTS_NODE, &cfg_bts_codec0_cmd);
- install_element(BTS_NODE, &cfg_bts_codec1_cmd);
- install_element(BTS_NODE, &cfg_bts_codec2_cmd);
- install_element(BTS_NODE, &cfg_bts_codec3_cmd);
- install_element(BTS_NODE, &cfg_bts_codec4_cmd);
- install_element(BTS_NODE, &cfg_bts_depends_on_cmd);
- install_element(BTS_NODE, &cfg_bts_no_depends_on_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_modes1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_modes2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_modes3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_modes4_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_thres1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_thres2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_thres3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_hyst1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_hyst2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_hyst3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_start_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_modes1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_modes2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_modes3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_modes4_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_thres1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_thres2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_thres3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_hyst1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_hyst2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_pcu_sock_cmd);
- install_element(BTS_NODE, &cfg_bts_acc_rotate_cmd);
- install_element(BTS_NODE, &cfg_bts_acc_rotate_quantum_cmd);
- install_element(BTS_NODE, &cfg_bts_acc_ramping_cmd);
- install_element(BTS_NODE, &cfg_bts_no_acc_ramping_cmd);
- install_element(BTS_NODE, &cfg_bts_acc_ramping_step_interval_cmd);
- install_element(BTS_NODE, &cfg_bts_acc_ramping_step_size_cmd);
- install_element(BTS_NODE, &cfg_bts_acc_ramping_chan_load_cmd);
- install_element(BTS_NODE, &cfg_bts_t3113_dynamic_cmd);
- install_element(BTS_NODE, &cfg_bts_no_t3113_dynamic_cmd);
- neighbor_ident_vty_init(network, network->neighbor_bss_cells);
- /* See also handover commands added on bts level from handover_vty.c */
-
- install_element(BTS_NODE, &cfg_trx_cmd);
- install_node(&trx_node, dummy_config_write);
- install_element(TRX_NODE, &cfg_trx_arfcn_cmd);
- install_element(TRX_NODE, &cfg_description_cmd);
- install_element(TRX_NODE, &cfg_no_description_cmd);
- install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
- install_element(TRX_NODE, &cfg_trx_max_power_red_cmd);
- install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd);
- install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd);
- install_element(TRX_NODE, &cfg_trx_rf_locked_cmd);
-
- install_element(TRX_NODE, &cfg_ts_cmd);
- install_node(&ts_node, dummy_config_write);
- install_element(TS_NODE, &cfg_ts_pchan_cmd);
- install_element(TS_NODE, &cfg_ts_pchan_compat_cmd);
- install_element(TS_NODE, &cfg_ts_tsc_cmd);
- install_element(TS_NODE, &cfg_ts_hopping_cmd);
- install_element(TS_NODE, &cfg_ts_hsn_cmd);
- install_element(TS_NODE, &cfg_ts_maio_cmd);
- install_element(TS_NODE, &cfg_ts_arfcn_add_cmd);
- install_element(TS_NODE, &cfg_ts_arfcn_del_cmd);
- install_element(TS_NODE, &cfg_ts_arfcn_del_all_cmd);
- install_element(TS_NODE, &cfg_ts_e1_subslot_cmd);
+ bts_vty_init();
+ mgcp_client_pool_vty_init(GSMNET_NODE, MGW_NODE, NULL, vty_global_gsm_network->mgw.mgw_pool);
install_element(ENABLE_NODE, &drop_bts_cmd);
install_element(ENABLE_NODE, &restart_bts_cmd);
- install_element(ENABLE_NODE, &bts_resend_cmd);
+ install_element(ENABLE_NODE, &bts_unblock_setup_ramping_cmd);
+ install_element(ENABLE_NODE, &bts_resend_sysinfo_cmd);
+ install_element(ENABLE_NODE, &bts_resend_power_ctrl_params_cmd);
+ install_element(ENABLE_NODE, &bts_c0_power_red_cmd);
install_element(ENABLE_NODE, &pdch_act_cmd);
install_element(ENABLE_NODE, &lchan_act_cmd);
+ install_element(ENABLE_NODE, &lchan_deact_cmd);
install_element(ENABLE_NODE, &lchan_act_all_cmd);
install_element(ENABLE_NODE, &lchan_act_all_bts_cmd);
install_element(ENABLE_NODE, &lchan_act_all_trx_cmd);
+ install_element(ENABLE_NODE, &vamos_modify_lchan_cmd);
install_element(ENABLE_NODE, &lchan_mdcx_cmd);
install_element(ENABLE_NODE, &lchan_set_borken_cmd);
+ install_element(ENABLE_NODE, &lchan_reassign_cmd);
+ install_element(ENABLE_NODE, &lchan_set_mspower_cmd);
install_element(ENABLE_NODE, &handover_subscr_conn_cmd);
install_element(ENABLE_NODE, &assignment_subscr_conn_cmd);
@@ -6839,14 +3618,18 @@ int bsc_vty_init(struct gsm_network *network)
install_element(BSC_NODE, &cfg_net_no_rf_off_time_cmd);
install_element(BSC_NODE, &cfg_net_bsc_missing_msc_ussd_cmd);
install_element(BSC_NODE, &cfg_net_bsc_no_missing_msc_text_cmd);
+ install_element(BSC_NODE, &cfg_bsc_bts_setup_ramping_cmd);
+ install_element(BSC_NODE, &cfg_bsc_no_bts_setup_ramping_cmd);
+ install_element(BSC_NODE, &cfg_bsc_bts_setup_ramping_step_size_cmd);
+ install_element(BSC_NODE, &cfg_bsc_bts_setup_ramping_step_interval_cmd);
install_node(&msc_node, config_write_msc);
- install_element(MSC_NODE, &cfg_net_bsc_ncc_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_mcc_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_lac_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_ci_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_rtp_base_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_codec_list_cmd);
+ install_element(MSC_NODE, &cfg_net_msc_ncc_cmd);
+ install_element(MSC_NODE, &cfg_net_msc_mcc_cmd);
+ install_element(MSC_NODE, &cfg_net_msc_lac_cmd);
+ install_element(MSC_NODE, &cfg_net_msc_ci_cmd);
+ install_element(MSC_NODE, &cfg_net_msc_rtp_base_cmd);
+ install_element(MSC_NODE, &cfg_net_msc_codec_list_cmd);
install_element(MSC_NODE, &cfg_net_msc_dest_cmd);
install_element(MSC_NODE, &cfg_net_msc_no_dest_cmd);
install_element(MSC_NODE, &cfg_net_msc_welcome_ussd_cmd);
@@ -6866,6 +3649,7 @@ int bsc_vty_init(struct gsm_network *network)
install_element(MSC_NODE, &cfg_net_msc_amr_5_15_cmd);
install_element(MSC_NODE, &cfg_net_msc_amr_4_75_cmd);
install_element(MSC_NODE, &cfg_net_msc_amr_octet_align_cmd);
+ install_element(MSC_NODE, &cfg_net_msc_amr_octet_align_deprecated_cmd);
install_element(MSC_NODE, &cfg_net_msc_lcls_mode_cmd);
install_element(MSC_NODE, &cfg_net_msc_lcls_mismtch_cmd);
install_element(MSC_NODE, &cfg_msc_cs7_bsc_addr_cmd);
@@ -6876,7 +3660,11 @@ int bsc_vty_init(struct gsm_network *network)
install_element(MSC_NODE, &cfg_msc_show_nri_cmd);
install_element(MSC_NODE, &cfg_msc_allow_attach_cmd);
install_element(MSC_NODE, &cfg_msc_no_allow_attach_cmd);
-
+ install_element(MSC_NODE, &cfg_msc_mgw_x_osmo_ign_cmd);
+ install_element(MSC_NODE, &cfg_msc_no_mgw_x_osmo_ign_cmd);
+ install_element(MSC_NODE, &cfg_msc_osmux_cmd);
+ /* Deprecated: Old MGCP config without pooling support in MSC node: */
+ mgcp_client_vty_init(network, MSC_NODE, network->mgw.conf);
/* Deprecated: ping time config, kept to support legacy config files. */
install_element(MSC_NODE, &cfg_net_msc_no_ping_time_cmd);
install_element(MSC_NODE, &cfg_net_msc_ping_time_cmd);
@@ -6891,13 +3679,9 @@ int bsc_vty_init(struct gsm_network *network)
install_element(ENABLE_NODE, &gen_position_trap_cmd);
install_element(ENABLE_NODE, &mscpool_roundrobin_next_cmd);
+ install_element(ENABLE_NODE, &msc_bssmap_reset_cmd);
install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd);
- mgcp_client_vty_init(network, MSC_NODE, network->mgw.conf);
- install_element(MSC_NODE, &cfg_msc_mgw_x_osmo_ign_cmd);
- install_element(MSC_NODE, &cfg_msc_no_mgw_x_osmo_ign_cmd);
- install_element(MSC_NODE, &cfg_msc_osmux_cmd);
-
return 0;
}
diff --git a/src/osmo-bsc/bssmap_reset.c b/src/osmo-bsc/bssmap_reset.c
new file mode 100644
index 000000000..fa6684d2f
--- /dev/null
+++ b/src/osmo-bsc/bssmap_reset.c
@@ -0,0 +1,257 @@
+/* (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Authors: Philipp Maier, Neels Hofmeyr
+ *
+ * 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/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/bssmap_reset.h>
+#include <osmocom/bsc/gsm_data.h>
+
+static struct osmo_fsm bssmap_reset_fsm;
+
+enum bssmap_reset_fsm_state {
+ BSSMAP_RESET_ST_DISC,
+ BSSMAP_RESET_ST_CONN,
+};
+
+static const struct value_string bssmap_reset_fsm_event_names[] = {
+ OSMO_VALUE_STRING(BSSMAP_RESET_EV_RX_RESET),
+ OSMO_VALUE_STRING(BSSMAP_RESET_EV_RX_RESET_ACK),
+ OSMO_VALUE_STRING(BSSMAP_RESET_EV_CONN_CFM_FAILURE),
+ OSMO_VALUE_STRING(BSSMAP_RESET_EV_CONN_CFM_SUCCESS),
+ {}
+};
+
+static const struct osmo_tdef_state_timeout bssmap_reset_timeouts[32] = {
+ [BSSMAP_RESET_ST_DISC] = { .T = 4 },
+};
+
+#define bssmap_reset_fsm_state_chg(FI, STATE) \
+ osmo_tdef_fsm_inst_state_chg(FI, STATE, \
+ bssmap_reset_timeouts, \
+ (bsc_gsmnet)->T_defs, \
+ -1)
+
+struct bssmap_reset *bssmap_reset_alloc(void *ctx, const char *label, const struct bssmap_reset_cfg *cfg)
+{
+ struct bssmap_reset *bssmap_reset;
+ struct osmo_fsm_inst *fi;
+
+ fi = osmo_fsm_inst_alloc(&bssmap_reset_fsm, ctx, NULL, LOGL_DEBUG, label);
+ OSMO_ASSERT(fi);
+
+ bssmap_reset = talloc_zero(fi, struct bssmap_reset);
+ OSMO_ASSERT(bssmap_reset);
+ *bssmap_reset = (struct bssmap_reset){
+ .fi = fi,
+ .cfg = *cfg,
+ };
+ fi->priv = bssmap_reset;
+
+ /* Immediately (1ms) kick off reset sending mechanism */
+ osmo_fsm_inst_state_chg_ms(fi, BSSMAP_RESET_ST_DISC, 1, 0);
+ return bssmap_reset;
+}
+
+void bssmap_reset_term_and_free(struct bssmap_reset *bssmap_reset)
+{
+ if (!bssmap_reset)
+ return;
+ osmo_fsm_inst_term(bssmap_reset->fi, OSMO_FSM_TERM_REQUEST, NULL);
+ talloc_free(bssmap_reset);
+}
+
+static void link_up(struct bssmap_reset *bssmap_reset)
+{
+ LOGPFSML(bssmap_reset->fi, LOGL_NOTICE, "link up\n");
+ bssmap_reset->conn_cfm_failures = 0;
+ if (bssmap_reset->cfg.ops.link_up)
+ bssmap_reset->cfg.ops.link_up(bssmap_reset->cfg.data);
+}
+
+static void link_lost(struct bssmap_reset *bssmap_reset)
+{
+ LOGPFSML(bssmap_reset->fi, LOGL_NOTICE, "link lost\n");
+ if (bssmap_reset->cfg.ops.link_lost)
+ bssmap_reset->cfg.ops.link_lost(bssmap_reset->cfg.data);
+}
+
+static void tx_reset(struct bssmap_reset *bssmap_reset)
+{
+ if (bssmap_reset->cfg.ops.tx_reset)
+ bssmap_reset->cfg.ops.tx_reset(bssmap_reset->cfg.data);
+}
+
+static void tx_reset_ack(struct bssmap_reset *bssmap_reset)
+{
+ if (bssmap_reset->cfg.ops.tx_reset_ack)
+ bssmap_reset->cfg.ops.tx_reset_ack(bssmap_reset->cfg.data);
+}
+
+static void bssmap_reset_disc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct bssmap_reset *bssmap_reset = (struct bssmap_reset*)fi->priv;
+ if (prev_state == BSSMAP_RESET_ST_CONN)
+ link_lost(bssmap_reset);
+}
+
+static void bssmap_reset_disc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct bssmap_reset *bssmap_reset = (struct bssmap_reset*)fi->priv;
+ switch (event) {
+
+ case BSSMAP_RESET_EV_RX_RESET:
+ tx_reset_ack(bssmap_reset);
+ bssmap_reset_fsm_state_chg(fi, BSSMAP_RESET_ST_CONN);
+ break;
+
+ case BSSMAP_RESET_EV_RX_RESET_ACK:
+ bssmap_reset_fsm_state_chg(fi, BSSMAP_RESET_ST_CONN);
+ break;
+
+ case BSSMAP_RESET_EV_CONN_CFM_FAILURE:
+ /* ignore */
+ break;
+
+ case BSSMAP_RESET_EV_CONN_CFM_SUCCESS:
+ /* A connection succeeded before we managed to do a RESET handshake?
+ * Then the calling code is not taking care to check bssmap_reset_is_conn_ready().
+ */
+ LOGPFSML(fi, LOGL_ERROR, "Connection success confirmed, but we have not seen a RESET-ACK; bug?\n");
+ break;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void bssmap_reset_conn_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct bssmap_reset *bssmap_reset = (struct bssmap_reset*)fi->priv;
+ if (prev_state != BSSMAP_RESET_ST_CONN)
+ link_up(bssmap_reset);
+}
+
+static void bssmap_reset_conn_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct bssmap_reset *bssmap_reset = (struct bssmap_reset*)fi->priv;
+
+ switch (event) {
+
+ case BSSMAP_RESET_EV_RX_RESET:
+ /* We were connected, but the remote side has restarted. */
+ link_lost(bssmap_reset);
+ tx_reset_ack(bssmap_reset);
+ link_up(bssmap_reset);
+ break;
+
+ case BSSMAP_RESET_EV_RX_RESET_ACK:
+ LOGPFSML(fi, LOGL_INFO, "Link is already up, ignoring RESET ACK\n");
+ break;
+
+ case BSSMAP_RESET_EV_CONN_CFM_FAILURE:
+ bssmap_reset->conn_cfm_failures++;
+ if (bssmap_reset->conn_cfm_failures > bssmap_reset->cfg.conn_cfm_failure_threshold)
+ bssmap_reset_fsm_state_chg(fi, BSSMAP_RESET_ST_DISC);
+ break;
+
+ case BSSMAP_RESET_EV_CONN_CFM_SUCCESS:
+ bssmap_reset->conn_cfm_failures = 0;
+ break;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static int bssmap_reset_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct bssmap_reset *bssmap_reset = (struct bssmap_reset*)fi->priv;
+
+ tx_reset(bssmap_reset);
+
+ /* (re-)enter disconnect state to resend RESET after timeout. */
+ bssmap_reset_fsm_state_chg(fi, BSSMAP_RESET_ST_DISC);
+
+ /* Return 0 to not terminate the fsm */
+ return 0;
+}
+
+#define S(x) (1 << (x))
+
+static struct osmo_fsm_state bssmap_reset_fsm_states[] = {
+ [BSSMAP_RESET_ST_DISC] = {
+ .name = "DISC",
+ .in_event_mask = 0
+ | S(BSSMAP_RESET_EV_RX_RESET)
+ | S(BSSMAP_RESET_EV_RX_RESET_ACK)
+ | S(BSSMAP_RESET_EV_CONN_CFM_FAILURE)
+ | S(BSSMAP_RESET_EV_CONN_CFM_SUCCESS)
+ ,
+ .out_state_mask = 0
+ | S(BSSMAP_RESET_ST_DISC)
+ | S(BSSMAP_RESET_ST_CONN)
+ ,
+ .onenter = bssmap_reset_disc_onenter,
+ .action = bssmap_reset_disc_action,
+ },
+ [BSSMAP_RESET_ST_CONN] = {
+ .name = "CONN",
+ .in_event_mask = 0
+ | S(BSSMAP_RESET_EV_RX_RESET)
+ | S(BSSMAP_RESET_EV_RX_RESET_ACK)
+ | S(BSSMAP_RESET_EV_CONN_CFM_FAILURE)
+ | S(BSSMAP_RESET_EV_CONN_CFM_SUCCESS)
+ ,
+ .out_state_mask = 0
+ | S(BSSMAP_RESET_ST_DISC)
+ | S(BSSMAP_RESET_ST_CONN)
+ ,
+ .onenter = bssmap_reset_conn_onenter,
+ .action = bssmap_reset_conn_action,
+ },
+};
+
+static struct osmo_fsm bssmap_reset_fsm = {
+ .name = "bssmap_reset",
+ .states = bssmap_reset_fsm_states,
+ .num_states = ARRAY_SIZE(bssmap_reset_fsm_states),
+ .log_subsys = DRESET,
+ .timer_cb = bssmap_reset_fsm_timer_cb,
+ .event_names = bssmap_reset_fsm_event_names,
+};
+
+bool bssmap_reset_is_conn_ready(const struct bssmap_reset *bssmap_reset)
+{
+ return bssmap_reset->fi->state == BSSMAP_RESET_ST_CONN;
+}
+
+void bssmap_reset_resend_reset(struct bssmap_reset *bssmap_reset)
+{
+ OSMO_ASSERT(bssmap_reset);
+
+ /* Immediately (1ms) kick off reset sending mechanism */
+ osmo_fsm_inst_state_chg_ms(bssmap_reset->fi, BSSMAP_RESET_ST_DISC, 1, 0);
+}
+
+static __attribute__((constructor)) void bssmap_reset_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&bssmap_reset_fsm) == 0);
+}
diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c
index ce1b20068..108c35539 100644
--- a/src/osmo-bsc/bts.c
+++ b/src/osmo-bsc/bts.c
@@ -1,5 +1,5 @@
/* (C) 2008-2018 by Harald Welte <laforge@gnumonks.org>
- * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -23,6 +23,9 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/paging.h>
+#include <osmocom/bsc/smscb.h>
const struct value_string bts_attribute_names[] = {
OSMO_VALUE_STRING(BTS_TYPE_VARIANT),
@@ -66,7 +69,7 @@ const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = {
{ GSM_BTS_TYPE_NANOBTS, "nanobts" },
{ GSM_BTS_TYPE_RBS2000, "rbs2000" },
{ GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" },
- { GSM_BTS_TYPE_OSMOBTS, "sysmobts" },
+ { GSM_BTS_TYPE_OSMOBTS, "osmo-bts" },
{ 0, NULL }
};
@@ -76,7 +79,7 @@ const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = {
{ GSM_BTS_TYPE_NANOBTS, "ip.access nanoBTS or compatible" },
{ GSM_BTS_TYPE_RBS2000, "Ericsson RBS2000 Series" },
{ GSM_BTS_TYPE_NOKIA_SITE, "Nokia {Metro,Ultra,In}Site" },
- { GSM_BTS_TYPE_OSMOBTS, "sysmocom sysmoBTS" },
+ { GSM_BTS_TYPE_OSMOBTS, "Osmocom Base Transceiver Station" },
{ 0, NULL }
};
@@ -90,12 +93,6 @@ const char *btstype2str(enum gsm_bts_type type)
return get_value_string(bts_type_names, type);
}
-static void bts_init_cbch_state(struct bts_smscb_chan_state *cstate, struct gsm_bts *bts)
-{
- cstate->bts = bts;
- INIT_LLIST_HEAD(&cstate->messages);
-}
-
static LLIST_HEAD(bts_models);
struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
@@ -120,7 +117,6 @@ int gsm_bts_model_register(struct gsm_bts_model *model)
return 0;
}
-static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 };
static const uint8_t bts_cell_timer_default[] =
{ 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 };
static const struct gprs_rlc_cfg rlc_cfg_default = {
@@ -146,19 +142,43 @@ static const struct gprs_rlc_cfg rlc_cfg_default = {
.initial_mcs = 6,
};
+static int gsm_bts_talloc_destructor(struct gsm_bts *bts)
+{
+ paging_destructor(bts);
+ bts_setup_ramp_remove(bts);
+
+ osmo_timer_del(&bts->cbch_timer);
+
+ bts->site_mgr->bts[0] = NULL;
+
+ if (bts->gprs.cell.mo.fi) {
+ osmo_fsm_inst_free(bts->gprs.cell.mo.fi);
+ bts->gprs.cell.mo.fi = NULL;
+ }
+
+ if (bts->mo.fi) {
+ osmo_fsm_inst_free(bts->mo.fi);
+ bts->mo.fi = NULL;
+ }
+
+ osmo_stat_item_group_free(bts->bts_statg);
+ rate_ctr_group_free(bts->bts_ctrs);
+ return 0;
+}
+
/* Initialize those parts that don't require osmo-bsc specific dependencies.
* This part is shared among the thin programs in osmo-bsc/src/utils/.
* osmo-bsc requires further initialization that pulls in more dependencies (see
* bsc_bts_alloc_register()). */
-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)
{
- struct gsm_bts *bts = talloc_zero(net, struct gsm_bts);
- struct gsm48_multi_rate_conf mr_cfg;
- int i;
+ struct gsm_bts *bts = talloc_zero(bts_sm, struct gsm_bts);
if (!bts)
return NULL;
+ talloc_set_destructor(bts, gsm_bts_talloc_destructor);
+
bts->nr = bts_num;
bts->num_trx = 0;
INIT_LLIST_HEAD(&bts->trx_list);
@@ -166,30 +186,24 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
bts->ms_max_power = 15; /* dBm */
- gsm_mo_init(&bts->mo, bts, NM_OC_BTS,
- bts->nr, 0xff, 0xff);
- gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER,
- 0xff, 0xff, 0xff);
-
- for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
- bts->gprs.nsvc[i].bts = bts;
- bts->gprs.nsvc[i].id = i;
- gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC,
- bts->nr, i, 0xff);
- }
- memcpy(&bts->gprs.nse.timer, bts_nse_timer_default,
- sizeof(bts->gprs.nse.timer));
- gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE,
- bts->nr, 0xff, 0xff);
+ bts->site_mgr = bts_sm;
+
+ bts->mo.fi = osmo_fsm_inst_alloc(&nm_bts_fsm, bts, bts,
+ LOGL_INFO, NULL);
+ osmo_fsm_inst_update_id_f(bts->mo.fi, "bts%d", bts->nr);
+ gsm_mo_init(&bts->mo, bts, NM_OC_BTS, bts->nr, 0xff, 0xff);
+
+ /* 3GPP TS 08.18, chapter 5.4.1: 0 is reserved for signalling */
+ bts->gprs.cell.bvci = 2;
memcpy(&bts->gprs.cell.timer, bts_cell_timer_default,
sizeof(bts->gprs.cell.timer));
- gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL,
- bts->nr, 0xff, 0xff);
memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default,
sizeof(bts->gprs.cell.rlc_cfg));
-
- /* 3GPP TS 08.18, chapter 5.4.1: 0 is reserved for signalling */
- bts->gprs.cell.bvci = 2;
+ bts->gprs.cell.mo.fi = osmo_fsm_inst_alloc(&nm_gprs_cell_fsm, bts,
+ &bts->gprs.cell, LOGL_INFO, NULL);
+ osmo_fsm_inst_update_id_f(bts->gprs.cell.mo.fi, "gprs-cell%d", bts->nr);
+ gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL,
+ bts->nr, 0xff, 0xff);
/* init statistics */
bts->bts_ctrs = rate_ctr_group_alloc(bts, &bts_ctrg_desc, bts->nr);
@@ -199,6 +213,51 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
}
bts->bts_statg = osmo_stat_item_group_alloc(bts, &bts_statg_desc, bts->nr);
+ bts->all_allocated.sdcch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_SDCCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+ bts->all_allocated.static_sdcch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_STATIC_SDCCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+ bts->all_allocated.tch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_TCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+ bts->all_allocated.static_tch = (struct osmo_time_cc){
+ .cfg = {
+ .gran_usec = 1*1000000,
+ .forget_sum_usec = 60*1000000,
+ .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_STATIC_TCH),
+ .T_gran = -16,
+ .T_round_threshold = -17,
+ .T_forget_sum = -18,
+ .T_defs = net->T_defs,
+ },
+ };
+
/* create our primary TRX */
bts->c0 = gsm_bts_trx_alloc(bts);
if (!bts->c0) {
@@ -210,11 +269,11 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
bts->c0->ts[0].pchan_from_config = GSM_PCHAN_CCCH_SDCCH4; /* TODO: really?? */
bts->ccch_load_ind_thresh = 10; /* 10% of Load: Start sending CCCH LOAD IND */
+ bts->ccch_load_ind_period = 1; /* Send CCCH LOAD IND every 1 second */
bts->rach_b_thresh = -1;
bts->rach_ldavg_slots = -1;
- bts->paging.free_chans_need = -1;
- INIT_LLIST_HEAD(&bts->paging.pending_requests);
+ paging_init(bts);
bts->features.data = &bts->_features_data[0];
bts->features.data_len = sizeof(bts->_features_data);
@@ -233,6 +292,10 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
bts->neigh_list_manual_mode = NL_MODE_AUTOMATIC;
bts->early_classmark_allowed_3g = true; /* 3g Early Classmark Sending controlled by bts->early_classmark_allowed param */
bts->si_unused_send_empty = true;
+ bts->chan_alloc_tch_signalling_policy = BTS_TCH_SIGNALLING_ALWAYS;
+ bts->chan_alloc_dyn_params.ul_rxlev_thresh = 50; /* >= -60 dBm */
+ bts->chan_alloc_dyn_params.ul_rxlev_avg_num = 2; /* at least 2 samples */
+ bts->chan_alloc_dyn_params.c0_chan_load_thresh = 60; /* >= 60% */
bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */
bts->si_common.cell_sel_par.rxlev_acc_min = 0;
bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list;
@@ -253,18 +316,25 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */
bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */
bts->si_common.rach_control.t2 = 4; /* no emergency calls */
+ bts->si_common.chan_desc.mscr = 1; /* Indicate R99 MSC in SI3 */
bts->si_common.chan_desc.att = 1; /* attachment required */
bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */
bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */
bts->si_common.chan_desc.t3212 = osmo_tdef_get(net->T_defs, 3212, OSMO_TDEF_CUSTOM, -1);
+ bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
+ bts->si_common.cell_sel_par.acs = 0;
+ bts->si_common.ncc_permitted = 0xff;
gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */
INIT_LLIST_HEAD(&bts->abis_queue);
INIT_LLIST_HEAD(&bts->loc_list);
- INIT_LLIST_HEAD(&bts->local_neighbors);
+ INIT_LLIST_HEAD(&bts->neighbors);
INIT_LLIST_HEAD(&bts->oml_fail_rep);
INIT_LLIST_HEAD(&bts->chan_rqd_queue);
+ /* Don't pin the BTS to any MGW by default: */
+ bts->mgw_pool_target = -1;
+
/* Enable all codecs by default. These get reset to a more fine grained selection IF a
* 'codec-support' config appears in the config file (see bsc_vty.c). */
bts->codec = (struct bts_codec_conf){
@@ -274,70 +344,200 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
};
/* Set reasonable defaults for AMR-FR and AMR-HR rate configuration.
- * (see also 3GPP TS 28.062, Table 7.11.3.1.3-2) */
- mr_cfg = (struct gsm48_multi_rate_conf) {
+ * The values are taken from 3GPP TS 51.010-1 (version 13.11.0).
+ * See 14.2.19.4.1 and 14.2.20.4.1 for AMR-FR and AMR-HR, respectively. */
+ static const struct gsm48_multi_rate_conf amr_fr_mr_cfg = {
.m4_75 = 1,
- .m5_15 = 0,
.m5_90 = 1,
- .m6_70 = 0,
- .m7_40 = 1,
- .m7_95 = 0,
- .m10_2 = 0,
+ .m7_95 = 1,
.m12_2 = 1
};
- memcpy(bts->mr_full.gsm48_ie, &mr_cfg, sizeof(bts->mr_full.gsm48_ie));
- bts->mr_full.ms_mode[0].mode = 0;
- bts->mr_full.ms_mode[1].mode = 2;
- bts->mr_full.ms_mode[2].mode = 4;
- bts->mr_full.ms_mode[3].mode = 7;
- bts->mr_full.bts_mode[0].mode = 0;
- bts->mr_full.bts_mode[1].mode = 2;
- bts->mr_full.bts_mode[2].mode = 4;
- bts->mr_full.bts_mode[3].mode = 7;
- for (i = 0; i < 3; i++) {
- bts->mr_full.ms_mode[i].hysteresis = 8;
- bts->mr_full.ms_mode[i].threshold = 32;
- bts->mr_full.bts_mode[i].hysteresis = 8;
- bts->mr_full.bts_mode[i].threshold = 32;
- }
- bts->mr_full.num_modes = 4;
-
- mr_cfg = (struct gsm48_multi_rate_conf) {
+ static const struct gsm48_multi_rate_conf amr_hr_mr_cfg = {
.m4_75 = 1,
- .m5_15 = 0,
.m5_90 = 1,
- .m6_70 = 0,
- .m7_40 = 1,
- .m7_95 = 0,
- .m10_2 = 0,
- .m12_2 = 0
+ .m6_70 = 1,
+ .m7_95 = 1,
};
- memcpy(bts->mr_half.gsm48_ie, &mr_cfg, sizeof(bts->mr_half.gsm48_ie));
- bts->mr_half.ms_mode[0].mode = 0;
- bts->mr_half.ms_mode[1].mode = 2;
- bts->mr_half.ms_mode[2].mode = 4;
- bts->mr_half.ms_mode[3].mode = 7;
- bts->mr_half.bts_mode[0].mode = 0;
- bts->mr_half.bts_mode[1].mode = 2;
- bts->mr_half.bts_mode[2].mode = 4;
- bts->mr_half.bts_mode[3].mode = 7;
- for (i = 0; i < 3; i++) {
- bts->mr_half.ms_mode[i].hysteresis = 8;
- bts->mr_half.ms_mode[i].threshold = 32;
- bts->mr_half.bts_mode[i].hysteresis = 8;
- bts->mr_half.bts_mode[i].threshold = 32;
- }
- bts->mr_half.num_modes = 3;
- bts_init_cbch_state(&bts->cbch_basic, bts);
- bts_init_cbch_state(&bts->cbch_extended, bts);
+ memcpy(bts->mr_full.gsm48_ie, &amr_fr_mr_cfg, sizeof(bts->mr_full.gsm48_ie));
+ memcpy(bts->mr_half.gsm48_ie, &amr_hr_mr_cfg, sizeof(bts->mr_half.gsm48_ie));
+
+ /* ^ C/I (dB) | FR / HR |
+ * | |
+ * | |
+ * MODE4 | |
+ * = | ----+---- THR_MX_Up(3) | 20.5 / 18.0 |
+ * | | |
+ * | = ----+---- THR_MX_Dn(4) | 18.5 / 16.0 |
+ * MODE3 | |
+ * | = ----+---- THR_MX_Up(2) | 14.5 / 14.0 |
+ * | | |
+ * = | ----+---- THR_MX_Dn(3) | 12.5 / 12.0 |
+ * MODE2 | |
+ * = | ----+---- THR_MX_Up(1) | 8.5 / 10.0 |
+ * | | |
+ * | = ----+---- THR_MX_Dn(2) | 6.5 / 8.0 |
+ * MODE1 | |
+ * | |
+ * | |
+ */
+ static const struct amr_mode amr_fr_ms_bts_mode[] = {
+ {
+ .mode = 0, /* 4.75k */
+ .threshold = 13, /* THR_MX_Dn(2): 6.5 dB */
+ .hysteresis = 4, /* THR_MX_Up(1): 8.5 dB */
+ },
+ {
+ .mode = 2, /* 5.90k */
+ .threshold = 25, /* THR_MX_Dn(3): 12.5 dB */
+ .hysteresis = 4, /* THR_MX_Up(2): 14.5 dB */
+ },
+ {
+ .mode = 5, /* 7.95k */
+ .threshold = 37, /* THR_MX_Dn(4): 18.5 dB */
+ .hysteresis = 4, /* THR_MX_Up(3): 20.5 dB */
+ },
+ {
+ .mode = 7, /* 12.2k */
+ /* this is the last mode, so no threshold */
+ },
+ };
+ static const struct amr_mode amr_hr_ms_bts_mode[] = {
+ {
+ .mode = 0, /* 4.75k */
+ .threshold = 16, /* THR_MX_Dn(2): 8.0 dB */
+ .hysteresis = 4, /* THR_MX_Up(1): 10.0 dB */
+ },
+ {
+ .mode = 2, /* 5.90k */
+ .threshold = 24, /* THR_MX_Dn(3): 12.0 dB */
+ .hysteresis = 4, /* THR_MX_Up(2): 14.0 dB */
+ },
+ {
+ .mode = 3, /* 6.70k */
+ .threshold = 32, /* THR_MX_Dn(4): 16.0 dB */
+ .hysteresis = 4, /* THR_MX_Up(3): 18.0 dB */
+ },
+ {
+ .mode = 5, /* 7.95k */
+ /* this is the last mode, so no threshold */
+ },
+ };
+
+ memcpy(&bts->mr_full.ms_mode[0], &amr_fr_ms_bts_mode[0], sizeof(amr_fr_ms_bts_mode));
+ memcpy(&bts->mr_full.bts_mode[0], &amr_fr_ms_bts_mode[0], sizeof(amr_fr_ms_bts_mode));
+ bts->mr_full.num_modes = ARRAY_SIZE(amr_fr_ms_bts_mode);
+
+ memcpy(&bts->mr_half.ms_mode[0], &amr_hr_ms_bts_mode[0], sizeof(amr_hr_ms_bts_mode));
+ memcpy(&bts->mr_half.bts_mode[0], &amr_hr_ms_bts_mode[0], sizeof(amr_hr_ms_bts_mode));
+ bts->mr_half.num_modes = ARRAY_SIZE(amr_hr_ms_bts_mode);
+
+ bts->use_osmux = OSMUX_USAGE_OFF;
+ bts_cbch_init(bts);
+ bts_etws_init(bts);
+
+ bts_setup_ramp_init_bts(bts);
acc_mgr_init(&bts->acc_mgr, bts);
acc_ramp_init(&bts->acc_ramp, bts);
+ /* Default RxQual threshold for ACCH repetition/overpower */
+ bts->rep_acch_cap.rxqual = 4;
+ bts->top_acch_cap.rxqual = 4;
+
+ /* Permit ACCH overpower only for speech channels using AMR */
+ bts->top_acch_chan_mode = TOP_ACCH_CHAN_MODE_SPEECH_V3;
+
+ /* MS Power Control parameters (defaults) */
+ power_ctrl_params_def_reset(&bts->ms_power_ctrl, GSM_PWR_CTRL_DIR_UL);
+
+ /* BS Power Control parameters (defaults) */
+ power_ctrl_params_def_reset(&bts->bs_power_ctrl, GSM_PWR_CTRL_DIR_DL);
+
+ /* Interference Measurement Parameters (defaults) */
+ bts->interf_meas_params_cfg = interf_meas_params_def;
+
+ bts->rach_max_delay = 63;
+
+ /* SRVCC is enabled by default */
+ bts->srvcc_fast_return_allowed = true;
+
return bts;
}
+/* Validate BTS configuration (ARFCN settings and physical channel configuration) */
+__attribute__((weak)) int gsm_bts_check_cfg(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+
+ if (!bts->model)
+ return -EFAULT;
+
+ switch (bts->band) {
+ case GSM_BAND_1800:
+ if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM1800 channel (%u) must be between 512-885.\n",
+ bts->nr, bts->c0->arfcn);
+ return -EINVAL;
+ }
+ break;
+ case GSM_BAND_1900:
+ if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) {
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM1900 channel (%u) must be between 512-810.\n",
+ bts->nr, bts->c0->arfcn);
+ return -EINVAL;
+ }
+ break;
+ case GSM_BAND_900:
+ if ((bts->c0->arfcn > 124 && bts->c0->arfcn < 955) ||
+ bts->c0->arfcn > 1023) {
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM900 channel (%u) must be between 0-124, 955-1023.\n",
+ bts->nr, bts->c0->arfcn);
+ return -EINVAL;
+ }
+ break;
+ case GSM_BAND_850:
+ if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) {
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM850 channel (%u) must be between 128-251.\n",
+ bts->nr, bts->c0->arfcn);
+ return -EINVAL;
+ }
+ break;
+ default:
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) Unsupported frequency band.\n", bts->nr);
+ return -EINVAL;
+ }
+
+ if (bts->features_known) {
+ if (!bts_gprs_mode_is_compat(bts, bts->gprs.mode)) {
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) GPRS mode set to '%s', but BTS does not support it\n", bts->nr,
+ bts_gprs_mode_name(bts->gprs.mode));
+ return -EINVAL;
+ }
+ if (bts->use_osmux == OSMUX_USAGE_ONLY &&
+ !osmo_bts_has_feature(&bts->features, BTS_FEAT_OSMUX)) {
+ LOGP(DNM, LOGL_ERROR,
+ "(bts=%u) osmux use set to 'only', but BTS does not support Osmux\n",
+ bts->nr);
+ return -EINVAL;
+ }
+ }
+
+ /* Verify the physical channel mapping */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (!trx_has_valid_pchan_config(trx)) {
+ LOGP(DNM, LOGL_ERROR, "TRX %u has invalid timeslot "
+ "configuration\n", trx->nr);
+ return -EINVAL;
+ }
+ }
+
+ if (!gsm_bts_check_ny1(bts))
+ return -EINVAL;
+
+ return 0;
+}
+
static char ts2str[255];
char *gsm_bts_name(const struct gsm_bts *bts)
@@ -366,6 +566,10 @@ bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cel
case CELL_IDENT_WHOLE_GLOBAL:
return gsm_bts_matches_lai(bts, &id->global.lai)
&& id->global.cell_identity == bts->cell_identity;
+ case CELL_IDENT_WHOLE_GLOBAL_PS:
+ return gsm_bts_matches_lai(bts, &id->global_ps.rai.lac)
+ && id->global_ps.rai.rac == bts->gprs.rac
+ && id->global_ps.cell_identity == bts->cell_identity;
case CELL_IDENT_LAC_AND_CI:
return id->lac_and_ci.lac == bts->location_area_code
&& id->lac_and_ci.ci == bts->cell_identity;
@@ -382,96 +586,93 @@ bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cel
case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
case CELL_IDENT_UTRAN_RNC:
case CELL_IDENT_UTRAN_LAC_RNC:
+ case CELL_IDENT_SAI:
return false;
default:
OSMO_ASSERT(false);
}
}
-static struct gsm_bts_ref *gsm_bts_ref_find(const struct llist_head *list, const struct gsm_bts *bts)
-{
- struct gsm_bts_ref *ref;
- if (!bts)
- return NULL;
- llist_for_each_entry(ref, list, entry) {
- if (ref->bts == bts)
- return ref;
- }
- return NULL;
-}
-
-/* Add a BTS reference to the local_neighbors list.
- * Return 1 if added, 0 if such an entry already existed, and negative on errors. */
-int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor)
+/* Return a LAC+CI cell identity for the given BTS.
+ * (For matching a BTS within the local BSS, the PLMN code is not important.) */
+void gsm_bts_cell_id(struct gsm0808_cell_id *cell_id, const struct gsm_bts *bts)
{
- struct gsm_bts_ref *ref;
- if (!bts || !neighbor)
- return -ENOMEM;
-
- if (bts == neighbor)
- return -EINVAL;
-
- /* Already got this entry? */
- ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor);
- if (ref)
- return 0;
-
- ref = talloc_zero(bts, struct gsm_bts_ref);
- if (!ref)
- return -ENOMEM;
- ref->bts = neighbor;
- llist_add_tail(&ref->entry, &bts->local_neighbors);
- return 1;
+ *cell_id = (struct gsm0808_cell_id){
+ .id_discr = CELL_IDENT_LAC_AND_CI,
+ .id.lac_and_ci = {
+ .lac = bts->location_area_code,
+ .ci = bts->cell_identity,
+ },
+ };
}
-/* Remove a BTS reference from the local_neighbors list.
- * Return 1 if removed, 0 if no such entry existed, and negative on errors. */
-int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor)
+/* Same as gsm_bts_cell_id(), but return in a single-entry gsm0808_cell_id_list2. Useful for e.g.
+ * gsm0808_cell_id_list_add() and gsm0808_cell_id_lists_same(). */
+void gsm_bts_cell_id_list(struct gsm0808_cell_id_list2 *cell_id_list, const struct gsm_bts *bts)
{
- struct gsm_bts_ref *ref;
- if (!bts || !neighbor)
- return -ENOMEM;
-
- ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor);
- if (!ref)
- return 0;
-
- llist_del(&ref->entry);
- talloc_free(ref);
- return 1;
+ struct gsm0808_cell_id cell_id;
+ struct gsm0808_cell_id_list2 add;
+ int rc;
+ gsm_bts_cell_id(&cell_id, bts);
+ gsm0808_cell_id_to_list(&add, &cell_id);
+ /* Since the target list is empty, this should always succeed. */
+ (*cell_id_list) = (struct gsm0808_cell_id_list2){};
+ rc = gsm0808_cell_id_list_add(cell_id_list, &add);
+ OSMO_ASSERT(rc > 0);
}
/* return the gsm_lchan for the CBCH (if it exists at all) */
struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts)
{
- struct gsm_lchan *lchan = NULL;
struct gsm_bts_trx *trx = bts->c0;
+ /* According to 3GPP TS 45.002, table 3, CBCH can be allocated
+ * either on C0/TS0 (CCCH+SDCCH4) or on C0..n/TS0..3 (SDCCH/8). */
if (trx->ts[0].pchan_from_config == GSM_PCHAN_CCCH_SDCCH4_CBCH)
- lchan = &trx->ts[0].lchan[2];
- else {
- int i;
- for (i = 0; i < 8; i++) {
- if (trx->ts[i].pchan_from_config == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) {
- lchan = &trx->ts[i].lchan[2];
- break;
- }
+ return &trx->ts[0].lchan[2]; /* C0/TS0 */
+
+ llist_for_each_entry(trx, &bts->trx_list, list) { /* C0..n */
+ unsigned int tn;
+ for (tn = 0; tn < 4; tn++) { /* TS0..3 */
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ if (ts->pchan_from_config == GSM_PCHAN_SDCCH8_SACCH8C_CBCH)
+ return &ts->lchan[2];
}
}
- return lchan;
+ return NULL;
+}
+
+int gsm_set_bts_model(struct gsm_bts *bts, struct gsm_bts_model *model)
+{
+ bts->model = model;
+
+ /* Copy hardcoded feature list from BTS model, unless the BTS supports
+ * reporting features at runtime (as of writing nanobts, OsmoBTS). */
+ if (!model || model->features_get_reported) {
+ memset(bts->_features_data, 0, sizeof(bts->_features_data));
+ bts->features_known = false;
+ } else {
+ memcpy(bts->_features_data, bts->model->_features_data, sizeof(bts->_features_data));
+ bts->features_known = true;
+ }
+
+ return 0;
}
int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type)
{
struct gsm_bts_model *model;
+ if (bts->type != GSM_BTS_TYPE_UNKNOWN && type != bts->type)
+ return -EBUSY;
+
model = bts_model_find(type);
if (!model)
return -EINVAL;
bts->type = type;
- bts->model = model;
+ gsm_set_bts_model(bts, model);
if (model->start && !model->started) {
int ret = model->start(bts->network);
@@ -481,9 +682,22 @@ int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type)
model->started = true;
}
+ if (model->bts_init) {
+ int rc = model->bts_init(bts);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* handle those TRX which are already allocated at the time we set the type */
+ if (model->trx_init) {
+ struct gsm_bts_trx *trx;
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ model->trx_init(trx);
+ }
+
switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
case GSM_BTS_TYPE_OSMOBTS:
+ case GSM_BTS_TYPE_NANOBTS:
/* Set the default OML Stream ID to 0xff */
bts->oml_tei = 0xff;
bts->c0->nominal_power = 23;
@@ -501,17 +715,21 @@ int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type)
break;
}
+ /* Enable dynamic Uplink power control by default (if supported) */
+ if (model->power_ctrl_enc_rsl_params != NULL)
+ bts->ms_power_ctrl.mode = GSM_PWR_CTRL_MODE_DYN_BTS;
+
return 0;
}
int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode)
{
if (mode != BTS_GPRS_NONE &&
- !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_GPRS)) {
+ !osmo_bts_has_feature(&bts->features, BTS_FEAT_GPRS)) {
return 0;
}
if (mode == BTS_GPRS_EGPRS &&
- !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_EGPRS)) {
+ !osmo_bts_has_feature(&bts->features, BTS_FEAT_EGPRS)) {
return 0;
}
@@ -533,16 +751,74 @@ struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num)
return NULL;
}
+void bts_store_uptime(struct gsm_bts *bts)
+{
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_UPTIME_SECONDS), bts_uptime(bts));
+}
+
+void bts_store_lchan_durations(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+ int i, j;
+ struct timespec now, elapsed;
+ uint64_t elapsed_ms;
+ uint64_t elapsed_tch_ms = 0;
+ uint64_t elapsed_sdcch_ms = 0;
+
+ /* Ignore BTS that are not in operation. */
+ if (!trx_is_usable(bts->c0))
+ return;
+
+ /* Grab storage time to be used for all lchans. */
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+
+ /* Iterate over all lchans. */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ for (j = 0; j < ARRAY_SIZE(ts->lchan); j++) {
+ struct gsm_lchan *lchan = &ts->lchan[j];
+
+ /* Ignore lchans whose activation timestamps are not yet set. */
+ if (lchan->active_stored.tv_sec == 0 && lchan->active_stored.tv_nsec == 0)
+ continue;
+
+ /* Calculate elapsed time since last storage. */
+ timespecsub(&now, &lchan->active_stored, &elapsed);
+ elapsed_ms = elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
+
+ /* Assign elapsed time to appropriate bucket. */
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_H:
+ case GSM_LCHAN_TCH_F:
+ elapsed_tch_ms += elapsed_ms;
+ break;
+ case GSM_LCHAN_SDCCH:
+ elapsed_sdcch_ms += elapsed_ms;
+ break;
+ default:
+ continue;
+ }
+
+ /* Update storage time. */
+ lchan->active_stored = now;
+ }
+ }
+ }
+
+ /* Export to rate counters. */
+ rate_ctr_add(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_TCH_ACTIVE_MILLISECONDS_TOTAL), elapsed_tch_ms);
+ rate_ctr_add(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_SDCCH_ACTIVE_MILLISECONDS_TOTAL), elapsed_sdcch_ms);
+}
+
unsigned long long bts_uptime(const struct gsm_bts *bts)
{
struct timespec tp;
- if (!bts->uptime || !bts->oml_link) {
- LOGP(DNM, LOGL_ERROR, "BTS %u OML link uptime unavailable\n", bts->nr);
+ if (!bts->uptime || !bts->oml_link)
return 0;
- }
- if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) {
+ if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp) != 0) {
LOGP(DNM, LOGL_ERROR, "BTS %u uptime computation failure: %s\n", bts->nr, strerror(errno));
return 0;
}
@@ -566,10 +842,6 @@ void gsm_bts_mo_reset(struct gsm_bts *bts)
unsigned int i;
gsm_abis_mo_reset(&bts->mo);
- gsm_abis_mo_reset(&bts->site_mgr.mo);
- for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++)
- gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo);
- gsm_abis_mo_reset(&bts->gprs.nse.mo);
gsm_abis_mo_reset(&bts->gprs.cell.mo);
llist_for_each_entry(trx, &bts->trx_list, list) {
@@ -596,7 +868,7 @@ void bts_depend_mark(struct gsm_bts *bts, int dep)
int idx, bit;
depends_calc_index_bit(dep, &idx, &bit);
- bts->depends_on[idx] |= 1 << bit;
+ bts->depends_on[idx] |= 1U << bit;
}
void bts_depend_clear(struct gsm_bts *bts, int dep)
@@ -604,7 +876,7 @@ void bts_depend_clear(struct gsm_bts *bts, int dep)
int idx, bit;
depends_calc_index_bit(dep, &idx, &bit);
- bts->depends_on[idx] &= ~(1 << bit);
+ bts->depends_on[idx] &= ~(1U << bit);
}
int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other)
@@ -613,7 +885,7 @@ int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other)
depends_calc_index_bit(other->nr, &idx, &bit);
/* Check if there is a depends bit */
- return (base->depends_on[idx] & (1 << bit)) > 0;
+ return (base->depends_on[idx] & (1U << bit)) > 0;
}
static int bts_is_online(struct gsm_bts *bts)
@@ -686,19 +958,6 @@ void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data)
gsm_trx_all_ts_dispatch(trx, ts_ev, data);
}
-
-/* Count number of free TS of given pchan type */
-int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
-{
- struct gsm_bts_trx *trx;
- int count = 0;
-
- llist_for_each_entry(trx, &bts->trx_list, list)
- count += trx_count_free_ts(trx, pchan);
-
- return count;
-}
-
/* set all system information types for a BTS */
int gsm_bts_set_system_infos(struct gsm_bts *bts)
{
@@ -718,3 +977,763 @@ int gsm_bts_set_system_infos(struct gsm_bts *bts)
return 0;
}
+
+int gsm_bts_set_c0_power_red(struct gsm_bts *bts, const uint8_t red)
+{
+ struct gsm_bts_trx *c0 = bts->c0;
+ unsigned int tn;
+ int rc;
+
+ if (!osmo_bts_has_feature(&bts->features, BTS_FEAT_BCCH_POWER_RED))
+ return -ENOTSUP;
+ if (bts->model->power_ctrl_set_c0_power_red == NULL)
+ return -ENOTSUP;
+
+ rc = bts->model->power_ctrl_set_c0_power_red(bts, red);
+ if (rc != 0)
+ return rc;
+
+ /* Timeslot 0 is always transmitting BCCH/CCCH */
+ c0->ts[0].c0_max_power_red_db = 0;
+
+ for (tn = 1; tn < ARRAY_SIZE(c0->ts); tn++) {
+ struct gsm_bts_trx_ts *ts = &c0->ts[tn];
+ struct gsm_bts_trx_ts *prev = ts - 1;
+
+ switch (ts->pchan_is) {
+ /* Not allowed on CCCH/BCCH */
+ case GSM_PCHAN_CCCH:
+ /* Preceeding timeslot shall not exceed 2 dB */
+ if (prev->c0_max_power_red_db > 0)
+ prev->c0_max_power_red_db = 2;
+ /* fall-through */
+ /* Not recommended on SDCCH/8 */
+ case GSM_PCHAN_SDCCH8_SACCH8C:
+ case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
+ ts->c0_max_power_red_db = 0;
+ break;
+ default:
+ ts->c0_max_power_red_db = red;
+ break;
+ }
+ }
+
+ /* Timeslot 7 is always preceding BCCH/CCCH */
+ if (c0->ts[7].c0_max_power_red_db > 0)
+ c0->ts[7].c0_max_power_red_db = 2;
+
+ bts->c0_max_power_red_db = red;
+
+ return 0;
+}
+
+void gsm_bts_stats_reset(struct gsm_bts *bts)
+{
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_USED), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_USED), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_TOTAL), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_H_USED), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_H_TOTAL), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_USED), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_TOTAL), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_PDCH_USED), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_PDCH_TOTAL), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_CBCH_USED), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_OSMO_DYN_USED), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_OSMO_DYN_TOTAL), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_T3113), 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_REQ_QUEUE_LENGTH), paging_pending_requests_nr(bts));
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_AVAILABLE_SLOTS), bts->paging.available_slots);
+}
+
+const struct rate_ctr_desc bts_ctr_description[] = {
+ [BTS_CTR_CHREQ_TOTAL] = \
+ { "chreq:total",
+ "Received channel requests" },
+ [BTS_CTR_CHREQ_ATTEMPTED_EMERG] = \
+ { "chreq:attempted_emerg",
+ "Received channel requests EMERG" },
+ [BTS_CTR_CHREQ_ATTEMPTED_CALL] = \
+ { "chreq:attempted_call",
+ "Received channel requests CALL" },
+ [BTS_CTR_CHREQ_ATTEMPTED_LOCATION_UPD] = \
+ { "chreq:attempted_location_upd",
+ "Received channel requests LOCATION_UPD" },
+ [BTS_CTR_CHREQ_ATTEMPTED_PAG] = \
+ { "chreq:attempted_pag",
+ "Received channel requests PAG" },
+ [BTS_CTR_CHREQ_ATTEMPTED_PDCH] = \
+ { "chreq:attempted_pdch",
+ "Received channel requests PDCH" },
+ [BTS_CTR_CHREQ_ATTEMPTED_OTHER] = \
+ { "chreq:attempted_other",
+ "Received channel requests OTHER" },
+ [BTS_CTR_CHREQ_ATTEMPTED_UNKNOWN] = \
+ { "chreq:attempted_unknown",
+ "Received channel requests UNKNOWN" },
+ [BTS_CTR_CHREQ_SUCCESSFUL] = \
+ { "chreq:successful",
+ "Successful channel requests (immediate assign sent)" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_EMERG] = \
+ { "chreq:successful_emerg",
+ "Sent Immediate Assignment for EMERG" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_CALL] = \
+ { "chreq:successful_call",
+ "Sent Immediate Assignment for CALL" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_LOCATION_UPD] = \
+ { "chreq:successful_location_upd",
+ "Sent Immediate Assignment for LOCATION_UPD" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_PAG] = \
+ { "chreq:successful_pag",
+ "Sent Immediate Assignment for PAG" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_PDCH] = \
+ { "chreq:successful_pdch",
+ "Sent Immediate Assignment for PDCH" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_OTHER] = \
+ { "chreq:successful_other",
+ "Sent Immediate Assignment for OTHER" },
+ [BTS_CTR_CHREQ_SUCCESSFUL_UNKNOWN] = \
+ { "chreq:successful_unknown",
+ "Sent Immediate Assignment for UNKNOWN" },
+ [BTS_CTR_CHREQ_NO_CHANNEL] = \
+ { "chreq:no_channel",
+ "Sent to MS no channel available" },
+ [BTS_CTR_CHREQ_MAX_DELAY_EXCEEDED] = \
+ { "chreq:max_delay_exceeded",
+ "Received channel requests with greater than permitted access delay" },
+ [BTS_CTR_CHAN_RF_FAIL] = \
+ { "chan:rf_fail",
+ "Received a RF failure indication from BTS" },
+ [BTS_CTR_CHAN_RF_FAIL_TCH] = \
+ { "chan:rf_fail_tch",
+ "Received a RF failure indication from BTS on a TCH channel" },
+ [BTS_CTR_CHAN_RF_FAIL_SDCCH] = \
+ { "chan:rf_fail_sdcch",
+ "Received a RF failure indication from BTS on an SDCCH channel" },
+ [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 RSL 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_PAGING_OVERLOAD] = \
+ { "paging:overload",
+ "Paging dropped due to BSC Paging queue overload" },
+ [BTS_CTR_CHAN_ACT_TOTAL] = \
+ { "chan_act:total",
+ "Total number of Channel Activations" },
+ [BTS_CTR_CHAN_ACT_SDCCH] = \
+ { "chan_act:sdcch",
+ "Number of SDCCH Channel Activations" },
+ [BTS_CTR_CHAN_ACT_TCH] = \
+ { "chan_act:tch",
+ "Number of TCH Channel Activations" },
+ [BTS_CTR_CHAN_ACT_NACK] = \
+ { "chan_act:nack",
+ "Number of Channel Activations that the BTS NACKed" },
+ [BTS_CTR_CHAN_TCH_ACTIVE_MILLISECONDS_TOTAL] = \
+ { "chan_tch:active_milliseconds:total",
+ "Cumulative number of milliseconds of TCH channel activity" },
+ [BTS_CTR_CHAN_SDCCH_ACTIVE_MILLISECONDS_TOTAL] = \
+ { "chan_sdcch:active_milliseconds:total",
+ "Cumulative number of milliseconds of SDCCH channel activity" },
+ [BTS_CTR_CHAN_TCH_FULLY_ESTABLISHED] = \
+ { "chan_tch:fully_established",
+ "Number of TCH channels which have reached the fully established state" },
+ [BTS_CTR_CHAN_SDCCH_FULLY_ESTABLISHED] = \
+ { "chan_sdcch:fully_established",
+ "Number of SDCCH channels which have reached the fully established state" },
+ [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_LCHAN_BORKEN_EV_TS_ERROR] = \
+ { "lchan_borken:event:ts_error",
+ "LCHAN_EV_TS_ERROR received in a BORKEN state" },
+ [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_ATTEMPTED_SIGN] = \
+ { "assignment:attempted_sign",
+ "Assignment of signaling lchan attempts" },
+ [BTS_CTR_ASSIGNMENT_ATTEMPTED_SPEECH] = \
+ { "assignment:attempted_speech",
+ "Assignment of speech lchan attempts" },
+ [BTS_CTR_ASSIGNMENT_COMPLETED] = \
+ { "assignment:completed",
+ "Assignment completed" },
+ [BTS_CTR_ASSIGNMENT_COMPLETED_SIGN] = \
+ { "assignment:completed_sign",
+ "Assignment of signaling lchan completed" },
+ [BTS_CTR_ASSIGNMENT_COMPLETED_SPEECH] = \
+ { "assignment:completed_speech",
+ "Assignment if speech lchan completed" },
+ [BTS_CTR_ASSIGNMENT_STOPPED] = \
+ { "assignment:stopped",
+ "Connection ended during Assignment" },
+ [BTS_CTR_ASSIGNMENT_STOPPED_SIGN] = \
+ { "assignment:stopped_sign",
+ "Connection ended during signaling lchan Assignment" },
+ [BTS_CTR_ASSIGNMENT_STOPPED_SPEECH] = \
+ { "assignment:stopped_speech",
+ "Connection ended during speech lchan Assignment" },
+ [BTS_CTR_ASSIGNMENT_NO_CHANNEL] = \
+ { "assignment:no_channel",
+ "Failure to allocate lchan for Assignment" },
+ [BTS_CTR_ASSIGNMENT_NO_CHANNEL_SIGN] = \
+ { "assignment:no_channel_sign",
+ "Failure to allocate signaling lchan for Assignment" },
+ [BTS_CTR_ASSIGNMENT_NO_CHANNEL_SPEECH] = \
+ { "assignment:no_channel_speech",
+ "Failure to allocate speech lchan for Assignment" },
+ [BTS_CTR_ASSIGNMENT_TIMEOUT] = \
+ { "assignment:timeout",
+ "Assignment timed out" },
+ [BTS_CTR_ASSIGNMENT_TIMEOUT_SIGN] = \
+ { "assignment:timeout_sign",
+ "Assignment of signaling lchan timed out" },
+ [BTS_CTR_ASSIGNMENT_TIMEOUT_SPEECH] = \
+ { "assignment:timeout_speech",
+ "Assignment of speech lchan timed out" },
+ [BTS_CTR_ASSIGNMENT_FAILED] = \
+ { "assignment:failed",
+ "Received Assignment Failure message" },
+ [BTS_CTR_ASSIGNMENT_FAILED_SIGN] = \
+ { "assignment:failed_sign",
+ "Received Assignment Failure message on signaling lchan" },
+ [BTS_CTR_ASSIGNMENT_FAILED_SPEECH] = \
+ { "assignment:failed_speech",
+ "Received Assignment Failure message on speech lchan" },
+ [BTS_CTR_ASSIGNMENT_ERROR] = \
+ { "assignment:error",
+ "Assignment failed for other reason" },
+ [BTS_CTR_ASSIGNMENT_ERROR_SIGN] = \
+ { "assignment:error_sign",
+ "Assignment of signaling lchan failed for other reason" },
+ [BTS_CTR_ASSIGNMENT_ERROR_SPEECH] = \
+ { "assignment:error_speech",
+ "Assignment of speech lchan failed for other reason" },
+ [BTS_CTR_LOCATION_UPDATE_ACCEPT] = \
+ { "location_update:accept",
+ "Location Update Accept" },
+ [BTS_CTR_LOCATION_UPDATE_REJECT] = \
+ { "location_update:reject",
+ "Location Update Reject" },
+ [BTS_CTR_LOCATION_UPDATE_DETACH] = \
+ { "location_update:detach",
+ "Location Update Detach" },
+ [BTS_CTR_LOCATION_UPDATE_UNKNOWN] = \
+ { "location_update:unknown",
+ "Location Update UNKNOWN" },
+ [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 inter-cell handover attempts" },
+ [BTS_CTR_INTRA_BSC_HO_COMPLETED] = \
+ { "intra_bsc_ho:completed",
+ "Intra-BSC inter-cell 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",
+ "Intra-BSC inter-cell HO failed for other reason" },
+
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_ATTEMPTED] = \
+ { "incoming_intra_bsc_ho:attempted",
+ "Incoming intra-BSC inter-cell handover attempts" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_COMPLETED] = \
+ { "incoming_intra_bsc_ho:completed",
+ "Incoming intra-BSC inter-cell handover completed" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_STOPPED] = \
+ { "incoming_intra_bsc_ho:stopped",
+ "Connection ended during HO" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_NO_CHANNEL] = \
+ { "incoming_intra_bsc_ho:no_channel",
+ "Failure to allocate lchan for HO" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_TIMEOUT] = \
+ { "incoming_intra_bsc_ho:timeout",
+ "Handover timed out" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_FAILED] = \
+ { "incoming_intra_bsc_ho:failed",
+ "Received Handover Fail messages" },
+ [BTS_CTR_INCOMING_INTRA_BSC_HO_ERROR] = \
+ { "incoming_intra_bsc_ho:error",
+ "Incoming intra-BSC inter-cell HO failed for other reason" },
+
+ [BTS_CTR_INTER_BSC_HO_OUT_ATTEMPTED] = \
+ { "interbsc_ho_out:attempted",
+ "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" },
+
+ [BTS_CTR_SRVCC_ATTEMPTED] = \
+ { "srvcc:attempted",
+ "Intra-BSC handover attempts" },
+ [BTS_CTR_SRVCC_COMPLETED] = \
+ { "srvcc:completed",
+ "Intra-BSC handover completed" },
+ [BTS_CTR_SRVCC_STOPPED] = \
+ { "srvcc:stopped",
+ "Connection ended during HO" },
+ [BTS_CTR_SRVCC_NO_CHANNEL] = \
+ { "srvcc:no_channel",
+ "Failure to allocate lchan for HO" },
+ [BTS_CTR_SRVCC_TIMEOUT] = \
+ { "srvcc:timeout",
+ "Handover timed out" },
+ [BTS_CTR_SRVCC_FAILED] = \
+ { "srvcc:failed",
+ "Received Handover Fail messages" },
+ [BTS_CTR_SRVCC_ERROR] = \
+ { "srvcc:error",
+ "Re-assignment failed for other reason" },
+ [BTS_CTR_ALL_ALLOCATED_SDCCH] = \
+ { "all_allocated:sdcch",
+ "Cumulative counter of seconds where all SDCCH channels were allocated" },
+ [BTS_CTR_ALL_ALLOCATED_STATIC_SDCCH] = \
+ { "all_allocated:static_sdcch",
+ "Cumulative counter of seconds where all non-dynamic SDCCH channels were allocated" },
+ [BTS_CTR_ALL_ALLOCATED_TCH] = \
+ { "all_allocated:tch",
+ "Cumulative counter of seconds where all TCH channels were allocated" },
+ [BTS_CTR_ALL_ALLOCATED_STATIC_TCH] = \
+ { "all_allocated:static_tch",
+ "Cumulative counter of seconds where all non-dynamic TCH channels were allocated" },
+
+ [BTS_CTR_CM_SERV_REJ] = \
+ { "cm_serv_rej", "MSC sent CM Service Reject" },
+ [BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_HLR] = \
+ { "cm_serv_rej:imsi_unknown_in_hlr",
+ "MSC sent CM Service Reject with cause IMSI_UNKNOWN_IN_HLR" },
+ [BTS_CTR_CM_SERV_REJ_ILLEGAL_MS] = \
+ { "cm_serv_rej:illegal_ms",
+ "MSC sent CM Service Reject with cause ILLEGAL_MS" },
+ [BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_VLR] = \
+ { "cm_serv_rej:imsi_unknown_in_vlr",
+ "MSC sent CM Service Reject with cause IMSI_UNKNOWN_IN_VLR" },
+ [BTS_CTR_CM_SERV_REJ_IMEI_NOT_ACCEPTED] = \
+ { "cm_serv_rej:imei_not_accepted",
+ "MSC sent CM Service Reject with cause IMEI_NOT_ACCEPTED" },
+ [BTS_CTR_CM_SERV_REJ_ILLEGAL_ME] = \
+ { "cm_serv_rej:illegal_me",
+ "MSC sent CM Service Reject with cause ILLEGAL_ME" },
+ [BTS_CTR_CM_SERV_REJ_PLMN_NOT_ALLOWED] = \
+ { "cm_serv_rej:plmn_not_allowed",
+ "MSC sent CM Service Reject with cause PLMN_NOT_ALLOWED" },
+ [BTS_CTR_CM_SERV_REJ_LOC_NOT_ALLOWED] = \
+ { "cm_serv_rej:loc_not_allowed",
+ "MSC sent CM Service Reject with cause LOC_NOT_ALLOWED" },
+ [BTS_CTR_CM_SERV_REJ_ROAMING_NOT_ALLOWED] = \
+ { "cm_serv_rej:roaming_not_allowed",
+ "MSC sent CM Service Reject with cause ROAMING_NOT_ALLOWED" },
+ [BTS_CTR_CM_SERV_REJ_NETWORK_FAILURE] = \
+ { "cm_serv_rej:network_failure",
+ "MSC sent CM Service Reject with cause NETWORK_FAILURE" },
+ [BTS_CTR_CM_SERV_REJ_SYNCH_FAILURE] = \
+ { "cm_serv_rej:synch_failure",
+ "MSC sent CM Service Reject with cause SYNCH_FAILURE" },
+ [BTS_CTR_CM_SERV_REJ_CONGESTION] = \
+ { "cm_serv_rej:congestion",
+ "MSC sent CM Service Reject with cause CONGESTION" },
+ [BTS_CTR_CM_SERV_REJ_SRV_OPT_NOT_SUPPORTED] = \
+ { "cm_serv_rej:srv_opt_not_supported",
+ "MSC sent CM Service Reject with cause SRV_OPT_NOT_SUPPORTED" },
+ [BTS_CTR_CM_SERV_REJ_RQD_SRV_OPT_NOT_SUPPORTED] = \
+ { "cm_serv_rej:rqd_srv_opt_not_supported",
+ "MSC sent CM Service Reject with cause RQD_SRV_OPT_NOT_SUPPORTED" },
+ [BTS_CTR_CM_SERV_REJ_SRV_OPT_TMP_OUT_OF_ORDER] = \
+ { "cm_serv_rej:srv_opt_tmp_out_of_order",
+ "MSC sent CM Service Reject with cause SRV_OPT_TMP_OUT_OF_ORDER" },
+ [BTS_CTR_CM_SERV_REJ_CALL_CAN_NOT_BE_IDENTIFIED] = \
+ { "cm_serv_rej:call_can_not_be_identified",
+ "MSC sent CM Service Reject with cause CALL_CAN_NOT_BE_IDENTIFIED" },
+ [BTS_CTR_CM_SERV_REJ_INCORRECT_MESSAGE] = \
+ { "cm_serv_rej:incorrect_message",
+ "MSC sent CM Service Reject with cause INCORRECT_MESSAGE" },
+ [BTS_CTR_CM_SERV_REJ_INVALID_MANDANTORY_INF] = \
+ { "cm_serv_rej:invalid_mandantory_inf",
+ "MSC sent CM Service Reject with cause INVALID_MANDANTORY_INF" },
+ [BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_IMPLEMENTED] = \
+ { "cm_serv_rej:msg_type_not_implemented",
+ "MSC sent CM Service Reject with cause MSG_TYPE_NOT_IMPLEMENTED" },
+ [BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_COMPATIBLE] = \
+ { "cm_serv_rej:msg_type_not_compatible",
+ "MSC sent CM Service Reject with cause MSG_TYPE_NOT_COMPATIBLE" },
+ [BTS_CTR_CM_SERV_REJ_INF_ELEME_NOT_IMPLEMENTED] = \
+ { "cm_serv_rej:inf_eleme_not_implemented",
+ "MSC sent CM Service Reject with cause INF_ELEME_NOT_IMPLEMENTED" },
+ [BTS_CTR_CM_SERV_REJ_CONDTIONAL_IE_ERROR] = \
+ { "cm_serv_rej:condtional_ie_error",
+ "MSC sent CM Service Reject with cause CONDTIONAL_IE_ERROR" },
+ [BTS_CTR_CM_SERV_REJ_MSG_NOT_COMPATIBLE] = \
+ { "cm_serv_rej:msg_not_compatible",
+ "MSC sent CM Service Reject with cause MSG_NOT_COMPATIBLE" },
+ [BTS_CTR_CM_SERV_REJ_PROTOCOL_ERROR] = \
+ { "cm_serv_rej:protocol_error",
+ "MSC sent CM Service Reject with cause PROTOCOL_ERROR" },
+ [BTS_CTR_CM_SERV_REJ_RETRY_IN_NEW_CELL] = \
+ { "cm_serv_rej:retry_in_new_cell",
+ "MSC sent CM Service Reject with cause 00110000..00111111, Retry upon entry in a new cell" },
+};
+
+const struct rate_ctr_group_desc bts_ctrg_desc = {
+ "bts",
+ "base transceiver station",
+ OSMO_STATS_CLASS_GLOBAL,
+ ARRAY_SIZE(bts_ctr_description),
+ bts_ctr_description,
+};
+
+const struct osmo_stat_item_desc bts_stat_desc[] = {
+ [BTS_STAT_UPTIME_SECONDS] = \
+ { "uptime:seconds",
+ "Seconds of uptime",
+ "s", 60, 0 },
+ [BTS_STAT_CHAN_LOAD_AVERAGE] = \
+ { "chanloadavg",
+ "Channel load average",
+ "%", 60, 0 },
+ [BTS_STAT_CHAN_CCCH_SDCCH4_USED] = \
+ { "chan_ccch_sdcch4:used",
+ "Number of CCCH+SDCCH4 channels used",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL] = \
+ { "chan_ccch_sdcch4:total",
+ "Number of CCCH+SDCCH4 channels total",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_TCH_F_USED] = \
+ { "chan_tch_f:used",
+ "Number of TCH/F channels used",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_TCH_F_TOTAL] = \
+ { "chan_tch_f:total",
+ "Number of TCH/F channels total",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_TCH_H_USED] = \
+ { "chan_tch_h:used",
+ "Number of TCH/H channels used",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_TCH_H_TOTAL] = \
+ { "chan_tch_h:total",
+ "Number of TCH/H channels total",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_SDCCH8_USED] = \
+ { "chan_sdcch8:used",
+ "Number of SDCCH8 channels used",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_SDCCH8_TOTAL] = \
+ { "chan_sdcch8:total",
+ "Number of SDCCH8 channels total",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_TCH_F_PDCH_USED] = \
+ { "chan_dynamic_ipaccess:used",
+ "Number of DYNAMIC/IPACCESS channels used",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_TCH_F_PDCH_TOTAL] = \
+ { "chan_dynamic_ipaccess:total",
+ "Number of DYNAMIC/IPACCESS channels total",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED] = \
+ { "chan_ccch_sdcch4_cbch:used",
+ "Number of CCCH+SDCCH4+CBCH channels used",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL] = \
+ { "chan_ccch_sdcch4_cbch:total",
+ "Number of CCCH+SDCCH4+CBCH channels total",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_SDCCH8_CBCH_USED] = \
+ { "chan_sdcch8_cbch:used",
+ "Number of SDCCH8+CBCH channels used",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL] = \
+ { "chan_sdcch8_cbch:total",
+ "Number of SDCCH8+CBCH channels total",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_OSMO_DYN_USED] = \
+ { "chan_dynamic_osmocom:used",
+ "Number of DYNAMIC/OSMOCOM channels used",
+ "", 60, 0 },
+ [BTS_STAT_CHAN_OSMO_DYN_TOTAL] = \
+ { "chan_dynamic_osmocom:total",
+ "Number of DYNAMIC/OSMOCOM channels total",
+ "", 60, 0 },
+ [BTS_STAT_T3122] = \
+ { "T3122",
+ "T3122 IMMEDIATE ASSIGNMENT REJECT wait indicator",
+ "s", 60, GSM_T3122_DEFAULT },
+ [BTS_STAT_RACH_BUSY] = \
+ { "rach_busy",
+ "RACH slots with signal above threshold",
+ "%", 60, 0 },
+ [BTS_STAT_RACH_ACCESS] = \
+ { "rach_access",
+ "RACH slots with access bursts in them",
+ "%", 60, 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 (same as num_trx:rsl_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 },
+ [BTS_STAT_NUM_TRX_RSL_CONNECTED] = \
+ { "num_trx:rsl_connected",
+ "Number of TRX in this BTS where RSL is up",
+ "" },
+ [BTS_STAT_NUM_TRX_TOTAL] = \
+ { "num_trx:total",
+ "Number of configured TRX in this BTS",
+ "" },
+ [BTS_STAT_PAGING_REQ_QUEUE_LENGTH] = \
+ { "paging:request_queue_length",
+ "Paging Request queue length",
+ "", 60, 0 },
+ [BTS_STAT_PAGING_AVAILABLE_SLOTS] = \
+ { "paging:available_slots",
+ "Available paging slots in this BTS",
+ "", 60, 0 },
+ [BTS_STAT_PAGING_T3113] = \
+ { "paging:t3113",
+ "T3113 paging timer",
+ "s", 60, 0 },
+};
+
+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,
+};
+
+/* Return 'true' if and only if Ny1 satisfies network requirements */
+bool gsm_bts_check_ny1(const struct gsm_bts *bts)
+{
+ unsigned long T3105, ny1, ny1_recommended;
+ T3105 = osmo_tdef_get(bts->network->T_defs, 3105, OSMO_TDEF_MS, -1);
+ ny1 = osmo_tdef_get(bts->network->T_defs, -3105, OSMO_TDEF_CUSTOM, -1);
+ if (!(T3105 * ny1 > GSM_T3124_MAX + GSM_NY1_REQ_DELTA)) {
+ /* See comment for GSM_NY1_DEFAULT */
+ ny1_recommended = (GSM_T3124_MAX + GSM_NY1_REQ_DELTA)/T3105 + 1;
+ LOGP(DNM, LOGL_ERROR, "Value of Ny1 should be higher. "
+ "Is: %lu, lowest recommendation: %lu\n",
+ ny1, ny1_recommended);
+ return false;
+ }
+ return true;
+}
diff --git a/src/osmo-bsc/bts_ctrl.c b/src/osmo-bsc/bts_ctrl.c
new file mode 100644
index 000000000..728d89f28
--- /dev/null
+++ b/src/osmo-bsc/bts_ctrl.c
@@ -0,0 +1,652 @@
+/*
+ * (C) 2013-2015 by Holger Hans Peter Freyther
+ * (C) 2013-2022 by sysmocom s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <time.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/bsc/ctrl.h>
+#include <osmocom/bsc/osmo_bsc_rf.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/ipaccess.h>
+#include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/neighbor_ident.h>
+
+static int location_equal(struct bts_location *a, struct bts_location *b)
+{
+ return ((a->tstamp == b->tstamp) && (a->valid == b->valid) && (a->lat == b->lat) &&
+ (a->lon == b->lon) && (a->height == b->height));
+}
+
+static void cleanup_locations(struct llist_head *locations)
+{
+ struct bts_location *myloc, *tmp;
+ int invalpos = 0, i = 0;
+
+ LOGP(DCTRL, LOGL_DEBUG, "Checking position list.\n");
+ llist_for_each_entry_safe(myloc, tmp, locations, list) {
+ i++;
+ if (i > 3) {
+ LOGP(DCTRL, LOGL_DEBUG, "Deleting old position.\n");
+ llist_del(&myloc->list);
+ talloc_free(myloc);
+ } else if (myloc->valid == BTS_LOC_FIX_INVALID) {
+ /* Only capture the newest of subsequent invalid positions */
+ invalpos++;
+ if (invalpos > 1) {
+ LOGP(DCTRL, LOGL_DEBUG, "Deleting subsequent invalid position.\n");
+ invalpos--;
+ i--;
+ llist_del(&myloc->list);
+ talloc_free(myloc);
+ }
+ } else {
+ invalpos = 0;
+ }
+ }
+ LOGP(DCTRL, LOGL_DEBUG, "Found %d positions.\n", i);
+}
+
+static int get_bts_loc(struct ctrl_cmd *cmd, void *data);
+
+void ctrl_generate_bts_location_state_trap(struct gsm_bts *bts, struct bsc_msc_data *msc)
+{
+ struct ctrl_cmd *cmd;
+ const char *oper, *admin, *policy;
+
+ cmd = ctrl_cmd_create(msc, CTRL_TYPE_TRAP);
+ if (!cmd) {
+ LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
+ return;
+ }
+
+ cmd->id = "0";
+ cmd->variable = talloc_asprintf(cmd, "bts.%d.location-state", bts->nr);
+
+ /* Prepare the location reply */
+ cmd->node = bts;
+ get_bts_loc(cmd, NULL);
+
+ oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts));
+ admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts));
+ policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts));
+
+ cmd->reply = talloc_asprintf_append(cmd->reply,
+ ",%s,%s,%s,%s,%s",
+ oper, admin, policy,
+ osmo_mcc_name(bts->network->plmn.mcc),
+ osmo_mnc_name(bts->network->plmn.mnc,
+ bts->network->plmn.mnc_3_digits));
+
+ osmo_bsc_send_trap(cmd, msc);
+ talloc_free(cmd);
+}
+
+void bsc_gen_location_state_trap(struct gsm_bts *bts)
+{
+ struct bsc_msc_data *msc;
+
+ llist_for_each_entry(msc, &bts->network->mscs, entry)
+ ctrl_generate_bts_location_state_trap(bts, msc);
+}
+
+CTRL_CMD_DEFINE(bts_loc, "location");
+static int get_bts_loc(struct ctrl_cmd *cmd, void *data)
+{
+ struct bts_location *curloc;
+ struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
+ if (!bts) {
+ cmd->reply = "bts not found.";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (llist_empty(&bts->loc_list)) {
+ cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0");
+ return CTRL_CMD_REPLY;
+ }
+
+ curloc = llist_entry(bts->loc_list.next, struct bts_location, list);
+
+ cmd->reply = talloc_asprintf(cmd, "%lu,%s,%f,%f,%f", curloc->tstamp,
+ get_value_string(bts_loc_fix_names, curloc->valid), curloc->lat, curloc->lon, curloc->height);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_loc(struct ctrl_cmd *cmd, void *data)
+{
+ char *saveptr, *lat, *lon, *height, *tstamp, *valid, *tmp;
+ struct bts_location *curloc, *lastloc;
+ int ret;
+ struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
+ if (!bts) {
+ cmd->reply = "bts not found.";
+ return CTRL_CMD_ERROR;
+ }
+
+ tmp = talloc_strdup(cmd, cmd->value);
+ if (!tmp)
+ goto oom;
+
+ tstamp = strtok_r(tmp, ",", &saveptr);
+ valid = strtok_r(NULL, ",", &saveptr);
+ lat = strtok_r(NULL, ",", &saveptr);
+ lon = strtok_r(NULL, ",", &saveptr);
+ height = strtok_r(NULL, "\0", &saveptr);
+
+ /* Check if one of the strtok results was NULL. This will probably never occur since we will only see verified
+ * input in this code path */
+ if ((tstamp == NULL) || (valid == NULL) || (lat == NULL) || (lon == NULL) || (height == NULL)) {
+ talloc_free(tmp);
+ cmd->reply = "parse error";
+ return CTRL_CMD_ERROR;
+ }
+
+ curloc = talloc_zero(tall_bsc_ctx, struct bts_location);
+ if (!curloc) {
+ talloc_free(tmp);
+ goto oom;
+ }
+ INIT_LLIST_HEAD(&curloc->list);
+
+ curloc->tstamp = atol(tstamp);
+ curloc->valid = get_string_value(bts_loc_fix_names, valid);
+ curloc->lat = atof(lat);
+ curloc->lon = atof(lon);
+ curloc->height = atof(height);
+ talloc_free(tmp);
+
+ lastloc = llist_entry(bts->loc_list.next, struct bts_location, list);
+
+ /* Add location to the end of the list */
+ llist_add(&curloc->list, &bts->loc_list);
+
+ ret = get_bts_loc(cmd, data);
+
+ if (!location_equal(curloc, lastloc))
+ bsc_gen_location_state_trap(bts);
+
+ cleanup_locations(&bts->loc_list);
+
+ return ret;
+
+oom:
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+}
+
+static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data)
+{
+ char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp;
+ time_t tstamp;
+ int valid;
+ double lat, lon, height __attribute__((unused));
+
+ tmp = talloc_strdup(cmd, value);
+ if (!tmp)
+ return 1;
+
+ tstampstr = strtok_r(tmp, ",", &saveptr);
+ validstr = strtok_r(NULL, ",", &saveptr);
+ latstr = strtok_r(NULL, ",", &saveptr);
+ lonstr = strtok_r(NULL, ",", &saveptr);
+ heightstr = strtok_r(NULL, "\0", &saveptr);
+
+ if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) ||
+ (lonstr == NULL) || (heightstr == NULL))
+ goto err;
+
+ tstamp = atol(tstampstr);
+ valid = get_string_value(bts_loc_fix_names, validstr);
+ lat = atof(latstr);
+ lon = atof(lonstr);
+ height = atof(heightstr);
+ talloc_free(tmp);
+ tmp = NULL;
+
+ if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) ||
+ (lon < -180) || (lon > 180) || (valid < 0)) {
+ goto err;
+ }
+
+ return 0;
+
+err:
+ talloc_free(tmp);
+ cmd->reply = talloc_strdup(cmd, "The format is <unixtime>,(invalid|fix2d|fix3d),<lat>,<lon>,<height>");
+ return 1;
+}
+
+/* BTS related commands below */
+CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535);
+CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535);
+
+static int set_bts_apply_config(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ if (!is_ipaccess_bts(bts)) {
+ cmd->reply = "BTS is not IP based";
+ return CTRL_CMD_ERROR;
+ }
+
+ ipaccess_drop_oml(bts, "ctrl bts.apply-configuration");
+ cmd->reply = "Tried to drop the BTS";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(bts_apply_config, "apply-configuration");
+
+static int set_bts_si(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ rc = gsm_bts_set_system_infos(bts);
+ if (rc != 0) {
+ cmd->reply = "Failed to generate SI";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "Generated new System Information";
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_WO_NOVRF(bts_si, "send-new-system-informations");
+
+static int set_bts_power_ctrl_defs(struct ctrl_cmd *cmd, void *data)
+{
+ const struct gsm_bts *bts = cmd->node;
+ const struct gsm_bts_trx *trx;
+
+ if (bts->ms_power_ctrl.mode != GSM_PWR_CTRL_MODE_DYN_BTS) {
+ cmd->reply = "BTS is not using dyn-bts mode";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (bts->model->power_ctrl_send_def_params == NULL) {
+ cmd->reply = "Not implemented for this BTS model";
+ return CTRL_CMD_ERROR;
+ }
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (bts->model->power_ctrl_send_def_params(trx) != 0) {
+ cmd->reply = "power_ctrl_send_def_params() failed";
+ return CTRL_CMD_ERROR;
+ }
+ }
+
+ cmd->reply = "Default power control parameters have been sent";
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_WO_NOVRF(bts_power_ctrl_defs, "send-power-control-defaults");
+
+static int get_bts_chan_load(struct ctrl_cmd *cmd, void *data)
+{
+ int i;
+ struct pchan_load pl;
+ struct gsm_bts *bts;
+ const char *space = "";
+
+ bts = cmd->node;
+ memset(&pl, 0, sizeof(pl));
+ bts_chan_load(&pl, bts);
+
+ cmd->reply = talloc_strdup(cmd, "");
+
+ for (i = 0; i < ARRAY_SIZE(pl.pchan); ++i) {
+ const struct load_counter *lc = &pl.pchan[i];
+
+ /* These can never have user load */
+ if (i == GSM_PCHAN_NONE)
+ continue;
+ if (i == GSM_PCHAN_CCCH)
+ continue;
+ if (i == GSM_PCHAN_PDCH)
+ continue;
+ if (i == GSM_PCHAN_UNKNOWN)
+ continue;
+
+ cmd->reply = talloc_asprintf_append(cmd->reply,
+ "%s%s,%u,%u",
+ space, gsm_pchan_name(i), lc->used, lc->total);
+ if (!cmd->reply)
+ goto error;
+ space = " ";
+ }
+
+ return CTRL_CMD_REPLY;
+
+error:
+ cmd->reply = "Memory allocation failure";
+ return CTRL_CMD_ERROR;
+}
+
+CTRL_CMD_DEFINE_RO(bts_chan_load, "channel-load");
+
+static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data)
+{
+ const struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = get_model_oml_status(bts);
+
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_RO(bts_oml_conn, "oml-connection-state");
+
+static int get_bts_oml_up(struct ctrl_cmd *cmd, void *data)
+{
+ const struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_asprintf(cmd, "%llu", bts_uptime(bts));
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_RO(bts_oml_up, "oml-uptime");
+
+static int verify_bts_gprs_mode(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int valid;
+ enum bts_gprs_mode mode;
+ struct gsm_bts *bts = cmd->node;
+
+ mode = bts_gprs_mode_parse(value, &valid);
+ if (!valid) {
+ cmd->reply = "Mode is not known";
+ return 1;
+ }
+
+ if (!bts_gprs_mode_is_compat(bts, mode)) {
+ cmd->reply = "bts does not support this mode";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_bts_gprs_mode(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_strdup(cmd, bts_gprs_mode_name(bts->gprs.mode));
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_gprs_mode(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ bts->gprs.mode = bts_gprs_mode_parse(cmd->value, NULL);
+ return get_bts_gprs_mode(cmd, data);
+}
+
+CTRL_CMD_DEFINE(bts_gprs_mode, "gprs-mode");
+
+static int get_bts_rf_state(struct ctrl_cmd *cmd, void *data)
+{
+ const char *oper, *admin, *policy;
+ struct gsm_bts *bts = cmd->node;
+
+ if (!bts) {
+ cmd->reply = "bts not found.";
+ return CTRL_CMD_ERROR;
+ }
+
+ oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts));
+ admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts));
+ policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts));
+
+ cmd->reply = talloc_asprintf(cmd, "%s,%s,%s", oper, admin, policy);
+ if (!cmd->reply) {
+ cmd->reply = "OOM.";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+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");
+
+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_bts_ctrl_cmds_install(void)
+{
+ int rc = 0;
+
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_power_ctrl_defs);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn);
+ 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 = bsc_bts_trx_ctrl_cmds_install();
+
+ return rc;
+}
diff --git a/src/osmo-bsc/bts_ericsson_rbs2000.c b/src/osmo-bsc/bts_ericsson_rbs2000.c
index 1297b3025..36b378318 100644
--- a/src/osmo-bsc/bts_ericsson_rbs2000.c
+++ b/src/osmo-bsc/bts_ericsson_rbs2000.c
@@ -35,16 +35,8 @@
static void bootstrap_om_bts(struct gsm_bts *bts)
{
- struct gsm_bts_trx *trx;
-
LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
- /* Global init (not bootstrapping) */
- abis_om2k_bts_init(bts);
-
- llist_for_each_entry(trx, &bts->trx_list, list)
- abis_om2k_trx_init(trx);
-
/* TODO: Should we wait for a Failure report? */
om2k_bts_fsm_start(bts);
}
@@ -53,12 +45,14 @@ static void bootstrap_om_trx(struct gsm_bts_trx *trx)
{
LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n",
trx->bts->nr, trx->nr);
- /* FIXME */
+
+ om2k_trx_fsm_start(trx);
}
static int shutdown_om(struct gsm_bts *bts)
{
gsm_bts_all_ts_dispatch(bts, TS_EV_OML_DOWN, NULL);
+ gsm_bts_stats_reset(bts);
/* FIXME */
return 0;
@@ -145,6 +139,10 @@ static int inp_sig_cb(unsigned int subsys, unsigned int signal,
LOGP(DNM, LOGL_NOTICE, "Line-%u TS-%u TEI-%u SAPI-%u: Link "
"Lost for Ericsson RBS2000. Re-starting DL Establishment\n",
isd->line->num, isd->ts_nr, isd->tei, isd->sapi);
+ if (isd->tei == isd->trx->bts->oml_tei)
+ om2k_bts_fsm_reset(isd->trx->bts);
+ else
+ om2k_trx_fsm_reset(isd->trx);
/* Some datalink for a given TEI/SAPI went down, try to re-start it */
e1i_ts = &isd->line->ts[isd->ts_nr-1];
OSMO_ASSERT(e1i_ts->type == E1INP_TS_TYPE_SIGN);
@@ -177,6 +175,11 @@ static void config_write_bts(struct vty *vty, struct gsm_bts *bts)
abis_om2k_config_write_bts(vty, bts);
}
+static void config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
+{
+ abis_om2k_config_write_trx(vty, trx);
+}
+
static int bts_model_rbs2k_start(struct gsm_network *net);
static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line)
@@ -184,17 +187,40 @@ static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line)
e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops);
}
+static int bts_model_rbs2k_bts_init(struct gsm_bts *bts)
+{
+ abis_om2k_bts_init(bts);
+ return 0;
+}
+
+static int bts_model_rbs2k_trx_init(struct gsm_bts_trx *trx)
+{
+ abis_om2k_trx_init(trx);
+ return 0;
+}
+
static struct gsm_bts_model model_rbs2k = {
.type = GSM_BTS_TYPE_RBS2000,
.name = "rbs2000",
.start = bts_model_rbs2k_start,
+ .bts_init = bts_model_rbs2k_bts_init,
+ .trx_init = bts_model_rbs2k_trx_init,
.oml_rcvmsg = &abis_om2k_rcvmsg,
.config_write_bts = &config_write_bts,
+ .config_write_trx = &config_write_trx,
.e1line_bind_ops = &bts_model_rbs2k_e1line_bind_ops,
};
static int bts_model_rbs2k_start(struct gsm_network *net)
{
+ osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
+ osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
+
+ return 0;
+}
+
+int bts_model_rbs2k_init(void)
+{
model_rbs2k.features.data = &model_rbs2k._features_data[0];
model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data);
@@ -204,13 +230,5 @@ static int bts_model_rbs2k_start(struct gsm_network *net)
osmo_bts_set_feature(&model_rbs2k.features, BTS_FEAT_HSCSD);
osmo_bts_set_feature(&model_rbs2k.features, BTS_FEAT_MULTI_TSC);
- osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
- osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
-
- return 0;
-}
-
-int bts_model_rbs2k_init(void)
-{
return gsm_bts_model_register(&model_rbs2k);
}
diff --git a/src/osmo-bsc/bts_init.c b/src/osmo-bsc/bts_init.c
index 18f1ed4c8..0e3debcf4 100644
--- a/src/osmo-bsc/bts_init.c
+++ b/src/osmo-bsc/bts_init.c
@@ -24,7 +24,7 @@ int bts_init(void)
bts_model_rbs2k_init();
bts_model_nanobts_init();
bts_model_nokia_site_init();
- bts_model_sysmobts_init();
+ bts_model_osmobts_init();
/* Your new BTS here. */
return 0;
}
diff --git a/src/osmo-bsc/bts_ipaccess_nanobts.c b/src/osmo-bsc/bts_ipaccess_nanobts.c
index 4c95196f8..9f17359d7 100644
--- a/src/osmo-bsc/bts_ipaccess_nanobts.c
+++ b/src/osmo-bsc/bts_ipaccess_nanobts.c
@@ -1,6 +1,7 @@
/* ip.access nanoBTS specific code */
/* (C) 2009-2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -31,11 +32,13 @@
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
+#include <osmocom/core/stat_item.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/abis_nm.h>
#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/abis_osmo.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/abis/subchan_demux.h>
#include <osmocom/gsm/ipa.h>
@@ -46,10 +49,16 @@
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/timeslot_fsm.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bts_sm.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/bsc_stats.h>
static int bts_model_nanobts_start(struct gsm_network *net);
static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line);
+static int power_ctrl_send_def_params(const struct gsm_bts_trx *trx);
+static int power_ctrl_enc_rsl_params(struct msgb *msg, const struct gsm_power_ctrl_params *cp);
+
static char *get_oml_status(const struct gsm_bts *bts)
{
if (bts->oml_link)
@@ -65,6 +74,11 @@ struct gsm_bts_model bts_model_nanobts = {
.oml_rcvmsg = &abis_nm_rcvmsg,
.oml_status = &get_oml_status,
.e1line_bind_ops = bts_model_nanobts_e1line_bind_ops,
+
+ /* MS/BS Power control specific API */
+ .power_ctrl_send_def_params = &power_ctrl_send_def_params,
+ .power_ctrl_enc_rsl_params = &power_ctrl_enc_rsl_params,
+
/* Some nanoBTS firmwares (if not all) don't support SI2ter and cause
* problems on some MS if it is enabled, see OS#3063. Disable it by
* default, can still be enabled through VTY cmd with same name.
@@ -117,6 +131,7 @@ struct gsm_bts_model bts_model_nanobts = {
[NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V },
},
},
+ .features_get_reported = true,
};
@@ -125,127 +140,52 @@ static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd)
{
uint8_t obj_class = nsd->obj_class;
void *obj = nsd->obj;
- struct gsm_nm_state *new_state = nsd->new_state;
+ struct gsm_bts_sm *bts_sm;
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
+ struct gsm_bts_bb_trx *bb_transc;
struct gsm_bts_trx_ts *ts;
- struct gsm_bts_gprs_nsvc *nsvc;
-
- struct msgb *msgb;
+ struct gsm_gprs_nsvc *nsvc;
+ struct gsm_gprs_nse *nse;
+ struct gsm_gprs_cell *cell;
if (!is_ipaccess_bts(nsd->bts))
return 0;
- /* This event-driven BTS setup is currently only required on nanoBTS */
-
- /* S_NM_STATECHG_ADM is called after we call chg_adm_state() and would create
- * endless loop */
- if (evt != S_NM_STATECHG_OPER)
- return 0;
-
switch (obj_class) {
case NM_OC_SITE_MANAGER:
- bts = container_of(obj, struct gsm_bts, site_mgr);
- if ((new_state->operational == NM_OPSTATE_ENABLED &&
- new_state->availability == NM_AVSTATE_OK) ||
- (new_state->operational == NM_OPSTATE_DISABLED &&
- new_state->availability == NM_AVSTATE_OFF_LINE))
- abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff);
+ bts_sm = obj;
+ osmo_fsm_inst_dispatch(bts_sm->mo.fi, NM_EV_STATE_CHG_REP, nsd);
break;
case NM_OC_BTS:
bts = obj;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- msgb = nanobts_attr_bts_get(bts);
- abis_nm_set_bts_attr(bts, msgb->data, msgb->len);
- msgb_free(msgb);
- abis_nm_chg_adm_state(bts, obj_class,
- bts->bts_nr, 0xff, 0xff,
- NM_STATE_UNLOCKED);
- abis_nm_opstart(bts, obj_class,
- bts->bts_nr, 0xff, 0xff);
- }
+ osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_STATE_CHG_REP, nsd);
+ break;
+ case NM_OC_BASEB_TRANSC:
+ bb_transc = obj;
+ osmo_fsm_inst_dispatch(bb_transc->mo.fi, NM_EV_STATE_CHG_REP, nsd);
break;
case NM_OC_CHANNEL:
ts = obj;
- trx = ts->trx;
- if (new_state->operational == NM_OPSTATE_DISABLED &&
- new_state->availability == NM_AVSTATE_DEPENDENCY) {
- enum abis_nm_chan_comb ccomb =
- abis_nm_chcomb4pchan(ts->pchan_from_config);
- if (abis_nm_set_channel_attr(ts, ccomb) == -EINVAL) {
- ipaccess_drop_oml_deferred(trx->bts);
- return -1;
- }
- abis_nm_chg_adm_state(trx->bts, obj_class,
- trx->bts->bts_nr, trx->nr, ts->nr,
- NM_STATE_UNLOCKED);
- abis_nm_opstart(trx->bts, obj_class,
- trx->bts->bts_nr, trx->nr, ts->nr);
- }
+ osmo_fsm_inst_dispatch(ts->mo.fi, NM_EV_STATE_CHG_REP, nsd);
break;
case NM_OC_RADIO_CARRIER:
- /* OPSTART done after Set Radio Carrier Attributes ACK is received */
+ trx = obj;
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_STATE_CHG_REP, nsd);
break;
case NM_OC_GPRS_NSE:
- bts = container_of(obj, struct gsm_bts, gprs.nse);
- if (bts->gprs.mode == BTS_GPRS_NONE)
- break;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- msgb = nanobts_attr_nse_get(bts);
- abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
- 0xff, 0xff, msgb->data,
- msgb->len);
- msgb_free(msgb);
- abis_nm_opstart(bts, obj_class, bts->bts_nr,
- 0xff, 0xff);
- }
+ nse = obj;
+ osmo_fsm_inst_dispatch(nse->mo.fi, NM_EV_STATE_CHG_REP, nsd);
break;
case NM_OC_GPRS_CELL:
- bts = container_of(obj, struct gsm_bts, gprs.cell);
- if (bts->gprs.mode == BTS_GPRS_NONE)
- break;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- msgb = nanobts_attr_cell_get(bts);
- abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
- 0, 0xff, msgb->data,
- msgb->len);
- msgb_free(msgb);
- abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
- 0, 0xff, NM_STATE_UNLOCKED);
- abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr,
- 0xff, 0xff, NM_STATE_UNLOCKED);
- abis_nm_opstart(bts, obj_class, bts->bts_nr,
- 0, 0xff);
- }
+ cell = obj;
+ osmo_fsm_inst_dispatch(cell->mo.fi, NM_EV_STATE_CHG_REP, nsd);
break;
case NM_OC_GPRS_NSVC:
nsvc = obj;
- bts = nsvc->bts;
- if (bts->gprs.mode == BTS_GPRS_NONE)
- break;
- /* We skip NSVC1 since we only use NSVC0 */
- if (nsvc->id == 1)
- break;
- if (!osmo_bts_has_feature(&bts->features, BTS_FEAT_IPV6_NSVC) &&
- nsvc->remote.u.sa.sa_family == AF_INET6) {
- LOGP(DLINP, LOGL_ERROR, "BTS %d does not support IPv6 but an IPv6 address was configured!\n", bts->nr);
- break;
- }
-
- if ((new_state->availability == NM_AVSTATE_OFF_LINE) ||
- (new_state->availability == NM_AVSTATE_DEPENDENCY)) {
- msgb = nanobts_attr_nscv_get(bts);
- abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
- nsvc->id, 0xff,
- msgb->data, msgb->len);
- msgb_free(msgb);
- abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
- nsvc->id, 0xff,
- NM_STATE_UNLOCKED);
- abis_nm_opstart(bts, obj_class, bts->bts_nr,
- nsvc->id, 0xff);
- }
+ osmo_fsm_inst_dispatch(nsvc->mo.fi, NM_EV_STATE_CHG_REP, nsd);
+ break;
default:
break;
}
@@ -258,44 +198,46 @@ static int sw_activ_rep(struct msgb *mb)
struct abis_om_fom_hdr *foh = msgb_l3(mb);
struct e1inp_sign_link *sign_link = mb->dst;
struct gsm_bts *bts = sign_link->trx->bts;
- struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
-
- if (!trx)
- return -EINVAL;
+ struct gsm_bts_trx *trx;
+ struct gsm_gprs_nsvc *nsvc;
+ struct gsm_bts_trx_ts *ts;
- if (!is_ipaccess_bts(trx->bts))
+ if (!is_ipaccess_bts(bts))
return 0;
switch (foh->obj_class) {
+ case NM_OC_SITE_MANAGER:
+ osmo_fsm_inst_dispatch(bts->site_mgr->mo.fi, NM_EV_SW_ACT_REP, NULL);
+ break;
+ case NM_OC_BTS:
+ osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_SW_ACT_REP, NULL);
+ break;
case NM_OC_BASEB_TRANSC:
- abis_nm_chg_adm_state(trx->bts, foh->obj_class,
- trx->bts->bts_nr, trx->nr, 0xff,
- NM_STATE_UNLOCKED);
- abis_nm_opstart(trx->bts, foh->obj_class,
- trx->bts->bts_nr, trx->nr, 0xff);
- /* TRX software is active, tell it to initiate RSL Link */
- abis_nm_ipaccess_rsl_connect(trx, trx->bts->ip_access.rsl_ip,
- 3003, trx->rsl_tei);
- break;
- case NM_OC_RADIO_CARRIER: {
- /*
- * Locking the radio carrier will make it go
- * offline again and we would come here. The
- * framework should determine that there was
- * no change and avoid recursion.
- *
- * This code is here to make sure that on start
- * a TRX remains locked.
- */
- /* Patch ARFCN into radio attribute */
- struct msgb *msgb = nanobts_attr_radio_get(trx->bts, trx);
- abis_nm_set_radio_attr(trx, msgb->data, msgb->len);
- msgb_free(msgb);
- abis_nm_chg_adm_state(trx->bts, foh->obj_class,
- trx->bts->bts_nr, trx->nr, 0xff,
- trx->mo.nm_state.administrative);
+ if (!(trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr)))
+ return -EINVAL;
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_SW_ACT_REP, NULL);
+ break;
+ case NM_OC_RADIO_CARRIER:
+ if (!(trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr)))
+ return -EINVAL;
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_SW_ACT_REP, NULL);
+ break;
+ case NM_OC_CHANNEL:
+ if (!(ts = abis_nm_get_ts(mb)))
+ return -EINVAL;
+ osmo_fsm_inst_dispatch(ts->mo.fi, NM_EV_SW_ACT_REP, NULL);
+ break;
+ case NM_OC_GPRS_NSE:
+ osmo_fsm_inst_dispatch(bts->site_mgr->gprs.nse.mo.fi, NM_EV_SW_ACT_REP, NULL);
+ break;
+ case NM_OC_GPRS_CELL:
+ osmo_fsm_inst_dispatch(bts->gprs.cell.mo.fi, NM_EV_SW_ACT_REP, NULL);
+ break;
+ case NM_OC_GPRS_NSVC:
+ if (!(nsvc = gsm_bts_sm_nsvc_num(bts->site_mgr, foh->obj_inst.trx_nr)))
+ return -EINVAL;
+ osmo_fsm_inst_dispatch(nsvc->mo.fi, NM_EV_SW_ACT_REP, NULL);
break;
- }
}
return 0;
}
@@ -311,22 +253,136 @@ static void nm_rx_opstart_ack_chan(struct msgb *oml_msg)
LOG_TS(ts, LOGL_ERROR, "Channel OPSTART ACK for uninitialized TS\n");
return;
}
-
+ osmo_fsm_inst_dispatch(ts->mo.fi, NM_EV_OPSTART_ACK, NULL);
osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_READY, NULL);
}
static void nm_rx_opstart_ack(struct msgb *oml_msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(oml_msg);
+ struct e1inp_sign_link *sign_link = oml_msg->dst;
+ struct gsm_bts *bts = sign_link->trx->bts;
+ struct gsm_bts_trx *trx;
+ struct gsm_gprs_nsvc *nsvc;
+
switch (foh->obj_class) {
+ case NM_OC_SITE_MANAGER:
+ osmo_fsm_inst_dispatch(bts->site_mgr->mo.fi, NM_EV_OPSTART_ACK, NULL);
+ break;
+ case NM_OC_BTS:
+ osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_OPSTART_ACK, NULL);
+ break;
+ case NM_OC_RADIO_CARRIER:
+ if (!(trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr)))
+ return;
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_OPSTART_ACK, NULL);
+ break;
+ case NM_OC_BASEB_TRANSC:
+ if (!(trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr)))
+ return;
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_OPSTART_ACK, NULL);
+ break;
case NM_OC_CHANNEL:
nm_rx_opstart_ack_chan(oml_msg);
break;
+ case NM_OC_GPRS_NSE:
+ osmo_fsm_inst_dispatch(bts->site_mgr->gprs.nse.mo.fi, NM_EV_OPSTART_ACK, NULL);
+ break;
+ case NM_OC_GPRS_CELL:
+ osmo_fsm_inst_dispatch(bts->gprs.cell.mo.fi, NM_EV_OPSTART_ACK, NULL);
+ break;
+ case NM_OC_GPRS_NSVC:
+ if (!(nsvc = gsm_bts_sm_nsvc_num(bts->site_mgr, foh->obj_inst.trx_nr)))
+ return;
+ osmo_fsm_inst_dispatch(nsvc->mo.fi, NM_EV_OPSTART_ACK, NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+static void nm_rx_opstart_nack(struct msgb *oml_msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(oml_msg);
+ struct e1inp_sign_link *sign_link = oml_msg->dst;
+ struct gsm_bts *bts = sign_link->trx->bts;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+ struct gsm_gprs_nsvc *nsvc;
+
+ switch (foh->obj_class) {
+ case NM_OC_SITE_MANAGER:
+ osmo_fsm_inst_dispatch(bts->site_mgr->mo.fi, NM_EV_OPSTART_NACK, NULL);
+ break;
+ case NM_OC_BTS:
+ osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_OPSTART_ACK, NULL);
+ break;
+ case NM_OC_RADIO_CARRIER:
+ if (!(trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr)))
+ return;
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_OPSTART_NACK, NULL);
+ break;
+ case NM_OC_BASEB_TRANSC:
+ if (!(trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr)))
+ return;
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_OPSTART_NACK, NULL);
+ break;
+ case NM_OC_CHANNEL:
+ if (!(ts = abis_nm_get_ts(oml_msg)))
+ return;
+ osmo_fsm_inst_dispatch(ts->mo.fi, NM_EV_OPSTART_NACK, NULL);
+ break;
+ case NM_OC_GPRS_NSE:
+ osmo_fsm_inst_dispatch(bts->site_mgr->gprs.nse.mo.fi, NM_EV_OPSTART_NACK, NULL);
+ break;
+ case NM_OC_GPRS_CELL:
+ osmo_fsm_inst_dispatch(bts->gprs.cell.mo.fi, NM_EV_OPSTART_NACK, NULL);
+ break;
+ case NM_OC_GPRS_NSVC:
+ if (!(nsvc = gsm_bts_sm_nsvc_num(bts->site_mgr, foh->obj_inst.trx_nr)))
+ return;
+ osmo_fsm_inst_dispatch(nsvc->mo.fi, NM_EV_OPSTART_NACK, NULL);
+ break;
default:
break;
}
}
+static void nm_rx_get_attr_rep(struct msgb *oml_msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(oml_msg);
+ struct e1inp_sign_link *sign_link = oml_msg->dst;
+ struct gsm_bts *bts = sign_link->trx->bts;
+ struct gsm_bts_trx *trx;
+
+ switch (foh->obj_class) {
+ case NM_OC_BTS:
+ osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_GET_ATTR_REP, NULL);
+ break;
+ case NM_OC_BASEB_TRANSC:
+ if (!(trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr)))
+ return;
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_GET_ATTR_REP, NULL);
+ break;
+ default:
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Get Attributes Response received on incorrect object class %d!\n", foh->obj_class);
+ }
+}
+
+static void nm_rx_set_bts_attr_ack(struct msgb *oml_msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(oml_msg);
+ struct e1inp_sign_link *sign_link = oml_msg->dst;
+ struct gsm_bts *bts = sign_link->trx->bts;
+
+ if (foh->obj_class != NM_OC_BTS) {
+ LOG_BTS(bts, DNM, LOGL_ERROR, "Set BTS Attr Ack received on non BTS object!\n");
+ return;
+ }
+ osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_SET_ATTR_ACK, NULL);
+}
+
+
static void nm_rx_set_radio_attr_ack(struct msgb *oml_msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(oml_msg);
@@ -334,12 +390,108 @@ static void nm_rx_set_radio_attr_ack(struct msgb *oml_msg)
struct gsm_bts *bts = sign_link->trx->bts;
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
- if (foh->obj_class != NM_OC_RADIO_CARRIER) {
- LOG_TRX(trx, DNM, LOGL_ERROR, "Set Radio Carrier Attr Ack received on non Radio Carrier object!\n");
+ if (!trx || foh->obj_class != NM_OC_RADIO_CARRIER) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Set Radio Carrier Attr Ack received on non Radio Carrier object!\n");
+ return;
+ }
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_SET_ATTR_ACK, NULL);
+}
+
+static void nm_rx_set_chan_attr_ack(struct msgb *oml_msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(oml_msg);
+ struct gsm_bts_trx_ts *ts = abis_nm_get_ts(oml_msg);
+
+ if (!ts || foh->obj_class != NM_OC_CHANNEL) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Set Channel Attr Ack received on non Radio Channel object!\n");
+ return;
+ }
+ osmo_fsm_inst_dispatch(ts->mo.fi, NM_EV_SET_ATTR_ACK, NULL);
+}
+
+static void nm_rx_ipacc_set_attr_ack(struct ipacc_ack_signal_data *sig_data)
+{
+ struct gsm_bts *bts = sig_data->bts;
+ struct abis_om_fom_hdr *foh = sig_data->foh;
+ void *obj;
+ struct gsm_gprs_nse *nse;
+ struct gsm_gprs_cell *cell;
+ struct gsm_gprs_nsvc *nsvc;
+
+ obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
+
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ nse = obj;
+ osmo_fsm_inst_dispatch(nse->mo.fi, NM_EV_SET_ATTR_ACK, NULL);
+ break;
+ case NM_OC_GPRS_CELL:
+ cell = obj;
+ osmo_fsm_inst_dispatch(cell->mo.fi, NM_EV_SET_ATTR_ACK, NULL);
+ break;
+ case NM_OC_GPRS_NSVC:
+ if (!(nsvc = gsm_bts_sm_nsvc_num(bts->site_mgr, foh->obj_inst.trx_nr)))
+ return;
+ osmo_fsm_inst_dispatch(nsvc->mo.fi, NM_EV_SET_ATTR_ACK, NULL);
+ break;
+ default:
+ LOGPFOH(DNM, LOGL_ERROR, foh, "IPACC Set Attr Ack received on incorrect object class %d!\n", foh->obj_class);
+ }
+}
+
+static void nm_rx_ipacc_rsl_connect_ack(struct ipacc_ack_signal_data *sig_data)
+{
+ struct gsm_bts *bts = sig_data->bts;
+ struct abis_om_fom_hdr *foh = sig_data->foh;
+ struct gsm_bts_trx *trx;
+
+ if (foh->obj_class != NM_OC_BASEB_TRANSC) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "IPACC RSL Connect ACK received on incorrect object class %d!\n", foh->obj_class);
return;
}
- abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr,
- trx->nr, 0xff);
+
+ trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_RSL_CONNECT_ACK, NULL);
+}
+
+static void nm_rx_ipacc_ack(struct ipacc_ack_signal_data *sig_data)
+{
+ switch (sig_data->foh->msg_type) {
+ case NM_MT_IPACC_SET_ATTR_ACK:
+ nm_rx_ipacc_set_attr_ack(sig_data);
+ break;
+ case NM_MT_IPACC_RSL_CONNECT_ACK:
+ nm_rx_ipacc_rsl_connect_ack(sig_data);
+ break;
+ default:
+ break;
+ }
+}
+
+static void nm_rx_ipacc_rsl_connect_nack(struct ipacc_ack_signal_data *sig_data)
+{
+ struct gsm_bts *bts = sig_data->bts;
+ struct abis_om_fom_hdr *foh = sig_data->foh;
+ struct gsm_bts_trx *trx;
+
+ if (foh->obj_class != NM_OC_BASEB_TRANSC) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "IPACC RSL Connect NACK received on incorrect object class %d!\n", foh->obj_class);
+ return;
+ }
+
+ trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_RSL_CONNECT_NACK, NULL);
+}
+
+static void nm_rx_ipacc_nack(struct ipacc_ack_signal_data *sig_data)
+{
+ switch (sig_data->foh->msg_type) {
+ case NM_MT_IPACC_RSL_CONNECT_ACK:
+ nm_rx_ipacc_rsl_connect_nack(sig_data);
+ break;
+ default:
+ break;
+ }
}
/* Callback function to be called every time we receive a signal from NM */
@@ -352,15 +504,32 @@ static int bts_ipa_nm_sig_cb(unsigned int subsys, unsigned int signal,
switch (signal) {
case S_NM_SW_ACTIV_REP:
return sw_activ_rep(signal_data);
- case S_NM_STATECHG_OPER:
- case S_NM_STATECHG_ADM:
+ case S_NM_STATECHG:
return nm_statechg_event(signal, signal_data);
case S_NM_OPSTART_ACK:
nm_rx_opstart_ack(signal_data);
return 0;
+ case S_NM_OPSTART_NACK:
+ nm_rx_opstart_nack(signal_data);
+ return 0;
+ case S_NM_GET_ATTR_REP:
+ nm_rx_get_attr_rep(signal_data);
+ return 0;
+ case S_NM_SET_BTS_ATTR_ACK:
+ nm_rx_set_bts_attr_ack(signal_data);
+ return 0;
case S_NM_SET_RADIO_ATTR_ACK:
nm_rx_set_radio_attr_ack(signal_data);
return 0;
+ case S_NM_SET_CHAN_ATTR_ACK:
+ nm_rx_set_chan_attr_ack(signal_data);
+ return 0;
+ case S_NM_IPACC_ACK:
+ nm_rx_ipacc_ack(signal_data);
+ return 0;
+ case S_NM_IPACC_NACK:
+ nm_rx_ipacc_nack(signal_data);
+ return 0;
default:
break;
}
@@ -409,13 +578,13 @@ find_bts_by_unitid(struct gsm_network *net, uint16_t site_id, uint16_t bts_id)
/* These are exported because they are used by the VTY interface. */
void ipaccess_drop_rsl(struct gsm_bts_trx *trx, const char *reason)
{
- if (!trx->rsl_link)
+ if (!trx->rsl_link_primary)
return;
LOG_TRX(trx, DLINP, LOGL_NOTICE, "Dropping RSL link: %s\n", reason);
- e1inp_sign_link_destroy(trx->rsl_link);
- trx->rsl_link = NULL;
- osmo_stat_item_dec(trx->bts->bts_statg->items[BTS_STAT_RSL_CONNECTED], 1);
+ e1inp_sign_link_destroy(trx->rsl_link_primary);
+ trx->rsl_link_primary = NULL;
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(trx->bts->bts_statg, BTS_STAT_RSL_CONNECTED), 1);
if (trx->bts->c0 == trx)
paging_flush_bts(trx->bts, NULL);
@@ -425,6 +594,9 @@ void ipaccess_drop_oml(struct gsm_bts *bts, const char *reason)
{
struct gsm_bts *rdep_bts;
struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts ;
+ uint8_t tn;
+ uint8_t i;
/* First of all, remove deferred drop if enabled */
osmo_timer_del(&bts->oml_drop_link_timer);
@@ -436,11 +608,34 @@ void ipaccess_drop_oml(struct gsm_bts *bts, const char *reason)
e1inp_sign_link_destroy(bts->oml_link);
bts->oml_link = NULL;
bts->uptime = 0;
- osmo_stat_item_dec(bts->bts_statg->items[BTS_STAT_OML_CONNECTED], 1);
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_OML_CONNECTED), 1);
+ gsm_bts_stats_reset(bts);
+
+ /* Also drop the associated OSMO link */
+ OSMO_ASSERT(bts->osmo_link);
+ e1inp_sign_link_destroy(bts->osmo_link);
+ bts->osmo_link = NULL;
+
+ bts_setup_ramp_remove(bts);
/* we have issues reconnecting RSL, drop everything. */
- llist_for_each_entry(trx, &bts->trx_list, list)
+ llist_for_each_entry(trx, &bts->trx_list, list) {
ipaccess_drop_rsl(trx, "OML link drop");
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_OML_DOWN, NULL);
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_OML_DOWN, NULL);
+ for (tn = 0; tn < TRX_NR_TS; tn++) {
+ ts = &trx->ts[tn];
+ osmo_fsm_inst_dispatch(ts->mo.fi, NM_EV_OML_DOWN, NULL);
+ }
+ }
+
+ osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_OML_DOWN, NULL);
+ osmo_fsm_inst_dispatch(bts->gprs.cell.mo.fi, NM_EV_OML_DOWN, NULL);
+
+ osmo_fsm_inst_dispatch(bts->site_mgr->mo.fi, NM_EV_OML_DOWN, NULL);
+ osmo_fsm_inst_dispatch(bts->site_mgr->gprs.nse.mo.fi, NM_EV_OML_DOWN, NULL);
+ for (i = 0; i < ARRAY_SIZE(bts->site_mgr->gprs.nsvc); i++)
+ osmo_fsm_inst_dispatch(bts->site_mgr->gprs.nsvc[i].mo.fi, NM_EV_OML_DOWN, NULL);
gsm_bts_all_ts_dispatch(bts, TS_EV_OML_DOWN, NULL);
@@ -498,7 +693,7 @@ static void ipaccess_sign_link_reject(const struct ipaccess_unit *dev, const str
/* Write to log and increase counter */
LOGP(DLINP, LOGL_ERROR, "Unable to find BTS configuration for %u/%u/%u, disconnecting\n", site_id, bts_id,
trx_id);
- rate_ctr_inc(&bsc_gsmnet->bsc_ctrs->ctr[BSC_CTR_UNKNOWN_UNIT_ID]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->bsc_ctrs, BSC_CTR_UNKNOWN_UNIT_ID));
/* Get remote IP */
if (osmo_sock_get_remote_ip(ts->driver.ipaccess.fd.fd, ip, sizeof(ip)))
@@ -545,15 +740,24 @@ ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line,
struct e1inp_sign_link *sign_link = NULL;
struct timespec tp;
int rc;
+ struct e1inp_ts *sign_ts = e1inp_line_ipa_oml_ts(line);
bts = find_bts_by_unitid(bsc_gsmnet, dev->site_id, dev->bts_id);
if (!bts) {
- ipaccess_sign_link_reject(dev, &line->ts[E1INP_SIGN_OML - 1]);
+ ipaccess_sign_link_reject(dev, sign_ts);
return NULL;
}
DEBUGP(DLINP, "%s: Identified BTS %u/%u/%u\n", e1inp_signtype_name(type),
dev->site_id, dev->bts_id, dev->trx_id);
+ /* Check if this BTS has a valid configuration. If not we will drop it
+ * immediately. */
+ if (gsm_bts_check_cfg(bts) != 0) {
+ LOGP(DLINP, LOGL_NOTICE, "(bts=%u) BTS config invalid, dropping BTS!\n", bts->nr);
+ ipaccess_drop_oml_deferred(bts);
+ return NULL;
+ }
+
switch(type) {
case E1INP_SIGN_OML:
/* remove old OML signal link for this BTS. */
@@ -568,17 +772,21 @@ ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line,
/* create new OML link. */
sign_link = bts->oml_link =
- e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1],
+ e1inp_sign_link_create(sign_ts,
E1INP_SIGN_OML, bts->c0,
bts->oml_tei, 0);
- rc = clock_gettime(CLOCK_MONOTONIC, &tp);
+ rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
bts->uptime = (rc < 0) ? 0 : tp.tv_sec; /* we don't need sub-second precision for uptime */
if (!(sign_link->trx->bts->ip_access.flags & OML_UP)) {
e1inp_event(sign_link->ts, S_L_INP_TEI_UP,
sign_link->tei, sign_link->sapi);
sign_link->trx->bts->ip_access.flags |= OML_UP;
}
- osmo_stat_item_inc(bts->bts_statg->items[BTS_STAT_OML_CONNECTED], 1);
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_OML_CONNECTED), 1);
+
+ /* Create link for E1INP_SIGN_OSMO */
+ //SAPI must be 0, no IPAC_PROTO_EXT_PCU, see ipaccess_bts_read_cb
+ bts->osmo_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OSMO, bts->c0, IPAC_PROTO_OSMO, 0);
break;
case E1INP_SIGN_RSL: {
struct e1inp_ts *ts;
@@ -593,12 +801,12 @@ ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line,
/* set new RSL link for this TRX. */
line = bts->oml_link->ts->line;
- ts = &line->ts[E1INP_SIGN_RSL + dev->trx_id - 1];
+ ts = e1inp_line_ipa_rsl_ts(line, dev->trx_id);
e1inp_ts_config_sign(ts, line);
- sign_link = trx->rsl_link =
+ sign_link = trx->rsl_link_primary =
e1inp_sign_link_create(ts, E1INP_SIGN_RSL,
- trx, trx->rsl_tei, 0);
- trx->rsl_link->ts->sign.delay = 0;
+ trx, trx->rsl_tei_primary, 0);
+ trx->rsl_link_primary->ts->sign.delay = 0;
if (!(sign_link->trx->bts->ip_access.flags &
(RSL_UP << sign_link->trx->nr))) {
e1inp_event(sign_link->ts, S_L_INP_TEI_UP,
@@ -606,7 +814,7 @@ ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line,
sign_link->trx->bts->ip_access.flags |=
(RSL_UP << sign_link->trx->nr);
}
- osmo_stat_item_inc(bts->bts_statg->items[BTS_STAT_RSL_CONNECTED], 1);
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_RSL_CONNECTED), 1);
break;
}
default:
@@ -618,7 +826,7 @@ ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line,
static void ipaccess_sign_link_down(struct e1inp_line *line)
{
/* No matter what link went down, we close both signal links. */
- struct e1inp_ts *ts = &line->ts[E1INP_SIGN_OML-1];
+ struct e1inp_ts *ts = e1inp_line_ipa_oml_ts(line);
struct gsm_bts *bts = NULL;
struct e1inp_sign_link *link;
@@ -651,6 +859,9 @@ static int ipaccess_sign_link(struct msgb *msg)
case E1INP_SIGN_OML:
ret = abis_nm_rcvmsg(msg);
break;
+ case E1INP_SIGN_OSMO:
+ ret = abis_osmo_rcvmsg(msg);
+ break;
default:
LOGP(DLINP, LOGL_ERROR, "Unknown signal link type %d\n",
link->type);
@@ -677,3 +888,153 @@ static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line)
{
e1inp_line_bind_ops(line, &ipaccess_e1inp_line_ops);
}
+
+static void enc_meas_proc_params(struct msgb *msg, uint8_t ptype,
+ const struct gsm_power_ctrl_meas_params *mp)
+{
+ struct ipac_preproc_ave_cfg *ave_cfg;
+ uint8_t *ie_len;
+
+ /* No averaging => no Measurement Averaging parameters */
+ if (mp->algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE)
+ return;
+
+ /* (TLV) Measurement Averaging parameters for RxLev/RxQual */
+ ie_len = msgb_tl_put(msg, RSL_IPAC_EIE_MEAS_AVG_CFG);
+
+ ave_cfg = (struct ipac_preproc_ave_cfg *) msgb_put(msg, sizeof(*ave_cfg));
+ ave_cfg->param_id = ptype & 0x03;
+
+ /* H_REQAVE and H_REQT */
+ ave_cfg->h_reqave = mp->h_reqave & 0x1f;
+ ave_cfg->h_reqt = mp->h_reqt & 0x1f;
+
+ /* Averaging method and parameters */
+ ave_cfg->ave_method = (mp->algo - 1) & 0x07;
+ switch (mp->algo) {
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:
+ msgb_v_put(msg, mp->ewma.alpha);
+ break;
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED:
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN:
+ /* FIXME: unknown format */
+ break;
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED:
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE:
+ /* No parameters here */
+ break;
+ }
+
+ /* Update length part of the containing IE */
+ *ie_len = msg->tail - (ie_len + 1);
+}
+
+static void enc_power_params(struct msgb *msg, const struct gsm_power_ctrl_params *cp)
+{
+ struct ipac_preproc_pc_comp *thresh_comp;
+ struct ipac_preproc_pc_thresh *thresh;
+
+ /* These parameters are valid for dynamic mode only */
+ OSMO_ASSERT(cp->mode == GSM_PWR_CTRL_MODE_DYN_BTS);
+
+ /* (TLV) Measurement Averaging Configure */
+ enc_meas_proc_params(msg, IPAC_RXQUAL_AVE, &cp->rxqual_meas);
+ enc_meas_proc_params(msg, IPAC_RXLEV_AVE, &cp->rxlev_meas);
+
+ /* (TV) Thresholds: {L,U}_RXLEV_XX_P and {L,U}_RXQUAL_XX_P */
+ if (cp->dir == GSM_PWR_CTRL_DIR_UL)
+ msgb_v_put(msg, RSL_IPAC_EIE_MS_PWR_CTL);
+ else
+ msgb_v_put(msg, RSL_IPAC_EIE_BS_PWR_CTL);
+
+ thresh = (struct ipac_preproc_pc_thresh *) msgb_put(msg, sizeof(*thresh));
+
+ /* {L,U}_RXLEV_XX_P (see 3GPP TS 45.008, A.3.2.1, a & b) */
+ thresh->l_rxlev = cp->rxlev_meas.lower_thresh & 0x3f;
+ thresh->u_rxlev = cp->rxlev_meas.upper_thresh & 0x3f;
+
+ /* {L,U}_RXQUAL_XX_P (see 3GPP TS 45.008, A.3.2.1, c & d) */
+ thresh->l_rxqual = cp->rxqual_meas.lower_thresh & 0x07;
+ thresh->u_rxqual = cp->rxqual_meas.upper_thresh & 0x07;
+
+ /* (TV) PC Threshold Comparators */
+ msgb_v_put(msg, RSL_IPAC_EIE_PC_THRESH_COMP);
+
+ thresh_comp = (struct ipac_preproc_pc_comp *) msgb_put(msg, sizeof(*thresh_comp));
+
+ /* RxLev: P1, N1, P2, N2 (see 3GPP TS 45.008, A.3.2.1, a & b) */
+ thresh_comp->p1 = cp->rxlev_meas.lower_cmp_p & 0x1f;
+ thresh_comp->n1 = cp->rxlev_meas.lower_cmp_n & 0x1f;
+ thresh_comp->p2 = cp->rxlev_meas.upper_cmp_p & 0x1f;
+ thresh_comp->n2 = cp->rxlev_meas.upper_cmp_n & 0x1f;
+
+ /* RxQual: P3, N3, P4, N4 (see 3GPP TS 45.008, A.3.2.1, c & d) */
+ thresh_comp->p3 = cp->rxqual_meas.lower_cmp_p & 0x1f;
+ thresh_comp->n3 = cp->rxqual_meas.lower_cmp_n & 0x1f;
+ thresh_comp->p4 = cp->rxqual_meas.upper_cmp_p & 0x1f;
+ thresh_comp->n4 = cp->rxqual_meas.upper_cmp_n & 0x1f;
+
+ /* Minimum interval between power level changes (P_CON_INTERVAL) */
+ thresh_comp->pc_interval = cp->ctrl_interval;
+
+ /* Change step limitations: POWER_{INC,RED}_STEP_SIZE */
+ thresh_comp->inc_step_size = cp->inc_step_size_db & 0x0f;
+ thresh_comp->red_step_size = cp->red_step_size_db & 0x0f;
+}
+
+void osmobts_enc_power_params_osmo_ext(struct msgb *msg, const struct gsm_power_ctrl_params *cp);
+static void add_power_params_ie(struct msgb *msg, enum abis_rsl_ie iei,
+ const struct gsm_bts_trx *trx,
+ const struct gsm_power_ctrl_params *cp)
+{
+ uint8_t *ie_len = msgb_tl_put(msg, iei);
+ uint8_t msg_len = msgb_length(msg);
+
+ enc_power_params(msg, cp);
+ if (iei == RSL_IE_MS_POWER_PARAM && is_osmobts(trx->bts))
+ osmobts_enc_power_params_osmo_ext(msg, cp);
+
+ *ie_len = msgb_length(msg) - msg_len;
+}
+
+static int power_ctrl_send_def_params(const struct gsm_bts_trx *trx)
+{
+ const struct gsm_power_ctrl_params *ms_power_ctrl = &trx->bts->ms_power_ctrl;
+ const struct gsm_power_ctrl_params *bs_power_ctrl = &trx->bts->bs_power_ctrl;
+ struct abis_rsl_common_hdr *ch;
+ struct msgb *msg;
+
+ /* Sending this message does not make sense if neither MS Power control
+ * nor BS Power control is to be performed by the BTS itself ('dyn-bts'). */
+ if (ms_power_ctrl->mode != GSM_PWR_CTRL_MODE_DYN_BTS &&
+ bs_power_ctrl->mode != GSM_PWR_CTRL_MODE_DYN_BTS)
+ return 0;
+
+ msg = rsl_msgb_alloc();
+ if (msg == NULL)
+ return -ENOMEM;
+
+ ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
+ ch->msg_discr = ABIS_RSL_MDISC_TRX;
+ ch->msg_type = RSL_MT_IPAC_MEAS_PREPROC_DFT;
+
+ /* BS/MS Power IEs (to be re-defined in channel specific messages) */
+ msgb_tv_put(msg, RSL_IE_MS_POWER, 0); /* dummy value */
+ msgb_tv_put(msg, RSL_IE_BS_POWER, 0); /* dummy value */
+
+ /* MS/BS Power Parameters IEs */
+ if (ms_power_ctrl->mode == GSM_PWR_CTRL_MODE_DYN_BTS)
+ add_power_params_ie(msg, RSL_IE_MS_POWER_PARAM, trx, ms_power_ctrl);
+ if (bs_power_ctrl->mode == GSM_PWR_CTRL_MODE_DYN_BTS)
+ add_power_params_ie(msg, RSL_IE_BS_POWER_PARAM, trx, bs_power_ctrl);
+
+ msg->dst = trx->rsl_link_primary;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+static int power_ctrl_enc_rsl_params(struct msgb *msg, const struct gsm_power_ctrl_params *cp)
+{
+ /* We send everything in "Measurement Pre-processing Defaults" */
+ return 0;
+}
diff --git a/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c b/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
index b7239326c..9fdbf434b 100644
--- a/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
+++ b/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
@@ -26,19 +26,24 @@
#include <osmocom/bsc/bts.h>
#include <osmocom/gsm/bts_features.h>
-struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts)
+/* 3GPP TS 52.021 section 8.6.1 Set BTS Attributes */
+struct msgb *nanobts_gen_set_bts_attr(struct gsm_bts *bts)
{
struct msgb *msgb;
uint8_t buf[256];
int rlt;
msgb = msgb_alloc(1024, "nanobts_attr_bts");
+ if (!msgb)
+ return NULL;
- memcpy(buf, "\x55\x5b\x61\x67\x6d\x73", 6);
- msgb_tv_fixed_put(msgb, NM_ATT_INTERF_BOUND, 6, buf);
-
- /* interference avg. period in numbers of SACCH multifr */
- msgb_tv_put(msgb, NM_ATT_INTAVE_PARAM, 0x06);
+ /* Interference level Boundaries: 0 .. X5 (3GPP TS 52.021 sec 9.4.25) */
+ msgb_tv_fixed_put(msgb, NM_ATT_INTERF_BOUND,
+ sizeof(bts->interf_meas_params_cfg.bounds_dbm),
+ &bts->interf_meas_params_cfg.bounds_dbm[0]);
+ /* Intave: Interference Averaging period (3GPP TS 52.021 sec 9.4.24) */
+ msgb_tv_put(msgb, NM_ATT_INTAVE_PARAM, bts->interf_meas_params_cfg.avg_period);
+ /* Connection Failure Criterion (3GPP TS 52.021 sec 9.4.14) */
rlt = gsm_bts_get_radio_link_timeout(bts);
if (rlt == -1) {
/* Osmocom extension: Use infinite radio link timeout */
@@ -51,28 +56,30 @@ struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts)
}
msgb_tl16v_put(msgb, NM_ATT_CONN_FAIL_CRIT, 2, buf);
+ /* T200 (3GPP TS 52.021 sec 9.4.53) */
memcpy(buf, "\x1e\x24\x24\xa8\x34\x21\xa8", 7);
msgb_tv_fixed_put(msgb, NM_ATT_T200, 7, buf);
+ /* Max Timing Advance (3GPP TS 52.021 sec 9.4.31) */
msgb_tv_put(msgb, NM_ATT_MAX_TA, 0x3f);
- /* seconds */
+ /* Overload Period (3GPP TS 52.021 sec 9.4.39), seconds */
memcpy(buf, "\x00\x01\x0a", 3);
msgb_tv_fixed_put(msgb, NM_ATT_OVERL_PERIOD, 3, buf);
- /* percent */
+ /* CCCH Load Threshold (3GPP TS 12.21 sec 9.4.12), percent */
msgb_tv_put(msgb, NM_ATT_CCCH_L_T, bts->ccch_load_ind_thresh);
- /* seconds */
- msgb_tv_put(msgb, NM_ATT_CCCH_L_I_P, 1);
+ /* CCCH Load Indication Period (3GPP TS 12.21 sec 9.4.11), seconds */
+ msgb_tv_put(msgb, NM_ATT_CCCH_L_I_P, bts->ccch_load_ind_period);
- /* busy threshold in - dBm */
+ /* RACH Busy Threshold (3GPP TS 12.21 sec 9.4.44), -dBm */
buf[0] = 90; /* -90 dBm as default "busy" threshold */
if (bts->rach_b_thresh != -1)
buf[0] = bts->rach_b_thresh & 0xff;
msgb_tv_put(msgb, NM_ATT_RACH_B_THRESH, buf[0]);
- /* rach load averaging 1000 slots */
+ /* RACH Load Averaging Slots (3GPP TS 12.21 sec 9.4.45), 1000 slots */
buf[0] = 0x03;
buf[1] = 0xe8;
if (bts->rach_ldavg_slots != -1) {
@@ -81,16 +88,19 @@ struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts)
}
msgb_tv_fixed_put(msgb, NM_ATT_LDAVG_SLOTS, 2, buf);
- /* 10 milliseconds */
- msgb_tv_put(msgb, NM_ATT_BTS_AIR_TIMER, osmo_tdef_get(bts->network->T_defs, 3105, OSMO_TDEF_MS, -1));
+ /* BTS Air Timer (3GPP TS 12.21 sec 9.4.10), 10 milliseconds */
+ msgb_tv_put(msgb, NM_ATT_BTS_AIR_TIMER, osmo_tdef_get(bts->network->T_defs, 3105, OSMO_TDEF_MS, -1)/10);
- /* 10 retransmissions of physical config */
- msgb_tv_put(msgb, NM_ATT_NY1, 10);
+ /* NY1 (3GPP TS 12.21 sec 9.4.37), number of retransmissions of physical config */
+ gsm_bts_check_ny1(bts);
+ msgb_tv_put(msgb, NM_ATT_NY1, osmo_tdef_get(bts->network->T_defs, -3105, OSMO_TDEF_CUSTOM, -1));
+ /* BCCH ARFCN (3GPP TS 12.21 sec 9.4.8) */
buf[0] = (bts->c0->arfcn >> 8) & 0x0f;
buf[1] = bts->c0->arfcn & 0xff;
msgb_tv_fixed_put(msgb, NM_ATT_BCCH_ARFCN, 2, buf);
+ /* BSIC (3GPP TS 12.21 sec 9.4.9) */
msgb_tv_put(msgb, NM_ATT_BSIC, bts->bsic);
abis_nm_ipaccess_cgi(buf, bts);
@@ -99,20 +109,23 @@ struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts)
return msgb;
}
-struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts)
+struct msgb *nanobts_gen_set_nse_attr(struct gsm_bts_sm *bts_sm)
{
struct msgb *msgb;
uint8_t buf[256];
+ struct gsm_bts *bts = gsm_bts_sm_get_bts(bts_sm);
msgb = msgb_alloc(1024, "nanobts_attr_bts");
+ if (!msgb)
+ return NULL;
/* NSEI 925 */
- buf[0] = bts->gprs.nse.nsei >> 8;
- buf[1] = bts->gprs.nse.nsei & 0xff;
+ buf[0] = bts_sm->gprs.nse.nsei >> 8;
+ buf[1] = bts_sm->gprs.nse.nsei & 0xff;
msgb_tl16v_put(msgb, NM_ATT_IPACC_NSEI, 2, buf);
/* all timers in seconds */
- OSMO_ASSERT(ARRAY_SIZE(bts->gprs.nse.timer) < sizeof(buf));
- memcpy(buf, bts->gprs.nse.timer, ARRAY_SIZE(bts->gprs.nse.timer));
+ OSMO_ASSERT(ARRAY_SIZE(bts_sm->gprs.nse.timer) < sizeof(buf));
+ memcpy(buf, bts_sm->gprs.nse.timer, ARRAY_SIZE(bts_sm->gprs.nse.timer));
msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_CFG, 7, buf);
/* all timers in seconds */
@@ -135,11 +148,13 @@ struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts)
return msgb;
}
-struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts)
+struct msgb *nanobts_gen_set_cell_attr(struct gsm_bts *bts)
{
struct msgb *msgb;
uint8_t buf[256];
msgb = msgb_alloc(1024, "nanobts_attr_bts");
+ if (!msgb)
+ return NULL;
/* routing area code */
buf[0] = bts->gprs.rac;
@@ -192,18 +207,20 @@ struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts)
return msgb;
}
-struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts)
+struct msgb *nanobts_gen_set_nsvc_attr(struct gsm_gprs_nsvc *nsvc)
{
struct msgb *msgb;
uint8_t buf[256];
msgb = msgb_alloc(1024, "nanobts_attr_bts");
+ if (!msgb)
+ return NULL;
/* 925 */
- buf[0] = bts->gprs.nsvc[0].nsvci >> 8;
- buf[1] = bts->gprs.nsvc[0].nsvci & 0xff;
+ buf[0] = nsvc->nsvci >> 8;
+ buf[1] = nsvc->nsvci & 0xff;
msgb_tl16v_put(msgb, NM_ATT_IPACC_NSVCI, 2, buf);
- switch (bts->gprs.nsvc->remote.u.sa.sa_family) {
+ switch (nsvc->remote.u.sa.sa_family) {
case AF_INET6:
/* all fields are encoded in network byte order */
/* protocol family */
@@ -211,20 +228,20 @@ struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts)
/* padding */
buf[1] = 0x00;
/* local udp port */
- osmo_store16be(bts->gprs.nsvc[0].local_port, &buf[2]);
+ osmo_store16be(nsvc->local_port, &buf[2]);
/* remote udp port */
- memcpy(&buf[4], &bts->gprs.nsvc[0].remote.u.sin6.sin6_port, sizeof(uint16_t));
+ memcpy(&buf[4], &nsvc->remote.u.sin6.sin6_port, sizeof(uint16_t));
/* remote ip address */
- memcpy(&buf[6], &bts->gprs.nsvc[0].remote.u.sin6.sin6_addr, sizeof(struct in6_addr));
+ memcpy(&buf[6], &nsvc->remote.u.sin6.sin6_addr, sizeof(struct in6_addr));
msgb_tl16v_put(msgb, NM_ATT_OSMO_NS_LINK_CFG, 6 + sizeof(struct in6_addr), buf);
break;
case AF_INET:
/* remote udp port */
- memcpy(&buf[0], &bts->gprs.nsvc[0].remote.u.sin.sin_port, sizeof(uint16_t));
+ memcpy(&buf[0], &nsvc->remote.u.sin.sin_port, sizeof(uint16_t));
/* remote ip address */
- memcpy(&buf[2], &bts->gprs.nsvc[0].remote.u.sin.sin_addr, sizeof(struct in_addr));
+ memcpy(&buf[2], &nsvc->remote.u.sin.sin_addr, sizeof(struct in_addr));
/* local udp port */
- osmo_store16be(bts->gprs.nsvc[0].local_port, &buf[6]);
+ osmo_store16be(nsvc->local_port, &buf[6]);
msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_LINK_CFG, 8, buf);
break;
default:
@@ -234,12 +251,14 @@ struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts)
return msgb;
}
-struct msgb *nanobts_attr_radio_get(struct gsm_bts *bts,
+struct msgb *nanobts_gen_set_radio_attr(struct gsm_bts *bts,
struct gsm_bts_trx *trx)
{
struct msgb *msgb;
uint8_t buf[256];
msgb = msgb_alloc(1024, "nanobts_attr_bts");
+ if (!msgb)
+ return NULL;
/* number of -2dB reduction steps / Pn */
msgb_tv_put(msgb, NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2);
diff --git a/src/osmo-bsc/bts_nokia_site.c b/src/osmo-bsc/bts_nokia_site.c
index 2b6f91876..2028009b3 100644
--- a/src/osmo-bsc/bts_nokia_site.c
+++ b/src/osmo-bsc/bts_nokia_site.c
@@ -80,6 +80,7 @@ static void bootstrap_om_trx(struct gsm_bts_trx *trx)
static int shutdown_om(struct gsm_bts *bts)
{
+ gsm_bts_stats_reset(bts);
/* TODO !? */
return 0;
}
@@ -203,8 +204,7 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
return 0;
switch (signal) {
- case S_NM_STATECHG_OPER:
- case S_NM_STATECHG_ADM:
+ case S_NM_STATECHG:
nm_statechg_evt(signal, signal_data);
break;
default:
@@ -797,6 +797,9 @@ static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id,
case GSM_PCHAN_CCCH_SDCCH4:
chan_config = 1;
break;
+ case GSM_PCHAN_CCCH_SDCCH4_CBCH:
+ chan_config = 3;
+ break;
case GSM_PCHAN_TCH_F:
chan_config = 6; /* 9 should work too */
break;
@@ -806,6 +809,9 @@ static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id,
case GSM_PCHAN_SDCCH8_SACCH8C:
chan_config = 4;
break;
+ case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
+ chan_config = 5;
+ break;
case GSM_PCHAN_PDCH:
chan_config = 11;
break;
@@ -1452,7 +1458,7 @@ static void nokia_abis_nm_fake_1221_ok(struct gsm_bts *bts)
struct gsm_bts_trx *trx;
mo_ok(&bts->mo);
- mo_ok(&bts->site_mgr.mo);
+ mo_ok(&bts->site_mgr->mo);
llist_for_each_entry(trx, &bts->trx_list, list) {
int i;
@@ -1758,14 +1764,6 @@ static struct gsm_network *my_net;
static int bts_model_nokia_site_start(struct gsm_network *net)
{
- model_nokia_site.features.data = &model_nokia_site._features_data[0];
- model_nokia_site.features.data_len =
- sizeof(model_nokia_site._features_data);
-
- osmo_bts_set_feature(&model_nokia_site.features, BTS_FEAT_HOPPING);
- osmo_bts_set_feature(&model_nokia_site.features, BTS_FEAT_HSCSD);
- osmo_bts_set_feature(&model_nokia_site.features, BTS_FEAT_MULTI_TSC);
-
osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
@@ -1777,5 +1775,12 @@ static int bts_model_nokia_site_start(struct gsm_network *net)
int bts_model_nokia_site_init(void)
{
+ model_nokia_site.features.data = &model_nokia_site._features_data[0];
+ model_nokia_site.features.data_len = sizeof(model_nokia_site._features_data);
+
+ osmo_bts_set_feature(&model_nokia_site.features, BTS_FEAT_HOPPING);
+ osmo_bts_set_feature(&model_nokia_site.features, BTS_FEAT_HSCSD);
+ osmo_bts_set_feature(&model_nokia_site.features, BTS_FEAT_MULTI_TSC);
+
return gsm_bts_model_register(&model_nokia_site);
}
diff --git a/src/osmo-bsc/bts_osmobts.c b/src/osmo-bsc/bts_osmobts.c
new file mode 100644
index 000000000..078cfe7a6
--- /dev/null
+++ b/src/osmo-bsc/bts_osmobts.c
@@ -0,0 +1,214 @@
+/* Osmocom OsmoBTS specific code */
+
+/* (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2021 by sysmocom - s.m.f.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 <arpa/inet.h>
+
+#include <osmocom/gsm/tlv.h>
+
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/abis/subchan_demux.h>
+#include <osmocom/abis/ipaccess.h>
+#include <osmocom/core/logging.h>
+
+extern struct gsm_bts_model bts_model_nanobts;
+
+static struct gsm_bts_model model_osmobts;
+
+static void enc_osmo_meas_proc_params(struct msgb *msg, const struct gsm_power_ctrl_params *mp)
+{
+ struct osmo_preproc_ave_cfg *ave_cfg;
+ uint8_t *ie_len;
+
+ /* No averaging => no Measurement Averaging parameters */
+ if (mp->ci_fr_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_hr_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_amr_fr_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_amr_hr_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_sdcch_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE &&
+ mp->ci_gprs_meas.algo == GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE)
+ return;
+
+ /* (TLV) Measurement Averaging parameters for RxLev/RxQual */
+ ie_len = msgb_tl_put(msg, RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG);
+
+ ave_cfg = (struct osmo_preproc_ave_cfg *) msgb_put(msg, sizeof(*ave_cfg));
+
+#define ENC_PROC(PARAMS, TO, TYPE) do { \
+ (TO)->TYPE.ave_enabled = (PARAMS)->TYPE##_meas.algo != GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE; \
+ if ((TO)->TYPE.ave_enabled) { \
+ /* H_REQAVE and H_REQT */ \
+ (TO)->TYPE.h_reqave = (PARAMS)->TYPE##_meas.h_reqave & 0x1f; \
+ (TO)->TYPE.h_reqt = (PARAMS)->TYPE##_meas.h_reqt & 0x1f; \
+ /* Averaging method and parameters */ \
+ (TO)->TYPE.ave_method = ((PARAMS)->TYPE##_meas.algo - 1) & 0x07; \
+ switch ((PARAMS)->TYPE##_meas.algo) { \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA: \
+ msgb_v_put(msg, (PARAMS)->TYPE##_meas.ewma.alpha); \
+ break; \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED: \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN: \
+ /* FIXME: unknown format */ \
+ break; \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED: \
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE: \
+ /* No parameters here */ \
+ break; \
+ } \
+ } \
+ } while (0)
+ ENC_PROC(mp, ave_cfg, ci_fr);
+ ENC_PROC(mp, ave_cfg, ci_hr);
+ ENC_PROC(mp, ave_cfg, ci_amr_fr);
+ ENC_PROC(mp, ave_cfg, ci_amr_hr);
+ ENC_PROC(mp, ave_cfg, ci_sdcch);
+ ENC_PROC(mp, ave_cfg, ci_gprs);
+#undef ENC_PROC
+
+ /* Update length part of the containing IE */
+ *ie_len = msg->tail - (ie_len + 1);
+}
+
+/* Appends Osmocom specific extension IEs into RSL_IE_MS_POWER_PARAM */
+void osmobts_enc_power_params_osmo_ext(struct msgb *msg, const struct gsm_power_ctrl_params *cp)
+{
+ struct osmo_preproc_pc_thresh *osmo_thresh;
+ struct osmo_preproc_pc_comp *osmo_thresh_comp;
+ uint8_t *ie_len;
+
+ /* (TLV) Measurement Averaging Configure (C/I) */
+ enc_osmo_meas_proc_params(msg, cp);
+
+ /* (TLV) Thresholds (C/I) */
+ ie_len = msgb_tl_put(msg, RSL_IPAC_EIE_OSMO_MS_PWR_CTL);
+ osmo_thresh = (struct osmo_preproc_pc_thresh *) msgb_put(msg, sizeof(*osmo_thresh));
+ #define ENC_THRESH_CI(TYPE) \
+ do { \
+ if (cp->TYPE##_meas.enabled) { \
+ osmo_thresh->l_##TYPE = cp->TYPE##_meas.lower_thresh; \
+ osmo_thresh->u_##TYPE = cp->TYPE##_meas.upper_thresh; \
+ } else { \
+ osmo_thresh->l_##TYPE = 0; \
+ osmo_thresh->u_##TYPE = 0; \
+ } \
+ } while (0)
+ ENC_THRESH_CI(ci_fr);
+ ENC_THRESH_CI(ci_hr);
+ ENC_THRESH_CI(ci_amr_fr);
+ ENC_THRESH_CI(ci_amr_hr);
+ ENC_THRESH_CI(ci_sdcch);
+ ENC_THRESH_CI(ci_gprs);
+ #undef ENC_THRESH_CI
+ /* Update length part of the containing IE */
+ *ie_len = msg->tail - (ie_len + 1);
+
+ /* (TLV) PC Threshold Comparators (C/I) */
+ ie_len = msgb_tl_put(msg, RSL_IPAC_EIE_OSMO_PC_THRESH_COMP);
+ osmo_thresh_comp = (struct osmo_preproc_pc_comp *) msgb_put(msg, sizeof(*osmo_thresh_comp));
+ #define ENC_THRESH_CI(TYPE) \
+ do { \
+ if (cp->TYPE##_meas.enabled) { \
+ osmo_thresh_comp->TYPE.lower_p = cp->TYPE##_meas.lower_cmp_p & 0x1f; \
+ osmo_thresh_comp->TYPE.lower_n = cp->TYPE##_meas.lower_cmp_n & 0x1f; \
+ osmo_thresh_comp->TYPE.upper_p = cp->TYPE##_meas.upper_cmp_p & 0x1f; \
+ osmo_thresh_comp->TYPE.upper_n = cp->TYPE##_meas.upper_cmp_n & 0x1f; \
+ } else { \
+ osmo_thresh_comp->TYPE.lower_p = 0; \
+ osmo_thresh_comp->TYPE.lower_n = 0; \
+ osmo_thresh_comp->TYPE.upper_p = 0; \
+ osmo_thresh_comp->TYPE.upper_n = 0; \
+ } \
+ } while (0)
+ ENC_THRESH_CI(ci_fr);
+ ENC_THRESH_CI(ci_hr);
+ ENC_THRESH_CI(ci_amr_fr);
+ ENC_THRESH_CI(ci_amr_hr);
+ ENC_THRESH_CI(ci_sdcch);
+ ENC_THRESH_CI(ci_gprs);
+ #undef ENC_THRESH_CI
+ /* Update length part of the containing IE */
+ *ie_len = msg->tail - (ie_len + 1);
+}
+
+static int power_ctrl_set_c0_power_red(const struct gsm_bts *bts,
+ const uint8_t red)
+{
+ struct abis_rsl_dchan_hdr *dh;
+ struct msgb *msg;
+
+ msg = rsl_msgb_alloc();
+ if (msg == NULL)
+ return -ENOMEM;
+
+ LOGP(DRSL, LOGL_NOTICE, "%sabling BCCH carrier power reduction "
+ "operation mode for BTS%u (maximum %u dB)\n",
+ red ? "En" : "Dis", bts->nr, red);
+
+ /* Abuse the standard BS POWER CONTROL message by specifying 'Common Channel'
+ * in the Protocol Discriminator field and 'BCCH' in the Channel Number IE. */
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ dh->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN;
+ dh->c.msg_type = RSL_MT_BS_POWER_CONTROL;
+ dh->ie_chan = RSL_IE_CHAN_NR;
+ dh->chan_nr = RSL_CHAN_BCCH;
+
+ msgb_tv_put(msg, RSL_IE_BS_POWER, red / 2);
+
+ msg->dst = bts->c0->rsl_link_primary;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+int bts_model_osmobts_init(void)
+{
+ model_osmobts = bts_model_nanobts;
+ model_osmobts.name = "osmo-bts";
+ model_osmobts.type = GSM_BTS_TYPE_OSMOBTS;
+
+ /* Unlike nanoBTS, osmo-bts does support SI2bis and SI2ter fine */
+ model_osmobts.force_combined_si = false;
+
+ /* Power control API */
+ model_osmobts.power_ctrl_set_c0_power_red = &power_ctrl_set_c0_power_red;
+
+ model_osmobts.features.data = &model_osmobts._features_data[0];
+ model_osmobts.features.data_len =
+ sizeof(model_osmobts._features_data);
+ memset(model_osmobts.features.data, 0, model_osmobts.features.data_len);
+
+ /* Adjust bts_init/bts_model_init in OsmoBTS to report new features.
+ * See also: doc/bts-features.txt */
+
+ model_osmobts.nm_att_tlvdef.def[NM_ATT_OSMO_NS_LINK_CFG].type = TLV_TYPE_TL16V;
+
+ return gsm_bts_model_register(&model_osmobts);
+}
diff --git a/src/osmo-bsc/bts_setup_ramp.c b/src/osmo-bsc/bts_setup_ramp.c
new file mode 100644
index 000000000..247f77654
--- /dev/null
+++ b/src/osmo-bsc/bts_setup_ramp.c
@@ -0,0 +1,249 @@
+/* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Alexander Couzens <acouzens@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 <stdbool.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bts_sm.h>
+#include <osmocom/bsc/bts_setup_ramp.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+
+
+static void _bts_setup_ramp_unblock_bts(struct gsm_bts *bts)
+{
+ llist_del_init(&bts->bts_setup_ramp.list);
+ bts->bts_setup_ramp.state = BTS_SETUP_RAMP_READY;
+
+ nm_fsm_dispatch_all_configuring(bts, NM_EV_SETUP_RAMP_READY, NULL);
+}
+
+/*!
+ * Unblock a BTS from BTS setup ramping to continue setup and configure.
+ *
+ * \param bts pointer to the bts
+ * \return 0 on success, -EINVAL when the BTS is not waiting.
+ */
+int bts_setup_ramp_unblock_bts(struct gsm_bts *bts)
+{
+ if (bts->bts_setup_ramp.state != BTS_SETUP_RAMP_WAIT)
+ return -EINVAL;
+
+ if (llist_empty(&bts->bts_setup_ramp.list))
+ return -EINVAL;
+
+ _bts_setup_ramp_unblock_bts(bts);
+ return 0;
+}
+
+/*!
+ * Timer callback and called by bts_setup_ramp_deactivate
+ * \param _net pointer to struct gsm_network
+ */
+static void bts_setup_ramp_timer_cb(void *_net)
+{
+ struct gsm_network *net = (struct gsm_network *) _net;
+ struct gsm_bts *bts, *n;
+ net->bts_setup_ramp.count = 0;
+
+ llist_for_each_entry_safe(bts, n, &net->bts_setup_ramp.head, bts_setup_ramp.list) {
+ net->bts_setup_ramp.count++;
+ _bts_setup_ramp_unblock_bts(bts);
+ LOG_BTS(bts, DNM, LOGL_INFO, "Unblock BTS %d from BTS ramping.\n", bts->nr);
+ if (bts_setup_ramp_active(net) && net->bts_setup_ramp.count >= net->bts_setup_ramp.step_size)
+ break;
+ }
+
+ if (bts_setup_ramp_active(net))
+ osmo_timer_schedule(&net->bts_setup_ramp.timer, net->bts_setup_ramp.step_interval, 0);
+}
+
+const struct value_string bts_setup_ramp_state_values[] = {
+ { BTS_SETUP_RAMP_INIT, "Initial" },
+ { BTS_SETUP_RAMP_WAIT, "Waiting" },
+ { BTS_SETUP_RAMP_READY, "Ready" },
+ { 0, NULL },
+};
+
+const char *bts_setup_ramp_get_state_str(struct gsm_bts *bts)
+{
+ return get_value_string_or_null(bts_setup_ramp_state_values, bts->bts_setup_ramp.state);
+}
+
+/* return true when state has been changed. */
+static bool check_config(struct gsm_network *net)
+{
+ bool new_state = (net->bts_setup_ramp.enabled
+ && net->bts_setup_ramp.step_size > 0
+ && net->bts_setup_ramp.step_interval > 0);
+
+ if (!new_state && bts_setup_ramp_active(net)) {
+ net->bts_setup_ramp.active = false;
+ osmo_timer_del(&net->bts_setup_ramp.timer);
+ /* clear bts list */
+ bts_setup_ramp_timer_cb(net);
+ return true;
+ } else if (new_state && !bts_setup_ramp_active(net)) {
+ net->bts_setup_ramp.active = true;
+ osmo_timer_schedule(&net->bts_setup_ramp.timer, net->bts_setup_ramp.step_interval, 0);
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ * Enable the bts setup ramping feature
+ *
+ * The BTS setup ramping prevents BSC overload when too many BTS tries to setup and
+ * configure at the same time. E.g. this might happen if there is a major network outage
+ * between all BTS and the BSC.
+ *
+ * \param[in] net a pointer to the gsm network
+ */
+void bts_setup_ramp_enable(struct gsm_network *net)
+{
+ net->bts_setup_ramp.enabled = true;
+ check_config(net);
+}
+
+/*!
+ * Disable the bts setup ramping feature
+ *
+ * \param[in] net a pointer to the gsm network
+ */
+void bts_setup_ramp_disable(struct gsm_network *net)
+{
+ net->bts_setup_ramp.enabled = false;
+ check_config(net);
+}
+
+/*! Checks if the bts setup ramp correct configured and active
+ *
+ * \param[in] net a pointer to the gsm network
+ * \return true if the bts setup ramp is active
+ */
+bool bts_setup_ramp_active(struct gsm_network *net)
+{
+ return net->bts_setup_ramp.active;
+}
+
+/*!
+ * Check if the BTS should wait to setup.
+ *
+ * Can be called multiple times by the same BTS.
+ *
+ * \param bts pointer to the bts
+ * \return true if the bts should wait
+ */
+bool bts_setup_ramp_wait(struct gsm_bts *bts)
+{
+ struct gsm_network *net = bts->network;
+
+ if (!bts_setup_ramp_active(net)) {
+ bts->bts_setup_ramp.state = BTS_SETUP_RAMP_READY;
+ return false;
+ }
+
+ switch (bts->bts_setup_ramp.state) {
+ case BTS_SETUP_RAMP_INIT:
+ break;
+ case BTS_SETUP_RAMP_WAIT:
+ return true;
+ case BTS_SETUP_RAMP_READY:
+ return false;
+ }
+
+ if (net->bts_setup_ramp.count < net->bts_setup_ramp.step_size) {
+ LOG_BTS(bts, DNM, LOGL_INFO,
+ "BTS %d can configure without waiting for BTS ramping.\n", bts->nr);
+
+ net->bts_setup_ramp.count++;
+ bts->bts_setup_ramp.state = BTS_SETUP_RAMP_READY;
+ return false;
+ }
+
+ bts->bts_setup_ramp.state = BTS_SETUP_RAMP_WAIT;
+ llist_add_tail(&bts->bts_setup_ramp.list, &net->bts_setup_ramp.head);
+ LOGP(DNM, LOGL_INFO, "BTS %d will wait for BTS ramping.\n", bts->nr);
+
+ return true;
+}
+
+void bts_setup_ramp_init_network(struct gsm_network *net)
+{
+ INIT_LLIST_HEAD(&net->bts_setup_ramp.head);
+ osmo_timer_setup(&net->bts_setup_ramp.timer, bts_setup_ramp_timer_cb, net);
+}
+
+void bts_setup_ramp_init_bts(struct gsm_bts *bts)
+{
+ /* Initialize bts_setup_ramp.list (llist_entry) to have llist_empty() available */
+ INIT_LLIST_HEAD(&bts->bts_setup_ramp.list);
+ bts->bts_setup_ramp.state = BTS_SETUP_RAMP_INIT;
+}
+
+/*!
+ * Remove the bts from the bts setup ramp waiting list and resets the BTS setup ramping state.
+ * Should be called when removing the BTS
+ *
+ * \param bts pointer to the bts
+ */
+void bts_setup_ramp_remove(struct gsm_bts *bts)
+{
+ if (!llist_empty(&bts->bts_setup_ramp.list))
+ llist_del_init(&bts->bts_setup_ramp.list);
+ bts->bts_setup_ramp.state = BTS_SETUP_RAMP_INIT;
+}
+
+/*!
+ * Set the BTS setup ramping step interval.
+ *
+ * Within the time window of \param step_interval only a limited amount (see step_size)
+ * of BTS will be configured.
+ *
+ * \param[in] net a pointer to the gsm network
+ * \param step_interval in seconds
+ */
+void bts_setup_ramp_set_step_interval(struct gsm_network *net, unsigned int step_interval)
+{
+ net->bts_setup_ramp.step_interval = step_interval;
+ check_config(net);
+}
+
+/*!
+ * Set the BTS setup ramping step_size
+ *
+ * Within the time window of step_interval only a limited amount of BTS (\param step_size)
+ * will be configured.
+ *
+ * \param[in] net a pointer to the gsm network
+ * \param step_size the step size
+ */
+void bts_setup_ramp_set_step_size(struct gsm_network *net, unsigned int step_size)
+{
+ net->bts_setup_ramp.step_size = step_size;
+ check_config(net);
+}
diff --git a/src/osmo-bsc/bts_siemens_bs11.c b/src/osmo-bsc/bts_siemens_bs11.c
index 08694eab0..ed2c4c34d 100644
--- a/src/osmo-bsc/bts_siemens_bs11.c
+++ b/src/osmo-bsc/bts_siemens_bs11.c
@@ -434,7 +434,7 @@ static void nm_reconfig_trx(struct gsm_bts_trx *trx)
abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts,
e1l->e1_ts_ss);
abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr,
- e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei);
+ e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei_primary);
/* Set Radio Attributes */
if (trx == trx->bts->c0)
@@ -552,6 +552,7 @@ static int shutdown_om(struct gsm_bts *bts)
/* Reset BTS Site manager resource */
abis_nm_bs11_reset_resource(bts);
+ gsm_bts_stats_reset(bts);
gsm_bts_all_ts_dispatch(bts, TS_EV_OML_DOWN, NULL);
return 0;
@@ -603,13 +604,6 @@ static int inp_sig_cb(unsigned int subsys, unsigned int signal,
static int bts_model_bs11_start(struct gsm_network *net)
{
- model_bs11.features.data = &model_bs11._features_data[0];
- model_bs11.features.data_len = sizeof(model_bs11._features_data);
-
- osmo_bts_set_feature(&model_bs11.features, BTS_FEAT_HOPPING);
- osmo_bts_set_feature(&model_bs11.features, BTS_FEAT_HSCSD);
- osmo_bts_set_feature(&model_bs11.features, BTS_FEAT_MULTI_TSC);
-
osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
@@ -618,5 +612,12 @@ static int bts_model_bs11_start(struct gsm_network *net)
int bts_model_bs11_init(void)
{
+ model_bs11.features.data = &model_bs11._features_data[0];
+ model_bs11.features.data_len = sizeof(model_bs11._features_data);
+
+ osmo_bts_set_feature(&model_bs11.features, BTS_FEAT_HOPPING);
+ osmo_bts_set_feature(&model_bs11.features, BTS_FEAT_HSCSD);
+ osmo_bts_set_feature(&model_bs11.features, BTS_FEAT_MULTI_TSC);
+
return gsm_bts_model_register(&model_bs11);
}
diff --git a/src/osmo-bsc/bts_sm.c b/src/osmo-bsc/bts_sm.c
new file mode 100644
index 000000000..ca572f146
--- /dev/null
+++ b/src/osmo-bsc/bts_sm.c
@@ -0,0 +1,112 @@
+/* (C) 2008-2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 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/gsm/abis_nm.h>
+
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bts_sm.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+
+static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 };
+
+static int gsm_bts_sm_talloc_destructor(struct gsm_bts_sm *bts_sm)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(bts_sm->gprs.nsvc); i++) {
+ if (bts_sm->gprs.nsvc[i].mo.fi) {
+ osmo_fsm_inst_free(bts_sm->gprs.nsvc[i].mo.fi);
+ bts_sm->gprs.nsvc[i].mo.fi = NULL;
+ }
+ }
+ if (bts_sm->gprs.nse.mo.fi) {
+ osmo_fsm_inst_free(bts_sm->gprs.nse.mo.fi);
+ bts_sm->gprs.nse.mo.fi = NULL;
+ }
+
+ if (bts_sm->mo.fi) {
+ osmo_fsm_inst_free(bts_sm->mo.fi);
+ bts_sm->mo.fi = NULL;
+ }
+ return 0;
+}
+
+struct gsm_bts_sm *gsm_bts_sm_alloc(struct gsm_network *net, uint8_t bts_num)
+{
+ struct gsm_bts_sm *bts_sm = talloc_zero(net, struct gsm_bts_sm);
+ struct gsm_bts *bts;
+ int i;
+ if (!bts_sm)
+ return NULL;
+
+ talloc_set_destructor(bts_sm, gsm_bts_sm_talloc_destructor);
+ bts_sm->mo.fi = osmo_fsm_inst_alloc(&nm_bts_sm_fsm, bts_sm, bts_sm,
+ LOGL_INFO, NULL);
+ osmo_fsm_inst_update_id_f(bts_sm->mo.fi, "bts_sm");
+
+ bts = gsm_bts_alloc(net, bts_sm, bts_num);
+ if (!bts) {
+ talloc_free(bts_sm);
+ return NULL;
+ }
+ bts_sm->bts[0] = bts;
+
+ gsm_mo_init(&bts_sm->mo, bts, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff);
+
+
+ bts_sm->gprs.nse.mo.fi = osmo_fsm_inst_alloc(&nm_gprs_nse_fsm, bts_sm, &bts_sm->gprs.nse,
+ LOGL_INFO, NULL);
+ osmo_fsm_inst_update_id_f(bts_sm->gprs.nse.mo.fi, "nse%d", bts_num);
+ gsm_mo_init(&bts_sm->gprs.nse.mo, bts, NM_OC_GPRS_NSE, bts->nr, 0xff, 0xff);
+ memcpy(&bts_sm->gprs.nse.timer, bts_nse_timer_default,
+ sizeof(bts_sm->gprs.nse.timer));
+
+ for (i = 0; i < ARRAY_SIZE(bts_sm->gprs.nsvc); i++) {
+ bts_sm->gprs.nsvc[i].bts = bts;
+ bts_sm->gprs.nsvc[i].id = i;
+ bts_sm->gprs.nsvc[i].mo.fi = osmo_fsm_inst_alloc(
+ &nm_gprs_nsvc_fsm, bts_sm,
+ &bts_sm->gprs.nsvc[i],
+ LOGL_INFO, NULL);
+ osmo_fsm_inst_update_id_f(bts_sm->gprs.nsvc[i].mo.fi,
+ "nsvc%d", i);
+ gsm_mo_init(&bts_sm->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC,
+ bts->nr, i, 0xff);
+ }
+ memcpy(&bts_sm->gprs.nse.timer, bts_nse_timer_default,
+ sizeof(bts_sm->gprs.nse.timer));
+ gsm_mo_init(&bts_sm->gprs.nse.mo, bts, NM_OC_GPRS_NSE,
+ bts->nr, 0xff, 0xff);
+
+ return bts_sm;
+}
+
+void gsm_bts_sm_mo_reset(struct gsm_bts_sm *bts_sm)
+{
+ int i;
+ gsm_abis_mo_reset(&bts_sm->mo);
+
+ gsm_abis_mo_reset(&bts_sm->gprs.nse.mo);
+ for (i = 0; i < ARRAY_SIZE(bts_sm->gprs.nsvc); i++)
+ gsm_abis_mo_reset(&bts_sm->gprs.nsvc[i].mo);
+
+ gsm_bts_mo_reset(bts_sm->bts[0]);
+}
diff --git a/src/osmo-bsc/bts_sysmobts.c b/src/osmo-bsc/bts_sysmobts.c
deleted file mode 100644
index d7d15ebc2..000000000
--- a/src/osmo-bsc/bts_sysmobts.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* sysmocom sysmoBTS specific code */
-
-/* (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
- *
- * 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 <arpa/inet.h>
-
-#include <osmocom/gsm/tlv.h>
-
-#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/bsc/signal.h>
-#include <osmocom/bsc/abis_nm.h>
-#include <osmocom/bsc/bts.h>
-#include <osmocom/abis/e1_input.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/bsc/abis_nm.h>
-#include <osmocom/bsc/abis_rsl.h>
-#include <osmocom/bsc/debug.h>
-#include <osmocom/abis/subchan_demux.h>
-#include <osmocom/abis/ipaccess.h>
-#include <osmocom/core/logging.h>
-
-extern struct gsm_bts_model bts_model_nanobts;
-
-static struct gsm_bts_model model_sysmobts;
-
-int bts_model_sysmobts_init(void)
-{
- model_sysmobts = bts_model_nanobts;
- model_sysmobts.name = "sysmobts";
- model_sysmobts.type = GSM_BTS_TYPE_OSMOBTS;
-
- /* Unlike nanoBTS, sysmoBTS supports SI2bis and SI2ter fine */
- model_sysmobts.force_combined_si = false;
-
- model_sysmobts.features.data = &model_sysmobts._features_data[0];
- model_sysmobts.features.data_len =
- sizeof(model_sysmobts._features_data);
- memset(model_sysmobts.features.data, 0, model_sysmobts.features.data_len);
-
- osmo_bts_set_feature(&model_sysmobts.features, BTS_FEAT_GPRS);
- osmo_bts_set_feature(&model_sysmobts.features, BTS_FEAT_EGPRS);
- osmo_bts_set_feature(&model_sysmobts.features, BTS_FEAT_PAGING_COORDINATION);
-
- model_sysmobts.nm_att_tlvdef.def[NM_ATT_OSMO_NS_LINK_CFG].type = TLV_TYPE_TL16V;
-
- return gsm_bts_model_register(&model_sysmobts);
-}
diff --git a/src/osmo-bsc/bts_trx.c b/src/osmo-bsc/bts_trx.c
index 25a3fc73e..49702d084 100644
--- a/src/osmo-bsc/bts_trx.c
+++ b/src/osmo-bsc/bts_trx.c
@@ -30,6 +30,31 @@
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/pcu_if.h>
#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/lchan.h>
+
+static int gsm_bts_trx_talloc_destructor(struct gsm_bts_trx *trx)
+{
+ unsigned int i;
+
+ if (trx->bb_transc.mo.fi) {
+ osmo_fsm_inst_free(trx->bb_transc.mo.fi);
+ trx->bb_transc.mo.fi = NULL;
+ }
+ if (trx->mo.fi) {
+ osmo_fsm_inst_free(trx->mo.fi);
+ trx->mo.fi = NULL;
+ }
+ for (i = 0; i < TRX_NR_TS; i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ if (ts->mo.fi) {
+ osmo_fsm_inst_free(ts->mo.fi);
+ ts->mo.fi = NULL;
+ }
+ ts_fsm_free(ts);
+ }
+ return 0;
+}
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
{
@@ -39,12 +64,22 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
if (!trx)
return NULL;
+ talloc_set_destructor(trx, gsm_bts_trx_talloc_destructor);
+
trx->bts = bts;
trx->nr = bts->num_trx++;
- trx->mo.nm_state.administrative = NM_STATE_UNLOCKED;
+ trx->rsl_tei_primary = trx->nr;
+
+ trx->mo.fi = osmo_fsm_inst_alloc(&nm_rcarrier_fsm, trx, trx,
+ LOGL_INFO, NULL);
+ osmo_fsm_inst_update_id_f(trx->mo.fi, "bts%d-trx%d", bts->nr, trx->nr);
gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER,
bts->nr, trx->nr, 0xff);
+
+ trx->bb_transc.mo.fi = osmo_fsm_inst_alloc(&nm_bb_transc_fsm, trx, &trx->bb_transc,
+ LOGL_INFO, NULL);
+ osmo_fsm_inst_update_id_f(trx->bb_transc.mo.fi, "bts%d-trx%d", bts->nr, trx->nr);
gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC,
bts->nr, trx->nr, 0xff);
@@ -60,6 +95,10 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
ts_fsm_alloc(ts);
+ ts->mo.fi = osmo_fsm_inst_alloc(&nm_chan_fsm, trx, ts,
+ LOGL_INFO, NULL);
+ osmo_fsm_inst_update_id_f(ts->mo.fi, "bts%d-trx%d-ts%d",
+ bts->nr, trx->nr, ts->nr);
gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL,
bts->nr, trx->nr, ts->nr);
@@ -69,22 +108,21 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
ts->hopping.ma.data = ts->hopping.ma_data;
for (l = 0; l < TS_MAX_LCHAN; l++) {
- struct gsm_lchan *lchan;
- char *name;
- lchan = &ts->lchan[l];
-
- lchan->ts = ts;
- lchan->nr = l;
- lchan->type = GSM_LCHAN_NONE;
-
- name = gsm_lchan_name_compute(lchan);
- lchan->name = talloc_strdup(trx, name);
+ struct gsm_lchan *lchan = &ts->lchan[l];
+ lchan_init(lchan, ts, l);
}
}
if (trx->nr != 0)
trx->nominal_power = bts->c0->nominal_power;
+ if (bts->model && bts->model->trx_init) {
+ if (bts->model->trx_init(trx) < 0) {
+ talloc_free(trx);
+ return NULL;
+ }
+ }
+
llist_add_tail(&trx->list, &bts->trx_list);
return trx;
@@ -111,37 +149,98 @@ struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
uint8_t cbits = chan_nr >> 3;
uint8_t lch_idx;
struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
+ bool vamos = false;
bool ok;
if (rc)
*rc = -EINVAL;
- if (cbits == 0x01) {
+ /* Why call ts_is_capable_of_pchan() here? Dynamic timeslots may receive RSL Channel Activation ACK on a
+ * timeslot that is in transition between pchan modes. That ACK actually confirms the pchan switch, so instead
+ * of checking the current pchan mode, we must allow any pchans that a dyn TS is capable of. */
+
+ /* Interpret Osmocom specific cbits only for OsmoBTS type */
+ if (trx->bts->model->type == GSM_BTS_TYPE_OSMOBTS) {
+ /* For VAMOS cbits, set vamos = true and handle cbits as their equivalent non-VAMOS cbits below. */
+ switch (cbits) {
+ case ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Bm_ACCHs:
+ case ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Lm_ACCHs(0):
+ case ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Lm_ACCHs(1):
+ cbits = (chan_nr & ~RSL_CHAN_OSMO_VAMOS_MASK) >> 3;
+ vamos = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (cbits) {
+ case ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs:
lch_idx = 0; /* TCH/F */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_F)
|| ts->pchan_on_init == GSM_PCHAN_PDCH; /* PDCH? really? */
- } else if ((cbits & 0x1e) == 0x02) {
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr %x cbits %x: ts %s is not capable of GSM_PCHAN_TCH_F\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
+ break;
+ case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0):
+ case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(1):
lch_idx = cbits & 0x1; /* TCH/H */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_H);
- } else if ((cbits & 0x1c) == 0x04) {
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_TCH_H\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
+ break;
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(1):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(2):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(3):
lch_idx = cbits & 0x3; /* SDCCH/4 */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_CCCH_SDCCH4);
- } else if ((cbits & 0x18) == 0x08) {
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_CCCH_SDCCH4\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
+ break;
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(1):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(2):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(3):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(4):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(5):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(6):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(7):
lch_idx = cbits & 0x7; /* SDCCH/8 */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_SDCCH8_SACCH8C);
- } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) {
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_SDCCH8_SACCH8C\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
+ break;
+ case ABIS_RSL_CHAN_NR_CBITS_BCCH:
+ case ABIS_RSL_CHAN_NR_CBITS_RACH:
+ case ABIS_RSL_CHAN_NR_CBITS_PCH_AGCH:
lch_idx = 0; /* CCCH? */
ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_CCCH);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_CCCH\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
/* FIXME: we should not return first sdcch4 !!! */
- } else if ((chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_OSMO_PDCH) {
+ break;
+ case ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH:
lch_idx = 0;
- ok = (ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH);
- } else
+ ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_PDCH);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_PDCH\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
+ break;
+ default:
return NULL;
+ }
if (rc && ok)
*rc = 0;
+ if (vamos)
+ lch_idx += ts->max_primary_lchans;
return &ts->lchan[lch_idx];
}
@@ -149,10 +248,9 @@ void gsm_trx_lock_rf(struct gsm_bts_trx *trx, bool locked, const char *reason)
{
uint8_t new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED;
-
+ /* State will be sent when BTS connects. */
if (!trx->bts || !trx->bts->oml_link) {
- /* Set initial state which will be sent when BTS connects. */
- trx->mo.nm_state.administrative = new_state;
+ trx->mo.force_rf_lock = locked;
return;
}
@@ -160,9 +258,7 @@ void gsm_trx_lock_rf(struct gsm_bts_trx *trx, bool locked, const char *reason)
get_value_string(abis_nm_adm_state_names, trx->mo.nm_state.administrative),
get_value_string(abis_nm_adm_state_names, new_state), reason);
- abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
- trx->bts->bts_nr, trx->nr, 0xff,
- new_state);
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_FORCE_LOCK, (void*)(intptr_t)locked);
}
bool trx_is_usable(const struct gsm_bts_trx *trx)
@@ -191,54 +287,6 @@ void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data
}
}
-int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
-{
- struct gsm_bts_trx_ts *ts;
- struct gsm_lchan *lchan;
- int j;
- int count = 0;
-
- if (!trx_is_usable(trx))
- return 0;
-
- for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
- ts = &trx->ts[j];
- if (!ts_is_usable(ts))
- continue;
-
- if (ts->pchan_is == GSM_PCHAN_PDCH) {
- /* Dynamic timeslots in PDCH mode will become TCH if needed. */
- switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_PDCH:
- if (pchan == GSM_PCHAN_TCH_F)
- count++;
- continue;
-
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- if (pchan == GSM_PCHAN_TCH_F)
- count++;
- else if (pchan == GSM_PCHAN_TCH_H)
- count += 2;
- continue;
-
- default:
- /* Not dynamic, not applicable. */
- continue;
- }
- }
-
- if (ts->pchan_is != pchan)
- continue;
-
- ts_for_each_lchan(lchan, ts) {
- if (lchan_state_is(lchan, LCHAN_ST_UNUSED))
- count++;
- }
- }
-
- return count;
-}
-
bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx)
{
bool combined = false;
@@ -278,6 +326,23 @@ bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx)
}
break;
+ case GSM_PCHAN_PDCH:
+ if (is_ericsson_bts(trx->bts)) {
+ /* NOTE: A static PDCH is usually handled by the BTS/PCU internally, the BSC
+ * will not actively manage this channel. It will just keep the timeslot
+ * unused so that it is free for the BTS/PCU to use it as PDCH. Not all BTSs
+ * work well in this scheme. Ericsson RBS BTSs support dynamic channels natively
+ * and require a channel activation on RSL level before the PDCH can be used.
+ * One could work around this by activating the PDCH once on startup and
+ * leave it on indefinetly but we decided not to do so. Users of Ericsson RBS
+ * BTSs must configure a dynamic PDCH channel. */
+ LOGP(DNM, LOGL_ERROR, "%s is not allowed, because Ericsson RBS does"
+ "not support static PDCH (use TCH/F_TCH/H_SDCCH8_PDCH)\n",
+ gsm_pchan_name(ts->pchan_from_config));
+ result = false;
+ }
+ break;
+
default:
/* CCCH on TS0 is mandatory for C0 */
if (trx->bts->c0 == trx && i == 0) {
@@ -285,6 +350,20 @@ bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx)
result = false;
}
}
+
+ if (trx->bts->features_known) {
+ const struct bitvec *ft = &trx->bts->features;
+
+ if (ts->hopping.enabled && !osmo_bts_has_feature(ft, BTS_FEAT_HOPPING)) {
+ LOGP(DNM, LOGL_ERROR, "TS%d has freq. hopping enabled, but BTS does not support it\n", i);
+ result = false;
+ }
+
+ if (ts->tsc != -1 && !osmo_bts_has_feature(ft, BTS_FEAT_MULTI_TSC)) {
+ LOGP(DNM, LOGL_ERROR, "TS%d has TSC != BCC, but BTS does not support it\n", i);
+ result = false;
+ }
+ }
}
return result;
@@ -329,7 +408,7 @@ static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len)
/* set all system information types for a TRX */
int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx)
{
- int i, rc;
+ int rc;
struct gsm_bts *bts = trx->bts;
uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n;
int si_len[_MAX_SYSINFO_TYPE];
@@ -367,42 +446,52 @@ int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx)
/* Second, we generate the selected SI via RSL */
for (n = 0; n < n_si; n++) {
- i = gen_si[n];
+ const enum osmo_sysinfo_type si_type = gen_si[n];
+
/* Only generate SI if this SI is not in "static" (user-defined) mode */
- if (!(bts->si_mode_static & (1 << i))) {
+ if (!(bts->si_mode_static & (1 << si_type))) {
/* Set SI as being valid. gsm_generate_si() might unset
* it, if SI is not required. */
- bts->si_valid |= (1 << i);
- rc = gsm_generate_si(bts, i);
+ bts->si_valid |= (1 << si_type);
+ rc = gsm_generate_si(bts, si_type);
if (rc < 0)
goto err_out;
- si_len[i] = rc;
+ si_len[si_type] = rc;
} else {
- if (i == SYSINFO_TYPE_5 || i == SYSINFO_TYPE_5bis
- || i == SYSINFO_TYPE_5ter)
- si_len[i] = 18;
- else if (i == SYSINFO_TYPE_6)
- si_len[i] = 11;
- else
- si_len[i] = 23;
+ switch (si_type) {
+ case SYSINFO_TYPE_5:
+ case SYSINFO_TYPE_5bis:
+ case SYSINFO_TYPE_5ter:
+ si_len[si_type] = 18;
+ break;
+ case SYSINFO_TYPE_6:
+ si_len[si_type] = 11;
+ break;
+ case SYSINFO_TYPE_10:
+ si_len[si_type] = 21;
+ break;
+ default:
+ si_len[si_type] = GSM_MACBLOCK_LEN;
+ }
}
}
/* Third, we send the selected SI via RSL */
for (n = 0; n < n_si; n++) {
- i = gen_si[n];
+ const enum osmo_sysinfo_type si_type = gen_si[n];
+
/* 3GPP TS 08.58 §8.5.1 BCCH INFORMATION. If we don't currently
* have this SI, we send a zero-length RSL BCCH FILLING /
* SACCH FILLING in order to deactivate the SI, in case it
* might have previously been active */
- if (!GSM_BTS_HAS_SI(bts, i)) {
+ if (!GSM_BTS_HAS_SI(bts, si_type)) {
if (bts->si_unused_send_empty)
- rc = rsl_si(trx, i, 0);
+ rc = rsl_si(trx, si_type, 0);
else
rc = 0; /* some nanoBTS fw don't like receiving empty unsupported SI */
} else
- rc = rsl_si(trx, i, si_len[i]);
+ rc = rsl_si(trx, si_type, si_len[si_type]);
if (rc < 0)
return rc;
}
@@ -415,6 +504,6 @@ int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx)
err_out:
LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u: error <%s>, "
"most likely a problem with neighbor cell list generation\n",
- get_value_string(osmo_sitype_strs, i), bts->nr, strerror(-rc));
+ get_value_string(osmo_sitype_strs, gen_si[n]), bts->nr, strerror(-rc));
return rc;
}
diff --git a/src/osmo-bsc/bts_trx_ctrl.c b/src/osmo-bsc/bts_trx_ctrl.c
new file mode 100644
index 000000000..ff58cb735
--- /dev/null
+++ b/src/osmo-bsc/bts_trx_ctrl.c
@@ -0,0 +1,212 @@
+/*
+ * (C) 2013-2015 by Holger Hans Peter Freyther
+ * (C) 2013-2022 by sysmocom s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <time.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/bsc/ctrl.h>
+#include <osmocom/bsc/osmo_bsc_rf.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/ipaccess.h>
+#include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/system_information.h>
+
+/*********************
+ * TS_NODE
+ *********************/
+
+static int verify_ts_hopping_arfcn_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int64_t arfcn;
+ enum gsm_band unused;
+ if (osmo_str_to_int64(&arfcn, value, 10, 0, 1024) < 0)
+ return 1;
+ if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ return 1;
+ }
+ return 0;
+}
+static int set_ts_hopping_arfcn_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts_trx_ts *ts = cmd->node;
+ int arfcn = atoi(cmd->value);
+
+ bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, ONE);
+
+ /* Update Cell Allocation (list of all the frequencies allocated to a cell) */
+ if (generate_cell_chan_alloc(ts->trx->bts) != 0) {
+ bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, ZERO); /* roll-back */
+ cmd->reply = "Failed to re-generate Cell Allocation";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+/* Parameter format: "<arfcn>" */
+CTRL_CMD_DEFINE_WO(ts_hopping_arfcn_add, "hopping-arfcn-add");
+
+static int verify_ts_hopping_arfcn_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int64_t arfcn;
+ enum gsm_band unused;
+ if (strcmp(value, "all") == 0)
+ return 0;
+ if (osmo_str_to_int64(&arfcn, value, 10, 0, 1024) < 0)
+ return 1;
+ if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ return 1;
+ }
+ return 0;
+}
+static int set_ts_hopping_arfcn_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts_trx_ts *ts = cmd->node;
+ bool all = (strcmp(cmd->value, "all") == 0);
+ int arfcn;
+
+ if (all) {
+ bitvec_zero(&ts->hopping.arfcns);
+ } else {
+ arfcn = atoi(cmd->value);
+ bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, ZERO);
+ }
+
+ /* Update Cell Allocation (list of all the frequencies allocated to a cell) */
+ if (generate_cell_chan_alloc(ts->trx->bts) != 0) {
+ if (!all)
+ bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, ONE); /* roll-back */
+ cmd->reply = "Failed to re-generate Cell Allocation";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+/* Parameter format: "(<arfcn>|all)" */
+CTRL_CMD_DEFINE_WO(ts_hopping_arfcn_del, "hopping-arfcn-del");
+
+
+static int bsc_bts_trx_ts_ctrl_cmds_install(void)
+{
+ int rc = 0;
+
+ rc |= ctrl_cmd_install(CTRL_NODE_TS, &cmd_ts_hopping_arfcn_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_TS, &cmd_ts_hopping_arfcn_del);
+
+ return rc;
+}
+
+/*********************
+ * TRX_NODE
+ *********************/
+
+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");
+
+/* TRX related commands below here */
+CTRL_HELPER_GET_INT(trx_max_power, struct gsm_bts_trx, max_power_red);
+static int verify_trx_max_power(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int tmp = atoi(value);
+
+ if (tmp < 0 || tmp > 22) {
+ cmd->reply = "Value must be between 0 and 22";
+ return -1;
+ }
+
+ if (tmp & 1) {
+ cmd->reply = "Value must be even";
+ return -1;
+ }
+
+ return 0;
+}
+CTRL_CMD_DEFINE_RANGE(trx_arfcn, "arfcn", struct gsm_bts_trx, arfcn, 0, 1023);
+
+static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_bts_trx *trx = cmd->node;
+ int old_power;
+
+ /* remember the old value, set the new one */
+ old_power = trx->max_power_red;
+ trx->max_power_red = atoi(cmd->value);
+
+ /* Maybe update the value */
+ if (old_power != trx->max_power_red) {
+ LOGP(DCTRL, LOGL_NOTICE,
+ "%s updating max_pwr_red(%d)\n",
+ gsm_trx_name(trx), trx->max_power_red);
+ abis_nm_update_max_power_red(trx);
+ }
+
+ return get_trx_max_power(cmd, _data);
+}
+CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction");
+
+int bsc_bts_trx_ctrl_cmds_install(void)
+{
+ int rc = 0;
+
+ 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);
+
+ rc |= bsc_bts_trx_ts_ctrl_cmds_install();
+
+ return rc;
+}
diff --git a/src/osmo-bsc/bts_trx_vty.c b/src/osmo-bsc/bts_trx_vty.c
new file mode 100644
index 000000000..84de9c5c6
--- /dev/null
+++ b/src/osmo-bsc/bts_trx_vty.c
@@ -0,0 +1,874 @@
+/* OsmoBSC interface to quagga VTY, TRX (and TS) node */
+/* (C) 2009-2017 by Harald Welte <laforge@gnumonks.org>
+ * (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 <stdlib.h>
+#include <stdbool.h>
+#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>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/bsc/vty.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/bsc/system_information.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/lchan.h>
+#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/lchan_select.h>
+#include <osmocom/bsc/bts.h>
+
+#include <inttypes.h>
+
+#include "../../bscconfig.h"
+
+#define X(x) (1 << x)
+
+static struct cmd_node trx_node = {
+ TRX_NODE,
+ "%s(config-net-bts-trx)# ",
+ 1,
+};
+
+static struct cmd_node ts_node = {
+ TS_NODE,
+ "%s(config-net-bts-trx-ts)# ",
+ 1,
+};
+
+/* utility functions */
+void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line,
+ const char *ts, const char *ss)
+{
+ e1_link->e1_nr = atoi(line);
+ e1_link->e1_ts = atoi(ts);
+ if (!strcmp(ss, "full"))
+ e1_link->e1_ts_ss = E1_SUBSLOT_FULL;
+ else
+ e1_link->e1_ts_ss = atoi(ss);
+}
+
+#define TRX_TEXT "Radio Transceiver\n"
+
+/* per TRX configuration */
+DEFUN_ATTR(cfg_trx,
+ cfg_trx_cmd,
+ "trx <0-255>",
+ TRX_TEXT
+ "Select a TRX to configure\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ int trx_nr = atoi(argv[0]);
+ struct gsm_bts *bts = vty->index;
+ struct gsm_bts_trx *trx;
+
+ if (trx_nr > bts->num_trx) {
+ vty_out(vty, "%% The next unused TRX number in this BTS is %u%s",
+ bts->num_trx, VTY_NEWLINE);
+ return CMD_WARNING;
+ } else if (trx_nr == bts->num_trx) {
+ /* we need to allocate a new one */
+ trx = gsm_bts_trx_alloc(bts);
+ } else
+ trx = gsm_bts_trx_num(bts, trx_nr);
+
+ if (!trx)
+ return CMD_WARNING;
+
+ vty->index = trx;
+ vty->node = TRX_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_trx_arfcn,
+ cfg_trx_arfcn_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "arfcn <0-1023>",
+ "Set the ARFCN for this TRX\n"
+ "Absolute Radio Frequency Channel Number\n")
+{
+ enum gsm_band unused;
+ struct gsm_bts_trx *trx = vty->index;
+ int arfcn = atoi(argv[0]);
+
+ if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* FIXME: check if this ARFCN is supported by this TRX */
+
+ trx->arfcn = arfcn;
+
+ /* Update Cell Allocation (list of all the frequencies allocated to a cell) */
+ if (generate_cell_chan_alloc(trx->bts) != 0) {
+ vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* FIXME: patch ARFCN into SYSTEM INFORMATION */
+ /* FIXME: use OML layer to update the ARFCN */
+ /* FIXME: use RSL layer to update SYSTEM INFORMATION */
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_trx_nominal_power,
+ cfg_trx_nominal_power_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "nominal power <-20-100>",
+ "Nominal TRX RF Power in dBm\n"
+ "Nominal TRX RF Power in dBm\n"
+ "Nominal TRX RF Power in dBm\n")
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ trx->nominal_power = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_trx_max_power_red,
+ cfg_trx_max_power_red_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "max_power_red <0-100>",
+ "Reduction of maximum BS RF Power (relative to nominal power)\n"
+ "Reduction of maximum BS RF Power in dB\n")
+{
+ int maxpwr_r = atoi(argv[0]);
+ struct gsm_bts_trx *trx = vty->index;
+ int upper_limit = 24; /* default 12.21 max power red. */
+
+ /* FIXME: check if our BTS type supports more than 12 */
+ if (maxpwr_r < 0 || maxpwr_r > upper_limit) {
+ vty_out(vty, "%% Power %d dB is not in the valid range%s",
+ maxpwr_r, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (maxpwr_r & 1) {
+ vty_out(vty, "%% Power %d dB is not an even value%s",
+ maxpwr_r, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ trx->max_power_red = maxpwr_r;
+
+ /* FIXME: make sure we update this using OML */
+
+ return CMD_SUCCESS;
+}
+
+/* NOTE: This requires a full restart as bsc_network_configure() is executed
+ * only once on startup from osmo_bsc_main.c */
+DEFUN(cfg_trx_rsl_e1,
+ cfg_trx_rsl_e1_cmd,
+ "rsl e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
+ "RSL Parameters\n"
+ "E1/T1 interface to be used for RSL\n"
+ "E1/T1 interface to be used for RSL\n"
+ "E1/T1 Line Number to be used for RSL\n"
+ "E1/T1 Timeslot to be used for RSL\n"
+ "E1/T1 Timeslot to be used for RSL\n"
+ "E1/T1 Sub-slot to be used for RSL\n"
+ "E1/T1 Sub-slot 0 is to be used for RSL\n"
+ "E1/T1 Sub-slot 1 is to be used for RSL\n"
+ "E1/T1 Sub-slot 2 is to be used for RSL\n"
+ "E1/T1 Sub-slot 3 is to be used for RSL\n"
+ "E1/T1 full timeslot is to be used for RSL\n")
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ parse_e1_link(&trx->rsl_e1_link, argv[0], argv[1], argv[2]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_trx_rsl_e1_tei,
+ cfg_trx_rsl_e1_tei_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "rsl e1 tei <0-63>",
+ "RSL Parameters\n"
+ "Set the TEI to be used for RSL\n"
+ "Set the TEI to be used for RSL\n"
+ "TEI to be used for RSL\n")
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ trx->rsl_tei_primary = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_trx_rf_locked,
+ cfg_trx_rf_locked_cmd,
+ "rf_locked (0|1)",
+ "Set or unset the RF Locking (Turn off RF of the TRX)\n"
+ "TRX is NOT RF locked (active)\n"
+ "TRX is RF locked (turned off)\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ int locked = atoi(argv[0]);
+ struct gsm_bts_trx *trx = vty->index;
+
+ gsm_trx_lock_rf(trx, locked, "vty");
+ return CMD_SUCCESS;
+}
+
+/* per TS configuration */
+DEFUN_ATTR(cfg_ts,
+ cfg_ts_cmd,
+ "timeslot <0-7>",
+ "Select a Timeslot to configure\n"
+ "Timeslot number\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ int ts_nr = atoi(argv[0]);
+ struct gsm_bts_trx *trx = vty->index;
+ struct gsm_bts_trx_ts *ts;
+
+ if (ts_nr >= TRX_NR_TS) {
+ vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s",
+ TRX_NR_TS, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ ts = &trx->ts[ts_nr];
+
+ vty->index = ts;
+ vty->node = TS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_ts_pchan,
+ cfg_ts_pchan_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "phys_chan_config PCHAN", /* dynamically generated! */
+ "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n")
+{
+ struct gsm_bts_trx_ts *ts = vty->index;
+ int pchanc;
+
+ pchanc = gsm_pchan_parse(argv[0]);
+ if (pchanc < 0)
+ return CMD_WARNING;
+
+ ts->pchan_from_config = pchanc;
+
+ return CMD_SUCCESS;
+}
+
+/* used for backwards compatibility with old config files that still
+ * have uppercase pchan type names. Also match older names for existing types. */
+DEFUN_HIDDEN(cfg_ts_pchan_compat,
+ cfg_ts_pchan_compat_cmd,
+ "phys_chan_config PCHAN",
+ "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n")
+{
+ struct gsm_bts_trx_ts *ts = vty->index;
+ int pchanc;
+
+ pchanc = gsm_pchan_parse(argv[0]);
+ if (pchanc < 0) {
+ if (strcasecmp(argv[0], "tch/f_tch/h_pdch") == 0) {
+ pchanc = GSM_PCHAN_OSMO_DYN;
+ } else {
+ vty_out(vty, "Unknown physical channel name '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_ERR_NO_MATCH;
+ }
+ }
+
+ ts->pchan_from_config = pchanc;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_ts_tsc,
+ cfg_ts_tsc_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "training_sequence_code <0-7>",
+ "Training Sequence Code of the Timeslot\n" "TSC\n")
+{
+ struct gsm_bts_trx_ts *ts = vty->index;
+ const struct gsm_bts *bts = ts->trx->bts;
+
+ if (bts->features_known && !osmo_bts_has_feature(&bts->features, BTS_FEAT_MULTI_TSC)) {
+ vty_out(vty, "%% This BTS does not support a TSC != BCC, "
+ "falling back to BCC%s", VTY_NEWLINE);
+ ts->tsc = -1;
+ return CMD_WARNING;
+ }
+
+ ts->tsc = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+#define HOPPING_STR "Configure frequency hopping\n"
+
+DEFUN_USRATTR(cfg_ts_hopping,
+ cfg_ts_hopping_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "hopping enabled (0|1)",
+ HOPPING_STR "Enable or disable frequency hopping\n"
+ "Disable frequency hopping\n" "Enable frequency hopping\n")
+{
+ struct gsm_bts_trx_ts *ts = vty->index;
+ const struct gsm_bts *bts = ts->trx->bts;
+ int enabled = atoi(argv[0]);
+
+ if (enabled && bts->features_known && !osmo_bts_has_feature(&bts->features, BTS_FEAT_HOPPING)) {
+ vty_out(vty, "%% BTS does not support freq. hopping%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ ts->hopping.enabled = enabled;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_ts_hsn,
+ cfg_ts_hsn_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "hopping sequence-number <0-63>",
+ HOPPING_STR
+ "Which hopping sequence to use for this channel\n"
+ "Hopping Sequence Number (HSN)\n")
+{
+ struct gsm_bts_trx_ts *ts = vty->index;
+
+ ts->hopping.hsn = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_ts_maio,
+ cfg_ts_maio_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "hopping maio <0-63>",
+ HOPPING_STR
+ "Which hopping MAIO to use for this channel\n"
+ "Mobile Allocation Index Offset (MAIO)\n")
+{
+ struct gsm_bts_trx_ts *ts = vty->index;
+
+ ts->hopping.maio = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_ts_arfcn_add,
+ cfg_ts_arfcn_add_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "hopping arfcn add <0-1023>",
+ HOPPING_STR "Configure hopping ARFCN list\n"
+ "Add an entry to the hopping ARFCN list\n" "ARFCN\n")
+{
+ enum gsm_band unused;
+ struct gsm_bts_trx_ts *ts = vty->index;
+ int arfcn = atoi(argv[0]);
+
+ if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bitvec_get_bit_pos(&ts->hopping.arfcns, arfcn) == ONE) {
+ vty_out(vty, "%% ARFCN %" PRIu16 " is already set%s", arfcn, VTY_NEWLINE);
+ return CMD_SUCCESS;
+ }
+
+ bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1);
+
+ /* Update Cell Allocation (list of all the frequencies allocated to a cell) */
+ if (generate_cell_chan_alloc(ts->trx->bts) != 0) {
+ vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE);
+ bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, ZERO); /* roll-back */
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_ts_arfcn_del,
+ cfg_ts_arfcn_del_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "hopping arfcn del <0-1023>",
+ HOPPING_STR "Configure hopping ARFCN list\n"
+ "Delete an entry to the hopping ARFCN list\n" "ARFCN\n")
+{
+ enum gsm_band unused;
+ struct gsm_bts_trx_ts *ts = vty->index;
+ int arfcn = atoi(argv[0]);
+
+ if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bitvec_get_bit_pos(&ts->hopping.arfcns, arfcn) != ONE) {
+ vty_out(vty, "%% ARFCN %" PRIu16 " is not set%s", arfcn, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0);
+
+ /* Update Cell Allocation (list of all the frequencies allocated to a cell) */
+ if (generate_cell_chan_alloc(ts->trx->bts) != 0) {
+ vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE);
+ /* It's unlikely to happen on removal, so we don't roll-back */
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_ts_arfcn_del_all,
+ cfg_ts_arfcn_del_all_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "hopping arfcn del-all",
+ HOPPING_STR "Configure hopping ARFCN list\n"
+ "Delete all previously configured entries\n")
+{
+ struct gsm_bts_trx_ts *ts = vty->index;
+
+ bitvec_zero(&ts->hopping.arfcns);
+
+ /* Update Cell Allocation (list of all the frequencies allocated to a cell) */
+ if (generate_cell_chan_alloc(ts->trx->bts) != 0) {
+ vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE);
+ /* It's unlikely to happen on removal, so we don't roll-back */
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* NOTE: This will have an effect on newly created voice lchans since the E1
+ * voice channels are handled by osmo-mgw and the information put in e1_link
+ * here is only used to generate the MGCP messages for the mgw. */
+DEFUN_ATTR(cfg_ts_e1_subslot,
+ cfg_ts_e1_subslot_cmd,
+ "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
+ "E1/T1 channel connected to this on-air timeslot\n"
+ "E1/T1 channel connected to this on-air timeslot\n"
+ "E1/T1 line connected to this on-air timeslot\n"
+ "E1/T1 timeslot connected to this on-air timeslot\n"
+ "E1/T1 timeslot connected to this on-air timeslot\n"
+ "E1/T1 sub-slot connected to this on-air timeslot\n"
+ "E1/T1 sub-slot 0 connected to this on-air timeslot\n"
+ "E1/T1 sub-slot 1 connected to this on-air timeslot\n"
+ "E1/T1 sub-slot 2 connected to this on-air timeslot\n"
+ "E1/T1 sub-slot 3 connected to this on-air timeslot\n"
+ "Full E1/T1 timeslot connected to this on-air timeslot\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts_trx_ts *ts = vty->index;
+
+ parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]);
+
+ return CMD_SUCCESS;
+}
+
+/* call vty_out() to print a string like " as TCH/H" for dynamic timeslots.
+ * Don't do anything if the ts is not dynamic. */
+static void vty_out_dyn_ts_status(struct vty *vty, struct gsm_bts_trx_ts *ts)
+{
+ enum gsm_phys_chan_config target;
+ if (ts_is_pchan_switching(ts, &target)) {
+ vty_out(vty, " switching %s -> %s", gsm_pchan_name(ts->pchan_is),
+ gsm_pchan_name(target));
+ } else if (ts->pchan_is != ts->pchan_on_init) {
+ vty_out(vty, " as %s", gsm_pchan_name(ts->pchan_is));
+ }
+}
+
+static void vty_out_dyn_ts_details(struct vty *vty, struct gsm_bts_trx_ts *ts)
+{
+ /* show dyn TS details, if applicable */
+ switch (ts->pchan_on_init) {
+ case GSM_PCHAN_OSMO_DYN:
+ vty_out(vty, " Osmocom Dyn TS:");
+ vty_out_dyn_ts_status(vty, ts);
+ vty_out(vty, VTY_NEWLINE);
+ break;
+ case GSM_PCHAN_TCH_F_PDCH:
+ vty_out(vty, " IPACC Dyn PDCH TS:");
+ vty_out_dyn_ts_status(vty, ts);
+ vty_out(vty, VTY_NEWLINE);
+ break;
+ default:
+ /* no dyn ts */
+ break;
+ }
+}
+
+static void meas_rep_dump_uni_vty(struct vty *vty,
+ struct gsm_meas_rep_unidir *mru,
+ const char *prefix,
+ const char *dir)
+{
+ vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ",
+ prefix, dir, rxlev2dbm(mru->full.rx_lev),
+ dir, rxlev2dbm(mru->sub.rx_lev));
+ vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s",
+ dir, mru->full.rx_qual, dir, mru->sub.rx_qual,
+ VTY_NEWLINE);
+}
+
+static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr,
+ const char *prefix)
+{
+ vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE);
+ vty_out(vty, "%s Flags: %s%s%s%s%s", prefix,
+ mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "",
+ mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "",
+ mr->flags & MEAS_REP_F_FPC ? "FPC " : "",
+ mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ",
+ VTY_NEWLINE);
+ if (mr->flags & MEAS_REP_F_MS_TO)
+ vty_out(vty, "%s MS Timing Offset: %d%s", prefix, mr->ms_timing_offset, VTY_NEWLINE);
+ if (mr->flags & MEAS_REP_F_MS_L1)
+ vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s",
+ prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE);
+ if (mr->flags & MEAS_REP_F_DL_VALID)
+ meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl");
+ meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul");
+}
+
+void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan)
+{
+ int idx;
+
+ vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s",
+ lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
+ lchan->nr, gsm_chan_t_name(lchan->type), VTY_NEWLINE);
+
+ if (lchan->activate.concluded) {
+ vty_out(vty, " Active for: %s seconds%s",
+ osmo_int_to_float_str_c(OTC_SELECT, gsm_lchan_active_duration_ms(lchan), 3),
+ VTY_NEWLINE);
+ }
+
+ vty_out_dyn_ts_details(vty, lchan->ts);
+ vty_out(vty, " Connection: %u, State: %s%s%s%s",
+ lchan->conn ? 1: 0, lchan_state_name(lchan),
+ lchan->fi && lchan->fi->state == LCHAN_ST_BORKEN ? " Error reason: " : "",
+ lchan->fi && lchan->fi->state == LCHAN_ST_BORKEN ? lchan->last_error : "",
+ VTY_NEWLINE);
+ vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s",
+ lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red
+ - lchan->bs_power_db,
+ ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
+ VTY_NEWLINE);
+
+ vty_out(vty, " Interference Level: ");
+ if (lchan->interf_dbm == INTERF_DBM_UNKNOWN)
+ vty_out(vty, "unknown");
+ else
+ vty_out(vty, "%d dBm (%u)", lchan->interf_dbm, lchan->interf_band);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, " Channel Mode / Codec: %s%s",
+ gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode),
+ VTY_NEWLINE);
+ if (!lchan_state_is(lchan, LCHAN_ST_UNUSED))
+ vty_out(vty, " Training Sequence: Set %d Code %u%s", (lchan->tsc_set > 0 ? lchan->tsc_set : 1), lchan->tsc, VTY_NEWLINE);
+ if (lchan->vamos.enabled)
+ vty_out(vty, " VAMOS: enabled%s", VTY_NEWLINE);
+ if (lchan->conn && lchan->conn->bsub) {
+ vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
+ bsc_subscr_dump_vty(vty, lchan->conn->bsub);
+ } else {
+ vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
+ }
+ if (is_ipaccess_bts(lchan->ts->trx->bts)) {
+ struct in_addr ia;
+ if (lchan->abis_ip.bound_ip) {
+ ia.s_addr = htonl(lchan->abis_ip.bound_ip);
+ vty_out(vty, " Bound IP: %s Port %u CONN_ID=%u",
+ inet_ntoa(ia), lchan->abis_ip.bound_port,
+ lchan->abis_ip.conn_id);
+ if (lchan->abis_ip.osmux.use)
+ vty_out(vty, " Osmux_CID=%u%s", lchan->abis_ip.osmux.local_cid, VTY_NEWLINE);
+ else
+ vty_out(vty, " RTP_TYPE2=%u%s", lchan->abis_ip.rtp_payload2, VTY_NEWLINE);
+ }
+ if (lchan->abis_ip.connect_ip) {
+ ia.s_addr = htonl(lchan->abis_ip.connect_ip);
+ vty_out(vty, " Conn. IP: %s Port %u SPEECH_MODE=0x%02x",
+ inet_ntoa(ia), lchan->abis_ip.connect_port,
+ lchan->abis_ip.speech_mode);
+ if (lchan->abis_ip.osmux.use) {
+ if (lchan->abis_ip.osmux.remote_cid_present)
+ vty_out(vty, " Osmux_CID=%u%s", lchan->abis_ip.osmux.remote_cid, VTY_NEWLINE);
+ else
+ vty_out(vty, " Osmux_CID=?%s", VTY_NEWLINE);
+ } else {
+ vty_out(vty, " RTP_TYPE=%u%s", lchan->abis_ip.rtp_payload, VTY_NEWLINE);
+ }
+ }
+ }
+
+ /* we want to report the last measurement report */
+ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
+ lchan->meas_rep_idx, 1);
+ meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " ");
+}
+
+void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan)
+{
+ struct gsm_meas_rep *mr;
+ int idx;
+
+ /* we want to report the last measurement report */
+ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
+ lchan->meas_rep_idx, 1);
+ mr = &lchan->meas_rep[idx];
+
+ vty_out(vty, "BTS %u, TRX %u, Timeslot %u %s",
+ lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
+ gsm_pchan_name(lchan->ts->pchan_on_init));
+ vty_out_dyn_ts_status(vty, lchan->ts);
+ vty_out(vty, ", Lchan %u", lchan->nr);
+
+ if (lchan_state_is(lchan, LCHAN_ST_UNUSED)) {
+ vty_out(vty, ", Type %s, State %s - Interference Level: ",
+ gsm_pchan_name(lchan->ts->pchan_is),
+ lchan_state_name(lchan));
+ if (lchan->interf_dbm == INTERF_DBM_UNKNOWN)
+ vty_out(vty, "unknown");
+ else
+ vty_out(vty, "%d dBm (%u)", lchan->interf_dbm, lchan->interf_band);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ return;
+ }
+
+ vty_out(vty, ", Type %s%s TSC-s%dc%u, State %s - L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s",
+ gsm_chan_t_name(lchan->type),
+ lchan->vamos.enabled ? " (VAMOS)" : "",
+ lchan->tsc_set > 0 ? lchan->tsc_set : 1,
+ lchan->tsc,
+ lchan_state_name(lchan),
+ mr->ms_l1.pwr,
+ rxlev2dbm(mr->dl.full.rx_lev),
+ rxlev2dbm(mr->ul.full.rx_lev),
+ VTY_NEWLINE);
+}
+
+void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts)
+{
+ vty_out(vty, "BTS %u, TRX %u, Timeslot %u, phys cfg %s (active %s)",
+ ts->trx->bts->nr, ts->trx->nr, ts->nr,
+ gsm_pchan_name(ts->pchan_on_init),
+ gsm_pchan_name(ts->pchan_is));
+ if (ts->pchan_is != ts->pchan_on_init)
+ vty_out(vty, " (%s mode)", gsm_pchan_name(ts->pchan_is));
+ vty_out(vty, ", TSC %u%s NM State: ", gsm_ts_tsc(ts), VTY_NEWLINE);
+ vty_out_dyn_ts_details(vty, ts);
+ net_dump_nmstate(vty, &ts->mo.nm_state);
+ if (!is_ipaccess_bts(ts->trx->bts))
+ vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s",
+ ts->e1_link.e1_nr, ts->e1_link.e1_ts,
+ ts->e1_link.e1_ts_ss, VTY_NEWLINE);
+}
+
+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)" */
+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);
+}
+
+void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx, bool print_rsl, bool show_connected)
+{
+ if (show_connected && !trx->rsl_link_primary)
+ return;
+
+ if (!show_connected && trx->rsl_link_primary)
+ return;
+
+ vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s",
+ trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE);
+ vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, "
+ "resulting BS power: %d dBm%s",
+ trx->nominal_power, trx->max_power_red,
+ trx->nominal_power - trx->max_power_red, VTY_NEWLINE);
+ vty_out(vty, " Radio Carrier NM State: ");
+ net_dump_nmstate(vty, &trx->mo.nm_state);
+ if (print_rsl)
+ vty_out(vty, " RSL State: %s%s", trx->rsl_link_primary? "connected" : "disconnected", VTY_NEWLINE);
+
+ vty_out(vty, " %sBaseband Transceiver NM State: ", is_ericsson_bts(trx->bts) ? "[Virtual] " : "");
+ net_dump_nmstate(vty, &trx->bb_transc.mo.nm_state);
+
+ if (is_ipaccess_bts(trx->bts)) {
+ vty_out(vty, " ip.access stream ID: 0x%02x ", trx->rsl_tei_primary);
+ e1isl_dump_vty_tcp(vty, trx->rsl_link_primary);
+ } else {
+ vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
+ e1isl_dump_vty(vty, trx->rsl_link_primary);
+ }
+
+ const struct load_counter *ll = &trx->lchan_load;
+ vty_out(vty, " Channel load: %u%%%s",
+ ll->total ? ll->used * 100 / ll->total : 0,
+ VTY_NEWLINE);
+}
+
+void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link,
+ const char *prefix)
+{
+ if (!e1_link->e1_ts)
+ return;
+
+ if (e1_link->e1_ts_ss == E1_SUBSLOT_FULL)
+ vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s",
+ prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE);
+ else
+ vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s",
+ prefix, e1_link->e1_nr, e1_link->e1_ts,
+ e1_link->e1_ts_ss, VTY_NEWLINE);
+}
+
+
+static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts)
+{
+ vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE);
+ if (ts->tsc != -1)
+ vty_out(vty, " training_sequence_code %u%s", ts->tsc, VTY_NEWLINE);
+ if (ts->pchan_from_config != GSM_PCHAN_NONE)
+ vty_out(vty, " phys_chan_config %s%s",
+ gsm_pchan_name(ts->pchan_from_config), VTY_NEWLINE);
+ vty_out(vty, " hopping enabled %u%s",
+ ts->hopping.enabled, VTY_NEWLINE);
+ if (ts->hopping.enabled) {
+ unsigned int i;
+ vty_out(vty, " hopping sequence-number %u%s",
+ ts->hopping.hsn, VTY_NEWLINE);
+ vty_out(vty, " hopping maio %u%s",
+ ts->hopping.maio, VTY_NEWLINE);
+ for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) {
+ if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i))
+ continue;
+ vty_out(vty, " hopping arfcn add %u%s",
+ i, VTY_NEWLINE);
+ }
+ }
+ config_write_e1_link(vty, &ts->e1_link, " ");
+
+ if (ts->trx->bts->model->config_write_ts)
+ ts->trx->bts->model->config_write_ts(vty, ts);
+}
+
+void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx)
+{
+ int i;
+
+ vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
+ vty_out(vty, " rf_locked %u%s",
+ trx->mo.force_rf_lock ? 1 : 0,
+ VTY_NEWLINE);
+ vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE);
+ vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE);
+ vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE);
+ config_write_e1_link(vty, &trx->rsl_e1_link, " rsl ");
+ vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei_primary, VTY_NEWLINE);
+
+ if (trx->bts->model->config_write_trx)
+ trx->bts->model->config_write_trx(vty, trx);
+
+ for (i = 0; i < TRX_NR_TS; i++)
+ config_write_ts_single(vty, &trx->ts[i]);
+}
+
+int bts_trx_vty_init(void)
+{
+ cfg_ts_pchan_cmd.string =
+ vty_cmd_string_from_valstr(tall_bsc_ctx,
+ gsm_pchant_names,
+ "phys_chan_config (", "|", ")",
+ VTY_DO_LOWER);
+ cfg_ts_pchan_cmd.doc =
+ vty_cmd_string_from_valstr(tall_bsc_ctx,
+ gsm_pchant_descs,
+ "Physical Channel Combination\n",
+ "\n", "", 0);
+
+ install_element(BTS_NODE, &cfg_trx_cmd);
+ install_node(&trx_node, dummy_config_write);
+ install_element(TRX_NODE, &cfg_trx_arfcn_cmd);
+ install_element(TRX_NODE, &cfg_description_cmd);
+ install_element(TRX_NODE, &cfg_no_description_cmd);
+ install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
+ install_element(TRX_NODE, &cfg_trx_max_power_red_cmd);
+ install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd);
+ install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd);
+ install_element(TRX_NODE, &cfg_trx_rf_locked_cmd);
+
+ install_element(TRX_NODE, &cfg_ts_cmd);
+ install_node(&ts_node, dummy_config_write);
+ install_element(TS_NODE, &cfg_ts_pchan_cmd);
+ install_element(TS_NODE, &cfg_ts_pchan_compat_cmd);
+ install_element(TS_NODE, &cfg_ts_tsc_cmd);
+ install_element(TS_NODE, &cfg_ts_hopping_cmd);
+ install_element(TS_NODE, &cfg_ts_hsn_cmd);
+ install_element(TS_NODE, &cfg_ts_maio_cmd);
+ install_element(TS_NODE, &cfg_ts_arfcn_add_cmd);
+ install_element(TS_NODE, &cfg_ts_arfcn_del_cmd);
+ install_element(TS_NODE, &cfg_ts_arfcn_del_all_cmd);
+ install_element(TS_NODE, &cfg_ts_e1_subslot_cmd);
+
+ return 0;
+}
diff --git a/src/osmo-bsc/bts_vty.c b/src/osmo-bsc/bts_vty.c
new file mode 100644
index 000000000..df15cc62e
--- /dev/null
+++ b/src/osmo-bsc/bts_vty.c
@@ -0,0 +1,4914 @@
+/* OsmoBSC interface to quagga VTY, BTS node */
+/* (C) 2009-2017 by Harald Welte <laforge@gnumonks.org>
+ * 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 <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/misc.h>
+#include <osmocom/vty/tdef_vty.h>
+#include <osmocom/ctrl/control_if.h>
+#include <osmocom/gprs/gprs_ns.h>
+
+#include <osmocom/bsc/vty.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/meas_rep.h>
+#include <osmocom/bsc/system_information.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/paging.h>
+#include <osmocom/bsc/pcu_if.h>
+#include <osmocom/bsc/handover_vty.h>
+#include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/smscb.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
+
+#include <inttypes.h>
+
+#include "../../bscconfig.h"
+
+#define X(x) (1 << x)
+
+/* 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 }
+};
+
+static struct cmd_node bts_node = {
+ BTS_NODE,
+ "%s(config-net-bts)# ",
+ 1,
+};
+
+static struct cmd_node power_ctrl_node = {
+ POWER_CTRL_NODE,
+ "%s(config-power-ctrl)# ",
+ 1,
+};
+
+/* per-BTS configuration */
+DEFUN_ATTR(cfg_bts,
+ cfg_bts_cmd,
+ "bts <0-255>",
+ "Select a BTS to configure\n"
+ BTS_NR_STR,
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+ int bts_nr = atoi(argv[0]);
+ struct gsm_bts *bts;
+
+ if (bts_nr > gsmnet->num_bts) {
+ vty_out(vty, "%% BTS number %d not valid (next BTS number must be %u)%s",
+ bts_nr, gsmnet->num_bts, VTY_NEWLINE);
+ return CMD_WARNING;
+ } else if (bts_nr == gsmnet->num_bts) {
+ /* allocate a new one */
+ bts = bsc_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN,
+ HARDCODED_BSIC);
+ } else
+ bts = gsm_bts_num(gsmnet, bts_nr);
+
+ if (!bts) {
+ vty_out(vty, "%% Unable to allocate BTS %u%s",
+ gsmnet->num_bts, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty->index = bts;
+ vty->index_sub = &bts->description;
+ vty->node = BTS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_type,
+ cfg_bts_type_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "type TYPE", /* dynamically created */
+ "Set the BTS type\n" "Type\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int rc;
+
+ rc = gsm_set_bts_type(bts, str2btstype(argv[0]));
+ if (rc == -EBUSY)
+ vty_out(vty, "%% Changing the type of an existing BTS is not supported.%s",
+ VTY_NEWLINE);
+ if (rc < 0)
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_bts_type_sysmobts,
+ cfg_bts_type_sysmobts_cmd,
+ "type sysmobts",
+ "Set the BTS type\n"
+ "Deprecated alias for 'osmo-bts'\n")
+{
+ const char *args[] = { "osmo-bts" };
+
+ vty_out(vty, "%% BTS type 'sysmobts' is deprecated, "
+ "use 'type osmo-bts' instead.%s", VTY_NEWLINE);
+
+ return cfg_bts_type(self, vty, 1, args);
+}
+
+DEFUN_USRATTR(cfg_bts_band,
+ cfg_bts_band_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "band BAND",
+ "Set the frequency band of this BTS\n" "Frequency band\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int band = gsm_band_parse(argv[0]);
+
+ if (band < 0) {
+ vty_out(vty, "%% BAND %d is not a valid GSM band%s",
+ band, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->band = band;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_dtxu,
+ cfg_bts_dtxu_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "dtx uplink [force]",
+ "Configure discontinuous transmission\n"
+ "Enable Uplink DTX for this BTS\n"
+ "MS 'shall' use DTXu instead of 'may' use (might not be supported by "
+ "older phones).\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->dtxu = (argc > 0) ? GSM48_DTX_SHALL_BE_USED : GSM48_DTX_MAY_BE_USED;
+ if (!is_ipaccess_bts(bts))
+ vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration "
+ "neither supported nor tested!%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_no_dtxu,
+ cfg_bts_no_dtxu_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "no dtx uplink",
+ NO_STR "Configure discontinuous transmission\n"
+ "Disable Uplink DTX for this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_dtxd,
+ cfg_bts_dtxd_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "dtx downlink",
+ "Configure discontinuous transmission\n"
+ "Enable Downlink DTX for this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->dtxd = true;
+ if (!is_ipaccess_bts(bts))
+ vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration "
+ "neither supported nor tested!%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_no_dtxd,
+ cfg_bts_no_dtxd_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "no dtx downlink",
+ NO_STR "Configure discontinuous transmission\n"
+ "Disable Downlink DTX for this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->dtxd = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_ci,
+ cfg_bts_ci_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "cell_identity <0-65535>",
+ "Set the Cell identity of this BTS\n" "Cell Identity\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int ci = atoi(argv[0]);
+
+ if (ci < 0 || ci > 0xffff) {
+ vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s",
+ ci, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts->cell_identity = ci;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_lac,
+ cfg_bts_lac_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "location_area_code (<0-65535>|<0x0000-0xffff>)",
+ "Set the Location Area Code (LAC) of this BTS\n"
+ "LAC in decimal format\n"
+ "LAC in hexadecimal format\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int lac;
+ if (osmo_str_to_int(&lac, argv[0], 0, 0, 0xffff) < 0)
+ return CMD_WARNING;
+
+ if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) {
+ vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s",
+ lac, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->location_area_code = lac;
+
+ return CMD_SUCCESS;
+}
+
+
+/* compatibility wrapper for old config files */
+DEFUN_HIDDEN(cfg_bts_tsc,
+ cfg_bts_tsc_cmd,
+ "training_sequence_code <0-7>",
+ "Set the Training Sequence Code (TSC) of this BTS\n" "TSC\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_bsic,
+ cfg_bts_bsic_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "base_station_id_code <0-63>",
+ "Set the Base Station Identity Code (BSIC) of this BTS\n"
+ "BSIC of this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int bsic = atoi(argv[0]);
+
+ if (bsic < 0 || bsic > 0x3f) {
+ vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s",
+ bsic, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts->bsic = bsic;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_unit_id,
+ cfg_bts_unit_id_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "ipa unit-id <0-65534> <0-255>",
+ "Abis/IP specific options\n"
+ "Set the IPA BTS Unit ID\n"
+ "Unit ID (Site)\n"
+ "Unit ID (BTS)\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int site_id = atoi(argv[0]);
+ int bts_id = atoi(argv[1]);
+
+ if (!is_ipaccess_bts(bts)) {
+ vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->ip_access.site_id = site_id;
+ bts->ip_access.bts_id = bts_id;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_bts_unit_id,
+ cfg_bts_deprecated_unit_id_cmd,
+ "ip.access unit_id <0-65534> <0-255>",
+ "Abis/IP specific options\n"
+ "Set the IPA BTS Unit ID\n"
+ "Unit ID (Site)\n"
+ "Unit ID (BTS)\n");
+
+DEFUN_USRATTR(cfg_bts_rsl_ip,
+ cfg_bts_rsl_ip_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "ipa rsl-ip A.B.C.D",
+ "Abis/IP specific options\n"
+ "Set the IPA RSL IP Address of the BSC\n"
+ "Destination IP address for RSL connection\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct in_addr ia;
+
+ if (!is_ipaccess_bts(bts)) {
+ vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ inet_aton(argv[0], &ia);
+ bts->ip_access.rsl_ip = ntohl(ia.s_addr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_bts_rsl_ip,
+ cfg_bts_deprecated_rsl_ip_cmd,
+ "ip.access rsl-ip A.B.C.D",
+ "Abis/IP specific options\n"
+ "Set the IPA RSL IP Address of the BSC\n"
+ "Destination IP address for RSL connection\n");
+
+#define NOKIA_STR "Nokia *Site related commands\n"
+
+DEFUN_USRATTR(cfg_bts_nokia_site_skip_reset,
+ cfg_bts_nokia_site_skip_reset_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "nokia_site skip-reset (0|1)",
+ NOKIA_STR
+ "Skip the reset step during bootstrap process of this BTS\n"
+ "Do NOT skip the reset\n" "Skip the reset\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->type != GSM_BTS_TYPE_NOKIA_SITE) {
+ vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->nokia.skip_reset = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_nokia_site_no_loc_rel_cnf,
+ cfg_bts_nokia_site_no_loc_rel_cnf_cmd,
+ "nokia_site no-local-rel-conf (0|1)",
+ NOKIA_STR
+ "Do not wait for RELease CONFirm message when releasing channel locally\n"
+ "Wait for RELease CONFirm\n" "Do not wait for RELease CONFirm\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (!is_nokia_bts(bts)) {
+ vty_out(vty, "%% BTS is not of Nokia *Site type%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->nokia.no_loc_rel_cnf = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_nokia_site_bts_reset_timer_cnf,
+ cfg_bts_nokia_site_bts_reset_timer_cnf_cmd,
+ "nokia_site bts-reset-timer <15-100>",
+ NOKIA_STR
+ "The amount of time between BTS_RESET is sent "
+ "and the BTS is being bootstrapped\n"
+ "Timer value (in seconds, default 15)\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (!is_nokia_bts(bts)) {
+ vty_out(vty, "%% BTS is not of Nokia *Site type%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->nokia.bts_reset_timer_cnf = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+#define OML_STR "Organization & Maintenance Link\n"
+#define IPA_STR "A-bis/IP Specific Options\n"
+
+DEFUN_USRATTR(cfg_bts_stream_id,
+ cfg_bts_stream_id_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "oml ipa stream-id <0-255> line E1_LINE",
+ OML_STR IPA_STR
+ "Set the ipa Stream ID of the OML link of this BTS\n" "Stream Identifier\n"
+ "Virtual E1 Line Number\n" "Virtual E1 Line Number\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int stream_id = atoi(argv[0]), linenr = atoi(argv[1]);
+
+ if (!is_ipaccess_bts(bts)) {
+ vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->oml_tei = stream_id;
+ /* This is used by e1inp_bind_ops callback for each BTS model. */
+ bts->oml_e1_link.e1_nr = linenr;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_bts_stream_id,
+ cfg_bts_deprecated_stream_id_cmd,
+ "oml ip.access stream_id <0-255> line E1_LINE",
+ OML_STR IPA_STR
+ "Set the ip.access Stream ID of the OML link of this BTS\n"
+ "Stream Identifier\n" "Virtual E1 Line Number\n" "Virtual E1 Line Number\n");
+
+#define OML_E1_STR OML_STR "OML E1/T1 Configuration\n"
+
+/* NOTE: This requires a full restart as bsc_network_configure() is executed
+ * only once on startup from osmo_bsc_main.c */
+DEFUN(cfg_bts_oml_e1,
+ cfg_bts_oml_e1_cmd,
+ "oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
+ OML_E1_STR
+ "E1/T1 line number to be used for OML\n"
+ "E1/T1 line number to be used for OML\n"
+ "E1/T1 timeslot to be used for OML\n"
+ "E1/T1 timeslot to be used for OML\n"
+ "E1/T1 sub-slot to be used for OML\n"
+ "Use E1/T1 sub-slot 0\n"
+ "Use E1/T1 sub-slot 1\n"
+ "Use E1/T1 sub-slot 2\n"
+ "Use E1/T1 sub-slot 3\n"
+ "Use full E1 slot 3\n"
+ )
+{
+ struct gsm_bts *bts = vty->index;
+
+ parse_e1_link(&bts->oml_e1_link, argv[0], argv[1], argv[2]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_oml_e1_tei,
+ cfg_bts_oml_e1_tei_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "oml e1 tei <0-63>",
+ OML_E1_STR
+ "Set the TEI to be used for OML\n"
+ "TEI Number\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->oml_tei = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+#define CHAN_ALLOC_CMD "channel allocator"
+#define CHAN_ALLOC_DESC \
+ "Channel Allocator\n" \
+ "Channel Allocator\n"
+
+#define CHAN_ALLOC_ASC_DSC "(ascending|descending)"
+#define CHAN_ALLOC_ASC_DSC_DESC \
+ "Allocate Timeslots and Transceivers in ascending order\n" \
+ "Allocate Timeslots and Transceivers in descending order\n"
+
+DEFUN_ATTR(cfg_bts_challoc_mode_all,
+ cfg_bts_challoc_mode_all_cmd,
+ CHAN_ALLOC_CMD " " CHAN_ALLOC_ASC_DSC,
+ CHAN_ALLOC_DESC CHAN_ALLOC_ASC_DSC_DESC,
+ CMD_ATTR_IMMEDIATE | CMD_ATTR_DEPRECATED)
+{
+ bool reverse = !strcmp(argv[0], "descending");
+ struct gsm_bts *bts = vty->index;
+
+ bts->chan_alloc_chan_req_reverse = reverse;
+ bts->chan_alloc_assignment_reverse = reverse;
+ bts->chan_alloc_handover_reverse = reverse;
+ bts->chan_alloc_assignment_dynamic = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_challoc_mode,
+ cfg_bts_challoc_mode_cmd,
+ CHAN_ALLOC_CMD
+ " mode (set-all|chan-req|assignment|handover) "
+ CHAN_ALLOC_ASC_DSC,
+ CHAN_ALLOC_DESC
+ "Channel allocation mode\n"
+ "Set a single mode for all variants\n"
+ "Channel allocation for CHANNEL REQUEST (RACH)\n"
+ "Channel allocation for assignment\n"
+ "Channel allocation for handover\n"
+ CHAN_ALLOC_ASC_DSC_DESC,
+ CMD_ATTR_IMMEDIATE)
+{
+ bool reverse = !strcmp(argv[1], "descending");
+ bool set_all = !strcmp(argv[0], "set-all");
+ struct gsm_bts *bts = vty->index;
+
+ if (set_all || !strcmp(argv[0], "chan-req"))
+ bts->chan_alloc_chan_req_reverse = reverse;
+ if (set_all || !strcmp(argv[0], "assignment")) {
+ bts->chan_alloc_assignment_reverse = reverse;
+ bts->chan_alloc_assignment_dynamic = false;
+ }
+ if (set_all || !strcmp(argv[0], "handover"))
+ bts->chan_alloc_handover_reverse = reverse;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_challoc_mode_ass_dynamic,
+ cfg_bts_challoc_mode_ass_dynamic_cmd,
+ CHAN_ALLOC_CMD " mode assignment dynamic",
+ CHAN_ALLOC_DESC
+ "Channel allocation mode\n"
+ "Channel allocation for assignment\n"
+ "Dynamic lchan selection based on configured parameters\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->chan_alloc_assignment_dynamic = true;
+
+ return CMD_SUCCESS;
+}
+
+#define CHAN_ALLOC_DYN_PARAM_CMD \
+ CHAN_ALLOC_CMD " dynamic-param"
+#define CHAN_ALLOC_DYN_PARAM_DESC \
+ CHAN_ALLOC_DESC \
+ "Parameters for dynamic channel allocation mode\n"
+
+DEFUN_ATTR(cfg_bts_challoc_dynamic_param_sort_by_trx_power,
+ cfg_bts_challoc_dynamic_param_sort_by_trx_power_cmd,
+ CHAN_ALLOC_DYN_PARAM_CMD " sort-by-trx-power (0|1)",
+ CHAN_ALLOC_DYN_PARAM_DESC
+ "Whether to sort TRX instances by their respective power levels\n"
+ "Do not sort, use the same order as in the configuration file\n"
+ "Sort TRX instances by their power levels in descending order\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->chan_alloc_dyn_params.sort_by_trx_power = (argv[0][0] == '1');
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_challoc_dynamic_param_ul_rxlev,
+ cfg_bts_challoc_dynamic_param_ul_rxlev_cmd,
+ CHAN_ALLOC_DYN_PARAM_CMD " ul-rxlev thresh <0-63> avg-num <1-10>",
+ CHAN_ALLOC_DYN_PARAM_DESC
+ "Uplink RxLev\n"
+ "Uplink RxLev threshold\n"
+ "Uplink RxLev threshold\n"
+ "Minimum number of RxLev samples for averaging\n"
+ "Minimum number of RxLev samples for averaging\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->chan_alloc_dyn_params.ul_rxlev_thresh = atoi(argv[0]);
+ bts->chan_alloc_dyn_params.ul_rxlev_avg_num = atoi(argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_challoc_dynamic_param_c0_chan_load,
+ cfg_bts_challoc_dynamic_param_c0_chan_load_cmd,
+ CHAN_ALLOC_DYN_PARAM_CMD " c0-chan-load thresh <0-100>",
+ CHAN_ALLOC_DYN_PARAM_DESC
+ "C0 (BCCH carrier) channel load\n"
+ "Channel load threshold\n"
+ "Channel load threshold (in %)\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->chan_alloc_dyn_params.c0_chan_load_thresh = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_chan_alloc_interf,
+ cfg_bts_chan_alloc_interf_cmd,
+ CHAN_ALLOC_CMD " avoid-interference (0|1)",
+ CHAN_ALLOC_DESC
+ "Configure whether reported interference levels from RES IND are used in channel allocation\n"
+ "Ignore interference levels (default). Always assign lchans in a deterministic order.\n"
+ "In channel allocation, prefer lchans with less interference.\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (!strcmp(argv[0], "0"))
+ bts->chan_alloc_avoid_interf = false;
+ else
+ bts->chan_alloc_avoid_interf = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_chan_alloc_tch_signalling_policy,
+ cfg_bts_chan_alloc_tch_signalling_policy_cmd,
+ CHAN_ALLOC_CMD " tch-signalling-policy (never|emergency|voice|always)",
+ CHAN_ALLOC_DESC
+ "Configure when TCH/H or TCH/F channels can be used to serve signalling if SDCCHs are exhausted\n"
+ "Never allow TCH for signalling purposes\n"
+ "Only allow TCH for signalling purposes when establishing an emergency call\n"
+ "Allow TCH for signalling purposes when establishing any voice call\n"
+ "Always allow TCH for signalling purposes (default)\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (!strcmp(argv[0], "never"))
+ bts->chan_alloc_tch_signalling_policy = BTS_TCH_SIGNALLING_NEVER;
+ else if (!strcmp(argv[0], "emergency"))
+ bts->chan_alloc_tch_signalling_policy = BTS_TCH_SIGNALLING_EMERG;
+ else if (!strcmp(argv[0], "voice"))
+ bts->chan_alloc_tch_signalling_policy = BTS_TCH_SIGNALLING_VOICE;
+ else
+ bts->chan_alloc_tch_signalling_policy = BTS_TCH_SIGNALLING_ALWAYS;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_chan_alloc_allow_tch_for_signalling,
+ cfg_bts_chan_alloc_allow_tch_for_signalling_cmd,
+ CHAN_ALLOC_CMD " allow-tch-for-signalling (0|1)",
+ CHAN_ALLOC_DESC
+ "Configure whether TCH/H or TCH/F channels can be used to serve non-call-related signalling if SDCCHs are exhausted\n"
+ "Forbid use of TCH for non-call-related signalling purposes\n"
+ "Allow use of TCH for non-call-related signalling purposes (default)\n",
+ CMD_ATTR_IMMEDIATE|CMD_ATTR_DEPRECATED)
+{
+ struct gsm_bts *bts = vty->index;
+
+ vty_out(vty, "%% 'allow-tch-for-signalling' is deprecated, use 'tch-signalling-policy' instead.%s", VTY_NEWLINE);
+
+ if (!strcmp(argv[0], "0"))
+ bts->chan_alloc_tch_signalling_policy = BTS_TCH_SIGNALLING_VOICE;
+ else
+ bts->chan_alloc_tch_signalling_policy = BTS_TCH_SIGNALLING_ALWAYS;
+
+ return CMD_SUCCESS;
+}
+
+#define RACH_STR "Random Access Control Channel\n"
+
+DEFUN_USRATTR(cfg_bts_rach_tx_integer,
+ cfg_bts_rach_tx_integer_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "rach tx integer <0-15>",
+ RACH_STR
+ "Set the raw tx integer value in RACH Control parameters IE\n"
+ "Set the raw tx integer value in RACH Control parameters IE\n"
+ "Raw tx integer value in RACH Control parameters IE\n")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf;
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_rach_max_trans,
+ cfg_bts_rach_max_trans_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "rach max transmission (1|2|4|7)",
+ RACH_STR
+ "Set the maximum number of RACH burst transmissions\n"
+ "Set the maximum number of RACH burst transmissions\n"
+ "Maximum number of 1 RACH burst transmissions\n"
+ "Maximum number of 2 RACH burst transmissions\n"
+ "Maximum number of 4 RACH burst transmissions\n"
+ "Maximum number of 7 RACH burst transmissions\n")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_rach_max_delay,
+ cfg_bts_rach_max_delay_cmd,
+ "rach max-delay <1-127>",
+ RACH_STR
+ "Set the max Access Delay IE value to accept in CHANnel ReQuireD\n"
+ "Maximum Access Delay IE value to accept in CHANnel ReQuireD\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ bts->rach_max_delay = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+#define REP_ACCH_STR "FACCH/SACCH repetition\n"
+
+DEFUN_USRATTR(cfg_bts_rep_dl_facch,
+ cfg_bts_rep_dl_facch_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "repeat dl-facch (command|all)",
+ REP_ACCH_STR
+ "Enable DL-FACCH repetition for this BTS\n"
+ "command LAPDm frames only\n"
+ "all LAPDm frames\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% repeated ACCH not supported by BTS %u%s",
+ bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[0], "command")) {
+ bts->rep_acch_cap.dl_facch_cmd = true;
+ bts->rep_acch_cap.dl_facch_all = false;
+ } else {
+ bts->rep_acch_cap.dl_facch_cmd = true;
+ bts->rep_acch_cap.dl_facch_all = true;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_rep_no_dl_facch,
+ cfg_bts_rep_no_dl_facch_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no repeat dl-facch",
+ NO_STR REP_ACCH_STR
+ "Disable DL-FACCH repetition for this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->rep_acch_cap.dl_facch_cmd = false;
+ bts->rep_acch_cap.dl_facch_all = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_rep_ul_dl_sacch,
+ cfg_bts_rep_ul_dl_sacch_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "repeat (ul-sacch|dl-sacch)",
+ REP_ACCH_STR
+ "Enable UL-SACCH repetition for this BTS\n"
+ "Enable DL-SACCH repetition for this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% repeated ACCH not supported by BTS %u%s",
+ bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (strcmp(argv[0], "ul-sacch") == 0)
+ bts->rep_acch_cap.ul_sacch = true;
+ else
+ bts->rep_acch_cap.dl_sacch = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_rep_no_ul_dl_sacch,
+ cfg_bts_rep_no_ul_dl_sacch_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no repeat (ul-sacch|dl-sacch)",
+ NO_STR REP_ACCH_STR
+ "Disable UL-SACCH repetition for this BTS\n"
+ "Disable DL-SACCH repetition for this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (strcmp(argv[0], "ul-sacch") == 0)
+ bts->rep_acch_cap.ul_sacch = false;
+ else
+ bts->rep_acch_cap.dl_sacch = false;
+
+ return CMD_SUCCESS;
+}
+
+/* See 3GPP TS 45.008, section 8.2.4 */
+#define RXQUAL_THRESH_CMD \
+ "rxqual (0|1|2|3|4|5|6|7)"
+#define RXQUAL_THRESH_CMD_DESC \
+ "Set RxQual (BER) threshold (default 4)\n" \
+ "BER >= 0% (always on)\n" \
+ "BER >= 0.2%\n" \
+ "BER >= 0.4%\n" \
+ "BER >= 0.8%\n" \
+ "BER >= 1.6% (default)\n" \
+ "BER >= 3.2%\n" \
+ "BER >= 6.4%\n" \
+ "BER >= 12.8%\n"
+
+DEFUN_USRATTR(cfg_bts_rep_rxqual,
+ cfg_bts_rep_rxqual_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "repeat " RXQUAL_THRESH_CMD,
+ REP_ACCH_STR RXQUAL_THRESH_CMD_DESC)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% repeated ACCH not supported by BTS %u%s",
+ bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* See also: GSM 05.08, section 8.2.4 */
+ bts->rep_acch_cap.rxqual = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+#define TOP_ACCH_STR "Temporary ACCH overpower\n"
+
+DEFUN_USRATTR(cfg_bts_top_dl_acch,
+ cfg_bts_top_dl_acch_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "overpower (dl-acch|dl-sacch|dl-facch) <1-4>",
+ TOP_ACCH_STR
+ "Enable overpower for both SACCH and FACCH\n"
+ "Enable overpower for SACCH only\n"
+ "Enable overpower for FACCH only\n"
+ "Overpower value in dB\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% ACCH overpower is not supported by BTS %u%s",
+ bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->top_acch_cap.sacch_enable = 0;
+ bts->top_acch_cap.facch_enable = 0;
+
+ if (!strcmp(argv[0], "dl-acch") || !strcmp(argv[0], "dl-sacch"))
+ bts->top_acch_cap.sacch_enable = 1;
+ if (!strcmp(argv[0], "dl-acch") || !strcmp(argv[0], "dl-facch"))
+ bts->top_acch_cap.facch_enable = 1;
+
+ bts->top_acch_cap.overpower_db = atoi(argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_top_no_dl_acch,
+ cfg_bts_top_no_dl_acch_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no overpower dl-acch",
+ NO_STR TOP_ACCH_STR
+ "Disable ACCH overpower for this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->top_acch_cap.overpower_db = 0;
+ bts->top_acch_cap.sacch_enable = 0;
+ bts->top_acch_cap.facch_enable = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_top_dl_acch_rxqual,
+ cfg_bts_top_dl_acch_rxqual_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "overpower " RXQUAL_THRESH_CMD,
+ TOP_ACCH_STR RXQUAL_THRESH_CMD_DESC)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% ACCH overpower is not supported by BTS %u%s",
+ bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->top_acch_cap.rxqual = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+static const struct value_string top_acch_chan_mode_name[] = {
+ { TOP_ACCH_CHAN_MODE_ANY, "any" },
+ { TOP_ACCH_CHAN_MODE_SPEECH_V3, "speech-amr" },
+ { 0, NULL }
+};
+
+DEFUN_USRATTR(cfg_bts_top_dl_acch_chan_mode,
+ cfg_bts_top_dl_acch_chan_mode_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "overpower chan-mode (speech-amr|any)",
+ TOP_ACCH_STR
+ "Allow temporary overpower for specific Channel mode(s)\n"
+ "Speech channels using AMR codec (default)\n"
+ "Any kind of channel mode\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% ACCH overpower is not supported by BTS %u%s",
+ bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->top_acch_chan_mode = get_string_value(top_acch_chan_mode_name, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+#define CD_STR "Channel Description\n"
+
+DEFUN_USRATTR(cfg_bts_chan_desc_att,
+ cfg_bts_chan_desc_att_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "channel-description attach (0|1)",
+ CD_STR
+ "Set if attachment is required\n"
+ "Attachment is NOT required\n"
+ "Attachment is required (standard)\n")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->si_common.chan_desc.att = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+ALIAS_DEPRECATED(cfg_bts_chan_desc_att,
+ cfg_bts_chan_dscr_att_cmd,
+ "channel-descrption attach (0|1)",
+ CD_STR
+ "Set if attachment is required\n"
+ "Attachment is NOT required\n"
+ "Attachment is required (standard)\n");
+
+DEFUN_USRATTR(cfg_bts_chan_desc_bs_pa_mfrms,
+ cfg_bts_chan_desc_bs_pa_mfrms_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "channel-description bs-pa-mfrms <2-9>",
+ CD_STR
+ "Set number of multiframe periods for paging groups\n"
+ "Number of multiframe periods for paging groups\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int bs_pa_mfrms = atoi(argv[0]);
+
+ bts->si_common.chan_desc.bs_pa_mfrms = bs_pa_mfrms - 2;
+ return CMD_SUCCESS;
+}
+ALIAS_DEPRECATED(cfg_bts_chan_desc_bs_pa_mfrms,
+ cfg_bts_chan_dscr_bs_pa_mfrms_cmd,
+ "channel-descrption bs-pa-mfrms <2-9>",
+ CD_STR
+ "Set number of multiframe periods for paging groups\n"
+ "Number of multiframe periods for paging groups\n");
+
+DEFUN_USRATTR(cfg_bts_chan_desc_bs_ag_blks_res,
+ cfg_bts_chan_desc_bs_ag_blks_res_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "channel-description bs-ag-blks-res <0-7>",
+ CD_STR
+ "Set number of blocks reserved for access grant\n"
+ "Number of blocks reserved for access grant\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int bs_ag_blks_res = atoi(argv[0]);
+
+ bts->si_common.chan_desc.bs_ag_blks_res = bs_ag_blks_res;
+ return CMD_SUCCESS;
+}
+ALIAS_DEPRECATED(cfg_bts_chan_desc_bs_ag_blks_res,
+ cfg_bts_chan_dscr_bs_ag_blks_res_cmd,
+ "channel-descrption bs-ag-blks-res <0-7>",
+ CD_STR
+ "Set number of blocks reserved for access grant\n"
+ "Number of blocks reserved for access grant\n");
+
+#define CCCH_STR "Common Control Channel\n"
+
+DEFUN_USRATTR(cfg_bts_ccch_load_ind_thresh,
+ cfg_bts_ccch_load_ind_thresh_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "ccch load-indication-threshold <0-100>",
+ CCCH_STR
+ "Percentage of CCCH load at which BTS sends RSL CCCH LOAD IND\n"
+ "CCCH Load Threshold in percent (Default: 10)\n")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->ccch_load_ind_thresh = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_ccch_load_ind_period,
+ cfg_bts_ccch_load_ind_period_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "ccch load-indication-period <0-255>",
+ CCCH_STR
+ "Period of time at which BTS sends RSL CCCH LOAD IND\n"
+ "CCCH Load Indication Period in seconds (Default: 1)\n")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->ccch_load_ind_period = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+#define NM_STR "Network Management\n"
+
+DEFUN_USRATTR(cfg_bts_rach_nm_b_thresh,
+ cfg_bts_rach_nm_b_thresh_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "rach nm busy threshold <0-255>",
+ RACH_STR NM_STR
+ "Set the NM Busy Threshold\n"
+ "Set the NM Busy Threshold\n"
+ "NM Busy Threshold in dB\n")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->rach_b_thresh = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_rach_nm_ldavg,
+ cfg_bts_rach_nm_ldavg_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "rach nm load average <0-65535>",
+ RACH_STR NM_STR
+ "Set the NM Loadaverage Slots value\n"
+ "Set the NM Loadaverage Slots value\n"
+ "NM Loadaverage Slots value\n")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->rach_ldavg_slots = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_cell_barred,
+ cfg_bts_cell_barred_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "cell barred (0|1)",
+ "Should this cell be barred from access?\n"
+ "Should this cell be barred from access?\n"
+ "Cell should NOT be barred\n"
+ "Cell should be barred\n")
+
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.rach_control.cell_bar = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_rach_ec_allowed,
+ cfg_bts_rach_ec_allowed_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "rach emergency call allowed (0|1)",
+ RACH_STR
+ "Should this cell allow emergency calls?\n"
+ "Should this cell allow emergency calls?\n"
+ "Should this cell allow emergency calls?\n"
+ "Do NOT allow emergency calls\n"
+ "Allow emergency calls\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (atoi(argv[0]) == 0)
+ bts->si_common.rach_control.t2 |= 0x4;
+ else
+ bts->si_common.rach_control.t2 &= ~0x4;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_rach_re_allowed,
+ cfg_bts_rach_re_allowed_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "rach call-reestablishment allowed (0|1)",
+ RACH_STR
+ "Resume calls after radio link failure\n"
+ "Resume calls after radio link failure\n"
+ "Forbid MS to reestablish calls\n"
+ "Allow MS to try to reestablish calls\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (atoi(argv[0]) == 0)
+ bts->si_common.rach_control.re = 1;
+ else
+ bts->si_common.rach_control.re = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_rach_ac_class,
+ cfg_bts_rach_ac_class_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "rach access-control-class (0|1|2|3|4|5|6|7|8|9|11|12|13|14|15) (barred|allowed)",
+ RACH_STR
+ "Set access control class\n"
+ "Access control class 0\n"
+ "Access control class 1\n"
+ "Access control class 2\n"
+ "Access control class 3\n"
+ "Access control class 4\n"
+ "Access control class 5\n"
+ "Access control class 6\n"
+ "Access control class 7\n"
+ "Access control class 8\n"
+ "Access control class 9\n"
+ "Access control class 11 for PLMN use\n"
+ "Access control class 12 for security services\n"
+ "Access control class 13 for public utilities (e.g. water/gas suppliers)\n"
+ "Access control class 14 for emergency services\n"
+ "Access control class 15 for PLMN staff\n"
+ "barred to use access control class\n"
+ "allowed to use access control class\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ uint8_t control_class;
+ uint8_t allowed = 0;
+
+ if (strcmp(argv[1], "allowed") == 0)
+ allowed = 1;
+
+ control_class = atoi(argv[0]);
+ if (control_class < 8)
+ if (allowed)
+ bts->si_common.rach_control.t3 &= ~(0x1 << control_class);
+ else
+ bts->si_common.rach_control.t3 |= (0x1 << control_class);
+ else
+ if (allowed)
+ bts->si_common.rach_control.t2 &= ~(0x1 << (control_class - 8));
+ else
+ bts->si_common.rach_control.t2 |= (0x1 << (control_class - 8));
+
+ if (control_class < 10)
+ acc_mgr_perm_subset_changed(&bts->acc_mgr, &bts->si_common.rach_control);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_ms_max_power,
+ cfg_bts_ms_max_power_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ms max power <0-40>",
+ "MS Options\n"
+ "Maximum transmit power of the MS\n"
+ "Maximum transmit power of the MS\n"
+ "Maximum transmit power of the MS in dBm\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->ms_max_power = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+#define CELL_STR "Cell Parameters\n"
+
+DEFUN_USRATTR(cfg_bts_cell_resel_hyst,
+ cfg_bts_cell_resel_hyst_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "cell reselection hysteresis <0-14>",
+ CELL_STR "Cell re-selection parameters\n"
+ "Cell Re-Selection Hysteresis in dB\n"
+ "Cell Re-Selection Hysteresis in dB\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_rxlev_acc_min,
+ cfg_bts_rxlev_acc_min_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "rxlev access min <0-63>",
+ "Minimum RxLev needed for cell access\n"
+ "Minimum RxLev needed for cell access\n"
+ "Minimum RxLev needed for cell access\n"
+ "Minimum RxLev needed for cell access (better than -110dBm)\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_cell_bar_qualify,
+ cfg_bts_cell_bar_qualify_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "cell bar qualify (0|1)",
+ CELL_STR "Cell Bar Qualify\n" "Cell Bar Qualify\n"
+ "Set CBQ to 0\n" "Set CBQ to 1\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_ro_sel_par.present = 1;
+ bts->si_common.cell_ro_sel_par.cbq = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_cell_resel_ofs,
+ cfg_bts_cell_resel_ofs_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "cell reselection offset <0-126>",
+ CELL_STR "Cell Re-Selection Parameters\n"
+ "Cell Re-Selection Offset (CRO) in dB\n"
+ "Cell Re-Selection Offset (CRO) in dB\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_ro_sel_par.present = 1;
+ bts->si_common.cell_ro_sel_par.cell_resel_off = atoi(argv[0])/2;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_temp_ofs,
+ cfg_bts_temp_ofs_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "temporary offset <0-60>",
+ "Cell selection temporary negative offset\n"
+ "Cell selection temporary negative offset\n"
+ "Cell selection temporary negative offset in dB\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_ro_sel_par.present = 1;
+ bts->si_common.cell_ro_sel_par.temp_offs = atoi(argv[0])/10;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_temp_ofs_inf,
+ cfg_bts_temp_ofs_inf_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "temporary offset infinite",
+ "Cell selection temporary negative offset\n"
+ "Cell selection temporary negative offset\n"
+ "Sets cell selection temporary negative offset to infinity\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_ro_sel_par.present = 1;
+ bts->si_common.cell_ro_sel_par.temp_offs = 7;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_penalty_time,
+ cfg_bts_penalty_time_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "penalty time <20-620>",
+ "Cell selection penalty time\n"
+ "Cell selection penalty time\n"
+ "Cell selection penalty time in seconds (by 20s increments)\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_ro_sel_par.present = 1;
+ bts->si_common.cell_ro_sel_par.penalty_time = (atoi(argv[0])-20)/20;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_penalty_time_rsvd,
+ cfg_bts_penalty_time_rsvd_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "penalty time reserved",
+ "Cell selection penalty time\n"
+ "Cell selection penalty time\n"
+ "Set cell selection penalty time to reserved value 31, "
+ "(indicate that CELL_RESELECT_OFFSET is subtracted from C2 "
+ "and TEMPORARY_OFFSET is ignored)\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_ro_sel_par.present = 1;
+ bts->si_common.cell_ro_sel_par.penalty_time = 31;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_radio_link_timeout,
+ cfg_bts_radio_link_timeout_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "radio-link-timeout <4-64>",
+ "Radio link timeout criterion (BTS side)\n"
+ "Radio link timeout value (lost SACCH block)\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ gsm_bts_set_radio_link_timeout(bts, atoi(argv[0]));
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_radio_link_timeout_inf,
+ cfg_bts_radio_link_timeout_inf_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "radio-link-timeout infinite",
+ "Radio link timeout criterion (BTS side)\n"
+ "Infinite Radio link timeout value (use only for BTS RF testing)\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% infinite radio link timeout not supported by BTS %u%s", bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "%% INFINITE RADIO LINK TIMEOUT, USE ONLY FOR BTS RF TESTING%s", VTY_NEWLINE);
+ gsm_bts_set_radio_link_timeout(bts, -1);
+
+ return CMD_SUCCESS;
+}
+
+#define GPRS_TEXT "GPRS Packet Network\n"
+
+#define GPRS_CHECK_ENABLED(bts) \
+ do { \
+ if (bts->gprs.mode == BTS_GPRS_NONE) { \
+ vty_out(vty, "%% GPRS is not enabled on BTS %u%s", \
+ bts->nr, VTY_NEWLINE); \
+ return CMD_WARNING; \
+ } \
+ } while (0)
+
+DEFUN_USRATTR(cfg_bts_prs_bvci,
+ cfg_bts_gprs_bvci_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs cell bvci <2-65535>",
+ GPRS_TEXT
+ "GPRS Cell Settings\n"
+ "GPRS BSSGP VC Identifier\n"
+ "GPRS BSSGP VC Identifier\n")
+{
+ /* ETSI TS 101 343: values 0 and 1 are reserved for signalling and PTM */
+ struct gsm_bts *bts = vty->index;
+
+ GPRS_CHECK_ENABLED(bts);
+
+ bts->gprs.cell.bvci = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_nsei,
+ cfg_bts_gprs_nsei_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs nsei <0-65535>",
+ GPRS_TEXT
+ "GPRS NS Entity Identifier\n"
+ "GPRS NS Entity Identifier\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ GPRS_CHECK_ENABLED(bts);
+
+ bts->site_mgr->gprs.nse.nsei = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+#define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \
+ "NSVC Logical Number\n"
+
+DEFUN_USRATTR(cfg_bts_gprs_nsvci,
+ cfg_bts_gprs_nsvci_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs nsvc <0-1> nsvci <0-65535>",
+ GPRS_TEXT NSVC_TEXT
+ "NS Virtual Connection Identifier\n"
+ "GPRS NS VC Identifier\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int idx = atoi(argv[0]);
+
+ GPRS_CHECK_ENABLED(bts);
+
+ bts->site_mgr->gprs.nsvc[idx].nsvci = atoi(argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_nsvc_lport,
+ cfg_bts_gprs_nsvc_lport_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs nsvc <0-1> local udp port <0-65535>",
+ GPRS_TEXT NSVC_TEXT
+ "GPRS NS Local UDP Port\n"
+ "GPRS NS Local UDP Port\n"
+ "GPRS NS Local UDP Port\n"
+ "GPRS NS Local UDP Port Number\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int idx = atoi(argv[0]);
+
+ GPRS_CHECK_ENABLED(bts);
+
+ bts->site_mgr->gprs.nsvc[idx].local_port = atoi(argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_nsvc_rport,
+ cfg_bts_gprs_nsvc_rport_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs nsvc <0-1> remote udp port <0-65535>",
+ GPRS_TEXT NSVC_TEXT
+ "GPRS NS Remote UDP Port\n"
+ "GPRS NS Remote UDP Port\n"
+ "GPRS NS Remote UDP Port\n"
+ "GPRS NS Remote UDP Port Number\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int idx = atoi(argv[0]);
+
+ GPRS_CHECK_ENABLED(bts);
+
+ /* sockaddr_in and sockaddr_in6 have the port at the same position */
+ bts->site_mgr->gprs.nsvc[idx].remote.u.sin.sin_port = htons(atoi(argv[1]));
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_nsvc_rip,
+ cfg_bts_gprs_nsvc_rip_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs nsvc <0-1> remote ip " VTY_IPV46_CMD,
+ GPRS_TEXT NSVC_TEXT
+ "GPRS NS Remote IP Address\n"
+ "GPRS NS Remote IP Address\n"
+ "GPRS NS Remote IPv4 Address\n"
+ "GPRS NS Remote IPv6 Address\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct osmo_sockaddr_str remote;
+ int idx = atoi(argv[0]);
+ int ret;
+
+ GPRS_CHECK_ENABLED(bts);
+
+ ret = osmo_sockaddr_str_from_str2(&remote, argv[1]);
+ if (ret) {
+ vty_out(vty, "%% Invalid IP address %s%s", argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Can't use osmo_sockaddr_str_to_sockaddr() because the port would be overridden */
+ bts->site_mgr->gprs.nsvc[idx].remote.u.sas.ss_family = remote.af;
+ switch (remote.af) {
+ case AF_INET:
+ osmo_sockaddr_str_to_in_addr(&remote, &bts->site_mgr->gprs.nsvc[idx].remote.u.sin.sin_addr);
+ break;
+ case AF_INET6:
+ osmo_sockaddr_str_to_in6_addr(&remote, &bts->site_mgr->gprs.nsvc[idx].remote.u.sin6.sin6_addr);
+ break;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_pag_free, cfg_bts_pag_free_cmd,
+ "paging free <-1-1024>",
+ "Paging options\n"
+ "Only page when having a certain amount of free slots\n"
+ "amount of required free paging slots. -1 to disable\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->paging.free_chans_need = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_ns_timer,
+ cfg_bts_gprs_ns_timer_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs ns timer " NS_TIMERS " <0-255>",
+ GPRS_TEXT "Network Service\n"
+ "Network Service Timer\n"
+ NS_TIMERS_HELP "Timer Value\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
+ int val = atoi(argv[1]);
+
+ GPRS_CHECK_ENABLED(bts);
+
+ if (idx < 0 || idx >= ARRAY_SIZE(bts->site_mgr->gprs.nse.timer))
+ return CMD_WARNING;
+
+ bts->site_mgr->gprs.nse.timer[idx] = val;
+
+ return CMD_SUCCESS;
+}
+
+#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)"
+#define BSSGP_TIMERS_HELP \
+ "Tbvc-block timeout\n" \
+ "Tbvc-block retries\n" \
+ "Tbvc-unblock retries\n" \
+ "Tbvcc-reset timeout\n" \
+ "Tbvc-reset retries\n" \
+ "Tbvc-suspend timeout\n" \
+ "Tbvc-suspend retries\n" \
+ "Tbvc-resume timeout\n" \
+ "Tbvc-resume retries\n" \
+ "Tbvc-capa-update timeout\n" \
+ "Tbvc-capa-update retries\n"
+
+DEFUN_USRATTR(cfg_bts_gprs_cell_timer,
+ cfg_bts_gprs_cell_timer_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs cell timer " BSSGP_TIMERS " <0-255>",
+ GPRS_TEXT "Cell / BSSGP\n"
+ "Cell/BSSGP Timer\n"
+ BSSGP_TIMERS_HELP "Timer Value\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]);
+ int val = atoi(argv[1]);
+
+ GPRS_CHECK_ENABLED(bts);
+
+ if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer))
+ return CMD_WARNING;
+
+ bts->gprs.cell.timer[idx] = val;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_rac,
+ cfg_bts_gprs_rac_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs routing area <0-255>",
+ GPRS_TEXT
+ "GPRS Routing Area Code\n"
+ "GPRS Routing Area Code\n"
+ "GPRS Routing Area Code\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ GPRS_CHECK_ENABLED(bts);
+
+ bts->gprs.rac = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_ctrl_ack,
+ cfg_bts_gprs_ctrl_ack_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "gprs control-ack-type-rach",
+ GPRS_TEXT
+ "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to "
+ "four access bursts format instead of default RLC/MAC control block\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ GPRS_CHECK_ENABLED(bts);
+
+ bts->gprs.ctrl_ack_type_use_block = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_ccn_active,
+ cfg_bts_gprs_ccn_active_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "gprs ccn-active (0|1|default)",
+ GPRS_TEXT
+ "Set CCN_ACTIVE in the GPRS Cell Options IE on the BCCH (SI13)\n"
+ "Disable\n" "Enable\n" "Default based on BTS type support\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->gprs.ccn.forced_vty = strcmp(argv[0], "default") != 0;
+
+ if (bts->gprs.ccn.forced_vty)
+ bts->gprs.ccn.active = argv[0][0] == '1';
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_pwr_ctrl_alpha,
+ cfg_bts_gprs_pwr_ctrl_alpha_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "gprs power-control alpha <0-10>",
+ GPRS_TEXT
+ "GPRS Global Power Control Parameters IE (SI13)\n"
+ "Set alpha\n"
+ "alpha for MS output power control in units of 0.1 (defaults to 0)\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->gprs.pwr_ctrl.alpha = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_no_bts_gprs_ctrl_ack,
+ cfg_no_bts_gprs_ctrl_ack_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "no gprs control-ack-type-rach",
+ NO_STR GPRS_TEXT
+ "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to "
+ "default RLC/MAC control block\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ GPRS_CHECK_ENABLED(bts);
+
+ bts->gprs.ctrl_ack_type_use_block = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_net_ctrl_ord,
+ cfg_bts_gprs_net_ctrl_ord_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "gprs network-control-order (nc0|nc1|nc2)",
+ GPRS_TEXT
+ "GPRS Network Control Order\n"
+ "MS controlled cell re-selection, no measurement reporting\n"
+ "MS controlled cell re-selection, MS sends measurement reports\n"
+ "Network controlled cell re-selection, MS sends measurement reports\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ GPRS_CHECK_ENABLED(bts);
+
+ bts->gprs.net_ctrl_ord = atoi(argv[0] + 2);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_mode,
+ cfg_bts_gprs_mode_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "gprs mode (none|gprs|egprs)",
+ GPRS_TEXT
+ "GPRS Mode for this BTS\n"
+ "GPRS Disabled on this BTS\n"
+ "GPRS Enabled on this BTS\n"
+ "EGPRS (EDGE) Enabled on this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+ enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0], NULL);
+
+ if (bts->features_known && !bts_gprs_mode_is_compat(bts, mode)) {
+ vty_out(vty, "%% This BTS type does not support %s%s", argv[0],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->gprs.mode = mode;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_bts_gprs_11bit_rach_support_for_egprs,
+ cfg_bts_gprs_11bit_rach_support_for_egprs_cmd,
+ "gprs 11bit_rach_support_for_egprs (0|1)",
+ GPRS_TEXT "EGPRS Packet Channel Request support\n"
+ "Disable EGPRS Packet Channel Request support\n"
+ "Enable EGPRS Packet Channel Request support\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ vty_out(vty, "%% 'gprs 11bit_rach_support_for_egprs' is now deprecated: "
+ "use '[no] gprs egprs-packet-channel-request' instead%s", VTY_NEWLINE);
+
+ bts->gprs.egprs_pkt_chan_request = (argv[0][0] == '1');
+
+ if (bts->gprs.mode == BTS_GPRS_NONE && bts->gprs.egprs_pkt_chan_request) {
+ vty_out(vty, "%% (E)GPRS is not enabled (see 'gprs mode')%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bts->gprs.mode != BTS_GPRS_EGPRS) {
+ vty_out(vty, "%% EGPRS Packet Channel Request support requires "
+ "EGPRS mode to be enabled (see 'gprs mode')%s", VTY_NEWLINE);
+ /* Do not return here, keep the old behaviour. */
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_gprs_egprs_pkt_chan_req,
+ cfg_bts_gprs_egprs_pkt_chan_req_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "gprs egprs-packet-channel-request",
+ GPRS_TEXT "EGPRS Packet Channel Request support")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->gprs.mode != BTS_GPRS_EGPRS) {
+ vty_out(vty, "%% EGPRS Packet Channel Request support requires "
+ "EGPRS mode to be enabled (see 'gprs mode')%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->gprs.egprs_pkt_chan_request = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_no_gprs_egprs_pkt_chan_req,
+ cfg_bts_no_gprs_egprs_pkt_chan_req_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "no gprs egprs-packet-channel-request",
+ NO_STR GPRS_TEXT "EGPRS Packet Channel Request support")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts->gprs.mode != BTS_GPRS_EGPRS) {
+ vty_out(vty, "%% EGPRS Packet Channel Request support requires "
+ "EGPRS mode to be enabled (see 'gprs mode')%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->gprs.egprs_pkt_chan_request = false;
+ return CMD_SUCCESS;
+}
+
+#define SI_TEXT "System Information Messages\n"
+#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)"
+#define SI_TYPE_HELP "System Information Type 1\n" \
+ "System Information Type 2\n" \
+ "System Information Type 3\n" \
+ "System Information Type 4\n" \
+ "System Information Type 5\n" \
+ "System Information Type 6\n" \
+ "System Information Type 7\n" \
+ "System Information Type 8\n" \
+ "System Information Type 9\n" \
+ "System Information Type 10\n" \
+ "System Information Type 13\n" \
+ "System Information Type 16\n" \
+ "System Information Type 17\n" \
+ "System Information Type 18\n" \
+ "System Information Type 19\n" \
+ "System Information Type 20\n" \
+ "System Information Type 2bis\n" \
+ "System Information Type 2ter\n" \
+ "System Information Type 2quater\n" \
+ "System Information Type 5bis\n" \
+ "System Information Type 5ter\n"
+
+DEFUN_USRATTR(cfg_bts_si_mode,
+ cfg_bts_si_mode_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "system-information " SI_TYPE_TEXT " mode (static|computed)",
+ SI_TEXT SI_TYPE_HELP
+ "System Information Mode\n"
+ "Static user-specified\n"
+ "Dynamic, BSC-computed\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int type;
+
+ type = get_string_value(osmo_sitype_strs, argv[0]);
+ if (type < 0) {
+ vty_out(vty, "%% Error SI Type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[1], "static"))
+ bts->si_mode_static |= (1 << type);
+ else
+ bts->si_mode_static &= ~(1 << type);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_si_static,
+ cfg_bts_si_static_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "system-information " SI_TYPE_TEXT " static HEXSTRING",
+ SI_TEXT SI_TYPE_HELP
+ "Static System Information filling\n"
+ "Static user-specified SI content in HEX notation\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int rc, type;
+
+ type = get_string_value(osmo_sitype_strs, argv[0]);
+ if (type < 0) {
+ vty_out(vty, "%% Error SI Type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!(bts->si_mode_static & (1 << type))) {
+ vty_out(vty, "%% SI Type %s is not configured in static mode%s",
+ get_value_string(osmo_sitype_strs, type), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Fill buffer with padding pattern */
+ memset(GSM_BTS_SI(bts, type), 0x2b, GSM_MACBLOCK_LEN);
+
+ /* Parse the user-specified SI in hex format, [partially] overwriting padding */
+ rc = osmo_hexparse(argv[1], GSM_BTS_SI(bts, type), GSM_MACBLOCK_LEN);
+ if (rc < 0 || rc > GSM_MACBLOCK_LEN) {
+ vty_out(vty, "%% Error parsing HEXSTRING%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Mark this SI as present */
+ bts->si_valid |= (1 << type);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_si_unused_send_empty,
+ cfg_bts_si_unused_send_empty_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "system-information unused-send-empty",
+ SI_TEXT
+ "Send BCCH Info with empty 'Full BCCH Info' TLV to notify disabled SI. "
+ "Some nanoBTS fw versions are known to fail upon receival of these messages.\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_unused_send_empty = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_no_si_unused_send_empty,
+ cfg_bts_no_si_unused_send_empty_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "no system-information unused-send-empty",
+ NO_STR SI_TEXT
+ "Avoid sending BCCH Info with empty 'Full BCCH Info' TLV to notify disabled SI. "
+ "Some nanoBTS fw versions are known to fail upon receival of these messages.\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (!is_ipaccess_bts(bts) || is_osmobts(bts)) {
+ vty_out(vty, "%% This command is only intended for ipaccess nanoBTS. See OS#3707.%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->si_unused_send_empty = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_early_cm,
+ cfg_bts_early_cm_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "early-classmark-sending (allowed|forbidden)",
+ "Early Classmark Sending\n"
+ "Early Classmark Sending is allowed\n"
+ "Early Classmark Sending is forbidden\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (!strcmp(argv[0], "allowed"))
+ bts->early_classmark_allowed = true;
+ else
+ bts->early_classmark_allowed = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_early_cm_3g,
+ cfg_bts_early_cm_3g_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "early-classmark-sending-3g (allowed|forbidden)",
+ "3G Early Classmark Sending\n"
+ "3G Early Classmark Sending is allowed\n"
+ "3G Early Classmark Sending is forbidden\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (!strcmp(argv[0], "allowed"))
+ bts->early_classmark_allowed_3g = true;
+ else
+ bts->early_classmark_allowed_3g = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_neigh_mode,
+ cfg_bts_neigh_mode_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "neighbor-list mode (automatic|manual|manual-si5)",
+ "Neighbor List\n" "Mode of Neighbor List generation\n"
+ "Automatically from all BTS in this BSC\n" "Manual\n"
+ "Manual with different lists for SI2 and SI5\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int mode = get_string_value(bts_neigh_mode_strs, argv[0]);
+
+ 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;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_neigh,
+ cfg_bts_neigh_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "neighbor-list (add|del) arfcn <0-1023>",
+ "Neighbor List\n" "Add to manual neighbor list\n"
+ "Delete from manual neighbor list\n" "ARFCN of neighbor\n"
+ "ARFCN of neighbor\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct bitvec *bv = &bts->si_common.neigh_list;
+ uint16_t arfcn = atoi(argv[1]);
+ enum gsm_band unused;
+
+ if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) {
+ vty_out(vty, "%% Cannot configure neighbor list in "
+ "automatic mode%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[0], "add"))
+ bitvec_set_bit_pos(bv, arfcn, 1);
+ else
+ bitvec_set_bit_pos(bv, arfcn, 0);
+
+ return CMD_SUCCESS;
+}
+
+/* help text should be kept in sync with EARFCN_*_INVALID defines */
+DEFUN_USRATTR(cfg_bts_si2quater_neigh_add,
+ cfg_bts_si2quater_neigh_add_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "si2quater neighbor-list add earfcn <0-65535> thresh-hi <0-31> "
+ "thresh-lo <0-32> prio <0-8> qrxlv <0-32> meas <0-8>",
+ "SI2quater Neighbor List\n" "SI2quater Neighbor List\n"
+ "Add to manual SI2quater neighbor list\n"
+ "EARFCN of neighbor\n" "EARFCN of neighbor\n"
+ "threshold high bits\n" "threshold high bits\n"
+ "threshold low bits\n" "threshold low bits (32 means NA)\n"
+ "priority\n" "priority (8 means NA)\n"
+ "QRXLEVMIN\n" "QRXLEVMIN (32 means NA)\n"
+ "measurement bandwidth\n" "measurement bandwidth (8 means NA)\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
+ uint16_t arfcn = atoi(argv[0]);
+ uint8_t thresh_hi = atoi(argv[1]), thresh_lo = atoi(argv[2]),
+ prio = atoi(argv[3]), qrx = atoi(argv[4]), meas = atoi(argv[5]);
+ int r = bts_earfcn_add(bts, arfcn, thresh_hi, thresh_lo, prio, qrx, meas);
+
+ switch (r) {
+ case 1:
+ vty_out(vty, "%% Warning: multiple threshold-high are not supported, overriding with %u%s",
+ thresh_hi, VTY_NEWLINE);
+ break;
+ case EARFCN_THRESH_LOW_INVALID:
+ vty_out(vty, "%% Warning: multiple threshold-low are not supported, overriding with %u%s",
+ thresh_lo, VTY_NEWLINE);
+ break;
+ case EARFCN_QRXLV_INVALID + 1:
+ vty_out(vty, "%% Warning: multiple QRXLEVMIN are not supported, overriding with %u%s",
+ qrx, VTY_NEWLINE);
+ break;
+ case EARFCN_PRIO_INVALID:
+ vty_out(vty, "%% Warning: multiple priorities are not supported, overriding with %u%s",
+ prio, VTY_NEWLINE);
+ break;
+ default:
+ if (r < 0) {
+ vty_out(vty, "%% Unable to add ARFCN %u: %s%s", arfcn, strerror(-r), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ if (si2q_num(bts) <= SI2Q_MAX_NUM)
+ return CMD_SUCCESS;
+
+ vty_out(vty, "%% Warning: not enough space in SI2quater (%u/%u used) for a given EARFCN %u%s",
+ bts->si2q_count, SI2Q_MAX_NUM, arfcn, VTY_NEWLINE);
+ osmo_earfcn_del(e, arfcn);
+
+ return CMD_WARNING;
+}
+
+DEFUN_USRATTR(cfg_bts_si2quater_neigh_del,
+ cfg_bts_si2quater_neigh_del_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "si2quater neighbor-list del earfcn <0-65535>",
+ "SI2quater Neighbor List\n"
+ "SI2quater Neighbor List\n"
+ "Delete from SI2quater manual neighbor list\n"
+ "EARFCN of neighbor\n"
+ "EARFCN\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
+ uint16_t arfcn = atoi(argv[0]);
+ int r = osmo_earfcn_del(e, arfcn);
+ if (r < 0) {
+ vty_out(vty, "%% Unable to delete arfcn %u: %s%s", arfcn,
+ strerror(-r), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_si2quater_uarfcn_add,
+ cfg_bts_si2quater_uarfcn_add_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "si2quater neighbor-list add uarfcn <0-16383> <0-511> <0-1>",
+ "SI2quater Neighbor List\n"
+ "SI2quater Neighbor List\n" "Add to manual SI2quater neighbor list\n"
+ "UARFCN of neighbor\n" "UARFCN of neighbor\n" "scrambling code\n"
+ "diversity bit\n")
+{
+ struct gsm_bts *bts = vty->index;
+ uint16_t arfcn = atoi(argv[0]), scramble = atoi(argv[1]);
+
+ switch(bts_uarfcn_add(bts, arfcn, scramble, atoi(argv[2]))) {
+ case -ENOMEM:
+ vty_out(vty, "%% Unable to add UARFCN: max number of UARFCNs (%u) reached%s",
+ MAX_EARFCN_LIST, VTY_NEWLINE);
+ return CMD_WARNING;
+ case -ENOSPC:
+ vty_out(vty, "%% Warning: not enough space in SI2quater for a given UARFCN (%u, %u)%s",
+ arfcn, scramble, VTY_NEWLINE);
+ return CMD_WARNING;
+ case -EADDRINUSE:
+ vty_out(vty, "%% Unable to add UARFCN: (%u, %u) is already added%s",
+ arfcn, scramble, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_si2quater_uarfcn_del,
+ cfg_bts_si2quater_uarfcn_del_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "si2quater neighbor-list del uarfcn <0-16383> <0-511>",
+ "SI2quater Neighbor List\n"
+ "SI2quater Neighbor List\n"
+ "Delete from SI2quater manual neighbor list\n"
+ "UARFCN of neighbor\n"
+ "UARFCN\n"
+ "scrambling code\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts_uarfcn_del(bts, atoi(argv[0]), atoi(argv[1])) < 0) {
+ vty_out(vty, "%% Unable to delete uarfcn: pair not found%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_si5_neigh,
+ cfg_bts_si5_neigh_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "si5 neighbor-list (add|del) arfcn <0-1023>",
+ "SI5 Neighbor List\n"
+ "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n"
+ "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n"
+ "ARFCN of neighbor\n")
+{
+ enum gsm_band unused;
+ struct gsm_bts *bts = vty->index;
+ struct bitvec *bv = &bts->si_common.si5_neigh_list;
+ uint16_t arfcn = atoi(argv[1]);
+
+ if (!bts->neigh_list_manual_mode) {
+ vty_out(vty, "%% Cannot configure neighbor list in "
+ "automatic mode%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[0], "add"))
+ bitvec_set_bit_pos(bv, arfcn, 1);
+ else
+ bitvec_set_bit_pos(bv, arfcn, 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_pcu_sock,
+ cfg_bts_pcu_sock_cmd,
+ "pcu-socket PATH",
+ "PCU Socket Path for using OsmoPCU co-located with BSC (legacy BTS)\n"
+ "Path in the file system for the unix-domain PCU socket\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ int rc;
+
+ osmo_talloc_replace_string(bts, &bts->pcu_sock_path, argv[0]);
+ pcu_sock_exit(bts);
+ rc = pcu_sock_init(bts->pcu_sock_path, bts);
+ if (rc < 0) {
+ vty_out(vty, "%% Error creating PCU socket `%s' for BTS %u%s",
+ bts->pcu_sock_path, bts->nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_acc_rotate,
+ cfg_bts_acc_rotate_cmd,
+ "access-control-class-rotate <0-10>",
+ "Enable Access Control Class allowed subset rotation\n"
+ "Size of the rotating allowed ACC 0-9 subset (default=10, no subset)\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ int len_allowed_adm = atoi(argv[0]);
+ acc_mgr_set_len_allowed_adm(&bts->acc_mgr, len_allowed_adm);
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_acc_rotate_quantum,
+ cfg_bts_acc_rotate_quantum_cmd,
+ "access-control-class-rotate-quantum <1-65535>",
+ "Time between rotation of ACC 0-9 generated subsets\n"
+ "Time in seconds (default=" OSMO_STRINGIFY_VAL(ACC_MGR_QUANTUM_DEFAULT) ")\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ uint32_t rotation_time_sec = (uint32_t)atoi(argv[0]);
+ acc_mgr_set_rotation_time(&bts->acc_mgr, rotation_time_sec);
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_acc_ramping,
+ cfg_bts_acc_ramping_cmd,
+ "access-control-class-ramping",
+ "Enable Access Control Class ramping\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ struct gsm_bts_trx *trx;
+
+ if (!acc_ramp_is_enabled(&bts->acc_ramp)) {
+ acc_ramp_set_enabled(&bts->acc_ramp, true);
+ /* Start ramping if at least one TRX is usable */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx_is_usable(trx)) {
+ acc_ramp_trigger(&bts->acc_ramp);
+ break;
+ }
+ }
+ }
+
+ /*
+ * ACC ramping takes effect either when the BTS reconnects RSL,
+ * or when RF administrative state changes to 'unlocked'.
+ */
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_no_acc_ramping,
+ cfg_bts_no_acc_ramping_cmd,
+ "no access-control-class-ramping",
+ NO_STR
+ "Disable Access Control Class ramping\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (acc_ramp_is_enabled(&bts->acc_ramp)) {
+ acc_ramp_abort(&bts->acc_ramp);
+ acc_ramp_set_enabled(&bts->acc_ramp, false);
+ if (gsm_bts_set_system_infos(bts) != 0) {
+ vty_out(vty, "%% Filed to (re)generate System Information "
+ "messages, check the logs%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_acc_ramping_step_interval,
+ cfg_bts_acc_ramping_step_interval_cmd,
+ "access-control-class-ramping-step-interval (<"
+ OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_INTERVAL_MIN) "-"
+ OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_INTERVAL_MAX) ">|dynamic)",
+ "Configure Access Control Class ramping step interval\n"
+ "Set a fixed step interval (in seconds)\n"
+ "Use dynamic step interval based on BTS channel load (deprecated, don't use, ignored)\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ bool dynamic = (strcmp(argv[0], "dynamic") == 0);
+ int error;
+
+ if (dynamic) {
+ vty_out(vty, "%% access-control-class-ramping-step-interval 'dynamic' value is deprecated, ignoring it%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
+ }
+
+ error = acc_ramp_set_step_interval(&bts->acc_ramp, atoi(argv[0]));
+ if (error != 0) {
+ if (error == -ERANGE)
+ vty_out(vty, "%% Unable to set ACC ramp step interval: value out of range%s", VTY_NEWLINE);
+ else
+ vty_out(vty, "%% Unable to set ACC ramp step interval: unknown error%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_acc_ramping_step_size,
+ cfg_bts_acc_ramping_step_size_cmd,
+ "access-control-class-ramping-step-size (<"
+ OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_SIZE_MIN) "-"
+ OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_SIZE_MAX) ">)",
+ "Configure Access Control Class ramping step size\n"
+ "Set the number of Access Control Classes to enable per ramping step\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ int error;
+
+ error = acc_ramp_set_step_size(&bts->acc_ramp, atoi(argv[0]));
+ if (error != 0) {
+ if (error == -ERANGE)
+ vty_out(vty, "%% Unable to set ACC ramp step size: value out of range%s", VTY_NEWLINE);
+ else
+ vty_out(vty, "%% Unable to set ACC ramp step size: unknown error%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_acc_ramping_chan_load,
+ cfg_bts_acc_ramping_chan_load_cmd,
+ "access-control-class-ramping-chan-load <0-100> <0-100>",
+ "Configure Access Control Class ramping channel load thresholds\n"
+ "Lower Channel load threshold (%) below which subset size of allowed broadcast ACCs can be increased\n"
+ "Upper channel load threshold (%) above which subset size of allowed broadcast ACCs can be decreased\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ int rc;
+
+ rc = acc_ramp_set_chan_load_thresholds(&bts->acc_ramp, atoi(argv[0]), atoi(argv[1]));
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to set ACC channel load thresholds%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+#define EXCL_RFLOCK_STR "Exclude this BTS from the global RF Lock\n"
+
+DEFUN_ATTR(cfg_bts_excl_rf_lock,
+ cfg_bts_excl_rf_lock_cmd,
+ "rf-lock-exclude",
+ EXCL_RFLOCK_STR,
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ bts->excl_from_rf_lock = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_no_excl_rf_lock,
+ cfg_bts_no_excl_rf_lock_cmd,
+ "no rf-lock-exclude",
+ NO_STR EXCL_RFLOCK_STR,
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ bts->excl_from_rf_lock = 0;
+ return CMD_SUCCESS;
+}
+
+#define FORCE_COMB_SI_STR "Force the generation of a single SI (no ter/bis)\n"
+
+DEFUN_USRATTR(cfg_bts_force_comb_si,
+ cfg_bts_force_comb_si_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "force-combined-si",
+ FORCE_COMB_SI_STR)
+{
+ struct gsm_bts *bts = vty->index;
+ bts->force_combined_si = 1;
+ bts->force_combined_si_set = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_no_force_comb_si,
+ cfg_bts_no_force_comb_si_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "no force-combined-si",
+ NO_STR FORCE_COMB_SI_STR)
+{
+ struct gsm_bts *bts = vty->index;
+ bts->force_combined_si = 0;
+ bts->force_combined_si_set = true;
+ return CMD_SUCCESS;
+}
+
+static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[])
+{
+ struct gsm_bts *bts = vty->index;
+ struct bts_codec_conf *codec = &bts->codec;
+ int i;
+
+ codec->hr = 0;
+ codec->efr = 0;
+ codec->amr = 0;
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "hr"))
+ codec->hr = 1;
+ if (!strcmp(argv[i], "efr"))
+ codec->efr = 1;
+ if (!strcmp(argv[i], "amr"))
+ codec->amr = 1;
+ }
+}
+
+#define CODEC_PAR_STR " (hr|efr|amr)"
+#define CODEC_HELP_STR "Half Rate\n" \
+ "Enhanced Full Rate\nAdaptive Multirate\n"
+
+DEFUN_USRATTR(cfg_bts_codec0,
+ cfg_bts_codec0_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "codec-support fr",
+ "Codec Support settings\nFullrate\n")
+{
+ _get_codec_from_arg(vty, 0, argv);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_codec1,
+ cfg_bts_codec1_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "codec-support fr" CODEC_PAR_STR,
+ "Codec Support settings\nFullrate\n"
+ CODEC_HELP_STR)
+{
+ _get_codec_from_arg(vty, 1, argv);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_codec2,
+ cfg_bts_codec2_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR,
+ "Codec Support settings\nFullrate\n"
+ CODEC_HELP_STR CODEC_HELP_STR)
+{
+ _get_codec_from_arg(vty, 2, argv);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_codec3,
+ cfg_bts_codec3_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR,
+ "Codec Support settings\nFullrate\n"
+ CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR)
+{
+ _get_codec_from_arg(vty, 3, argv);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_codec4,
+ cfg_bts_codec4_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR,
+ "Codec Support settings\nFullrate\n"
+ CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR)
+{
+ _get_codec_from_arg(vty, 4, argv);
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_depends_on, cfg_bts_depends_on_cmd,
+ "depends-on-bts <0-255>",
+ "This BTS can only be started if another one is up\n"
+ BTS_NR_STR, CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ struct gsm_bts *other_bts;
+ int dep = atoi(argv[0]);
+
+
+ if (!is_ipaccess_bts(bts)) {
+ vty_out(vty, "%% This feature is only available for IP systems.%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ other_bts = gsm_bts_num(bts->network, dep);
+ if (!other_bts || !is_ipaccess_bts(other_bts)) {
+ vty_out(vty, "%% This feature is only available for IP systems.%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (dep >= bts->nr) {
+ vty_out(vty, "%% Need to depend on an already declared unit.%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts_depend_mark(bts, dep);
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_no_depends_on, cfg_bts_no_depends_on_cmd,
+ "no depends-on-bts <0-255>",
+ NO_STR "This BTS can only be started if another one is up\n"
+ BTS_NR_STR, CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ int dep = atoi(argv[0]);
+
+ bts_depend_clear(bts, dep);
+ return CMD_SUCCESS;
+}
+
+#define AMR_TEXT "Adaptive Multi Rate settings\n"
+#define AMR_MODE_TEXT "Codec modes to use with AMR codec. Only specific mode combinations make sense," \
+ " see 3GPP TS 28.062, Table 7.11.3.1.3-2. Note that S1 = '0 2 4 7' is defined mandatory in BSSAP.\n"
+ // !! ^ that means '0 2 4 7' is the only useful setting for this vty cmd !!
+#define AMR_START_TEXT "Initial codec mode to use with AMR\n" \
+ "Automatically\nFirst mode (lowest rate)\nSecond mode\nThird mode\nFourth mode (highest rate)\n"
+#define AMR_MS_BTS_TEXT "MS side\nBTS side\n"
+#define AMR_TH_TEXT "Lower threshold(s) for switching between codec modes\n" AMR_MS_BTS_TEXT
+#define AMR_HY_TEXT "Hysteresis value(s) to obtain the higher threshold(s) " \
+ "for switching between codec modes\n" AMR_MS_BTS_TEXT
+
+static int get_amr_from_arg(struct vty *vty, int argc, const char *argv[], int full)
+{
+ struct gsm_bts *bts = vty->index;
+ struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
+ struct gsm48_multi_rate_conf *mr_conf =
+ (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
+ int i;
+ int mode;
+ int mode_prev = -1;
+
+ /* Check if mode parameters are in order */
+ for (i = 0; i < argc; i++) {
+ mode = atoi(argv[i]);
+ if (mode_prev > mode) {
+ vty_out(vty, "%% Modes must be listed in order%s",
+ VTY_NEWLINE);
+ return -1;
+ }
+
+ if (mode_prev == mode) {
+ vty_out(vty, "%% Modes must be unique %s", VTY_NEWLINE);
+ return -2;
+ }
+ mode_prev = mode;
+ }
+
+ /* Prepare the multirate configuration IE */
+ mr->gsm48_ie[1] = 0;
+ for (i = 0; i < argc; i++)
+ mr->gsm48_ie[1] |= 1 << atoi(argv[i]);
+ mr_conf->icmi = 0;
+
+ /* Store actual mode identifier values */
+ for (i = 0; i < argc; i++) {
+ mr->ms_mode[i].mode = atoi(argv[i]);
+ mr->bts_mode[i].mode = atoi(argv[i]);
+ }
+ mr->num_modes = argc;
+
+ /* Trim excess threshold and hysteresis values from previous config */
+ for (i = argc - 1; i < 4; i++) {
+ mr->ms_mode[i].threshold = 0;
+ mr->bts_mode[i].threshold = 0;
+ mr->ms_mode[i].hysteresis = 0;
+ mr->bts_mode[i].hysteresis = 0;
+ }
+ return 0;
+}
+
+static void get_amr_th_from_arg(struct vty *vty, int argc, const char *argv[], int full)
+{
+ struct gsm_bts *bts = vty->index;
+ struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
+ struct amr_mode *modes;
+ int i;
+
+ modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode;
+ for (i = 0; i < argc - 1; i++)
+ modes[i].threshold = atoi(argv[i + 1]);
+}
+
+static void get_amr_hy_from_arg(struct vty *vty, int argc, const char *argv[], int full)
+{
+ struct gsm_bts *bts = vty->index;
+ struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
+ struct amr_mode *modes;
+ int i;
+
+ modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode;
+ for (i = 0; i < argc - 1; i++)
+ modes[i].hysteresis = atoi(argv[i + 1]);
+}
+
+static void get_amr_start_from_arg(struct vty *vty, const char *argv[], int full)
+{
+ struct gsm_bts *bts = vty->index;
+ struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
+ struct gsm48_multi_rate_conf *mr_conf =
+ (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
+ int num = 0, i;
+
+ for (i = 0; i < ((full) ? 8 : 6); i++) {
+ if ((mr->gsm48_ie[1] & (1 << i))) {
+ num++;
+ }
+ }
+
+ if (argv[0][0] == 'a' || num == 0) {
+ mr_conf->icmi = 0;
+ mr_conf->smod = 0;
+ } else {
+ mr_conf->icmi = 1;
+ if (num < atoi(argv[0]))
+ mr_conf->smod = num - 1;
+ else
+ mr_conf->smod = atoi(argv[0]) - 1;
+ }
+}
+
+/* Give the current amr configuration a final consistency check by feeding the
+ * the configuration into the gsm48 multirate IE generator function */
+static int check_amr_config(struct vty *vty)
+{
+ int rc = 0;
+ struct amr_multirate_conf *mr;
+ const struct gsm48_multi_rate_conf *mr_conf;
+ struct gsm_bts *bts = vty->index;
+ int vty_rc = CMD_SUCCESS;
+
+ mr = &bts->mr_full;
+ mr_conf = (struct gsm48_multi_rate_conf*) mr->gsm48_ie;
+ rc = gsm48_multirate_config(NULL, mr_conf, mr->ms_mode, mr->num_modes);
+ if (rc != 0) {
+ vty_out(vty,
+ "%% Invalid AMR multirate configuration (tch-f, ms) - check parameters%s",
+ VTY_NEWLINE);
+ vty_rc = CMD_WARNING;
+ }
+
+ rc = gsm48_multirate_config(NULL, mr_conf, mr->bts_mode, mr->num_modes);
+ if (rc != 0) {
+ vty_out(vty,
+ "%% Invalid AMR multirate configuration (tch-f, bts) - check parameters%s",
+ VTY_NEWLINE);
+ vty_rc = CMD_WARNING;
+ }
+
+ mr = &bts->mr_half;
+ mr_conf = (struct gsm48_multi_rate_conf*) mr->gsm48_ie;
+ rc = gsm48_multirate_config(NULL, mr_conf, mr->ms_mode, mr->num_modes);
+ if (rc != 0) {
+ vty_out(vty,
+ "%% Invalid AMR multirate configuration (tch-h, ms) - check parameters%s",
+ VTY_NEWLINE);
+ vty_rc = CMD_WARNING;
+ }
+
+ rc = gsm48_multirate_config(NULL, mr_conf, mr->bts_mode, mr->num_modes);
+ if (rc != 0) {
+ vty_out(vty,
+ "%% Invalid AMR multirate configuration (tch-h, bts) - check parameters%s",
+ VTY_NEWLINE);
+ vty_rc = CMD_WARNING;
+ }
+
+ return vty_rc;
+}
+
+#define AMR_TCHF_PAR_STR " (0|1|2|3|4|5|6|7)"
+#define AMR_TCHF_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" \
+ "10,2k\n12,2k\n"
+
+#define AMR_TCHH_PAR_STR " (0|1|2|3|4|5)"
+#define AMR_TCHH_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n"
+
+#define AMR_TH_HELP_STR(a, b) \
+ "Threshold between codec mode " a " and " b " (in 0.5 dB steps)\n"
+#define AMR_HY_HELP_STR(a, b) \
+ "Hysteresis between codec mode " a " and " b " (in 0.5 dB steps)\n"
+
+DEFUN_USRATTR(cfg_bts_amr_fr_modes1,
+ cfg_bts_amr_fr_modes1_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f modes" AMR_TCHF_PAR_STR,
+ AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
+ AMR_TCHF_HELP_STR)
+{
+ if (get_amr_from_arg(vty, 1, argv, 1))
+ return CMD_WARNING;
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_modes2,
+ cfg_bts_amr_fr_modes2_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR,
+ AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
+ AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR)
+{
+ if (get_amr_from_arg(vty, 2, argv, 1))
+ return CMD_WARNING;
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_modes3,
+ cfg_bts_amr_fr_modes3_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR,
+ AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
+ AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR)
+{
+ if (get_amr_from_arg(vty, 3, argv, 1))
+ return CMD_WARNING;
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_modes4,
+ cfg_bts_amr_fr_modes4_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR,
+ AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
+ AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR)
+{
+ if (get_amr_from_arg(vty, 4, argv, 1))
+ return CMD_WARNING;
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_start_mode,
+ cfg_bts_amr_fr_start_mode_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f start-mode (auto|1|2|3|4)",
+ AMR_TEXT "Full Rate\n" AMR_START_TEXT)
+{
+ get_amr_start_from_arg(vty, argv, 1);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_thres1,
+ cfg_bts_amr_fr_thres1_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f threshold (ms|bts) <0-63>",
+ AMR_TEXT "Full Rate\n" AMR_TH_TEXT
+ AMR_TH_HELP_STR("1", "2"))
+{
+ get_amr_th_from_arg(vty, 2, argv, 1);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_thres2,
+ cfg_bts_amr_fr_thres2_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f threshold (ms|bts) <0-63> <0-63>",
+ AMR_TEXT "Full Rate\n" AMR_TH_TEXT
+ AMR_TH_HELP_STR("1", "2")
+ AMR_TH_HELP_STR("2", "3"))
+{
+ get_amr_th_from_arg(vty, 3, argv, 1);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_thres3,
+ cfg_bts_amr_fr_thres3_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f threshold (ms|bts) <0-63> <0-63> <0-63>",
+ AMR_TEXT "Full Rate\n" AMR_TH_TEXT
+ AMR_TH_HELP_STR("1", "2")
+ AMR_TH_HELP_STR("2", "3")
+ AMR_TH_HELP_STR("3", "4"))
+{
+ get_amr_th_from_arg(vty, 4, argv, 1);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_hyst1,
+ cfg_bts_amr_fr_hyst1_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f hysteresis (ms|bts) <0-15>",
+ AMR_TEXT "Full Rate\n" AMR_HY_TEXT
+ AMR_HY_HELP_STR("1", "2"))
+{
+ get_amr_hy_from_arg(vty, 2, argv, 1);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_hyst2,
+ cfg_bts_amr_fr_hyst2_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f hysteresis (ms|bts) <0-15> <0-15>",
+ AMR_TEXT "Full Rate\n" AMR_HY_TEXT
+ AMR_HY_HELP_STR("1", "2")
+ AMR_HY_HELP_STR("2", "3"))
+{
+ get_amr_hy_from_arg(vty, 3, argv, 1);
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_amr_fr_hyst3,
+ cfg_bts_amr_fr_hyst3_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-f hysteresis (ms|bts) <0-15> <0-15> <0-15>",
+ AMR_TEXT "Full Rate\n" AMR_HY_TEXT
+ AMR_HY_HELP_STR("1", "2")
+ AMR_HY_HELP_STR("2", "3")
+ AMR_HY_HELP_STR("3", "4"))
+{
+ get_amr_hy_from_arg(vty, 4, argv, 1);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_modes1,
+ cfg_bts_amr_hr_modes1_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h modes" AMR_TCHH_PAR_STR,
+ AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
+ AMR_TCHH_HELP_STR)
+{
+ if (get_amr_from_arg(vty, 1, argv, 0))
+ return CMD_WARNING;
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_modes2,
+ cfg_bts_amr_hr_modes2_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR,
+ AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
+ AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR)
+{
+ if (get_amr_from_arg(vty, 2, argv, 0))
+ return CMD_WARNING;
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_modes3,
+ cfg_bts_amr_hr_modes3_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR,
+ AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
+ AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR)
+{
+ if (get_amr_from_arg(vty, 3, argv, 0))
+ return CMD_WARNING;
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_modes4,
+ cfg_bts_amr_hr_modes4_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR,
+ AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
+ AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR)
+{
+ if (get_amr_from_arg(vty, 4, argv, 0))
+ return CMD_WARNING;
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_start_mode,
+ cfg_bts_amr_hr_start_mode_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h start-mode (auto|1|2|3|4)",
+ AMR_TEXT "Half Rate\n" AMR_START_TEXT)
+{
+ get_amr_start_from_arg(vty, argv, 0);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_thres1,
+ cfg_bts_amr_hr_thres1_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h threshold (ms|bts) <0-63>",
+ AMR_TEXT "Half Rate\n" AMR_TH_TEXT
+ AMR_TH_HELP_STR("1", "2"))
+{
+ get_amr_th_from_arg(vty, 2, argv, 0);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_thres2,
+ cfg_bts_amr_hr_thres2_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h threshold (ms|bts) <0-63> <0-63>",
+ AMR_TEXT "Half Rate\n" AMR_TH_TEXT
+ AMR_TH_HELP_STR("1", "2")
+ AMR_TH_HELP_STR("2", "3"))
+{
+ get_amr_th_from_arg(vty, 3, argv, 0);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_thres3,
+ cfg_bts_amr_hr_thres3_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h threshold (ms|bts) <0-63> <0-63> <0-63>",
+ AMR_TEXT "Half Rate\n" AMR_TH_TEXT
+ AMR_TH_HELP_STR("1", "2")
+ AMR_TH_HELP_STR("2", "3")
+ AMR_TH_HELP_STR("3", "4"))
+{
+ get_amr_th_from_arg(vty, 4, argv, 0);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_hyst1,
+ cfg_bts_amr_hr_hyst1_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h hysteresis (ms|bts) <0-15>",
+ AMR_TEXT "Half Rate\n" AMR_HY_TEXT
+ AMR_HY_HELP_STR("1", "2"))
+{
+ get_amr_hy_from_arg(vty, 2, argv, 0);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_hyst2,
+ cfg_bts_amr_hr_hyst2_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h hysteresis (ms|bts) <0-15> <0-15>",
+ AMR_TEXT "Half Rate\n" AMR_HY_TEXT
+ AMR_HY_HELP_STR("1", "2")
+ AMR_HY_HELP_STR("2", "3"))
+{
+ get_amr_hy_from_arg(vty, 3, argv, 0);
+ return check_amr_config(vty);
+}
+
+DEFUN_USRATTR(cfg_bts_amr_hr_hyst3,
+ cfg_bts_amr_hr_hyst3_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "amr tch-h hysteresis (ms|bts) <0-15> <0-15> <0-15>",
+ AMR_TEXT "Half Rate\n" AMR_HY_TEXT
+ AMR_HY_HELP_STR("1", "2")
+ AMR_HY_HELP_STR("2", "3")
+ AMR_HY_HELP_STR("3", "4"))
+{
+ get_amr_hy_from_arg(vty, 4, argv, 0);
+ return check_amr_config(vty);
+}
+
+#define OSMUX_STR "RTP multiplexing\n"
+DEFUN_USRATTR(cfg_bts_osmux,
+ cfg_bts_osmux_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "osmux (on|off|only)",
+ OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only use OSMUX\n")
+{
+ struct gsm_bts *bts = vty->index;
+ enum osmux_usage use;
+
+ if (strcmp(argv[0], "off") == 0)
+ use = OSMUX_USAGE_OFF;
+ else if (strcmp(argv[0], "on") == 0)
+ use = OSMUX_USAGE_ON;
+ else if (strcmp(argv[0], "only") == 0)
+ use = OSMUX_USAGE_ONLY;
+ else
+ goto err;
+
+ if (!is_osmobts(bts))
+ goto err;
+
+ if (bts->features_known && use != OSMUX_USAGE_OFF &&
+ !osmo_bts_has_feature(&bts->features, BTS_FEAT_OSMUX))
+ goto err;
+
+ bts->use_osmux = use;
+ return CMD_SUCCESS;
+
+err:
+ LOGP(DNM, LOGL_ERROR,
+ "(bts=%u) Unable to set 'osmux %s', BTS does not support Osmux\n",
+ bts->nr, argv[0]);
+ return CMD_WARNING;
+}
+
+DEFUN_USRATTR(cfg_bts_mgw_pool_target,
+ cfg_bts_mgw_pool_target_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "mgw pool-target <0-255> [strict]",
+ "MGW configuration for this specific BTS\n"
+ "Pin BTS to use a single MGW in the pool\n"
+ "Reference Number of the MGW (in the config) to pin to\n"
+ "Strictly prohibit use of other MGWs if the pinned one is not available\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int mgw_nr = atoi(argv[0]);
+ bool strict = argc > 1;
+ bts->mgw_pool_target = mgw_nr;
+ bts->mgw_pool_target_strict = strict;
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_no_mgw_pool_target,
+ cfg_bts_no_mgw_pool_target_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no mgw pool-target",
+ NO_STR "MGW configuration for this specific BTS\n"
+ "Avoid pinning the BTS to any specific MGW (default)\n")
+{
+ struct gsm_bts *bts = vty->index;
+ bts->mgw_pool_target = -1;
+ bts->mgw_pool_target_strict = false;
+ return CMD_SUCCESS;
+}
+
+#define TNUM_STR "T-number, optionally preceded by 't' or 'T'\n"
+DEFUN_ATTR(cfg_bts_t3113_dynamic, cfg_bts_t3113_dynamic_cmd,
+ "timer-dynamic TNNNN",
+ "Calculate T3113 dynamically based on channel config and load (default)\n"
+ TNUM_STR,
+ CMD_ATTR_IMMEDIATE)
+{
+ struct osmo_tdef *d;
+ struct gsm_bts *bts = vty->index;
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+
+ d = osmo_tdef_vty_parse_T_arg(vty, gsmnet->T_defs, argv[0]);
+ if (!d)
+ return CMD_WARNING;
+
+ switch (d->T) {
+ case 3113:
+ bts->T3113_dynamic = true;
+ break;
+ default:
+ vty_out(vty, "%% T%d cannot be set to dynamic%s", d->T, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_no_t3113_dynamic, cfg_bts_no_t3113_dynamic_cmd,
+ "no timer-dynamic TNNNN",
+ NO_STR
+ "Set given timer to non-dynamic and use the default or user provided fixed value\n"
+ TNUM_STR,
+ CMD_ATTR_IMMEDIATE)
+{
+ struct osmo_tdef *d;
+ struct gsm_bts *bts = vty->index;
+ struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+
+ d = osmo_tdef_vty_parse_T_arg(vty, gsmnet->T_defs, argv[0]);
+ if (!d)
+ return CMD_WARNING;
+
+ switch (d->T) {
+ case 3113:
+ bts->T3113_dynamic = false;
+ break;
+ default:
+ vty_out(vty, "%% T%d already is non-dynamic%s", d->T, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_interf_meas_avg_period,
+ cfg_bts_interf_meas_avg_period_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "interference-meas avg-period <1-31>",
+ "Interference measurement parameters\n"
+ "Averaging period (Intave)\n"
+ "Number of SACCH multiframes\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->interf_meas_params_cfg.avg_period = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_interf_meas_level_bounds,
+ cfg_bts_interf_meas_level_bounds_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "interference-meas level-bounds "
+ "<-120-0> <-120-0> <-120-0> <-120-0> <-120-0> <-120-0>",
+ "Interference measurement parameters\n"
+ "Interference level Boundaries. 3GPP do not specify whether these should be in ascending or descending"
+ " order (3GPP TS 48.058 9.3.21 / 3GPP TS 52.021 9.4.25). OsmoBSC supports either ordering, but possibly"
+ " some BTS models only return meaningful interference levels with one specific ordering.\n"
+ "Interference boundary 0 (dBm)\n"
+ "Interference boundary X1 (dBm)\n"
+ "Interference boundary X2 (dBm)\n"
+ "Interference boundary X3 (dBm)\n"
+ "Interference boundary X4 (dBm)\n"
+ "Interference boundary X5 (dBm)\n")
+{
+ struct gsm_bts *bts = vty->index;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(bts->interf_meas_params_cfg.bounds_dbm); i++) {
+ bts->interf_meas_params_cfg.bounds_dbm[i] = abs(atoi(argv[i]));
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_srvcc_fast_return, cfg_bts_srvcc_fast_return_cmd,
+ "srvcc fast-return (allow|forbid)",
+ "SRVCC Configuration\n"
+ "Allow or forbid Fast Return to 4G on Channel Release in this BTS\n"
+ "Allow\n"
+ "Forbid\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->srvcc_fast_return_allowed = strcmp(argv[0], "allow") == 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_bts_immediate_assignment, cfg_bts_immediate_assignment_cmd,
+ "immediate-assignment (post-chan-ack|pre-chan-ack|pre-ts-ack)",
+ "Configure time of Immediate Assignment after ChanRqd RACH (Abis optimization)\n"
+ "Send the Immediate Assignment after the Channel Activation ACK (normal sequence)\n"
+ "Send the Immediate Assignment directly after Channel Activation (early), without waiting for the ACK;"
+ " This may help with double allocations on high latency Abis links\n"
+ "EXPERIMENTAL: If a dynamic timeslot switch is necessary, send the Immediate Assignment even before the"
+ " timeslot is switched, i.e. even before the Channel Activation is sent (very early)\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (!strcmp(argv[0], "pre-ts-ack"))
+ bts->imm_ass_time = IMM_ASS_TIME_PRE_TS_ACK;
+ else if (!strcmp(argv[0], "pre-chan-ack"))
+ bts->imm_ass_time = IMM_ASS_TIME_PRE_CHAN_ACK;
+ else
+ bts->imm_ass_time = IMM_ASS_TIME_POST_CHAN_ACK;
+ return CMD_SUCCESS;
+}
+
+#define BS_POWER_CONTROL_CMD \
+ "bs-power-control"
+#define MS_POWER_CONTROL_CMD \
+ "ms-power-control"
+#define POWER_CONTROL_CMD \
+ "(" BS_POWER_CONTROL_CMD "|" MS_POWER_CONTROL_CMD ")"
+#define POWER_CONTROL_DESC \
+ "BS (Downlink) power control parameters\n" \
+ "MS (Uplink) power control parameters\n"
+
+#define BTS_POWER_CTRL_PARAMS(bts) \
+ (strcmp(argv[0], BS_POWER_CONTROL_CMD) == 0) ? \
+ &bts->bs_power_ctrl : &bts->ms_power_ctrl
+
+DEFUN_USRATTR(cfg_bts_no_power_ctrl,
+ cfg_bts_no_power_ctrl_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no " POWER_CONTROL_CMD,
+ NO_STR POWER_CONTROL_DESC)
+{
+ struct gsm_power_ctrl_params *params;
+ struct gsm_bts *bts = vty->index;
+
+ params = BTS_POWER_CTRL_PARAMS(bts);
+ params->mode = GSM_PWR_CTRL_MODE_NONE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_power_ctrl,
+ cfg_bts_power_ctrl_cmd,
+ POWER_CONTROL_CMD,
+ POWER_CONTROL_DESC)
+{
+ struct gsm_power_ctrl_params *params;
+ struct gsm_bts *bts = vty->index;
+
+ params = BTS_POWER_CTRL_PARAMS(bts);
+ vty->node = POWER_CTRL_NODE;
+ vty->index = params;
+
+ /* Change the prefix to reflect MS/BS difference */
+ if (params->dir == GSM_PWR_CTRL_DIR_UL)
+ power_ctrl_node.prompt = "%s(config-ms-power-ctrl)# ";
+ else
+ power_ctrl_node.prompt = "%s(config-bs-power-ctrl)# ";
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_mode,
+ cfg_power_ctrl_mode_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "mode (static|dyn-bts|dyn-bsc) [reset]",
+ "Power control mode\n"
+ "Instruct the MS/BTS to use a static power level\n"
+ "Power control to be performed dynamically by the BTS itself\n"
+ "Power control to be performed dynamically at this BSC\n"
+ "Reset to default parameters for the given mode\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+
+ /* Do we need to reset? */
+ if (argc > 1) {
+ vty_out(vty, "%% Reset to default parameters%s", VTY_NEWLINE);
+ power_ctrl_params_def_reset(params, params->dir);
+ }
+
+ if (strcmp(argv[0], "static") == 0)
+ params->mode = GSM_PWR_CTRL_MODE_STATIC;
+ else if (strcmp(argv[0], "dyn-bts") == 0)
+ params->mode = GSM_PWR_CTRL_MODE_DYN_BTS;
+ else if (strcmp(argv[0], "dyn-bsc") == 0) {
+ if (params->dir == GSM_PWR_CTRL_DIR_DL) {
+ vty_out(vty, "%% mode dyn-bsc not supported for Downlink.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ params->mode = GSM_PWR_CTRL_MODE_DYN_BSC;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_bs_power,
+ cfg_power_ctrl_bs_power_cmd,
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "bs-power (static|dyn-max) <0-30>",
+ "BS Power IE value to be sent to the BTS\n"
+ "Fixed BS Power reduction value (for static mode)\n"
+ "Maximum BS Power reduction value (for dynamic mode)\n"
+ "BS Power reduction value (in dB, even numbers only)\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ bool dynamic = !strcmp(argv[0], "dyn-max");
+ int value = atoi(argv[1]);
+
+ if (params->dir != GSM_PWR_CTRL_DIR_DL) {
+ vty_out(vty, "%% This command is only valid for "
+ "'bs-power-control' node%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (value % 2 != 0) {
+ vty_out(vty, "%% Incorrect BS Power reduction value, "
+ "an even number is expected%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (dynamic) /* maximum value */
+ params->bs_power_max_db = value;
+ else /* static (fixed) value */
+ params->bs_power_val_db = value;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ctrl_interval,
+ cfg_power_ctrl_ctrl_interval_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ctrl-interval <0-31>",
+ "Set power control interval (for dynamic mode)\n"
+ "P_CON_INTERVAL, in units of 2 SACCH periods (0.96 seconds)(default=1)\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+
+ params->ctrl_interval = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_step_size,
+ cfg_power_ctrl_step_size_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "step-size inc <2-6> red <2-4>",
+ "Set power change step size (for dynamic mode)\n"
+ "Increase step size (default is 4 dB)\n"
+ "Step size (2, 4, or 6 dB)\n"
+ "Reduce step size (default is 2 dB)\n"
+ "Step size (2 or 4 dB)\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ int inc_step_size_db = atoi(argv[0]);
+ int red_step_size_db = atoi(argv[1]);
+
+ if (inc_step_size_db % 2 || red_step_size_db % 2) {
+ vty_out(vty, "%% Power change step size must be "
+ "an even number%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Recommendation: POW_RED_STEP_SIZE <= POW_INCR_STEP_SIZE */
+ if (red_step_size_db > inc_step_size_db) {
+ vty_out(vty, "%% Increase step size (%d) should be greater "
+ "than reduce step size (%d), consider changing it%s",
+ inc_step_size_db, red_step_size_db, VTY_NEWLINE);
+ }
+
+ /* Recommendation: POW_INCR_STEP_SIZE <= (U_RXLEV_XX_P - L_RXLEV_XX_P) */
+ const struct gsm_power_ctrl_meas_params *mp = &params->rxlev_meas;
+ if (inc_step_size_db > (mp->upper_thresh - mp->lower_thresh)) {
+ vty_out(vty, "%% Increase step size (%d) should be less or equal "
+ "than/to the RxLev threshold window (%d, upper - lower), "
+ "consider changing it%s", inc_step_size_db,
+ mp->upper_thresh - mp->lower_thresh, VTY_NEWLINE);
+ }
+
+ params->inc_step_size_db = inc_step_size_db;
+ params->red_step_size_db = red_step_size_db;
+
+ return CMD_SUCCESS;
+}
+
+#define POWER_CONTROL_MEAS_RXLEV_DESC \
+ "RxLev value (signal strength, 0 is worst, 63 is best)\n"
+#define POWER_CONTROL_MEAS_RXQUAL_DESC \
+ "RxQual value (signal quality, 0 is best, 7 is worst)\n"
+#define POWER_CONTROL_MEAS_CI_DESC \
+ "C/I value (Carrier-to-Interference (dB), 0 is worst, 30 is best)\n"
+
+DEFUN_USRATTR(cfg_power_ctrl_rxlev_thresh,
+ cfg_power_ctrl_rxlev_thresh_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "rxlev-thresh lower <0-63> upper <0-63>",
+ "Set target RxLev thresholds (for dynamic mode)\n"
+ "Lower RxLev value (default is 32, i.e. -78 dBm)\n"
+ "Lower " POWER_CONTROL_MEAS_RXLEV_DESC
+ "Upper RxLev value (default is 38, i.e. -72 dBm)\n"
+ "Upper " POWER_CONTROL_MEAS_RXLEV_DESC)
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ int lower = atoi(argv[0]);
+ int upper = atoi(argv[1]);
+
+ if (lower > upper) {
+ vty_out(vty, "%% Lower 'rxlev-thresh' (%d) must be less than upper (%d)%s",
+ lower, upper, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ params->rxlev_meas.lower_thresh = lower;
+ params->rxlev_meas.upper_thresh = upper;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_rxqual_thresh,
+ cfg_power_ctrl_rxqual_thresh_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "rxqual-thresh lower <0-7> upper <0-7>",
+ "Set target RxQual thresholds (for dynamic mode)\n"
+ "Lower RxQual value (default is 3, i.e. 0.8% <= BER < 1.6%)\n"
+ "Lower " POWER_CONTROL_MEAS_RXQUAL_DESC
+ "Upper RxQual value (default is 0, i.e. BER < 0.2%)\n"
+ "Upper " POWER_CONTROL_MEAS_RXQUAL_DESC)
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ int lower = atoi(argv[0]);
+ int upper = atoi(argv[1]);
+
+ /* RxQual: 0 is best, 7 is worst, so upper must be less */
+ if (upper > lower) {
+ vty_out(vty, "%% Upper 'rxqual-rxqual' (%d) must be less than lower (%d)%s",
+ upper, lower, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ params->rxqual_meas.lower_thresh = lower;
+ params->rxqual_meas.upper_thresh = upper;
+
+ return CMD_SUCCESS;
+}
+
+#define VTY_CMD_CI_TYPE "(fr-efr|hr|amr-fr|amr-hr|sdcch|gprs)"
+#define VTY_CMD_CI_OR_ALL_TYPE "(fr-efr|hr|amr-fr|amr-hr|sdcch|gprs|all)"
+#define VTY_DESC_CI_TYPE \
+ "Channel Type FR/EFR\n" \
+ "Channel Type HR\n" \
+ "Channel Type AMR FR\n" \
+ "Channel Type AMR HR\n" \
+ "Channel Type SDCCH\n" \
+ "Channel Type (E)GPRS\n"
+#define VTY_DESC_CI_OR_ALL_TYPE VTY_DESC_CI_TYPE "All Channel Types\n"
+
+static struct gsm_power_ctrl_meas_params *ci_thresh_by_conn_type(struct gsm_power_ctrl_params *params, const char *type)
+{
+ if (!strcmp(type, "fr-efr"))
+ return &params->ci_fr_meas;
+ if (!strcmp(type, "hr"))
+ return &params->ci_hr_meas;
+ if (!strcmp(type, "amr-fr"))
+ return &params->ci_amr_fr_meas;
+ if (!strcmp(type, "amr-hr"))
+ return &params->ci_amr_hr_meas;
+ if (!strcmp(type, "sdcch"))
+ return &params->ci_sdcch_meas;
+ if (!strcmp(type, "gprs"))
+ return &params->ci_gprs_meas;
+ OSMO_ASSERT(false);
+ return NULL;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_thresh,
+ cfg_power_ctrl_ci_thresh_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ci-thresh " VTY_CMD_CI_TYPE " lower <0-30> upper <0-30>",
+ "Set target C/I thresholds (for dynamic mode), only available in ms-power-control\n"
+ VTY_DESC_CI_TYPE
+ "Lower C/I value\n"
+ "Lower " POWER_CONTROL_MEAS_CI_DESC
+ "Upper C/I value\n"
+ "Upper " POWER_CONTROL_MEAS_CI_DESC)
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ const char *type = argv[0];
+ int lower = atoi(argv[1]);
+ int upper = atoi(argv[2]);
+ struct gsm_power_ctrl_meas_params *meas_params;
+
+ if (params->mode == GSM_PWR_CTRL_MODE_DYN_BSC) {
+ vty_out(vty, "%% C/I based power loop not possible in dyn-bsc mode!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (params->dir != GSM_PWR_CTRL_DIR_UL) {
+ vty_out(vty, "%% C/I based power loop only possible in Uplink!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (lower > upper) {
+ vty_out(vty, "%% Lower 'rxqual-rxqual' (%d) must be less than upper (%d)%s",
+ upper, lower, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ meas_params = ci_thresh_by_conn_type(params, type);
+
+ meas_params->lower_thresh = lower;
+ meas_params->upper_thresh = upper;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_thresh_disable,
+ cfg_power_ctrl_ci_thresh_disable_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ci-thresh " VTY_CMD_CI_OR_ALL_TYPE " (enable|disable)",
+ "Set target C/I thresholds (for dynamic mode), only available in ms-power-control\n"
+ VTY_DESC_CI_OR_ALL_TYPE
+ "Enable C/I comparison in control loop\n"
+ "Disable C/I comparison in control loop\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+
+ bool enable = strcmp(argv[1], "enable") == 0;
+
+ if (strcmp(argv[0], "all") == 0) {
+ params->ci_fr_meas.enabled = enable;
+ params->ci_hr_meas.enabled = enable;
+ params->ci_amr_fr_meas.enabled = enable;
+ params->ci_amr_hr_meas.enabled = enable;
+ params->ci_sdcch_meas.enabled = enable;
+ params->ci_gprs_meas.enabled = enable;
+ } else {
+ struct gsm_power_ctrl_meas_params *meas_params = ci_thresh_by_conn_type(params, argv[0]);
+ meas_params->enabled = enable;
+ }
+
+ return CMD_SUCCESS;
+}
+
+#define POWER_CONTROL_MEAS_THRESH_COMP_CMD(meas) \
+ meas " lower <0-31> <0-31> upper <0-31> <0-31>"
+#define POWER_CONTROL_MEAS_THRESH_COMP_DESC(meas, opt_param, lp, ln, up, un) \
+ "Set " meas " threshold comparators (for dynamic mode)\n" \
+ opt_param \
+ "Lower " meas " threshold comparators (see 3GPP TS 45.008, A.3.2.1)\n" lp ln \
+ "Upper " meas " threshold comparators (see 3GPP TS 45.008, A.3.2.1)\n" up un
+
+DEFUN_USRATTR(cfg_power_ctrl_rxlev_thresh_comp,
+ cfg_power_ctrl_rxlev_thresh_comp_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ POWER_CONTROL_MEAS_THRESH_COMP_CMD("rxlev-thresh-comp"),
+ POWER_CONTROL_MEAS_THRESH_COMP_DESC("RxLev", /*empty*/,
+ "P1 (default 10)\n", "N1 (default 12)\n",
+ "P2 (default 10)\n", "N2 (default 12)\n"))
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ int lower_cmp_p = atoi(argv[0]);
+ int lower_cmp_n = atoi(argv[1]);
+ int upper_cmp_p = atoi(argv[2]);
+ int upper_cmp_n = atoi(argv[3]);
+
+ if (lower_cmp_p > lower_cmp_n) {
+ vty_out(vty, "%% Lower RxLev P1 %d must be less than N1 %d%s",
+ lower_cmp_p, lower_cmp_n, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (upper_cmp_p > upper_cmp_n) {
+ vty_out(vty, "%% Upper RxLev P2 %d must be less than N2 %d%s",
+ upper_cmp_p, upper_cmp_n, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ params->rxlev_meas.lower_cmp_p = lower_cmp_p;
+ params->rxlev_meas.lower_cmp_n = lower_cmp_n;
+ params->rxlev_meas.upper_cmp_p = upper_cmp_p;
+ params->rxlev_meas.upper_cmp_n = upper_cmp_n;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_rxqual_thresh_comp,
+ cfg_power_ctrl_rxqual_thresh_comp_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ POWER_CONTROL_MEAS_THRESH_COMP_CMD("rxqual-thresh-comp"),
+ POWER_CONTROL_MEAS_THRESH_COMP_DESC("RxQual", /*empty*/,
+ "P3 (default 5)\n", "N3 (default 7)\n",
+ "P4 (default 15)\n", "N4 (default 18)\n"))
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ int lower_cmp_p = atoi(argv[0]);
+ int lower_cmp_n = atoi(argv[1]);
+ int upper_cmp_p = atoi(argv[2]);
+ int upper_cmp_n = atoi(argv[3]);
+
+ if (lower_cmp_p > lower_cmp_n) {
+ vty_out(vty, "%% Lower RxQual P3 %d must be less than N3 %d%s",
+ lower_cmp_p, lower_cmp_n, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (upper_cmp_p > upper_cmp_n) {
+ vty_out(vty, "%% Upper RxQual P4 %d must be less than N4 %d%s",
+ upper_cmp_p, upper_cmp_n, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ params->rxqual_meas.lower_cmp_p = lower_cmp_p;
+ params->rxqual_meas.lower_cmp_n = lower_cmp_n;
+ params->rxqual_meas.upper_cmp_p = upper_cmp_p;
+ params->rxqual_meas.upper_cmp_n = upper_cmp_n;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_thresh_comp,
+ cfg_power_ctrl_ci_thresh_comp_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ POWER_CONTROL_MEAS_THRESH_COMP_CMD("ci-thresh-comp " VTY_CMD_CI_TYPE),
+ POWER_CONTROL_MEAS_THRESH_COMP_DESC("Carrier-to_interference (C/I)",
+ VTY_DESC_CI_TYPE,
+ "Lower P (default 5)\n", "Lower N (default 7)\n",
+ "Upper P (default 15)\n", "Upper N (default 18)\n"))
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *meas_params;
+ int lower_cmp_p = atoi(argv[1]);
+ int lower_cmp_n = atoi(argv[2]);
+ int upper_cmp_p = atoi(argv[3]);
+ int upper_cmp_n = atoi(argv[4]);
+
+ if (lower_cmp_p > lower_cmp_n) {
+ vty_out(vty, "%% Lower C/I P %d must be less than N %d%s",
+ lower_cmp_p, lower_cmp_n, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (upper_cmp_p > upper_cmp_n) {
+ vty_out(vty, "%% Upper C/I P %d must be less than N %d%s",
+ upper_cmp_p, upper_cmp_n, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ meas_params = ci_thresh_by_conn_type(params, argv[0]);
+
+ meas_params->lower_cmp_p = lower_cmp_p;
+ meas_params->lower_cmp_n = lower_cmp_n;
+ meas_params->upper_cmp_p = upper_cmp_p;
+ meas_params->upper_cmp_n = upper_cmp_n;
+
+ return CMD_SUCCESS;
+}
+
+#define POWER_CONTROL_MEAS_AVG_CMD \
+ "(rxlev-avg|rxqual-avg)"
+#define POWER_CONTROL_MEAS_AVG_DESC \
+ "RxLev (signal strength) measurement averaging (for dynamic mode)\n" \
+ "RxQual (signal quality) measurement averaging (for dynamic mode)\n"
+
+#define POWER_CONTROL_MEAS_AVG_PARAMS(params) \
+ (strncmp(argv[0], "rxlev", 5) == 0) ? \
+ &params->rxlev_meas : &params->rxqual_meas
+
+DEFUN_USRATTR(cfg_power_ctrl_no_avg,
+ cfg_power_ctrl_no_avg_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no " POWER_CONTROL_MEAS_AVG_CMD,
+ NO_STR POWER_CONTROL_MEAS_AVG_DESC)
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+
+ avg_params = POWER_CONTROL_MEAS_AVG_PARAMS(params);
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_avg_params,
+ cfg_power_ctrl_avg_params_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ POWER_CONTROL_MEAS_AVG_CMD " params hreqave <1-31> hreqt <1-31>",
+ POWER_CONTROL_MEAS_AVG_DESC "Configure general averaging parameters\n"
+ "Hreqave: the period over which an average is produced\n"
+ "Hreqave value (so that Hreqave * Hreqt < 32)\n"
+ "Hreqt: the number of averaged results that are maintained\n"
+ "Hreqt value (so that Hreqave * Hreqt < 32)\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+ int h_reqave = atoi(argv[1]);
+ int h_reqt = atoi(argv[2]);
+
+ if (h_reqave * h_reqt > 31) {
+ vty_out(vty, "%% Hreqave (%d) * Hreqt (%d) = %d must be < 32%s",
+ h_reqave, h_reqt, h_reqave * h_reqt, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ avg_params = POWER_CONTROL_MEAS_AVG_PARAMS(params);
+ avg_params->h_reqave = h_reqave;
+ avg_params->h_reqt = h_reqt;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_avg_algo,
+ cfg_power_ctrl_avg_algo_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ /* FIXME: add algorithm specific parameters */
+ POWER_CONTROL_MEAS_AVG_CMD " algo (unweighted|weighted|mod-median)",
+ POWER_CONTROL_MEAS_AVG_DESC "Select the averaging algorithm\n"
+ "Un-weighted average\n" "Weighted average\n"
+ "Modified median calculation\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+
+ avg_params = POWER_CONTROL_MEAS_AVG_PARAMS(params);
+ if (strcmp(argv[1], "unweighted") == 0)
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED;
+ else if (strcmp(argv[1], "weighted") == 0)
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED;
+ else if (strcmp(argv[1], "mod-median") == 0)
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_avg_osmo_ewma,
+ cfg_power_ctrl_avg_osmo_ewma_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ POWER_CONTROL_MEAS_AVG_CMD " algo osmo-ewma beta <1-99>",
+ POWER_CONTROL_MEAS_AVG_DESC "Select the averaging algorithm\n"
+ "Exponentially Weighted Moving Average (EWMA)\n"
+ "Smoothing factor (in %): beta = (100 - alpha)\n"
+ "1% - lowest smoothing, 99% - highest smoothing\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+ const struct gsm_bts *bts;
+
+ if (params->dir == GSM_PWR_CTRL_DIR_UL)
+ bts = container_of(params, struct gsm_bts, ms_power_ctrl);
+ else
+ bts = container_of(params, struct gsm_bts, bs_power_ctrl);
+
+ if (bts->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% EWMA is an OsmoBTS specific algorithm, "
+ "it's not usable for other BTS types%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ avg_params = POWER_CONTROL_MEAS_AVG_PARAMS(params);
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA;
+ avg_params->ewma.alpha = 100 - atoi(argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+/* C/I related power control measurements */
+#define POWER_CONTROL_CI_MEAS_AVG_DESC \
+ "C/I (Carrier-to-Interference) measurement averaging (for dynamic mode)\n"
+
+DEFUN_USRATTR(cfg_power_ctrl_no_ci_avg,
+ cfg_power_ctrl_no_ci_avg_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "no ci-avg " VTY_CMD_CI_TYPE,
+ NO_STR POWER_CONTROL_CI_MEAS_AVG_DESC VTY_DESC_CI_TYPE)
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+
+ avg_params = ci_thresh_by_conn_type(params, argv[0]);
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_avg_params,
+ cfg_power_ctrl_ci_avg_params_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ci-avg " VTY_CMD_CI_TYPE " params hreqave <1-31> hreqt <1-31>",
+ POWER_CONTROL_CI_MEAS_AVG_DESC VTY_DESC_CI_TYPE
+ "Configure general averaging parameters\n"
+ "Hreqave: the period over which an average is produced\n"
+ "Hreqave value (so that Hreqave * Hreqt < 32)\n"
+ "Hreqt: the number of averaged results that are maintained\n"
+ "Hreqt value (so that Hreqave * Hreqt < 32)\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+ int h_reqave = atoi(argv[1]);
+ int h_reqt = atoi(argv[2]);
+
+ if (h_reqave * h_reqt > 31) {
+ vty_out(vty, "%% Hreqave (%d) * Hreqt (%d) = %d must be < 32%s",
+ h_reqave, h_reqt, h_reqave * h_reqt, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ avg_params = ci_thresh_by_conn_type(params, argv[0]);
+ avg_params->h_reqave = h_reqave;
+ avg_params->h_reqt = h_reqt;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_avg_algo,
+ cfg_power_ctrl_ci_avg_algo_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ /* FIXME: add algorithm specific parameters */
+ "ci-avg " VTY_CMD_CI_TYPE " algo (unweighted|weighted|mod-median)",
+ POWER_CONTROL_CI_MEAS_AVG_DESC VTY_DESC_CI_TYPE
+ "Select the averaging algorithm\n"
+ "Un-weighted average\n" "Weighted average\n"
+ "Modified median calculation\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+
+ avg_params = ci_thresh_by_conn_type(params, argv[0]);
+ if (strcmp(argv[1], "unweighted") == 0)
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED;
+ else if (strcmp(argv[1], "weighted") == 0)
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED;
+ else if (strcmp(argv[1], "mod-median") == 0)
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_power_ctrl_ci_avg_osmo_ewma,
+ cfg_power_ctrl_ci_avg_osmo_ewma_cmd,
+ X(BSC_VTY_ATTR_VENDOR_SPECIFIC) |
+ X(BSC_VTY_ATTR_NEW_LCHAN),
+ "ci-avg " VTY_CMD_CI_TYPE " algo osmo-ewma beta <1-99>",
+ POWER_CONTROL_CI_MEAS_AVG_DESC VTY_DESC_CI_TYPE
+ "Select the averaging algorithm\n"
+ "Exponentially Weighted Moving Average (EWMA)\n"
+ "Smoothing factor (in %): beta = (100 - alpha)\n"
+ "1% - lowest smoothing, 99% - highest smoothing\n")
+{
+ struct gsm_power_ctrl_params *params = vty->index;
+ struct gsm_power_ctrl_meas_params *avg_params;
+ const struct gsm_bts *bts;
+
+ if (params->dir == GSM_PWR_CTRL_DIR_UL)
+ bts = container_of(params, struct gsm_bts, ms_power_ctrl);
+ else
+ bts = container_of(params, struct gsm_bts, bs_power_ctrl);
+
+ if (bts->type != GSM_BTS_TYPE_OSMOBTS) {
+ vty_out(vty, "%% EWMA is an OsmoBTS specific algorithm, "
+ "it's not usable for other BTS types%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ avg_params = ci_thresh_by_conn_type(params, argv[0]);
+ avg_params->algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA;
+ avg_params->ewma.alpha = 100 - atoi(argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+static void vty_out_neigh_list(struct vty *vty, struct bitvec *bv)
+{
+ int count = 0;
+ 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, %zu-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)
+{
+ unsigned int i;
+ bool no_features = true;
+ vty_out(vty, " Features:%s", VTY_NEWLINE);
+
+ for (i = 0; i < _NUM_BTS_FEAT; i++) {
+ if (osmo_bts_has_feature(&bts->features, i)) {
+ vty_out(vty, " %03u ", i);
+ vty_out(vty, "%-40s%s", osmo_bts_features_desc(i), VTY_NEWLINE);
+ no_features = false;
+ }
+ }
+
+ if (no_features)
+ vty_out(vty, " (not available)%s", VTY_NEWLINE);
+}
+
+void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
+{
+ struct pchan_load pl;
+ unsigned long long sec;
+ struct gsm_bts_trx *trx;
+ int ts_hopping_total;
+ int ts_non_hopping_total;
+ const struct rate_ctr *activations_tch;
+ const struct rate_ctr *activations_sdcch;
+
+ vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, "
+ "BSIC %u (NCC=%u, BCC=%u) and %u TRX%s",
+ bts->nr, btstype2str(bts->type), gsm_band_name(bts->band),
+ bts->cell_identity,
+ bts->location_area_code, bts->bsic,
+ bts->bsic >> 3, bts->bsic & 7,
+ bts->num_trx, VTY_NEWLINE);
+ vty_out(vty, " Description: %s%s",
+ bts->description ? bts->description : "(null)", VTY_NEWLINE);
+
+ vty_out(vty, " ARFCNs:");
+ ts_hopping_total = 0;
+ ts_non_hopping_total = 0;
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ int ts_nr;
+ int ts_hopping = 0;
+ int ts_non_hopping = 0;
+ for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
+ if (ts->hopping.enabled)
+ ts_hopping++;
+ else
+ ts_non_hopping++;
+ }
+
+ if (ts_non_hopping)
+ vty_out(vty, " %u", trx->arfcn);
+ ts_hopping_total += ts_hopping;
+ ts_non_hopping_total += ts_non_hopping;
+ }
+ if (ts_hopping_total) {
+ if (ts_non_hopping_total)
+ vty_out(vty, " / Hopping on %d of %d timeslots",
+ ts_hopping_total, ts_hopping_total + ts_non_hopping_total);
+ else
+ vty_out(vty, " Hopping on all %d timeslots", ts_hopping_total);
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ if (strnlen(bts->pcu_version, MAX_VERSION_LENGTH))
+ vty_out(vty, " PCU version %s connected%s", bts->pcu_version,
+ VTY_NEWLINE);
+ vty_out(vty, " BCCH carrier power reduction (maximum): %u dB%s",
+ bts->c0_max_power_red_db, VTY_NEWLINE);
+ vty_out(vty, " MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE);
+ vty_out(vty, " Minimum Rx Level for Access: %i dBm%s",
+ rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min),
+ VTY_NEWLINE);
+ vty_out(vty, " Cell Reselection Hysteresis: %u dBm%s",
+ bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
+ vty_out(vty, " Access Control Class rotation allow mask: 0x%" PRIx16 "%s",
+ bts->acc_mgr.allowed_subset_mask, VTY_NEWLINE);
+ vty_out(vty, " Access Control Class ramping: %senabled%s",
+ acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "not ", VTY_NEWLINE);
+ if (acc_ramp_is_enabled(&bts->acc_ramp)) {
+ vty_out(vty, " Access Control Class ramping step interval: %u seconds%s",
+ acc_ramp_get_step_interval(&bts->acc_ramp), VTY_NEWLINE);
+ vty_out(vty, " Access Control Class channel load thresholds: (%" PRIu8 ", %" PRIu8 ")%s",
+ bts->acc_ramp.chan_load_lower_threshold,
+ bts->acc_ramp.chan_load_upper_threshold, VTY_NEWLINE);
+ vty_out(vty, " enabling %u Access Control Class%s per ramping step%s",
+ acc_ramp_get_step_size(&bts->acc_ramp),
+ acc_ramp_get_step_size(&bts->acc_ramp) > 1 ? "es" : "", VTY_NEWLINE);
+ }
+ vty_out(vty, " RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer,
+ VTY_NEWLINE);
+ vty_out(vty, " RACH Max transmissions: %u%s",
+ rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
+ VTY_NEWLINE);
+ vty_out(vty, " RACH Max Delay (Max Access Delay IE in CHANnel ReQuireD): %u%s",
+ bts->rach_max_delay, VTY_NEWLINE);
+ if (bts->si_common.rach_control.cell_bar)
+ vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE);
+ if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
+ vty_out(vty, " Uplink DTX: %s%s",
+ (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ?
+ "enabled" : "forced", VTY_NEWLINE);
+ else
+ vty_out(vty, " Uplink DTX: not enabled%s", VTY_NEWLINE);
+ vty_out(vty, " Downlink DTX: %senabled%s", bts->dtxd ? "" : "not ",
+ VTY_NEWLINE);
+ vty_out(vty, " Channel Description Attachment: %s%s",
+ (bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE);
+ vty_out(vty, " Channel Description BS-PA-MFRMS: %u%s",
+ bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE);
+ vty_out(vty, " Channel Description BS-AG_BLKS-RES: %u%s",
+ bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE);
+ vty_out(vty, " System Information present: 0x%08x, static: 0x%08x%s",
+ bts->si_valid, bts->si_mode_static, VTY_NEWLINE);
+ vty_out(vty, " Early Classmark Sending: 2G %s, 3G %s%s%s",
+ bts->early_classmark_allowed ? "allowed" : "forbidden",
+ bts->early_classmark_allowed_3g ? "allowed" : "forbidden",
+ bts->early_classmark_allowed_3g && !bts->early_classmark_allowed ?
+ " (forbidden by 2G bit)" : "",
+ VTY_NEWLINE);
+ if (bts->pcu_sock_path)
+ vty_out(vty, " PCU Socket Path: %s%s", bts->pcu_sock_path, VTY_NEWLINE);
+ if (is_ipaccess_bts(bts))
+ vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s",
+ bts->ip_access.site_id, bts->ip_access.bts_id,
+ bts->oml_tei, VTY_NEWLINE);
+ else if (bts->type == GSM_BTS_TYPE_NOKIA_SITE)
+ vty_out(vty, " Skip Reset: %d%s",
+ bts->nokia.skip_reset, VTY_NEWLINE);
+ vty_out(vty, " NM State: ");
+ net_dump_nmstate(vty, &bts->mo.nm_state);
+ vty_out(vty, " Site Mgr NM State: ");
+ net_dump_nmstate(vty, &bts->site_mgr->mo.nm_state);
+
+ if (bts->gprs.mode != BTS_GPRS_NONE) {
+ vty_out(vty, " GPRS NSE: ");
+ net_dump_nmstate(vty, &bts->site_mgr->gprs.nse.mo.nm_state);
+ vty_out(vty, " GPRS CELL: ");
+ net_dump_nmstate(vty, &bts->gprs.cell.mo.nm_state);
+ vty_out(vty, " GPRS NSVC0: ");
+ net_dump_nmstate(vty, &bts->site_mgr->gprs.nsvc[0].mo.nm_state);
+ vty_out(vty, " GPRS NSVC1: ");
+ net_dump_nmstate(vty, &bts->site_mgr->gprs.nsvc[1].mo.nm_state);
+ } else
+ vty_out(vty, " GPRS: not configured%s", VTY_NEWLINE);
+
+ vty_out(vty, " Paging: %u pending requests, %u free slots%s",
+ paging_pending_requests_nr(bts),
+ bts->paging.available_slots, VTY_NEWLINE);
+ if (is_ipaccess_bts(bts)) {
+ vty_out(vty, " OML Link: ");
+ e1isl_dump_vty_tcp(vty, bts->oml_link);
+ vty_out(vty, " OML Link state: %s", get_model_oml_status(bts));
+ if (bts_setup_ramp_active(bts->network))
+ vty_out(vty, " BTS Ramping: %s", bts_setup_ramp_get_state_str(bts));
+ sec = bts_uptime(bts);
+ if (sec)
+ vty_out(vty, " %llu days %llu hours %llu min. %llu sec.",
+ OSMO_SEC2DAY(sec), OSMO_SEC2HRS(sec), OSMO_SEC2MIN(sec), sec % 60);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ } else {
+ vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
+ e1isl_dump_vty(vty, bts->oml_link);
+ }
+
+ vty_out(vty, " Neighbor Cells: ");
+ switch (bts->neigh_list_manual_mode) {
+ default:
+ case NL_MODE_AUTOMATIC:
+ vty_out(vty, "Automatic");
+ /* generate_bcch_chan_list() should populate si_common.neigh_list */
+ break;
+ case NL_MODE_MANUAL:
+ vty_out(vty, "Manual");
+ break;
+ case NL_MODE_MANUAL_SI5SEP:
+ vty_out(vty, "Manual/separate SI5");
+ break;
+ }
+ vty_out(vty, ", ARFCNs:");
+ vty_out_neigh_list(vty, &bts->si_common.neigh_list);
+ if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) {
+ vty_out(vty, " SI5:");
+ vty_out_neigh_list(vty, &bts->si_common.si5_neigh_list);
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ /* FIXME: chan_desc */
+ memset(&pl, 0, sizeof(pl));
+ bts_chan_load(&pl, bts);
+ vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE);
+ dump_pchan_load_vty(vty, " ", &pl);
+
+ bts_dump_vty_cbch(vty, &bts->cbch_basic);
+ bts_dump_vty_cbch(vty, &bts->cbch_extended);
+
+ vty_out(vty, " Channel Requests : %"PRIu64" total, %"PRIu64" no channel%s",
+ rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_TOTAL)->current,
+ rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHREQ_NO_CHANNEL)->current,
+ VTY_NEWLINE);
+
+ vty_out(vty, " Channel Activations :%s", VTY_NEWLINE);
+ activations_tch = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_ACT_TCH);
+ vty_out(vty, " TCH %"PRIu64"", activations_tch->current);
+ if (activations_tch->intv[RATE_CTR_INTV_HOUR].rate > 0) {
+ const struct rate_ctr *active_time_tch_ms = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_TCH_ACTIVE_MILLISECONDS_TOTAL);
+ vty_out(vty, " (avg lifespan %s seconds in last hour)",
+ osmo_int_to_float_str_c(OTC_SELECT,
+ active_time_tch_ms->intv[RATE_CTR_INTV_HOUR].rate
+ / activations_tch->intv[RATE_CTR_INTV_HOUR].rate, 3));
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+ activations_sdcch = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_ACT_SDCCH);
+ vty_out(vty, " SDCCH %"PRIu64"", activations_sdcch->current);
+ if (activations_sdcch->intv[RATE_CTR_INTV_HOUR].rate > 0) {
+ const struct rate_ctr *active_time_sdcch_ms = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_SDCCH_ACTIVE_MILLISECONDS_TOTAL);
+ vty_out(vty, " (avg lifespan %s seconds in last hour)",
+ osmo_int_to_float_str_c(OTC_SELECT,
+ active_time_sdcch_ms->intv[RATE_CTR_INTV_HOUR].rate
+ / activations_sdcch->intv[RATE_CTR_INTV_HOUR].rate, 3));
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, " Channel Failures : %"PRIu64" rf_failures, %"PRIu64" rll failures%s",
+ rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_RF_FAIL)->current,
+ rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_RLL_ERR)->current,
+ VTY_NEWLINE);
+ vty_out(vty, " BTS failures : %"PRIu64" OML, %"PRIu64" RSL%s",
+ rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_BTS_OML_FAIL)->current,
+ rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_BTS_RSL_FAIL)->current,
+ VTY_NEWLINE);
+
+ vty_out_stat_item_group(vty, " ", bts->bts_statg);
+
+ bts_dump_vty_features(vty, bts);
+}
+
+static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts)
+{
+ unsigned int i;
+ struct gsm_bts_sm *bts_sm = bts->site_mgr;
+ vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode),
+ VTY_NEWLINE);
+ if (bts->gprs.mode == BTS_GPRS_NONE)
+ return;
+
+ vty_out(vty, " gprs routing area %u%s", bts->gprs.rac,
+ VTY_NEWLINE);
+ vty_out(vty, " gprs network-control-order nc%u%s",
+ bts->gprs.net_ctrl_ord, VTY_NEWLINE);
+ if (!bts->gprs.ctrl_ack_type_use_block)
+ vty_out(vty, " gprs control-ack-type-rach%s", VTY_NEWLINE);
+ if (bts->gprs.ccn.forced_vty)
+ vty_out(vty, " gprs ccn-active %d%s",
+ bts->gprs.ccn.active ? 1 : 0, VTY_NEWLINE);
+ vty_out(vty, " gprs power-control alpha %u%s",
+ bts->gprs.pwr_ctrl.alpha, VTY_NEWLINE);
+ vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci,
+ VTY_NEWLINE);
+ for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++)
+ vty_out(vty, " gprs cell timer %s %u%s",
+ get_value_string(gprs_bssgp_cfg_strs, i),
+ bts->gprs.cell.timer[i], VTY_NEWLINE);
+ vty_out(vty, " gprs nsei %u%s", bts_sm->gprs.nse.nsei,
+ VTY_NEWLINE);
+ for (i = 0; i < ARRAY_SIZE(bts_sm->gprs.nse.timer); i++)
+ vty_out(vty, " gprs ns timer %s %u%s",
+ get_value_string(gprs_ns_timer_strs, i),
+ bts_sm->gprs.nse.timer[i], VTY_NEWLINE);
+ for (i = 0; i < ARRAY_SIZE(bts_sm->gprs.nsvc); i++) {
+ const struct gsm_gprs_nsvc *nsvc = &bts_sm->gprs.nsvc[i];
+ struct osmo_sockaddr_str remote;
+
+ vty_out(vty, " gprs nsvc %u nsvci %u%s", i,
+ nsvc->nsvci, VTY_NEWLINE);
+
+ vty_out(vty, " gprs nsvc %u local udp port %u%s", i,
+ nsvc->local_port, VTY_NEWLINE);
+
+ /* Most likely, the remote address is not configured (AF_UNSPEC).
+ * Printing the port alone makes no sense, so let's just skip both. */
+ if (osmo_sockaddr_str_from_sockaddr(&remote, &nsvc->remote.u.sas) != 0)
+ continue;
+
+ vty_out(vty, " gprs nsvc %u remote ip %s%s",
+ i, remote.ip, VTY_NEWLINE);
+ vty_out(vty, " gprs nsvc %u remote udp port %u%s",
+ i, remote.port, VTY_NEWLINE);
+ }
+
+ /* EGPRS specific parameters */
+ if (bts->gprs.mode == BTS_GPRS_EGPRS) {
+ if (bts->gprs.egprs_pkt_chan_request)
+ vty_out(vty, " gprs egprs-packet-channel-request%s", VTY_NEWLINE);
+ }
+}
+
+/* Write the model data if there is one */
+static void config_write_bts_model(struct vty *vty, struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+
+ if (!bts->model)
+ return;
+
+ if (bts->model->config_write_bts)
+ bts->model->config_write_bts(vty, bts);
+
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ config_write_trx_single(vty, trx);
+}
+
+static void write_amr_modes(struct vty *vty, const char *prefix,
+ const char *name, struct amr_mode *modes, int num)
+{
+ int i;
+
+ vty_out(vty, " %s threshold %s", prefix, name);
+ for (i = 0; i < num - 1; i++)
+ vty_out(vty, " %d", modes[i].threshold);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, " %s hysteresis %s", prefix, name);
+ for (i = 0; i < num - 1; i++)
+ vty_out(vty, " %d", modes[i].hysteresis);
+ vty_out(vty, "%s", VTY_NEWLINE);
+}
+
+static void config_write_bts_amr(struct vty *vty, struct gsm_bts *bts,
+ struct amr_multirate_conf *mr, int full)
+{
+ struct gsm48_multi_rate_conf *mr_conf;
+ const char *prefix = (full) ? "amr tch-f" : "amr tch-h";
+ int i, num;
+
+ if (!(mr->gsm48_ie[1]))
+ return;
+
+ mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
+
+ num = 0;
+ vty_out(vty, " %s modes", prefix);
+ for (i = 0; i < ((full) ? 8 : 6); i++) {
+ if ((mr->gsm48_ie[1] & (1 << i))) {
+ vty_out(vty, " %d", i);
+ num++;
+ }
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+ if (num > 4)
+ num = 4;
+ if (num > 1) {
+ write_amr_modes(vty, prefix, "ms", mr->ms_mode, num);
+ write_amr_modes(vty, prefix, "bts", mr->bts_mode, num);
+ }
+ vty_out(vty, " %s start-mode ", prefix);
+ if (mr_conf->icmi) {
+ num = 0;
+ for (i = 0; i < ((full) ? 8 : 6) && num < 4; i++) {
+ if ((mr->gsm48_ie[1] & (1 << i)))
+ num++;
+ if (mr_conf->smod == num - 1) {
+ vty_out(vty, "%d%s", num, VTY_NEWLINE);
+ break;
+ }
+ }
+ } else
+ vty_out(vty, "auto%s", VTY_NEWLINE);
+}
+
+/* TODO: generalize and move indention handling to libosmocore */
+#define cfg_out(fmt, args...) \
+ vty_out(vty, "%*s" fmt, indent, "", ##args);
+
+static void config_write_power_ctrl_meas(struct vty *vty, unsigned int indent,
+ const struct gsm_power_ctrl_meas_params *mp,
+ const char *param, const char *param2)
+{
+ if (strcmp(param, "ci") == 0) {
+ cfg_out("%s-thresh%s %s%s",
+ param, param2, mp->enabled ? "enable" : "disable",
+ VTY_NEWLINE);
+ }
+
+ cfg_out("%s-thresh%s lower %u upper %u%s",
+ param, param2, mp->lower_thresh, mp->upper_thresh,
+ VTY_NEWLINE);
+ cfg_out("%s-thresh-comp%s lower %u %u upper %u %u%s",
+ param, param2, mp->lower_cmp_p, mp->lower_cmp_n,
+ mp->upper_cmp_p, mp->upper_cmp_n,
+ VTY_NEWLINE);
+
+ switch (mp->algo) {
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE:
+ /* Do not print any averaging parameters */
+ return; /* we're done */
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_UNWEIGHTED:
+ cfg_out("%s-avg%s algo unweighted%s", param, param2, VTY_NEWLINE);
+ break;
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_WEIGHTED:
+ cfg_out("%s-avg%s algo weighted%s", param, param2, VTY_NEWLINE);
+ break;
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_MOD_MEDIAN:
+ cfg_out("%s-avg%s algo mod-median%s", param, param2, VTY_NEWLINE);
+ break;
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:
+ cfg_out("%s-avg%s algo osmo-ewma beta %u%s",
+ param, param2, 100 - mp->ewma.alpha,
+ VTY_NEWLINE);
+ break;
+ }
+
+ cfg_out("%s-avg%s params hreqave %u hreqt %u%s",
+ param, param2, mp->h_reqave, mp->h_reqt,
+ VTY_NEWLINE);
+}
+
+static void config_write_power_ctrl(struct vty *vty, unsigned int indent,
+ const struct gsm_bts *bts,
+ const struct gsm_power_ctrl_params *cp)
+{
+ const char *node_name;
+
+ if (cp->dir == GSM_PWR_CTRL_DIR_UL)
+ node_name = "ms-power-control";
+ else
+ node_name = "bs-power-control";
+
+ switch (cp->mode) {
+ case GSM_PWR_CTRL_MODE_NONE:
+ cfg_out("no %s%s", node_name, VTY_NEWLINE);
+ break;
+ case GSM_PWR_CTRL_MODE_STATIC:
+ cfg_out("%s%s", node_name, VTY_NEWLINE);
+ cfg_out(" mode static%s", VTY_NEWLINE);
+ if (cp->dir == GSM_PWR_CTRL_DIR_DL && cp->bs_power_val_db != 0)
+ cfg_out(" bs-power static %u%s", cp->bs_power_val_db, VTY_NEWLINE);
+ break;
+ case GSM_PWR_CTRL_MODE_DYN_BTS:
+ case GSM_PWR_CTRL_MODE_DYN_BSC:
+ cfg_out("%s%s", node_name, VTY_NEWLINE);
+ cfg_out(" mode %s%s",
+ cp->mode == GSM_PWR_CTRL_MODE_DYN_BTS ? "dyn-bts" : "dyn-bsc", VTY_NEWLINE);
+ if (cp->dir == GSM_PWR_CTRL_DIR_DL)
+ cfg_out(" bs-power dyn-max %u%s", cp->bs_power_max_db, VTY_NEWLINE);
+
+ cfg_out(" ctrl-interval %u%s", cp->ctrl_interval, VTY_NEWLINE);
+ cfg_out(" step-size inc %u red %u%s",
+ cp->inc_step_size_db, cp->red_step_size_db,
+ VTY_NEWLINE);
+
+ /* Measurement processing / averaging parameters */
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->rxlev_meas, "rxlev", "");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->rxqual_meas, "rxqual", "");
+ if (cp->dir == GSM_PWR_CTRL_DIR_UL && is_osmobts(bts)
+ && cp->mode == GSM_PWR_CTRL_MODE_DYN_BTS) {
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_fr_meas, "ci", " fr-efr");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_hr_meas, "ci", " hr");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_amr_fr_meas, "ci", " amr-fr");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_amr_hr_meas, "ci", " amr-hr");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_sdcch_meas, "ci", " sdcch");
+ config_write_power_ctrl_meas(vty, indent + 1, &cp->ci_gprs_meas, "ci", " gprs");
+ }
+ break;
+ }
+}
+
+#undef cfg_out
+
+static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
+{
+ int i;
+ uint8_t tmp;
+
+ vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE);
+ vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE);
+ if (bts->description)
+ vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE);
+ vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE);
+ vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE);
+ vty_out(vty, " location_area_code 0x%04x%s", bts->location_area_code,
+ VTY_NEWLINE);
+ if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
+ vty_out(vty, " dtx uplink%s%s",
+ (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? "" : " force",
+ VTY_NEWLINE);
+ if (bts->dtxd)
+ vty_out(vty, " dtx downlink%s", VTY_NEWLINE);
+ vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE);
+ vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE);
+ vty_out(vty, " cell reselection hysteresis %u%s",
+ bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
+ vty_out(vty, " rxlev access min %u%s",
+ bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE);
+
+ if (bts->si_common.cell_ro_sel_par.present) {
+ struct osmo_gsm48_si_selection_params *sp;
+ sp = &bts->si_common.cell_ro_sel_par;
+
+ if (sp->cbq)
+ vty_out(vty, " cell bar qualify %u%s",
+ sp->cbq, VTY_NEWLINE);
+
+ if (sp->cell_resel_off)
+ vty_out(vty, " cell reselection offset %u%s",
+ sp->cell_resel_off*2, VTY_NEWLINE);
+
+ if (sp->temp_offs == 7)
+ vty_out(vty, " temporary offset infinite%s",
+ VTY_NEWLINE);
+ else if (sp->temp_offs)
+ vty_out(vty, " temporary offset %u%s",
+ sp->temp_offs*10, VTY_NEWLINE);
+
+ if (sp->penalty_time == 31)
+ vty_out(vty, " penalty time reserved%s",
+ VTY_NEWLINE);
+ else if (sp->penalty_time)
+ vty_out(vty, " penalty time %u%s",
+ (sp->penalty_time*20)+20, VTY_NEWLINE);
+ }
+
+ if (gsm_bts_get_radio_link_timeout(bts) < 0)
+ vty_out(vty, " radio-link-timeout infinite%s", VTY_NEWLINE);
+ else
+ vty_out(vty, " radio-link-timeout %d%s",
+ gsm_bts_get_radio_link_timeout(bts), VTY_NEWLINE);
+
+ vty_out(vty, " channel allocator mode chan-req %s%s",
+ bts->chan_alloc_chan_req_reverse ? "descending" : "ascending",
+ VTY_NEWLINE);
+ if (bts->chan_alloc_assignment_dynamic) {
+ vty_out(vty, " channel allocator mode assignment dynamic%s",
+ VTY_NEWLINE);
+ vty_out(vty, " channel allocator dynamic-param sort-by-trx-power %c%s",
+ bts->chan_alloc_dyn_params.sort_by_trx_power ? '1' : '0',
+ VTY_NEWLINE);
+ vty_out(vty, " channel allocator dynamic-param ul-rxlev thresh %u avg-num %u%s",
+ bts->chan_alloc_dyn_params.ul_rxlev_thresh,
+ bts->chan_alloc_dyn_params.ul_rxlev_avg_num,
+ VTY_NEWLINE);
+ vty_out(vty, " channel allocator dynamic-param c0-chan-load thresh %u%s",
+ bts->chan_alloc_dyn_params.c0_chan_load_thresh,
+ VTY_NEWLINE);
+ } else {
+ vty_out(vty, " channel allocator mode assignment %s%s",
+ bts->chan_alloc_assignment_reverse ? "descending" : "ascending",
+ VTY_NEWLINE);
+ }
+ vty_out(vty, " channel allocator mode handover %s%s",
+ bts->chan_alloc_handover_reverse ? "descending" : "ascending",
+ VTY_NEWLINE);
+ if (bts->chan_alloc_avoid_interf)
+ vty_out(vty, " channel allocator avoid-interference 1%s", VTY_NEWLINE);
+ if (bts->chan_alloc_tch_signalling_policy == BTS_TCH_SIGNALLING_NEVER)
+ vty_out(vty, " channel allocator tch-signalling-policy never%s", VTY_NEWLINE);
+ else if (bts->chan_alloc_tch_signalling_policy == BTS_TCH_SIGNALLING_EMERG)
+ vty_out(vty, " channel allocator tch-signalling-policy emergency%s", VTY_NEWLINE);
+ else if (bts->chan_alloc_tch_signalling_policy == BTS_TCH_SIGNALLING_VOICE)
+ vty_out(vty, " channel allocator tch-signalling-policy voice%s", VTY_NEWLINE);
+ vty_out(vty, " rach tx integer %u%s",
+ bts->si_common.rach_control.tx_integer, VTY_NEWLINE);
+ vty_out(vty, " rach max transmission %u%s",
+ rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
+ VTY_NEWLINE);
+ vty_out(vty, " rach max-delay %u%s", bts->rach_max_delay, VTY_NEWLINE);
+
+ vty_out(vty, " channel-description attach %u%s",
+ bts->si_common.chan_desc.att, VTY_NEWLINE);
+ vty_out(vty, " channel-description bs-pa-mfrms %u%s",
+ bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE);
+ vty_out(vty, " channel-description bs-ag-blks-res %u%s",
+ bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE);
+
+ if (bts->ccch_load_ind_thresh != 10)
+ vty_out(vty, " ccch load-indication-threshold %u%s",
+ bts->ccch_load_ind_thresh, VTY_NEWLINE);
+ if (bts->ccch_load_ind_period != 1)
+ vty_out(vty, " ccch load-indication-period %u%s",
+ bts->ccch_load_ind_period, VTY_NEWLINE);
+ if (bts->rach_b_thresh != -1)
+ vty_out(vty, " rach nm busy threshold %u%s",
+ bts->rach_b_thresh, VTY_NEWLINE);
+ if (bts->rach_ldavg_slots != -1)
+ vty_out(vty, " rach nm load average %u%s",
+ bts->rach_ldavg_slots, VTY_NEWLINE);
+ if (bts->si_common.rach_control.cell_bar)
+ vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
+ if ((bts->si_common.rach_control.t2 & 0x4) == 0)
+ vty_out(vty, " rach emergency call allowed 1%s", VTY_NEWLINE);
+ if (bts->si_common.rach_control.re == 0)
+ vty_out(vty, " rach call-reestablishment allowed 1%s", VTY_NEWLINE);
+ if ((bts->si_common.rach_control.t3) != 0)
+ for (i = 0; i < 8; i++)
+ if (bts->si_common.rach_control.t3 & (0x1 << i))
+ vty_out(vty, " rach access-control-class %d barred%s", i, VTY_NEWLINE);
+ if ((bts->si_common.rach_control.t2 & 0xfb) != 0)
+ for (i = 0; i < 8; i++)
+ if ((i != 2) && (bts->si_common.rach_control.t2 & (0x1 << i)))
+ vty_out(vty, " rach access-control-class %d barred%s", i+8, VTY_NEWLINE);
+ if (bts->acc_mgr.len_allowed_adm < 10)
+ vty_out(vty, " access-control-class-rotate %" PRIu8 "%s", bts->acc_mgr.len_allowed_adm, VTY_NEWLINE);
+ if (bts->acc_mgr.rotation_time_sec != ACC_MGR_QUANTUM_DEFAULT)
+ vty_out(vty, " access-control-class-rotate-quantum %" PRIu32 "%s", bts->acc_mgr.rotation_time_sec, VTY_NEWLINE);
+ vty_out(vty, " %saccess-control-class-ramping%s", acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "no ", VTY_NEWLINE);
+ if (acc_ramp_is_enabled(&bts->acc_ramp)) {
+ vty_out(vty, " access-control-class-ramping-step-interval %u%s",
+ acc_ramp_get_step_interval(&bts->acc_ramp), VTY_NEWLINE);
+ vty_out(vty, " access-control-class-ramping-step-size %u%s", acc_ramp_get_step_size(&bts->acc_ramp),
+ VTY_NEWLINE);
+ vty_out(vty, " access-control-class-ramping-chan-load %u %u%s",
+ bts->acc_ramp.chan_load_lower_threshold, bts->acc_ramp.chan_load_upper_threshold, VTY_NEWLINE);
+ }
+ if (!bts->si_unused_send_empty)
+ vty_out(vty, " no system-information unused-send-empty%s", VTY_NEWLINE);
+ for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) {
+ if (bts->si_mode_static & (1 << i)) {
+ vty_out(vty, " system-information %s mode static%s",
+ get_value_string(osmo_sitype_strs, i), VTY_NEWLINE);
+ vty_out(vty, " system-information %s static %s%s",
+ get_value_string(osmo_sitype_strs, i),
+ osmo_hexdump_nospc(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN),
+ VTY_NEWLINE);
+ }
+ }
+ vty_out(vty, " early-classmark-sending %s%s",
+ bts->early_classmark_allowed ? "allowed" : "forbidden", VTY_NEWLINE);
+ vty_out(vty, " early-classmark-sending-3g %s%s",
+ bts->early_classmark_allowed_3g ? "allowed" : "forbidden", VTY_NEWLINE);
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ case GSM_BTS_TYPE_OSMOBTS:
+ vty_out(vty, " ipa unit-id %u %u%s",
+ bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
+ if (bts->ip_access.rsl_ip) {
+ struct in_addr ia;
+ ia.s_addr = htonl(bts->ip_access.rsl_ip);
+ vty_out(vty, " ipa rsl-ip %s%s", inet_ntoa(ia),
+ VTY_NEWLINE);
+ }
+ vty_out(vty, " oml ipa stream-id %u line %u%s",
+ bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE);
+ break;
+ case GSM_BTS_TYPE_NOKIA_SITE:
+ vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE);
+ vty_out(vty, " nokia_site no-local-rel-conf %d%s",
+ bts->nokia.no_loc_rel_cnf, VTY_NEWLINE);
+ vty_out(vty, " nokia_site bts-reset-timer %d%s", bts->nokia.bts_reset_timer_cnf, VTY_NEWLINE);
+ /* fall through: Nokia requires "oml e1" parameters also */
+ default:
+ config_write_e1_link(vty, &bts->oml_e1_link, " oml ");
+ vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE);
+ break;
+ }
+
+ /* if we have a limit, write it */
+ if (bts->paging.free_chans_need >= 0)
+ vty_out(vty, " paging free %d%s", bts->paging.free_chans_need, VTY_NEWLINE);
+ if (!bts->T3113_dynamic)
+ vty_out(vty, " no timer-dynamic T3113%s", VTY_NEWLINE);
+
+ vty_out(vty, " neighbor-list mode %s%s",
+ get_value_string(bts_neigh_mode_strs, bts->neigh_list_manual_mode), VTY_NEWLINE);
+ if (bts->neigh_list_manual_mode != NL_MODE_AUTOMATIC) {
+ for (i = 0; i < 1024; i++) {
+ if (bitvec_get_bit_pos(&bts->si_common.neigh_list, i))
+ vty_out(vty, " neighbor-list add arfcn %u%s",
+ i, VTY_NEWLINE);
+ }
+ }
+ if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) {
+ for (i = 0; i < 1024; i++) {
+ if (bitvec_get_bit_pos(&bts->si_common.si5_neigh_list, i))
+ vty_out(vty, " si5 neighbor-list add arfcn %u%s",
+ i, VTY_NEWLINE);
+ }
+ }
+
+ for (i = 0; i < MAX_EARFCN_LIST; i++) {
+ struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
+ if (e->arfcn[i] != OSMO_EARFCN_INVALID) {
+ vty_out(vty, " si2quater neighbor-list add earfcn %u "
+ "thresh-hi %u", e->arfcn[i], e->thresh_hi);
+
+ vty_out(vty, " thresh-lo %u",
+ e->thresh_lo_valid ? e->thresh_lo : 32);
+
+ vty_out(vty, " prio %u",
+ e->prio_valid ? e->prio : 8);
+
+ vty_out(vty, " qrxlv %u",
+ e->qrxlm_valid ? e->qrxlm : 32);
+
+ tmp = e->meas_bw[i];
+ vty_out(vty, " meas %u",
+ (tmp != OSMO_EARFCN_MEAS_INVALID) ? tmp : 8);
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ }
+
+ for (i = 0; i < bts->si_common.uarfcn_length; i++) {
+ vty_out(vty, " si2quater neighbor-list add uarfcn %u %u %u%s",
+ bts->si_common.data.uarfcn_list[i],
+ bts->si_common.data.scramble_list[i] & ~(1 << 9),
+ (bts->si_common.data.scramble_list[i] >> 9) & 1,
+ VTY_NEWLINE);
+ }
+
+ neighbor_ident_vty_write_bts(vty, " ", bts);
+
+ vty_out(vty, " codec-support fr");
+ if (bts->codec.hr)
+ vty_out(vty, " hr");
+ if (bts->codec.efr)
+ vty_out(vty, " efr");
+ if (bts->codec.amr)
+ vty_out(vty, " amr");
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ config_write_bts_amr(vty, bts, &bts->mr_full, 1);
+ config_write_bts_amr(vty, bts, &bts->mr_half, 0);
+
+ if (bts->use_osmux != OSMUX_USAGE_OFF) {
+ vty_out(vty, " osmux %s%s", bts->use_osmux == OSMUX_USAGE_ON ? "on" : "only",
+ VTY_NEWLINE);
+ }
+
+ if (bts->mgw_pool_target > -1) {
+ vty_out(vty, " mgw pool-target %u%s%s",
+ bts->mgw_pool_target,
+ bts->mgw_pool_target_strict ? " strict" : "",
+ VTY_NEWLINE);
+ }
+
+ config_write_bts_gprs(vty, bts);
+
+ if (bts->excl_from_rf_lock)
+ vty_out(vty, " rf-lock-exclude%s", VTY_NEWLINE);
+
+ if (bts->force_combined_si_set)
+ vty_out(vty, " %sforce-combined-si%s",
+ bts->force_combined_si ? "" : "no ", VTY_NEWLINE);
+
+ for (i = 0; i < ARRAY_SIZE(bts->depends_on); ++i) {
+ int j;
+
+ if (bts->depends_on[i] == 0)
+ continue;
+
+ for (j = 0; j < sizeof(bts->depends_on[i]) * 8; ++j) {
+ int bts_nr;
+
+ if ((bts->depends_on[i] & (1<<j)) == 0)
+ continue;
+
+ bts_nr = (i * sizeof(bts->depends_on[i]) * 8) + j;
+ vty_out(vty, " depends-on-bts %d%s", bts_nr, VTY_NEWLINE);
+ }
+ }
+ if (bts->pcu_sock_path)
+ vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE);
+
+ ho_vty_write_bts(vty, bts);
+
+ if (bts->top_acch_cap.overpower_db > 0) {
+ const struct abis_rsl_osmo_temp_ovp_acch_cap *top = \
+ &bts->top_acch_cap;
+ const char *mode = NULL;
+
+ if (top->sacch_enable && top->facch_enable)
+ mode = "dl-acch";
+ else if (top->sacch_enable)
+ mode = "dl-sacch";
+ else if (top->facch_enable)
+ mode = "dl-facch";
+ else /* shall not happen */
+ OSMO_ASSERT(0);
+
+ vty_out(vty, " overpower %s %u%s",
+ mode, top->overpower_db, VTY_NEWLINE);
+ vty_out(vty, " overpower rxqual %u%s",
+ top->rxqual, VTY_NEWLINE);
+ vty_out(vty, " overpower chan-mode %s%s",
+ get_value_string(top_acch_chan_mode_name,
+ bts->top_acch_chan_mode),
+ VTY_NEWLINE);
+ }
+
+ if (bts->rep_acch_cap.dl_facch_all)
+ vty_out(vty, " repeat dl-facch all%s", VTY_NEWLINE);
+ else if (bts->rep_acch_cap.dl_facch_cmd)
+ vty_out(vty, " repeat dl-facch command%s", VTY_NEWLINE);
+ if (bts->rep_acch_cap.dl_sacch)
+ vty_out(vty, " repeat dl-sacch%s", VTY_NEWLINE);
+ if (bts->rep_acch_cap.ul_sacch)
+ vty_out(vty, " repeat ul-sacch%s", VTY_NEWLINE);
+ if (bts->rep_acch_cap.ul_sacch
+ || bts->rep_acch_cap.dl_facch_cmd
+ || bts->rep_acch_cap.dl_facch_cmd)
+ vty_out(vty, " repeat rxqual %u%s", bts->rep_acch_cap.rxqual, VTY_NEWLINE);
+
+ if (bts->interf_meas_params_cfg.avg_period != interf_meas_params_def.avg_period) {
+ vty_out(vty, " interference-meas avg-period %u%s",
+ bts->interf_meas_params_cfg.avg_period,
+ VTY_NEWLINE);
+ }
+ if (memcmp(bts->interf_meas_params_cfg.bounds_dbm,
+ interf_meas_params_def.bounds_dbm,
+ sizeof(interf_meas_params_def.bounds_dbm))) {
+ vty_out(vty, " interference-meas level-bounds "
+ "%d %d %d %d %d %d%s",
+ -1 * bts->interf_meas_params_cfg.bounds_dbm[0],
+ -1 * bts->interf_meas_params_cfg.bounds_dbm[1],
+ -1 * bts->interf_meas_params_cfg.bounds_dbm[2],
+ -1 * bts->interf_meas_params_cfg.bounds_dbm[3],
+ -1 * bts->interf_meas_params_cfg.bounds_dbm[4],
+ -1 * bts->interf_meas_params_cfg.bounds_dbm[5],
+ VTY_NEWLINE);
+ }
+
+ if (!bts->srvcc_fast_return_allowed)
+ vty_out(vty, " srvcc fast-return forbid%s", VTY_NEWLINE);
+
+ switch (bts->imm_ass_time) {
+ default:
+ case IMM_ASS_TIME_POST_CHAN_ACK:
+ /* default value */
+ break;
+ case IMM_ASS_TIME_PRE_CHAN_ACK:
+ vty_out(vty, " immediate-assignment pre-chan-ack%s", VTY_NEWLINE);
+ break;
+ case IMM_ASS_TIME_PRE_TS_ACK:
+ vty_out(vty, " immediate-assignment pre-ts-ack%s", VTY_NEWLINE);
+ break;
+ }
+
+ /* BS/MS Power Control parameters */
+ config_write_power_ctrl(vty, 2, bts, &bts->bs_power_ctrl);
+ config_write_power_ctrl(vty, 2, bts, &bts->ms_power_ctrl);
+
+ config_write_bts_model(vty, bts);
+}
+
+int config_write_bts(struct vty *v)
+{
+ struct gsm_network *gsmnet = gsmnet_from_vty(v);
+ struct gsm_bts *bts;
+
+ llist_for_each_entry(bts, &gsmnet->bts_list, list)
+ config_write_bts_single(v, bts);
+
+ return CMD_SUCCESS;
+}
+
+int bts_vty_init(void)
+{
+ cfg_bts_type_cmd.string =
+ vty_cmd_string_from_valstr(tall_bsc_ctx,
+ bts_type_names,
+ "type (", "|", ")",
+ VTY_DO_LOWER);
+ cfg_bts_type_cmd.doc =
+ vty_cmd_string_from_valstr(tall_bsc_ctx,
+ bts_type_descs,
+ "BTS Vendor/Type\n",
+ "\n", "", 0);
+
+ install_element(GSMNET_NODE, &cfg_bts_cmd);
+ install_node(&bts_node, config_write_bts);
+ install_element(BTS_NODE, &cfg_bts_type_cmd);
+ install_element(BTS_NODE, &cfg_bts_type_sysmobts_cmd);
+ install_element(BTS_NODE, &cfg_description_cmd);
+ install_element(BTS_NODE, &cfg_no_description_cmd);
+ install_element(BTS_NODE, &cfg_bts_band_cmd);
+ install_element(BTS_NODE, &cfg_bts_ci_cmd);
+ install_element(BTS_NODE, &cfg_bts_dtxu_cmd);
+ install_element(BTS_NODE, &cfg_bts_dtxd_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_dtxu_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_dtxd_cmd);
+ install_element(BTS_NODE, &cfg_bts_lac_cmd);
+ install_element(BTS_NODE, &cfg_bts_tsc_cmd);
+ install_element(BTS_NODE, &cfg_bts_bsic_cmd);
+ install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
+ install_element(BTS_NODE, &cfg_bts_deprecated_unit_id_cmd);
+ install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd);
+ install_element(BTS_NODE, &cfg_bts_deprecated_rsl_ip_cmd);
+ install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd);
+ install_element(BTS_NODE, &cfg_bts_nokia_site_no_loc_rel_cnf_cmd);
+ install_element(BTS_NODE, &cfg_bts_nokia_site_bts_reset_timer_cnf_cmd);
+ install_element(BTS_NODE, &cfg_bts_stream_id_cmd);
+ install_element(BTS_NODE, &cfg_bts_deprecated_stream_id_cmd);
+ install_element(BTS_NODE, &cfg_bts_oml_e1_cmd);
+ install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd);
+ install_element(BTS_NODE, &cfg_bts_challoc_mode_cmd);
+ install_element(BTS_NODE, &cfg_bts_challoc_mode_all_cmd);
+ install_element(BTS_NODE, &cfg_bts_challoc_mode_ass_dynamic_cmd);
+ install_element(BTS_NODE, &cfg_bts_challoc_dynamic_param_sort_by_trx_power_cmd);
+ install_element(BTS_NODE, &cfg_bts_challoc_dynamic_param_ul_rxlev_cmd);
+ install_element(BTS_NODE, &cfg_bts_challoc_dynamic_param_c0_chan_load_cmd);
+ install_element(BTS_NODE, &cfg_bts_chan_alloc_interf_cmd);
+ install_element(BTS_NODE, &cfg_bts_chan_alloc_tch_signalling_policy_cmd);
+ install_element(BTS_NODE, &cfg_bts_chan_alloc_allow_tch_for_signalling_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_max_delay_cmd);
+ install_element(BTS_NODE, &cfg_bts_chan_desc_att_cmd);
+ install_element(BTS_NODE, &cfg_bts_chan_dscr_att_cmd);
+ install_element(BTS_NODE, &cfg_bts_chan_desc_bs_pa_mfrms_cmd);
+ install_element(BTS_NODE, &cfg_bts_chan_dscr_bs_pa_mfrms_cmd);
+ install_element(BTS_NODE, &cfg_bts_chan_desc_bs_ag_blks_res_cmd);
+ install_element(BTS_NODE, &cfg_bts_chan_dscr_bs_ag_blks_res_cmd);
+ install_element(BTS_NODE, &cfg_bts_ccch_load_ind_thresh_cmd);
+ install_element(BTS_NODE, &cfg_bts_ccch_load_ind_period_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd);
+ install_element(BTS_NODE, &cfg_bts_cell_barred_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_re_allowed_cmd);
+ install_element(BTS_NODE, &cfg_bts_rach_ac_class_cmd);
+ install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd);
+ install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd);
+ install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd);
+ install_element(BTS_NODE, &cfg_bts_cell_bar_qualify_cmd);
+ install_element(BTS_NODE, &cfg_bts_cell_resel_ofs_cmd);
+ install_element(BTS_NODE, &cfg_bts_temp_ofs_cmd);
+ install_element(BTS_NODE, &cfg_bts_temp_ofs_inf_cmd);
+ install_element(BTS_NODE, &cfg_bts_penalty_time_cmd);
+ install_element(BTS_NODE, &cfg_bts_penalty_time_rsvd_cmd);
+ install_element(BTS_NODE, &cfg_bts_radio_link_timeout_cmd);
+ install_element(BTS_NODE, &cfg_bts_radio_link_timeout_inf_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_11bit_rach_support_for_egprs_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_gprs_egprs_pkt_chan_req_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_egprs_pkt_chan_req_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_ctrl_ack_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_ccn_active_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_pwr_ctrl_alpha_cmd);
+ install_element(BTS_NODE, &cfg_no_bts_gprs_ctrl_ack_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd);
+ install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd);
+ install_element(BTS_NODE, &cfg_bts_pag_free_cmd);
+ install_element(BTS_NODE, &cfg_bts_si_mode_cmd);
+ install_element(BTS_NODE, &cfg_bts_si_static_cmd);
+ install_element(BTS_NODE, &cfg_bts_si_unused_send_empty_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_si_unused_send_empty_cmd);
+ install_element(BTS_NODE, &cfg_bts_early_cm_cmd);
+ install_element(BTS_NODE, &cfg_bts_early_cm_3g_cmd);
+ install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd);
+ install_element(BTS_NODE, &cfg_bts_neigh_cmd);
+ install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd);
+ install_element(BTS_NODE, &cfg_bts_si2quater_neigh_add_cmd);
+ install_element(BTS_NODE, &cfg_bts_si2quater_neigh_del_cmd);
+ install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_add_cmd);
+ install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_del_cmd);
+ install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd);
+ install_element(BTS_NODE, &cfg_bts_force_comb_si_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_force_comb_si_cmd);
+ install_element(BTS_NODE, &cfg_bts_codec0_cmd);
+ install_element(BTS_NODE, &cfg_bts_codec1_cmd);
+ install_element(BTS_NODE, &cfg_bts_codec2_cmd);
+ install_element(BTS_NODE, &cfg_bts_codec3_cmd);
+ install_element(BTS_NODE, &cfg_bts_codec4_cmd);
+ install_element(BTS_NODE, &cfg_bts_depends_on_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_depends_on_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_modes1_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_modes2_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_modes3_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_modes4_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_thres1_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_thres2_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_thres3_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_hyst1_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_hyst2_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_hyst3_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_fr_start_mode_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_modes1_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_modes2_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_modes3_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_modes4_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_thres1_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_thres2_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_thres3_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_hyst1_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_hyst2_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd);
+ install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd);
+ install_element(BTS_NODE, &cfg_bts_osmux_cmd);
+ install_element(BTS_NODE, &cfg_bts_mgw_pool_target_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_mgw_pool_target_cmd);
+ install_element(BTS_NODE, &cfg_bts_pcu_sock_cmd);
+ install_element(BTS_NODE, &cfg_bts_acc_rotate_cmd);
+ install_element(BTS_NODE, &cfg_bts_acc_rotate_quantum_cmd);
+ install_element(BTS_NODE, &cfg_bts_acc_ramping_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_acc_ramping_cmd);
+ install_element(BTS_NODE, &cfg_bts_acc_ramping_step_interval_cmd);
+ install_element(BTS_NODE, &cfg_bts_acc_ramping_step_size_cmd);
+ install_element(BTS_NODE, &cfg_bts_acc_ramping_chan_load_cmd);
+ install_element(BTS_NODE, &cfg_bts_t3113_dynamic_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_t3113_dynamic_cmd);
+ install_element(BTS_NODE, &cfg_bts_rep_dl_facch_cmd);
+ install_element(BTS_NODE, &cfg_bts_rep_no_dl_facch_cmd);
+ install_element(BTS_NODE, &cfg_bts_rep_ul_dl_sacch_cmd);
+ install_element(BTS_NODE, &cfg_bts_rep_no_ul_dl_sacch_cmd);
+ install_element(BTS_NODE, &cfg_bts_rep_rxqual_cmd);
+ install_element(BTS_NODE, &cfg_bts_top_dl_acch_cmd);
+ install_element(BTS_NODE, &cfg_bts_top_no_dl_acch_cmd);
+ install_element(BTS_NODE, &cfg_bts_top_dl_acch_rxqual_cmd);
+ install_element(BTS_NODE, &cfg_bts_top_dl_acch_chan_mode_cmd);
+ install_element(BTS_NODE, &cfg_bts_interf_meas_avg_period_cmd);
+ install_element(BTS_NODE, &cfg_bts_interf_meas_level_bounds_cmd);
+ install_element(BTS_NODE, &cfg_bts_srvcc_fast_return_cmd);
+ install_element(BTS_NODE, &cfg_bts_immediate_assignment_cmd);
+
+ neighbor_ident_vty_init();
+ /* See also handover commands added on bts level from handover_vty.c */
+
+ install_element(BTS_NODE, &cfg_bts_power_ctrl_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_power_ctrl_cmd);
+ install_node(&power_ctrl_node, dummy_config_write);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_mode_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_bs_power_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ctrl_interval_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_step_size_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_rxlev_thresh_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_rxqual_thresh_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_thresh_disable_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_thresh_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_rxlev_thresh_comp_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_rxqual_thresh_comp_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_thresh_comp_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_no_avg_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_avg_params_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_avg_algo_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_avg_osmo_ewma_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_no_ci_avg_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_avg_params_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_avg_algo_cmd);
+ install_element(POWER_CTRL_NODE, &cfg_power_ctrl_ci_avg_osmo_ewma_cmd);
+
+
+ return bts_trx_vty_init();
+}
diff --git a/src/osmo-bsc/cbch_scheduler.c b/src/osmo-bsc/cbch_scheduler.c
index 802180419..d699e7ede 100644
--- a/src/osmo-bsc/cbch_scheduler.c
+++ b/src/osmo-bsc/cbch_scheduler.c
@@ -60,6 +60,9 @@ static int bts_smscb_sched_add_before(struct bts_smscb_page **sched_arr, int sch
OSMO_ASSERT(smscb->num_pages <= ARRAY_SIZE(smscb->page));
OSMO_ASSERT(smscb->num_pages >= 1);
+ if (last_idx >= sched_arr_size)
+ return -ERANGE;
+
for (i = smscb->num_pages - 1; i >= 0; i--) {
while (sched_arr[arr_idx]) {
arr_idx--;
@@ -132,7 +135,7 @@ int bts_smscb_gen_sched_arr(struct bts_smscb_chan_state *cstate, struct bts_smsc
}
last_page = rc;
- while (last_page < cstate->sched_arr_size) {
+ while (last_page + smscb->input.rep_period < cstate->sched_arr_size) {
/* store further instances in a way that the last block of the N+1th instance
* happens no later than "interval" after the last block of the Nth instance */
rc = bts_smscb_sched_add_before(arr, arr_size,
@@ -243,7 +246,7 @@ static void bts_cbch_send_one(struct bts_smscb_chan_state *cstate)
bts_smscb_page_done(cstate, page);
}
-static void bts_cbch_timer(void *data)
+void bts_cbch_timer_cb(void *data)
{
struct gsm_bts *bts = (struct gsm_bts *)data;
@@ -256,7 +259,6 @@ static void bts_cbch_timer(void *data)
/* There is one SMSCB message (page) per eight 51-multiframes, i.e. 1.882 seconds */
void bts_cbch_timer_schedule(struct gsm_bts *bts)
{
- osmo_timer_setup(&bts->cbch_timer, &bts_cbch_timer, bts);
osmo_timer_schedule(&bts->cbch_timer, 1, 882920);
}
diff --git a/src/osmo-bsc/cbsp_link.c b/src/osmo-bsc/cbsp_link.c
index 492679942..849b802fe 100644
--- a/src/osmo-bsc/cbsp_link.c
+++ b/src/osmo-bsc/cbsp_link.c
@@ -21,7 +21,6 @@
#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/smscb.h>
#include <osmocom/bsc/bsc_msc_data.h>
@@ -301,6 +300,13 @@ int cbsp_tx_decoded(struct bsc_cbc_link *cbc, struct osmo_cbsp_decoded *cbsp)
{
struct msgb *msg;
+ if (!cbc->client.cli && !cbc->server.srv) {
+ LOGP(DCBS, LOGL_INFO, "Discarding Tx CBSP Message Type %s, link is down\n",
+ get_value_string(cbsp_msg_type_names, cbsp->msg_type));
+ talloc_free(cbsp);
+ return 0;
+ }
+
msg = osmo_cbsp_encode(cbc, cbsp);
if (!msg) {
LOGP(DCBS, LOGL_ERROR, "Unable to encode CBSP Message Type %s: %s\n",
@@ -312,349 +318,7 @@ int cbsp_tx_decoded(struct bsc_cbc_link *cbc, struct osmo_cbsp_decoded *cbsp)
osmo_stream_cli_send(cbc->client.cli, msg);
else if (cbc->server.srv)
osmo_stream_srv_send(cbc->server.srv, msg);
- else {
- LOGP(DCBS, LOGL_ERROR, "Discarding CBSP Message, link is down: %s\n", msgb_hexdump(msg));
- msgb_free(msg);
- }
talloc_free(cbsp);
return 0;
}
-
-static struct bsc_cbc_link *vty_cbc_data(struct vty *vty)
-{
- return bsc_gsmnet->cbc;
-}
-
-/*********************************************************************************
- * VTY Interface (Configuration + Introspection)
- *********************************************************************************/
-
-DEFUN(cfg_cbc, cfg_cbc_cmd,
- "cbc", "Configure CBSP Link to Cell Broadcast Centre\n")
-{
- vty->node = CBC_NODE;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_mode, cfg_cbc_mode_cmd,
- "mode (server|client|disabled)",
- "Set OsmoBSC as CBSP server or client\n"
- "CBSP Server: listen for inbound TCP connections from a remote Cell Broadcast Centre\n"
- "CBSP Client: establish outbound TCP connection to a remote Cell Broadcast Centre\n"
- "Disable CBSP link\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- cbc->mode = get_string_value(bsc_cbc_link_mode_names, argv[0]);
- OSMO_ASSERT(cbc->mode >= 0);
-
- /* Immediately restart/stop CBSP only when coming from a telnet session. The settings from the config file take
- * effect in osmo_bsc_main.c's invocation of bsc_cbc_link_restart(). */
- if (vty->type != VTY_FILE)
- bsc_cbc_link_restart();
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_server, cfg_cbc_server_cmd,
- "server", "Configure OsmoBSC's CBSP server role\n")
-{
- vty->node = CBC_SERVER_NODE;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_server_local_ip, cfg_cbc_server_local_ip_cmd,
- "local-ip " VTY_IPV46_CMD,
- "Set IP Address to listen on for inbound CBSP from a Cell Broadcast Centre\n"
- "IPv4 address\n" "IPv6 address\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- osmo_sockaddr_str_from_str(&cbc->server.local_addr, argv[0], cbc->server.local_addr.port);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_server_local_port, cfg_cbc_server_local_port_cmd,
- "local-port <1-65535>",
- "Set TCP port to listen on for inbound CBSP from a Cell Broadcast Centre\n"
- "CBSP port number (Default: " OSMO_STRINGIFY_VAL(CBSP_TCP_PORT) ")\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- cbc->server.local_addr.port = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_client, cfg_cbc_client_cmd,
- "client", "Configure OsmoBSC's CBSP client role\n")
-{
- vty->node = CBC_CLIENT_NODE;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_client_remote_ip, cfg_cbc_client_remote_ip_cmd,
- "remote-ip " VTY_IPV46_CMD,
- "Set IP Address of the Cell Broadcast Centre, to establish CBSP link to\n"
- "IPv4 address\n" "IPv6 address\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- osmo_sockaddr_str_from_str(&cbc->client.remote_addr, argv[0], cbc->client.remote_addr.port);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_client_remote_port, cfg_cbc_client_remote_port_cmd,
- "remote-port <1-65535>",
- "Set TCP port of the Cell Broadcast Centre, to establish CBSP link to\n"
- "CBSP port number (Default: " OSMO_STRINGIFY_VAL(CBSP_TCP_PORT) ")\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- cbc->client.remote_addr.port = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_client_local_ip, cfg_cbc_client_local_ip_cmd,
- "local-ip " VTY_IPV46_CMD,
- "Set local bind address for the outbound CBSP link to the Cell Broadcast Centre\n"
- "IPv4 address\n" "IPv6 address\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- osmo_sockaddr_str_from_str(&cbc->client.local_addr, argv[0], cbc->client.local_addr.port);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_client_local_port, cfg_cbc_client_local_port_cmd,
- "local-port <1-65535>",
- "Set local bind port for the outbound CBSP link to the Cell Broadcast Centre\n"
- "port number\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- cbc->client.local_addr.port = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_client_no_local_ip, cfg_cbc_client_no_local_ip_cmd,
- "no local-ip",
- NO_STR "Remove local IP address bind config for the CBSP client mode\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- cbc->client.local_addr = (struct osmo_sockaddr_str){ .port = cbc->client.local_addr.port };
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_cbc_client_no_local_port, cfg_cbc_client_no_local_port_cmd,
- "no local-port",
- NO_STR "Remove local TCP port bind config for the CBSP client mode\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- cbc->client.local_addr.port = 0;
- return CMD_SUCCESS;
-}
-
-static struct cmd_node cbc_node = {
- CBC_NODE,
- "%s(config-cbc)# ",
- 1,
-};
-
-static struct cmd_node cbc_server_node = {
- CBC_SERVER_NODE,
- "%s(config-cbc-server)# ",
- 1,
-};
-
-static struct cmd_node cbc_client_node = {
- CBC_CLIENT_NODE,
- "%s(config-cbc-client)# ",
- 1,
-};
-
-static int config_write_cbc(struct vty *vty)
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
-
- bool default_server_local;
- bool default_client_remote;
- bool default_client_local;
-
- default_server_local = !osmo_sockaddr_str_cmp(&cbc->server.local_addr,
- &bsc_cbc_default_server_local_addr);
- default_client_remote = !osmo_sockaddr_str_is_set(&cbc->client.remote_addr);
- default_client_local = !osmo_sockaddr_str_is_set(&cbc->client.local_addr);
-
- /* If all reflects default values, skip the 'cbc' section */
- if (cbc->mode == BSC_CBC_LINK_MODE_DISABLED
- && default_server_local
- && default_client_remote && default_client_local)
- return 0;
-
- vty_out(vty, "cbc%s", VTY_NEWLINE);
- vty_out(vty, " mode %s%s", bsc_cbc_link_mode_name(cbc->mode), VTY_NEWLINE);
-
- if (!default_server_local) {
- vty_out(vty, " server%s", VTY_NEWLINE);
-
- if (strcmp(cbc->server.local_addr.ip, bsc_cbc_default_server_local_addr.ip))
- vty_out(vty, " local-ip %s%s", cbc->server.local_addr.ip, VTY_NEWLINE);
- if (cbc->server.local_addr.port != bsc_cbc_default_server_local_addr.port)
- vty_out(vty, " local-port %u%s", cbc->server.local_addr.port, VTY_NEWLINE);
- }
-
- if (!(default_client_remote && default_client_local)) {
- vty_out(vty, " client%s", VTY_NEWLINE);
-
- if (osmo_sockaddr_str_is_set(&cbc->client.remote_addr)) {
- vty_out(vty, " remote-ip %s%s", cbc->client.remote_addr.ip, VTY_NEWLINE);
- if (cbc->client.remote_addr.port != CBSP_TCP_PORT)
- vty_out(vty, " remote-port %u%s", cbc->client.remote_addr.port, VTY_NEWLINE);
- }
-
- if (cbc->client.local_addr.ip[0])
- vty_out(vty, " local-ip %s%s", cbc->client.local_addr.ip, VTY_NEWLINE);
- if (cbc->client.local_addr.port)
- vty_out(vty, " local-port %u%s", cbc->client.local_addr.port, VTY_NEWLINE);
- }
-
- return 0;
-}
-
-DEFUN(show_cbc, show_cbc_cmd,
- "show cbc",
- SHOW_STR "Display state of CBC / CBSP\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
-
- switch (cbc->mode) {
- case BSC_CBC_LINK_MODE_DISABLED:
- vty_out(vty, "CBSP link is disabled%s", VTY_NEWLINE);
- break;
-
- case BSC_CBC_LINK_MODE_SERVER:
- vty_out(vty, "OsmoBSC is configured as CBSP Server on " OSMO_SOCKADDR_STR_FMT "%s",
- OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->server.local_addr), VTY_NEWLINE);
- vty_out(vty, "CBSP Server Connection: %s%s",
- cbc->server.sock_name ? cbc->server.sock_name : "Disconnected", VTY_NEWLINE);
- break;
-
- case BSC_CBC_LINK_MODE_CLIENT:
- vty_out(vty, "OsmoBSC is configured as CBSP Client to remote CBC at " OSMO_SOCKADDR_STR_FMT "%s",
- OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->client.remote_addr), VTY_NEWLINE);
- vty_out(vty, "CBSP Client Connection: %s%s",
- cbc->client.sock_name ? cbc->client.sock_name : "Disconnected", VTY_NEWLINE);
- break;
- }
- return CMD_SUCCESS;
-}
-
-/* --- Deprecated 'cbc' commands for backwards compat --- */
-
-DEFUN_DEPRECATED(cfg_cbc_remote_ip, cfg_cbc_remote_ip_cmd,
- "remote-ip A.B.C.D",
- "IP Address of the Cell Broadcast Centre\n"
- "IP Address of the Cell Broadcast Centre\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- vty_out(vty, "%% cbc/remote-ip config is deprecated, instead use cbc/client/remote-ip and cbc/ mode%s",
- VTY_NEWLINE);
- osmo_sockaddr_str_from_str(&cbc->client.remote_addr, argv[0], cbc->client.remote_addr.port);
- cbc->mode = BSC_CBC_LINK_MODE_CLIENT;
- if (vty->type != VTY_FILE)
- bsc_cbc_link_restart();
- return CMD_SUCCESS;
-}
-DEFUN_DEPRECATED(cfg_cbc_no_remote_ip, cfg_cbc_no_remote_ip_cmd,
- "no remote-ip",
- NO_STR "Remove IP address of CBC; disables outbound CBSP connections\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- vty_out(vty, "%% cbc/remote-ip config is deprecated, instead use cbc/client/remote-ip and cbc/mode%s",
- VTY_NEWLINE);
- if (cbc->mode == BSC_CBC_LINK_MODE_CLIENT) {
- cbc->mode = BSC_CBC_LINK_MODE_DISABLED;
- if (vty->type != VTY_FILE)
- bsc_cbc_link_restart();
- }
- return CMD_SUCCESS;
-}
-
-DEFUN_DEPRECATED(cfg_cbc_remote_port, cfg_cbc_remote_port_cmd,
- "remote-port <1-65535>",
- "TCP Port number of the Cell Broadcast Centre (Default: 48049)\n"
- "TCP Port number of the Cell Broadcast Centre (Default: 48049)\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- vty_out(vty, "%% cbc/remote-port config is deprecated, instead use cbc/client/remote-port%s",
- VTY_NEWLINE);
- cbc->client.remote_addr.port = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN_DEPRECATED(cfg_cbc_listen_port, cfg_cbc_listen_port_cmd,
- "listen-port <1-65535>",
- "Local TCP port at which BSC listens for incoming CBSP connections from CBC\n"
- "Local TCP port at which BSC listens for incoming CBSP connections from CBC\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- vty_out(vty, "%% cbc/listen-port config is deprecated, instead use cbc/server/local-port and cbc/mode%s",
- VTY_NEWLINE);
- cbc->mode = BSC_CBC_LINK_MODE_SERVER;
- cbc->server.local_addr.port = atoi(argv[0]);
- if (vty->type != VTY_FILE)
- bsc_cbc_link_restart();
- return CMD_SUCCESS;
-}
-
-DEFUN_DEPRECATED(cfg_cbc_no_listen_port, cfg_cbc_no_listen_port_cmd,
- "no listen-port",
- NO_STR "Remove CBSP Listen Port; disables inbound CBSP connections\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- vty_out(vty, "%% cbc/listen-port config is deprecated, instead use cbc/server/local-port and cbc/mode%s",
- VTY_NEWLINE);
- if (cbc->mode == BSC_CBC_LINK_MODE_SERVER) {
- cbc->mode = BSC_CBC_LINK_MODE_DISABLED;
- if (vty->type != VTY_FILE)
- bsc_cbc_link_restart();
- }
- return CMD_SUCCESS;
-}
-
-DEFUN_DEPRECATED(cfg_cbc_listen_ip, cfg_cbc_listen_ip_cmd,
- "listen-ip A.B.C.D",
- "Local IP Address where BSC listens for incoming CBC connections (Default: 127.0.0.1)\n"
- "Local IP Address where BSC listens for incoming CBC connections\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- vty_out(vty, "%% cbc/listen-ip config is deprecated, instead use cbc/server/local-ip%s",
- VTY_NEWLINE);
- osmo_sockaddr_str_from_str(&cbc->server.local_addr, argv[0], cbc->server.local_addr.port);
- return CMD_SUCCESS;
-}
-
-void cbc_vty_init(void)
-{
- install_element_ve(&show_cbc_cmd);
-
- install_element(CONFIG_NODE, &cfg_cbc_cmd);
- install_node(&cbc_node, config_write_cbc);
- install_element(CBC_NODE, &cfg_cbc_mode_cmd);
-
- install_element(CBC_NODE, &cfg_cbc_server_cmd);
- install_node(&cbc_server_node, NULL);
- install_element(CBC_SERVER_NODE, &cfg_cbc_server_local_ip_cmd);
- install_element(CBC_SERVER_NODE, &cfg_cbc_server_local_port_cmd);
-
- install_element(CBC_NODE, &cfg_cbc_client_cmd);
- install_node(&cbc_client_node, NULL);
- install_element(CBC_CLIENT_NODE, &cfg_cbc_client_remote_ip_cmd);
- install_element(CBC_CLIENT_NODE, &cfg_cbc_client_remote_port_cmd);
- install_element(CBC_CLIENT_NODE, &cfg_cbc_client_local_ip_cmd);
- install_element(CBC_CLIENT_NODE, &cfg_cbc_client_local_port_cmd);
- install_element(CBC_CLIENT_NODE, &cfg_cbc_client_no_local_ip_cmd);
- install_element(CBC_CLIENT_NODE, &cfg_cbc_client_no_local_port_cmd);
-
- /* Deprecated, for backwards compat */
- install_element(CBC_NODE, &cfg_cbc_remote_ip_cmd);
- install_element(CBC_NODE, &cfg_cbc_no_remote_ip_cmd);
- install_element(CBC_NODE, &cfg_cbc_remote_port_cmd);
- install_element(CBC_NODE, &cfg_cbc_listen_port_cmd);
- install_element(CBC_NODE, &cfg_cbc_no_listen_port_cmd);
- install_element(CBC_NODE, &cfg_cbc_listen_ip_cmd);
-}
diff --git a/src/osmo-bsc/chan_alloc.c b/src/osmo-bsc/chan_alloc.c
index 3569d4eaa..4fbf8be0b 100644
--- a/src/osmo-bsc/chan_alloc.c
+++ b/src/osmo-bsc/chan_alloc.c
@@ -44,8 +44,12 @@ void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts)
struct gsm_bts_trx *trx;
llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct load_counter *ll = &trx->lchan_load;
int i;
+ /* init per-TRX load counters */
+ memset(ll, 0, sizeof(*ll));
+
/* skip administratively deactivated transceivers */
if (!trx_is_usable(trx))
continue;
@@ -59,38 +63,32 @@ void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts)
if (!nm_is_running(&ts->mo.nm_state))
continue;
- /* Dynamic timeslots have to be counted separately
- * when not in TCH/F or TCH/H mode because they don't
- * have an lchan's allocated to them. At the same time,
- * dynamic timeslots in NONE and PDCH modes are same
- * as in UNUSED mode from the CS channel load perspective
- * beause they can be switched to TCH mode at any moment.
- * I.e. they are "available" for TCH. */
- if ((ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH ||
+ /* A dynamic timeslot currently in PDCH mode are available as TCH or SDCCH8, beause they can be switched
+ * to TCH or SDCCH mode at any moment. Count TCH/F_TCH/H_SDCCH8_PDCH as one total timeslot, even though it may
+ * be switched to TCH/H and would then count as two -- hence opt for pessimistic load. */
+ if ((ts->pchan_on_init == GSM_PCHAN_OSMO_DYN ||
ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH) &&
(ts->pchan_is == GSM_PCHAN_NONE ||
ts->pchan_is == GSM_PCHAN_PDCH)) {
+ ll->total++;
pl->total++;
+ /* Below loop would not count this timeslot, since in PDCH mode it has no usable
+ * timeslots. But let's make it clear that the timeslot must not be counted again: */
+ continue;
}
- /* Count allocated logical channels.
- * Note: A GSM_PCHAN_TCH_F_TCH_H_PDCH can be switched
- * to a single TCH/F or to two TCH/H. So when it's in
- * the TCH/H mode, total number of available channels
- * is 1 more than when it's in the TCH/F mode.
- * I.e. "total" count will fluctuate depending on
- * whether GSM_PCHAN_TCH_F_TCH_H_PDCH timeslot is
- * in TCH/F or TCH/H (or in NONE/PDCH) mode. */
- ts_for_each_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {
/* don't even count CBCH slots in total */
if (lchan->type == GSM_LCHAN_CBCH)
continue;
+ ll->total++;
pl->total++;
/* lchans under a BORKEN TS should be counted
* as used just as BORKEN lchans under a normal TS */
if (ts->fi->state == TS_ST_BORKEN) {
+ ll->used++;
pl->used++;
continue;
}
@@ -99,6 +97,7 @@ void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts)
case LCHAN_ST_UNUSED:
break;
default:
+ ll->used++;
pl->used++;
break;
}
@@ -129,36 +128,36 @@ static void chan_load_stat_set(enum gsm_phys_chan_config pchan,
case GSM_PCHAN_UNKNOWN:
break;
case GSM_PCHAN_CCCH_SDCCH4:
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_CCCH_SDCCH4_USED], lc->used);
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL], lc->total);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_USED), lc->used);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL), lc->total);
break;
case GSM_PCHAN_TCH_F:
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_TCH_F_USED], lc->used);
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_TCH_F_TOTAL], lc->total);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_USED), lc->used);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_TOTAL), lc->total);
break;
case GSM_PCHAN_TCH_H:
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_TCH_H_USED], lc->used);
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_TCH_H_TOTAL], lc->total);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_H_USED), lc->used);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_H_TOTAL), lc->total);
break;
case GSM_PCHAN_SDCCH8_SACCH8C:
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_SDCCH8_USED], lc->used);
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_SDCCH8_TOTAL], lc->total);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_USED), lc->used);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_TOTAL), lc->total);
break;
case GSM_PCHAN_TCH_F_PDCH:
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_TCH_F_PDCH_USED], lc->used);
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_TCH_F_PDCH_TOTAL], lc->total);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_PDCH_USED), lc->used);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_PDCH_TOTAL), lc->total);
break;
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED], lc->used);
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL], lc->total);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED), lc->used);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL), lc->total);
break;
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_SDCCH8_CBCH_USED], lc->used);
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL], lc->total);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_CBCH_USED), lc->used);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL), lc->total);
break;
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_USED], lc->used);
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_TOTAL], lc->total);
+ case GSM_PCHAN_OSMO_DYN:
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_OSMO_DYN_USED), lc->used);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_OSMO_DYN_TOTAL), lc->total);
break;
default:
LOG_BTS(bts, DRLL, LOGL_NOTICE, "Unknown channel type %d\n", pchan);
@@ -240,7 +239,7 @@ bts_update_t3122_chan_load(struct gsm_bts *bts)
(load & 0xffffff00) >> 8, (load & 0xff) / 10);
bts->chan_load_avg = ((load & 0xffffff00) >> 8);
OSMO_ASSERT(bts->chan_load_avg <= 100);
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_LOAD_AVERAGE], bts->chan_load_avg);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_LOAD_AVERAGE), bts->chan_load_avg);
/* Calculate new T3122 wait indicator. */
wait_ind = ((used / total) * max_wait_ind);
@@ -252,5 +251,5 @@ bts_update_t3122_chan_load(struct gsm_bts *bts)
LOG_BTS(bts, DRLL, LOGL_DEBUG, "T3122 wait indicator set to %"PRIu64" seconds\n", wait_ind);
bts->T3122 = (uint8_t)wait_ind;
- osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_T3122], wait_ind);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_T3122), wait_ind);
}
diff --git a/src/osmo-bsc/chan_counts.c b/src/osmo-bsc/chan_counts.c
new file mode 100644
index 000000000..bf863688d
--- /dev/null
+++ b/src/osmo-bsc/chan_counts.c
@@ -0,0 +1,310 @@
+/* count total, allocated and free channels of all types.
+ *
+ * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bts_trx.h>
+#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/chan_counts.h>
+#include <osmocom/bsc/bsc_stats.h>
+#include <osmocom/bsc/signal.h>
+
+static const unsigned int lchans_per_pchan[_GSM_PCHAN_MAX][_GSM_LCHAN_MAX] = {
+ [GSM_PCHAN_NONE] = {0},
+ [GSM_PCHAN_CCCH] = { [GSM_LCHAN_CCCH] = 1, },
+ [GSM_PCHAN_PDCH] = { [GSM_LCHAN_PDTCH] = 1, },
+ [GSM_PCHAN_CCCH_SDCCH4] = {
+ [GSM_LCHAN_CCCH] = 1,
+ [GSM_LCHAN_SDCCH] = 3,
+ },
+ [GSM_PCHAN_TCH_F] = { [GSM_LCHAN_TCH_F] = 1, },
+ [GSM_PCHAN_TCH_H] = { [GSM_LCHAN_TCH_H] = 2, },
+ [GSM_PCHAN_SDCCH8_SACCH8C] = { [GSM_LCHAN_SDCCH] = 8, },
+ [GSM_PCHAN_CCCH_SDCCH4_CBCH] = {
+ [GSM_LCHAN_CCCH] = 1,
+ [GSM_LCHAN_SDCCH] = 3,
+ [GSM_LCHAN_CBCH] = 1,
+ },
+ [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = {
+ [GSM_LCHAN_SDCCH] = 8,
+ [GSM_LCHAN_CBCH] = 1,
+ },
+ [GSM_PCHAN_OSMO_DYN] = {
+ [GSM_LCHAN_TCH_F] = 1,
+ [GSM_LCHAN_TCH_H] = 2,
+ [GSM_LCHAN_SDCCH] = 8,
+ [GSM_LCHAN_PDTCH] = 1,
+ },
+ [GSM_PCHAN_TCH_F_PDCH] = {
+ [GSM_LCHAN_TCH_F] = 1,
+ [GSM_LCHAN_PDTCH] = 1,
+ },
+};
+
+static inline void chan_counts_per_pchan_add(struct chan_counts *dst,
+ enum chan_counts_dim1 dim1, enum chan_counts_dim2 dim2,
+ enum gsm_phys_chan_config pchan)
+{
+ int i;
+ for (i = 0; i < _GSM_LCHAN_MAX; i++)
+ dst->val[dim1][dim2][i] += lchans_per_pchan[pchan][i];
+}
+
+static const char *chan_counts_dim1_name[_CHAN_COUNTS1_NUM] = {
+ [CHAN_COUNTS1_ALL] = "all",
+ [CHAN_COUNTS1_STATIC] = "static",
+ [CHAN_COUNTS1_DYNAMIC] = "dynamic",
+};
+
+static const char *chan_counts_dim2_name[_CHAN_COUNTS2_NUM] = {
+ [CHAN_COUNTS2_MAX_TOTAL] = "max",
+ [CHAN_COUNTS2_CURRENT_TOTAL] = "current",
+ [CHAN_COUNTS2_ALLOCATED] = "alloc",
+ [CHAN_COUNTS2_FREE] = "free",
+};
+
+int chan_counts_to_str_buf(char *buf, size_t buflen, const struct chan_counts *c)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ int i1, i2, i3;
+ OSMO_STRBUF_PRINTF(sb, "{");
+ for (i1 = 0; i1 < _CHAN_COUNTS1_NUM; i1++) {
+ for (i2 = 0; i2 < _CHAN_COUNTS2_NUM; i2++) {
+ bool p12 = false;
+
+ for (i3 = 0; i3 < _GSM_LCHAN_MAX; i3++) {
+
+ int v = c->val[i1][i2][i3];
+ if (v) {
+ if (!p12) {
+ p12 = true;
+ OSMO_STRBUF_PRINTF(sb, " %s.%s{", chan_counts_dim1_name[i1],
+ chan_counts_dim2_name[i2]);
+ }
+ OSMO_STRBUF_PRINTF(sb, " %s=%d", gsm_chan_t_name(i3), v);
+ }
+ }
+
+ if (p12)
+ OSMO_STRBUF_PRINTF(sb, " }");
+ }
+ }
+ OSMO_STRBUF_PRINTF(sb, " }");
+ return sb.chars_needed;
+}
+
+char *chan_counts_to_str_c(void *ctx, const struct chan_counts *c)
+{
+ OSMO_NAME_C_IMPL(ctx, 64, "ERROR", chan_counts_to_str_buf, c)
+}
+
+void chan_counts_for_ts(struct chan_counts *ts_counts, const struct gsm_bts_trx_ts *ts)
+{
+ const struct gsm_lchan *lchan;
+ bool ts_is_dynamic;
+
+ chan_counts_zero(ts_counts);
+
+ if (!ts_is_usable(ts))
+ return;
+
+ /* Count the full potential nr of lchans for dynamic TS */
+ chan_counts_per_pchan_add(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL, ts->pchan_on_init);
+
+ switch (ts->pchan_on_init) {
+ case GSM_PCHAN_TCH_F_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
+ ts_is_dynamic = true;
+ break;
+ default:
+ ts_is_dynamic = false;
+ break;
+ }
+
+ if (ts_is_dynamic && ts->pchan_is == GSM_PCHAN_PDCH) {
+ /* Dynamic timeslots in PDCH mode can become TCH or SDCCH immediately,
+ * so set CURRENT_TOTAL = MAX_TOTAL. */
+ chan_counts_dim3_add(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL,
+ ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL);
+ } else {
+ /* Static TS, or dyn TS that are currently fixed on a specific pchan: count lchans for the
+ * current pchan mode. */
+ chan_counts_per_pchan_add(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL, ts->pchan_is);
+ }
+
+ /* Count currently allocated lchans */
+ ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {
+ if (!lchan_state_is(lchan, LCHAN_ST_UNUSED))
+ ts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_ALLOCATED][lchan->type]++;
+ }
+
+ chan_counts_dim3_add(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,
+ ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL);
+ chan_counts_dim3_sub(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,
+ ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_ALLOCATED);
+
+ if (ts_is_dynamic)
+ chan_counts_dim2_add(ts_counts, CHAN_COUNTS1_DYNAMIC, ts_counts, CHAN_COUNTS1_ALL);
+ else
+ chan_counts_dim2_add(ts_counts, CHAN_COUNTS1_STATIC, ts_counts, CHAN_COUNTS1_ALL);
+}
+
+static void chan_counts_diff(struct chan_counts *diff, const struct chan_counts *left, const struct chan_counts *right)
+{
+ chan_counts_zero(diff);
+ chan_counts_add(diff, right);
+ chan_counts_sub(diff, left);
+}
+
+static void _chan_counts_ts_update(struct gsm_bts_trx_ts *ts, const struct chan_counts *ts_new_counts)
+{
+ struct chan_counts diff;
+
+ chan_counts_diff(&diff, &ts->chan_counts, ts_new_counts);
+ if (chan_counts_is_zero(&diff))
+ return;
+
+ ts->chan_counts = *ts_new_counts;
+ chan_counts_add(&ts->trx->chan_counts, &diff);
+ chan_counts_add(&ts->trx->bts->chan_counts, &diff);
+ chan_counts_add(&bsc_gsmnet->chan_counts, &diff);
+
+ all_allocated_update_bts(ts->trx->bts);
+ all_allocated_update_bsc();
+
+ LOGP(DLGLOBAL, LOGL_DEBUG, "change in channel counts: ts %u-%u-%u: %s\n",
+ ts->trx->bts->nr, ts->trx->nr, ts->nr, chan_counts_to_str_c(OTC_SELECT, &diff));
+ LOGP(DLGLOBAL, LOGL_DEBUG, "bsc channel counts: %s\n",
+ chan_counts_to_str_c(OTC_SELECT, &bsc_gsmnet->chan_counts));
+}
+
+/* Re-count this TS, and update ts->chan_counts. If the new ts->chan_counts differ, propagate the difference to
+ * trx->chan_counts, bts->chan_counts and gsm_network->chan_counts. */
+void chan_counts_ts_update(struct gsm_bts_trx_ts *ts)
+{
+ struct chan_counts ts_new_counts;
+ chan_counts_for_ts(&ts_new_counts, ts);
+ _chan_counts_ts_update(ts, &ts_new_counts);
+}
+
+void chan_counts_ts_clear(struct gsm_bts_trx_ts *ts)
+{
+ struct chan_counts ts_new_counts = {0};
+ _chan_counts_ts_update(ts, &ts_new_counts);
+}
+
+void chan_counts_trx_update(struct gsm_bts_trx *trx)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ chan_counts_ts_update(ts);
+ }
+}
+
+static int chan_counts_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
+{
+ struct nm_running_chg_signal_data *nsd;
+ struct gsm_bts_trx *trx;
+ if (signal != S_NM_RUNNING_CHG)
+ return 0;
+ nsd = signal_data;
+ switch (nsd->obj_class) {
+ case NM_OC_RADIO_CARRIER:
+ trx = (struct gsm_bts_trx *)nsd->obj;
+ break;
+ case NM_OC_BASEB_TRANSC:
+ trx = gsm_bts_bb_trx_get_trx((struct gsm_bts_bb_trx *)nsd->obj);
+ break;
+ default:
+ return 0;
+ }
+ chan_counts_trx_update(trx);
+ return 0;
+}
+
+void chan_counts_sig_init(void)
+{
+ osmo_signal_register_handler(SS_NM, chan_counts_sig_cb, NULL);
+}
+
+void chan_counts_bsc_verify(void)
+{
+ struct gsm_bts *bts;
+ struct chan_counts bsc_counts = {0};
+ struct chan_counts diff;
+
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
+ struct gsm_bts_trx *trx;
+ struct chan_counts bts_counts = {0};
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct chan_counts trx_counts = {0};
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct chan_counts ts_counts;
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ chan_counts_for_ts(&ts_counts, ts);
+
+ chan_counts_diff(&diff, &ts->chan_counts, &ts_counts);
+ if (!chan_counts_is_zero(&diff)) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "internal error in channel counts, on bts-trx-ts %u-%u-%u, fixing."
+ " diff: %s\n",
+ bts->nr, trx->nr, ts->nr,
+ chan_counts_to_str_c(OTC_SELECT, &diff));
+ ts->chan_counts = ts_counts;
+ }
+
+ chan_counts_add(&trx_counts, &ts_counts);
+ }
+
+ chan_counts_diff(&diff, &trx->chan_counts, &trx_counts);
+ if (!chan_counts_is_zero(&diff)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "internal error in channel counts, on bts-trx %u-%u, fixing."
+ " diff: %s\n",
+ bts->nr, trx->nr, chan_counts_to_str_c(OTC_SELECT, &diff));
+ trx->chan_counts = trx_counts;
+ }
+
+ chan_counts_add(&bts_counts, &trx_counts);
+ }
+
+ chan_counts_diff(&diff, &bts->chan_counts, &bts_counts);
+ if (!chan_counts_is_zero(&diff)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "internal error in channel counts, on bts %u, fixing. diff: %s\n",
+ bts->nr, chan_counts_to_str_c(OTC_SELECT, &diff));
+ bts->chan_counts = bts_counts;
+ }
+
+ chan_counts_add(&bsc_counts, &bts_counts);
+ }
+
+ chan_counts_diff(&diff, &bsc_gsmnet->chan_counts, &bsc_counts);
+ if (!chan_counts_is_zero(&diff)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "internal error in overall channel counts, fixing. diff: %s\n",
+ chan_counts_to_str_c(OTC_SELECT, &diff));
+ bsc_gsmnet->chan_counts = bsc_counts;
+ }
+}
diff --git a/src/osmo-bsc/codec_pref.c b/src/osmo-bsc/codec_pref.c
index 58a7867c5..4092c1ac0 100644
--- a/src/osmo-bsc/codec_pref.c
+++ b/src/osmo-bsc/codec_pref.c
@@ -61,8 +61,7 @@ static int full_rate_from_perm_spch(bool * full_rate,
return 0;
}
-/* Helper function for match_codec_pref(), looks up a matching chan mode for
- * a given permitted speech value */
+/* Look up a matching chan mode for a given permitted speech value */
static enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech)
{
switch (speech) {
@@ -88,8 +87,7 @@ static enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech spe
}
}
-/* Helper function for match_codec_pref(), looks up a matching permitted speech
- * value for a given msc audio codec pref */
+/* Look up a matching permitted speech value for a given msc audio codec pref */
static enum gsm0808_permitted_speech audio_support_to_gsm88(const struct gsm_audio_support *audio)
{
if (audio->hr) {
@@ -125,10 +123,9 @@ static enum gsm0808_permitted_speech audio_support_to_gsm88(const struct gsm_aud
}
}
-/* Helper function for match_codec_pref(), tests if a given audio support
- * matches one of the permitted speech settings of the channel type element.
- * The matched permitted speech value is then also compared against the
- * speech codec list. (optional, only relevant for AoIP) */
+/* Test if a given audio support matches one of the permitted speech settings
+ * of the channel type element. The matched permitted speech value is then also
+ * compared against the speech codec list. (optional, only relevant for AoIP) */
static bool test_codec_pref(const struct gsm0808_speech_codec **sc_match,
const struct gsm0808_speech_codec_list *scl,
const struct gsm0808_channel_type *ct,
@@ -177,37 +174,48 @@ static bool test_codec_pref(const struct gsm0808_speech_codec **sc_match,
return false;
}
-/* Helper function to check if the given permitted speech value is supported
- * by the BTS. (vty option bts->codec-support). */
-static bool test_codec_support_bts(const struct gsm_bts *bts, uint8_t perm_spch)
+static bool test_codec_support_bts_rate(const struct gsm_bts *bts, const bool full_rate)
{
+ unsigned int i;
struct gsm_bts_trx *trx;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ for (i = 0; i < TRX_NR_TS; i++) {
+ switch (trx->ts[i].pchan_from_config) {
+ case GSM_PCHAN_OSMO_DYN:
+ return true;
+ case GSM_PCHAN_TCH_F:
+ case GSM_PCHAN_TCH_F_PDCH:
+ if (full_rate)
+ return true;
+ break;
+ case GSM_PCHAN_TCH_H:
+ if (!full_rate)
+ return true;
+ break;
+ default:
+ continue;
+ }
+ }
+ }
+
+ return false;
+}
+
+/* Check if the given permitted speech value is supported by the BTS
+ * (vty option bts->codec-support). */
+static bool test_codec_support_bts(const struct gsm_bts *bts, uint8_t perm_spch)
+{
const struct bts_codec_conf *bts_codec = &bts->codec;
- unsigned int i;
bool full_rate;
int rc;
- enum gsm_phys_chan_config pchan;
- bool rate_match = false;
/* Check if the BTS provides a physical channel that matches the
* bandwidth of the desired codec. */
rc = full_rate_from_perm_spch(&full_rate, perm_spch);
if (rc < 0)
return false;
- llist_for_each_entry(trx, &bts->trx_list, list) {
- for (i = 0; i < TRX_NR_TS; i++) {
- pchan = trx->ts[i].pchan_from_config;
- if (pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH)
- rate_match = true;
- else if (full_rate && pchan == GSM_PCHAN_TCH_F)
- rate_match = true;
- else if (full_rate && pchan == GSM_PCHAN_TCH_F_PDCH)
- rate_match = true;
- else if (!full_rate && pchan == GSM_PCHAN_TCH_H)
- rate_match = true;
- }
- }
- if (!rate_match)
+ if (!test_codec_support_bts_rate(bts, full_rate))
return false;
/* Check codec support */
@@ -217,21 +225,12 @@ static bool test_codec_support_bts(const struct gsm_bts *bts, uint8_t perm_spch)
* selectively disable GSM-RF per BTS via VTY. */
return true;
case GSM0808_PERM_FR2:
- if (bts_codec->efr)
- return true;
- break;
+ return (bool)bts_codec->efr;
case GSM0808_PERM_FR3:
- if (bts_codec->amr)
- return true;
- break;
- case GSM0808_PERM_HR1:
- if (bts_codec->hr)
- return true;
- break;
case GSM0808_PERM_HR3:
- if (bts_codec->amr)
- return true;
- break;
+ return (bool)bts_codec->amr;
+ case GSM0808_PERM_HR1:
+ return (bool)bts_codec->hr;
default:
return false;
}
@@ -308,8 +307,8 @@ static int match_amr_s15_s0(struct channel_mode_and_rate *ch_mode_rate, const st
return 0;
}
-/*! Match the codec preferences from local config with a received codec preferences IEs received from the
- * MSC and the BTS' codec configuration.
+/*! Match the codec preferences from local config with codec preference IEs
+ * received from the MSC and the BTS' codec configuration.
* \param[out] ch_mode_rate resulting codec and rate information
* \param[in] ct GSM 08.08 channel type received from MSC.
* \param[in] scl GSM 08.08 speech codec list received from MSC (optional).
@@ -336,7 +335,7 @@ int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate,
* indeed available with the current BTS and MSC configuration */
for (i = 0; i < msc->audio_length; i++) {
/* Pick a permitted speech value from the global codec configuration list */
- perm_spch = audio_support_to_gsm88(msc->audio_support[i]);
+ perm_spch = audio_support_to_gsm88(&msc->audio_support[i]);
/* Determine if the result is a half or full rate codec */
rc = full_rate_from_perm_spch(&full_rate, perm_spch);
@@ -376,7 +375,7 @@ int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate,
break;
}
- /* Exit without result, in case no match can be deteched */
+ /* Exit without result, in case no match can be detected */
if (!match) {
ch_mode_rate->chan_mode = GSM48_CMODE_SIGN;
ch_mode_rate->chan_rate = CH_RATE_SDCCH;
@@ -407,7 +406,7 @@ void gen_bss_supported_codec_list(struct gsm0808_speech_codec_list *scl,
for (i = 0; i < msc->audio_length; i++) {
/* Pick a permitted speech value from the global codec configuration list */
- perm_spch = audio_support_to_gsm88(msc->audio_support[i]);
+ perm_spch = audio_support_to_gsm88(&msc->audio_support[i]);
/* Check this permitted speech value against the BTS specific parameters.
* if the BTS does not support the codec, try the next one */
@@ -422,8 +421,8 @@ void gen_bss_supported_codec_list(struct gsm0808_speech_codec_list *scl,
/* AMR (HR/FR version 3) is the only codec that requires a codec
* configuration (S0-S15). Determine the current configuration and update
* the cfg flag. */
- if (msc->audio_support[i]->ver == 3)
- scl->codec[scl->len].cfg = gen_bss_supported_amr_s15_s0(msc, bts, msc->audio_support[i]->hr);
+ if (msc->audio_support[i].ver == 3)
+ scl->codec[scl->len].cfg = gen_bss_supported_amr_s15_s0(msc, bts, msc->audio_support[i].hr);
scl->len++;
}
@@ -504,3 +503,24 @@ int check_codec_pref(struct llist_head *mscs)
return rc;
}
+
+/* The AMR modes represented by S1 (3GPP TS 28.062 Table 7.11.3.1.3-2) are mandatory in BSSAP.
+ * Check each msc's cfg whether all rates required for S1 are allowed. */
+int check_amr_modes(struct llist_head *mscs)
+{
+ struct bsc_msc_data *msc;
+ int rc = 0;
+ /* Get the modes defined by FR S1: */
+ const uint8_t s1_modes = gsm0808_amr_modes_from_cfg[1][1];
+
+ llist_for_each_entry(msc, mscs, entry) {
+ const uint8_t msc_modes = gsm48_multi_rate_conf_get_amr_modes(&msc->amr_conf);
+ if ((msc_modes & s1_modes) != s1_modes) {
+ LOGP(DMSC, LOGL_ERROR,
+ "msc %d: WARNING: 'amr-config' forbids one or more of the S1 rates, which are mandatory"
+ " on BSSAP (4.75, 5.90, 7.40, 12.2)\n", msc->nr);
+ rc = -1;
+ }
+ }
+ return rc;
+}
diff --git a/src/osmo-bsc/e1_config.c b/src/osmo-bsc/e1_config.c
index 9ccbbfd70..db77234c9 100644
--- a/src/osmo-bsc/e1_config.c
+++ b/src/osmo-bsc/e1_config.c
@@ -90,7 +90,7 @@ int e1_reconfig_trx(struct gsm_bts_trx *trx)
if (trx->bts->type == GSM_BTS_TYPE_RBS2000) {
struct e1inp_sign_link *oml_link;
oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx,
- trx->rsl_tei, SAPI_OML);
+ trx->rsl_tei_primary, SAPI_OML);
if (!oml_link) {
LOG_TRX(trx, DLINP, LOGL_ERROR, "TRX OML link creation failed\n");
return -ENOMEM;
@@ -100,14 +100,14 @@ int e1_reconfig_trx(struct gsm_bts_trx *trx)
trx->oml_link = oml_link;
}
rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
- trx, trx->rsl_tei, SAPI_RSL);
+ trx, trx->rsl_tei_primary, SAPI_RSL);
if (!rsl_link) {
LOG_TRX(trx, DLINP, LOGL_ERROR, "TRX RSL link creation failed\n");
return -ENOMEM;
}
- if (trx->rsl_link)
- e1inp_sign_link_destroy(trx->rsl_link);
- trx->rsl_link = rsl_link;
+ if (trx->rsl_link_primary)
+ e1inp_sign_link_destroy(trx->rsl_link_primary);
+ trx->rsl_link_primary = rsl_link;
for (i = 0; i < TRX_NR_TS; i++)
e1_reconfig_ts(&trx->ts[i]);
@@ -196,7 +196,7 @@ int e1_reconfig_bts(struct gsm_bts *bts)
if (bts->oml_link)
e1inp_sign_link_destroy(bts->oml_link);
bts->oml_link = oml_link;
- rc = clock_gettime(CLOCK_MONOTONIC, &tp);
+ rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
bts->uptime = (rc < 0) ? 0 : tp.tv_sec; /* we don't need sub-second precision for uptime */
llist_for_each_entry(trx, &bts->trx_list, list)
diff --git a/src/osmo-bsc/gsm_04_08_rr.c b/src/osmo-bsc/gsm_04_08_rr.c
index a44812618..ee772df6c 100644
--- a/src/osmo-bsc/gsm_04_08_rr.c
+++ b/src/osmo-bsc/gsm_04_08_rr.c
@@ -46,12 +46,12 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/bts.h>
-
+#include <osmocom/bsc/lchan.h>
int gsm48_sendmsg(struct msgb *msg)
{
if (msg->lchan)
- msg->dst = msg->lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(msg->lchan);
msg->l3h = msg->data;
return rsl_data_request(msg, 0);
@@ -232,11 +232,11 @@ int get_reason_by_chreq(uint8_t ra, int neci)
return GSM_CHREQ_REASON_OTHER;
}
-static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg)
+static int put_mr_config_for_ms(struct msgb *msg, const struct gsm48_multi_rate_conf *mr_conf_filtered,
+ const struct amr_multirate_conf *mr_modes)
{
- if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
- msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0],
- lchan->mr_ms_lv + 1);
+ msgb_put_u8(msg, GSM48_IE_MUL_RATE_CFG);
+ return gsm48_multirate_config(msg, mr_conf_filtered, mr_modes->ms_mode, mr_modes->num_modes);
}
#define CELL_SEL_IND_AFTER_REL_EARCFN_ENTRY (1+16+4+1+1)
@@ -244,7 +244,7 @@ static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg)
#define CELL_SEL_IND_AFTER_REL_MAX_BYTES OSMO_BYTES_FOR_BITS(CELL_SEL_IND_AFTER_REL_MAX_BITS)
/* Generate a CSN.1 encoded "Cell Selection Indicator after release of all TCH and SDCCH"
- * as per TF 44.018 version 15.3.0 Table 10.5.2.1e.1. This only generates the "value"
+ * as per TS 44.018 version 15.3.0 Table 10.5.2.1e.1. This only generates the "value"
* part of the IE, not the tag+length wrapper */
static int generate_cell_sel_ind_after_rel(uint8_t *out, unsigned int out_len, const struct gsm_bts *bts)
{
@@ -313,10 +313,10 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan)
cause = msgb_put(msg, 1);
cause[0] = lchan->release.rr_cause;
- if (lchan->release.is_csfb) {
+ if (lchan->release.last_eutran_plmn_valid) {
uint8_t buf[CELL_SEL_IND_AFTER_REL_MAX_BYTES];
int len;
-
+ /* FIXME: so far we assume all configured neigbhors match last_eutran_plmn */
len = generate_cell_sel_ind_after_rel(buf, sizeof(buf), lchan->ts->trx->bts);
if (len == 0) {
LOGPLCHAN(lchan, DRR, LOGL_NOTICE, "MSC indicated CSFB Fast Return, but "
@@ -325,8 +325,9 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan)
msgb_tlv_put(msg, GSM48_IE_CELL_SEL_IND_AFTER_REL, len, buf);
}
- DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d RR-Cause: 0x%x '%s'\n",
- lchan->nr, lchan->type, lchan->release.rr_cause, rr_cause_name(lchan->release.rr_cause));
+ DEBUGP(DRR, "%s Tx Channel Release (cause=0x%02x '%s')\n",
+ gsm_lchan_name(lchan), lchan->release.rr_cause,
+ rr_cause_name(lchan->release.rr_cause));
/* Send actual release request to MS */
return gsm48_sendmsg(msg);
@@ -358,7 +359,8 @@ int gsm48_send_rr_classmark_enquiry(struct gsm_lchan *lchan)
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_CLSM_ENQ;
- DEBUGP(DRR, "%s TX CLASSMARK ENQUIRY %u\n", gsm_lchan_name(lchan), msgb_length(msg));
+ DEBUGP(DRR, "%s Tx CLASSMARK ENQUIRY (len=%u)\n",
+ gsm_lchan_name(lchan), msgb_length(msg));
return gsm48_sendmsg(msg);
}
@@ -368,16 +370,14 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
{
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CIPH");
struct gsm48_hdr *gh;
- uint8_t ciph_mod_set;
+ uint8_t ciph_mod_set = 0x00;
msg->lchan = lchan;
- DEBUGP(DRR, "TX CIPHERING MODE CMD\n");
+ DEBUGP(DRR, "%s Tx CIPHERING MODE CMD\n", gsm_lchan_name(lchan));
- if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0))
- ciph_mod_set = 0;
- else
- ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1;
+ if (lchan->encr.alg_a5_n > 0)
+ ciph_mod_set = (lchan->encr.alg_a5_n - 1) << 1 | 0x01;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
gh->proto_discr = GSM48_PDISC_RR;
@@ -397,12 +397,12 @@ static void gsm48_cell_desc(struct gsm48_cell_desc *cd,
}
/*! \brief Encode a TS 04.08 multirate config LV according to 10.5.2.21aa.
- * \param[out] lv caller-allocated buffer of 7 bytes. First octet is is length.
+ * \param[out] msg msgb to append to.
* \param[in] mr_conf multi-rate configuration to encode (selected modes).
* \param[in] modes array describing the AMR modes.
* \param[in] num_modes length of the modes array.
* \returns 0 on success, -EINVAL on failure. */
-int gsm48_multirate_config(uint8_t *lv,
+int gsm48_multirate_config(struct msgb *msg,
const struct gsm48_multi_rate_conf *mr_conf,
const struct amr_mode *modes, unsigned int num_modes)
{
@@ -413,15 +413,17 @@ int gsm48_multirate_config(uint8_t *lv,
bool mode_valid;
uint8_t *gsm48_ie = (uint8_t *) mr_conf;
const struct amr_mode *modes_selected[4];
+ uint8_t *len;
+ uint8_t *data;
/* Check if modes for consistency (order and duplicates) */
- for (i = 0; i < num_modes; i++) {
- if (i > 0 && modes[i - 1].mode > modes[i].mode) {
+ for (i = 1; i < num_modes; i++) {
+ if (modes[i - 1].mode > modes[i].mode) {
LOGP(DRR, LOGL_ERROR,
"BUG: Multirate codec with inconsistent config (mode order).\n");
return -EINVAL;
}
- if (i > 0 && modes[i - 1].mode == modes[i].mode) {
+ if (modes[i - 1].mode == modes[i].mode) {
LOGP(DRR, LOGL_ERROR,
"BUG: Multirate codec with inconsistent config (duplicate modes).\n");
return -EINVAL;
@@ -482,52 +484,91 @@ int gsm48_multirate_config(uint8_t *lv,
/* When the caller is not interested in any result, skip the actual
* composition of the IE (dry run) */
- if (!lv)
+ if (!msg)
return 0;
/* Compose output buffer */
- lv[0] = (num == 1) ? 2 : (num + 2);
- memcpy(lv + 1, gsm48_ie, 2);
+ /* length */
+ len = msgb_put(msg, 1);
+
+ /* Write octet 3 (Multirate speech version, NSCB, ICMI, spare, Start mode)
+ * and octet 4 (Set of AMR codec modes) */
+ data = msgb_put(msg, 2);
+ memcpy(data, gsm48_ie, 2);
if (num == 1)
- return 0;
+ goto return_msg;
- lv[3] = modes_selected[0]->threshold & 0x3f;
- lv[4] = modes_selected[0]->hysteresis << 4;
+ /* more than 1 mode: write octet 5 and 6: threshold 1 and hysteresis 1 */
+ data = msgb_put(msg, 2);
+ data[0] = modes_selected[0]->threshold & 0x3f;
+ data[1] = modes_selected[0]->hysteresis << 4;
if (num == 2)
- return 0;
- lv[4] |= (modes_selected[1]->threshold & 0x3f) >> 2;
- lv[5] = modes_selected[1]->threshold << 6;
- lv[5] |= (modes_selected[1]->hysteresis & 0x0f) << 2;
+ goto return_msg;
+
+ /* more than 2 modes: complete octet 6 and add octet 7: threshold 2 and hysteresis 2.
+ * Threshold 2 starts in octet 6. */
+ data[1] |= (modes_selected[1]->threshold & 0x3f) >> 2;
+ /* octet 7 */
+ data = msgb_put(msg, 1);
+ data[0] = modes_selected[1]->threshold << 6;
+ data[0] |= (modes_selected[1]->hysteresis & 0x0f) << 2;
if (num == 3)
- return 0;
- lv[5] |= (modes_selected[2]->threshold & 0x3f) >> 4;
- lv[6] = modes_selected[2]->threshold << 4;
- lv[6] |= modes_selected[2]->hysteresis & 0x0f;
-
+ goto return_msg;
+
+ /* four modes: complete octet 7 and add octet 8: threshold 3 and hysteresis 3.
+ * Threshold 3 starts in octet 7. */
+ data[0] |= (modes_selected[2]->threshold & 0x3f) >> 4;
+ /* octet 8 */
+ data = msgb_put(msg, 1);
+ data[0] = modes_selected[2]->threshold << 4;
+ data[0] |= modes_selected[2]->hysteresis & 0x0f;
+
+return_msg:
+ /* Place written len in the IE length field. msg->tail points one byte after the last data octet, len points at
+ * the L octet of the TLV. */
+ *len = (msg->tail - 1) - len;
return 0;
}
#define GSM48_HOCMD_CCHDESC_LEN 16
/* Chapter 9.1.15: Handover Command */
-struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref)
+struct msgb *gsm48_make_ho_cmd(const struct gsm_lchan *new_lchan,
+ enum handover_scope ho_scope, bool async,
+ uint8_t power_command, uint8_t ho_ref)
{
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 HO CMD");
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_ho_cmd *ho =
(struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho));
+ const struct gsm_bts *bts = new_lchan->ts->trx->bts;
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_HANDO_CMD;
/* mandatory bits */
- gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts);
- gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan);
+ gsm48_cell_desc(&ho->cell_desc, bts);
+ if (gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan, gsm_ts_tsc(new_lchan->ts), false)) {
+ msgb_free(msg);
+ return NULL;
+ }
ho->ho_ref = ho_ref;
ho->power_command = power_command;
+ /* Synchronization Indication, TV (see 3GPP TS 44.018, 9.1.15.1).
+ * In the case of inter-RAT handover, always include this IE for the sake of
+ * explicitness. In the case of intra-RAT handover, include this IE only for
+ * the synchronized handover. If omitted, non-synchronized handover is assumed. */
+ if (!async || (ho_scope & HO_INTER_BSC_IN)) {
+ /* Only the SI field (Non-synchronized/Synchronized) is present.
+ * TODO: ROT (Report Observed Time Difference), currently 0.
+ * TODO: NCI (Normal cell indication), currently 0. */
+ const uint8_t sync_ind = async ? 0x00 : 0x01;
+ /* T (4 bit) + V (4 bit), see 3GPP TS 44.018, 10.5.2.39 */
+ msgb_v_put(msg, GSM48_IE_SYNC_IND | (sync_ind & 0x0f));
+ }
+
if (new_lchan->ts->hopping.enabled) {
- struct gsm_bts *bts = new_lchan->ts->trx->bts;
struct gsm48_system_information_type_1 *si1;
si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1);
@@ -536,9 +577,8 @@ struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_comman
GSM48_HOCMD_CCHDESC_LEN,
si1->cell_channel_description);
}
- /* FIXME: optional bits for type of synchronization? */
- msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->tch_mode);
+ msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->current_ch_mode_rate.chan_mode);
/* Mobile Allocation (after time), TLV (see 3GPP TS 44.018, 10.5.2.21) */
if (new_lchan->ts->hopping.enabled) {
@@ -547,35 +587,45 @@ struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_comman
new_lchan->ts->hopping.ma_data);
}
+ /* (O) Cipher Mode Setting, TV (see 3GPP TS 44.018, 9.1.15.10).
+ * Omitted in the case of intra-RAT (GERAN-to-GERAN) handover.
+ * Shall be included in the case of inter-RAT handover. */
+ if (ho_scope & HO_INTER_BSC_IN) {
+ uint8_t cms = 0x00;
+ if (new_lchan->encr.alg_a5_n > 0)
+ cms = (new_lchan->encr.alg_a5_n - 1) << 1 | 1;
+ /* T (4 bit) + V (4 bit), see 3GPP TS 44.018, 10.5.2.9 */
+ msgb_v_put(msg, GSM48_IE_CIP_MODE_SET | (cms & 0x0f));
+ }
+
/* in case of multi rate we need to attach a config */
- if (new_lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
- msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, new_lchan->mr_ms_lv[0],
- new_lchan->mr_ms_lv + 1);
+ if (gsm48_chan_mode_to_non_vamos(new_lchan->current_ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ if (put_mr_config_for_ms(msg, &new_lchan->current_mr_conf,
+ (new_lchan->type == GSM_LCHAN_TCH_F) ? &bts->mr_full : &bts->mr_half)) {
+ LOG_LCHAN(new_lchan, LOGL_ERROR, "Cannot encode MultiRate Configuration IE\n");
+ msgb_free(msg);
+ return NULL;
+ }
+ }
return msg;
}
-int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
- uint8_t power_command, uint8_t ho_ref)
-{
- struct msgb *msg = gsm48_make_ho_cmd(new_lchan, power_command, ho_ref);
- if (!msg)
- return -EINVAL;
- msg->lchan = old_lchan;
- return gsm48_sendmsg(msg);
-}
-
/* Chapter 9.1.2: Assignment Command */
-int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command)
+int gsm48_send_rr_ass_cmd(struct gsm_lchan *current_lchan, struct gsm_lchan *new_lchan, uint8_t power_command)
{
+ int rc;
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ASS CMD");
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_ass_cmd *ass =
(struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass));
+ struct gsm_bts *bts = new_lchan->ts->trx->bts;
- DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode);
+ DEBUGP(DRR, "%s Tx ASSIGNMENT COMMAND (tch_mode=0x%02x)\n",
+ gsm_lchan_name(current_lchan),
+ new_lchan->current_ch_mode_rate.chan_mode);
- msg->lchan = dest_lchan;
+ msg->lchan = current_lchan;
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_ASS_CMD;
@@ -587,26 +637,45 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan,
* the chan_desc. But as long as multi-slot configurations
* are not used we seem to be fine.
*/
- gsm48_lchan2chan_desc(&ass->chan_desc, lchan);
+ rc = gsm48_lchan2chan_desc(&ass->chan_desc, new_lchan, new_lchan->tsc, false);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
+ }
ass->power_command = power_command;
/* Cell Channel Description (freq. hopping), TV (see 3GPP TS 44.018, 10.5.2.1b) */
- if (lchan->ts->hopping.enabled) {
- uint8_t *chan_desc = msgb_put(msg, 1 + 16); /* tag + fixed length */
- generate_cell_chan_list(chan_desc + 1, dest_lchan->ts->trx->bts);
- chan_desc[0] = GSM48_IE_CELL_CH_DESC;
+ if (new_lchan->ts->hopping.enabled) {
+ const struct gsm48_system_information_type_1 *si1 = GSM_BTS_SI(bts, 1);
+ msgb_tv_fixed_put(msg, GSM48_IE_CELL_CH_DESC,
+ sizeof(si1->cell_channel_description),
+ si1->cell_channel_description);
}
- msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode);
+ msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->current_ch_mode_rate.chan_mode);
/* Mobile Allocation (freq. hopping), TLV (see 3GPP TS 44.018, 10.5.2.21) */
- if (lchan->ts->hopping.enabled) {
- msgb_tlv_put(msg, GSM48_IE_MA_AFTER, lchan->ts->hopping.ma_len,
- lchan->ts->hopping.ma_data);
+ if (new_lchan->ts->hopping.enabled) {
+ msgb_tlv_put(msg, GSM48_IE_MA_AFTER, new_lchan->ts->hopping.ma_len,
+ new_lchan->ts->hopping.ma_data);
}
/* in case of multi rate we need to attach a config */
- mr_config_for_ms(lchan, msg);
+ if (gsm48_chan_mode_to_non_vamos(new_lchan->current_ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ int rc = put_mr_config_for_ms(msg, &new_lchan->current_mr_conf,
+ (new_lchan->type == GSM_LCHAN_TCH_F) ? &bts->mr_full : &bts->mr_half);
+ if (rc) {
+ LOG_LCHAN(current_lchan, LOGL_ERROR, "Cannot encode MultiRate Configuration IE\n");
+ msgb_free(msg);
+ return rc;
+ }
+ }
+
+ /* For VAMOS, include the TSC Set number in the Extended TSC Set IE.
+ * We don't put any PS domain related values, only the lowest two CS domain bits.
+ * Convert from spec conforming "human readable" TSC Set 1-4 to 0-3 on the wire. */
+ if (new_lchan->vamos.enabled && new_lchan->tsc_set > 0)
+ msgb_tv_put(msg, GSM48_IE_EXTENDED_TSC_SET, new_lchan->tsc_set - 1);
return gsm48_sendmsg(msg);
}
@@ -636,25 +705,43 @@ int gsm48_send_rr_app_info(struct gsm_lchan *lchan, uint8_t apdu_id, uint8_t apd
/* 9.1.5 Channel mode modify: Modify the mode on the MS side */
int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode)
{
+ int rc;
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CHN MOD");
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_chan_mode_modify *cmm =
(struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
+ struct gsm_bts *bts = lchan->ts->trx->bts;
- DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
+ DEBUGP(DRR, "%s Tx CHANNEL MODE MODIFY (mode=0x%02x)\n",
+ gsm_lchan_name(lchan), mode);
- lchan->tch_mode = mode;
msg->lchan = lchan;
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
- /* fill the channel information element, this code
- * should probably be shared with rsl_rx_chan_rqd() */
- gsm48_lchan2chan_desc(&cmm->chan_desc, lchan);
+ rc = gsm48_lchan2chan_desc(&cmm->chan_desc, lchan, lchan->modify.tsc, false);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
+ }
cmm->mode = mode;
/* in case of multi rate we need to attach a config */
- mr_config_for_ms(lchan, msg);
+ if (gsm48_chan_mode_to_non_vamos(lchan->modify.ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ int rc = put_mr_config_for_ms(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;
+ }
+ }
+
+ if (lchan->modify.info.vamos && lchan->modify.tsc_set > 0) {
+ /* Add the Extended TSC Set IE. So far we only need a TSC Set sent for VAMOS.
+ * Convert from spec conforming "human readable" TSC Set 1-4 to 0-3 on the wire */
+ msgb_tv_put(msg, GSM48_IE_EXTENDED_TSC_SET, (lchan->modify.tsc_set - 1) & 0x3);
+ }
return gsm48_sendmsg(msg);
}
@@ -668,32 +755,64 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
LOG_LCHAN(msg->lchan, LOGL_DEBUG, "CHANNEL MODE MODIFY ACK for %s\n",
gsm48_chan_mode_name(mod->mode));
- if (mod->mode != msg->lchan->tch_mode) {
+ if (mod->mode != msg->lchan->modify.ch_mode_rate.chan_mode) {
LOG_LCHAN(msg->lchan, LOGL_ERROR,
"CHANNEL MODE MODIFY ACK has wrong mode: Wanted: %s Got: %s\n",
- gsm48_chan_mode_name(msg->lchan->tch_mode),
+ gsm48_chan_mode_name(msg->lchan->modify.ch_mode_rate.chan_mode),
gsm48_chan_mode_name(mod->mode));
return -1;
}
- /* update the channel type */
- switch (mod->mode) {
- case GSM48_CMODE_SIGN:
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
- break;
- case GSM48_CMODE_SPEECH_V1:
- case GSM48_CMODE_SPEECH_EFR:
- case GSM48_CMODE_SPEECH_AMR:
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
- break;
- case GSM48_CMODE_DATA_14k5:
- case GSM48_CMODE_DATA_12k0:
- case GSM48_CMODE_DATA_6k0:
- case GSM48_CMODE_DATA_3k6:
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA;
- break;
+ return 0;
+}
+
+/* Get the ARFCN from the BCCH channel list by the index "BCCH-FREQ-NCELL i"
+ * (idx) as described in 3GPP TS 144.018 § 10.5.2.20. The BCCH channel list is
+ * split into two sub lists, each ascendingly ordered by ARFCN > 0:
+ * 1) SI* and SI*bis entries
+ * 2) SI*ter entries (if available)
+ * ARFCN 0 is at the end of each sub list.
+ * Both sub lists are stored in one bitvec (nbv), iterate twice through it. */
+static int neigh_list_get_arfcn(struct gsm_bts *bts, const struct bitvec *nbv, unsigned int idx)
+{
+ unsigned int arfcn, i = 0;
+
+ /* First sub list, ARFCN > 0 */
+ for (arfcn = 1; arfcn < nbv->data_len * 8; arfcn++) {
+ if (bitvec_get_bit_pos(nbv, arfcn) == ZERO)
+ continue;
+ /* Skip SI*ter */
+ if (!band_compatible(bts, arfcn))
+ continue;
+ if (i == idx)
+ return arfcn;
+ i++;
+ }
+
+ /* First sub list, ARFCN == 0 (last position) */
+ if (bitvec_get_bit_pos(nbv, 0) == ONE && band_compatible(bts, 0)) {
+ if (i == idx)
+ return 0;
+ i++;
}
+ /* Second sub list, ARFCN > 0 */
+ for (arfcn = 1; arfcn < nbv->data_len * 8; arfcn++) {
+ if (bitvec_get_bit_pos(nbv, arfcn) == ZERO)
+ continue;
+ /* Require SI*ter */
+ if (band_compatible(bts, arfcn))
+ continue;
+ if (i == idx)
+ return arfcn;
+ i++;
+ }
+
+ /* Second sub list, ARFCN == 0 (last position) */
+ if (bitvec_get_bit_pos(nbv, 0) == ONE && !band_compatible(bts, 0) && i == idx)
+ return 0;
+
+ LOGP(DRR, LOGL_ERROR, "Invalid BCCH channel list index %d in measurement report\n", idx);
return 0;
}
@@ -731,7 +850,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[0];
mrc->rxlev = data[3] & 0x3f;
mrc->neigh_idx = data[4] >> 3;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5);
if (rep->num_cell < 2)
return 0;
@@ -739,7 +858,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[1];
mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7);
mrc->neigh_idx = (data[6] >> 2) & 0x1f;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4);
if (rep->num_cell < 3)
return 0;
@@ -747,7 +866,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[2];
mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6);
mrc->neigh_idx = (data[8] >> 1) & 0x1f;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3);
if (rep->num_cell < 4)
return 0;
@@ -755,7 +874,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[3];
mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5);
mrc->neigh_idx = data[10] & 0x1f;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = data[11] >> 2;
if (rep->num_cell < 5)
return 0;
@@ -763,7 +882,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[4];
mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4);
mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7);
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = (data[13] >> 1) & 0x3f;
if (rep->num_cell < 6)
return 0;
@@ -771,7 +890,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[5];
mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3);
mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6);
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = data[15] & 0x3f;
return 0;
@@ -946,7 +1065,7 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_RR_HO_FAIL, msg);
break;
case GSM48_MT_RR_CIPH_M_COMPL:
- bsc_cipher_mode_compl(conn, msg, conn->lchan->encr.alg_id);
+ bsc_cipher_mode_compl(conn, msg, conn->lchan->encr.alg_a5_n);
break;
case GSM48_MT_RR_ASS_COMPL:
if (conn->assignment.fi)
@@ -974,6 +1093,9 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
case GSM48_MT_RR_CLSM_CHG:
handle_classmark_chg(conn, msg);
break;
+ case GSM48_MT_RR_UTRAN_CLSM_CHG:
+ /* TODO: forward to the MSC? */
+ break;
case GSM48_MT_RR_APP_INFO:
/* Passing RR APP INFO to MSC, not quite
* according to spec */
@@ -992,15 +1114,18 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
* VTY configuration does not permit. */
if (msg_type == GSM48_MT_CC_EMERG_SETUP) {
if (msg->lchan->ts->trx->bts->si_common.rach_control.t2 & 0x4) {
- LOG_LCHAN(msg->lchan, LOGL_NOTICE, "MS attempts EMERGENCY SETUP although EMERGENCY CALLS"
- " are not allowed in sysinfo (spec violation by MS!)\n");
- lchan_release(msg->lchan, true, true, GSM48_RR_CAUSE_PREMPTIVE_REL);
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "MS attempts EMERGENCY SETUP although EMERGENCY CALLS"
+ " are not allowed in sysinfo (cfg: network / bts / rach emergency call allowed 0)\n");
+ lchan_release(msg->lchan, true, true, GSM48_RR_CAUSE_PROT_ERROR_UNSPC,
+ gscon_last_eutran_plmn(msg->lchan->conn));
break;
}
if (!conn->sccp.msc->allow_emerg) {
- LOG_LCHAN(msg->lchan, LOGL_NOTICE, "MS attempts EMERGENCY SETUP, but EMERGENCY CALLS are"
- " denied on this BSC (check BTS config!)\n");
- lchan_release(msg->lchan, true, true, GSM48_RR_CAUSE_PREMPTIVE_REL);
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "MS attempts EMERGENCY SETUP, but EMERGENCY CALLS are"
+ " denied on MSC %d (cfg: msc %d / allow-emergency deny)\n",
+ conn->sccp.msc->nr, conn->sccp.msc->nr);
+ lchan_release(msg->lchan, true, true, GSM48_RR_CAUSE_PROT_ERROR_UNSPC,
+ gscon_last_eutran_plmn(msg->lchan->conn));
break;
}
}
diff --git a/src/osmo-bsc/gsm_08_08.c b/src/osmo-bsc/gsm_08_08.c
index ce7606f28..a2bab4bed 100644
--- a/src/osmo-bsc/gsm_08_08.c
+++ b/src/osmo-bsc/gsm_08_08.c
@@ -26,6 +26,7 @@
#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/a_reset.h>
@@ -34,6 +35,7 @@
#include <osmocom/bsc/lcs_loc_req.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/mncc.h>
#include <osmocom/gsm/gsm48.h>
@@ -69,21 +71,23 @@ void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn,
{
int rc;
struct msgb *resp;
+ struct gsm_bts *bts;
if (!msc_connected(conn))
return;
- LOGP(DMSC, LOGL_NOTICE, "Tx MSC SAPI N REJECT (dlci=0x%02x, cause='%s')\n",
- dlci, gsm0808_cause_name(cause));
+ bts = conn_get_bts(conn);
+ LOG_BTS(bts, DMSC, LOGL_NOTICE, "Tx MSC SAPI N REJECT (dlci=0x%02x, cause='%s')\n",
+ dlci, gsm0808_cause_name(cause));
resp = gsm0808_create_sapi_reject_cause(dlci, cause);
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_SAPI_N_REJECT]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_SAPI_N_REJECT));
rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
if (rc != 0)
msgb_free(resp);
}
/*! MS->MSC: Tell MSC that ciphering has been enabled. */
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr)
+void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_a5_n)
{
int rc;
struct msgb *resp;
@@ -92,30 +96,13 @@ void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *
return;
LOGP(DMSC, LOGL_DEBUG, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n");
- resp = gsm0808_create_cipher_complete(msg, chosen_encr);
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_CIPHER_COMPLETE]);
+ resp = gsm0808_create_cipher_complete(msg, ALG_A5_NR_TO_BSSAP(chosen_a5_n));
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_CIPHER_COMPLETE));
rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
if (rc != 0)
msgb_free(resp);
}
-/* 9.2.5 CM service accept */
-int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACK");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-
- msg->lchan = conn->lchan;
-
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_CM_SERV_ACC;
-
- DEBUGP(DMM, "-> CM SERVICE ACK\n");
-
- gscon_submit_rsl_dtap(conn, msg, 0, 0);
- return 0;
-}
-
static bool is_cm_service_for_emerg(struct msgb *msg)
{
struct gsm48_service_request *cm;
@@ -232,7 +219,7 @@ static struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
if (nri_matches_msc) {
LOG_NRI(LOGL_DEBUG, "matches msc %d, but this MSC is currently not connected\n",
msc->nr);
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_MSCPOOL_SUBSCR_ATTACH_LOST]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_MSCPOOL_SUBSCR_ATTACH_LOST));
}
continue;
}
@@ -244,10 +231,10 @@ static struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
msc->nr);
} else {
LOG_NRI(LOGL_DEBUG, "matches msc %d\n", msc->nr);
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_MSCPOOL_SUBSCR_KNOWN]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_MSCPOOL_SUBSCR_KNOWN));
if (is_emerg) {
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_MSCPOOL_EMERG_FORWARDED]);
- rate_ctr_inc(&bsc_gsmnet->bsc_ctrs->ctr[BSC_CTR_MSCPOOL_EMERG_FORWARDED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_MSCPOOL_EMERG_FORWARDED));
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->bsc_ctrs, BSC_CTR_MSCPOOL_EMERG_FORWARDED));
}
return msc;
}
@@ -282,9 +269,9 @@ static struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
* them are usable -- wrap to the start. */
msc_target = msc_round_robin_next ? : msc_round_robin_first;
if (!msc_target) {
- rate_ctr_inc(&bsc_gsmnet->bsc_ctrs->ctr[BSC_CTR_MSCPOOL_SUBSCR_NO_MSC]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->bsc_ctrs, BSC_CTR_MSCPOOL_SUBSCR_NO_MSC));
if (is_emerg)
- rate_ctr_inc(&bsc_gsmnet->bsc_ctrs->ctr[BSC_CTR_MSCPOOL_EMERG_LOST]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->bsc_ctrs, BSC_CTR_MSCPOOL_EMERG_LOST));
return NULL;
}
@@ -292,13 +279,13 @@ static struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
osmo_mobile_identity_to_str_c(OTC_SELECT, mi), msc_target->nr);
if (is_null_nri)
- rate_ctr_inc(&msc_target->msc_ctrs->ctr[MSC_CTR_MSCPOOL_SUBSCR_REATTACH]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc_target->msc_ctrs, MSC_CTR_MSCPOOL_SUBSCR_REATTACH));
else
- rate_ctr_inc(&msc_target->msc_ctrs->ctr[MSC_CTR_MSCPOOL_SUBSCR_NEW]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc_target->msc_ctrs, MSC_CTR_MSCPOOL_SUBSCR_NEW));
if (is_emerg) {
- rate_ctr_inc(&msc_target->msc_ctrs->ctr[MSC_CTR_MSCPOOL_EMERG_FORWARDED]);
- rate_ctr_inc(&bsc_gsmnet->bsc_ctrs->ctr[BSC_CTR_MSCPOOL_EMERG_FORWARDED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc_target->msc_ctrs, MSC_CTR_MSCPOOL_EMERG_FORWARDED));
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->bsc_ctrs, BSC_CTR_MSCPOOL_EMERG_FORWARDED));
}
/* An MSC was picked by round-robin, so update the next round-robin nr to pick */
@@ -346,6 +333,7 @@ static void parse_powercap(struct gsm_subscriber_connection *conn, struct msgb *
/* No power cap in other messages */
return;
}
+ break;
/* FIXME: pwr_lev in Paging Response? */
default:
/* No power cap in other messages */
@@ -401,11 +389,10 @@ int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_chan
if (osmo_mobile_identity_decode_from_l3(&mi, msg, false)) {
LOG_COMPL_L3(pdisc, mtype, LOGL_ERROR, "Cannot extract Mobile Identity: %s\n",
msgb_hexdump_c(OTC_SELECT, msg));
- /* Likely this is an invalid Complete Layer 3 message that deserves to be rejected. However, the current
- * state of our ttcn3 tests does send invalid Layer 3 Info in some tests and expects osmo-bsc to not
- * care about that. So, changing the behavior to rejecting on missing MI causes test failure and, if at
- * all, should happen in a separate patch.
- * See e.g. BSC_Tests.TC_chan_rel_rll_rel_ind: "dt := * f_est_dchan('23'O, 23, '00010203040506'O);"
+ /* Likely this is an invalid Complete Layer 3 message that deserves to be rejected. However, the BSC is
+ * not expected to look at this layer so it's duty of the MSC to reject it.
+ * Hence, keep on going with the conn without an assigned bsc_subscr, forwarding the L3 to the MSC and
+ * letting it take decision on the matter.
*/
} else {
bsub = bsc_subscr_find_or_create_by_mi(bsc_gsmnet->bsc_subscribers, &mi, __func__);
@@ -413,8 +400,13 @@ int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_chan
/* If this Mobile Identity already has an active bsc_subscr, look whether there also is an active A-interface
* conn for this subscriber. This may be the case during a Perform Location Request (LCS) from the MSC that
- * started on an IDLE MS, and now the MS is becoming active. Associate with the existing conn. */
- if (bsub)
+ * started on an IDLE MS, and now the MS is becoming active. Associate with the existing conn.
+ *
+ * However, for a CM Re-Establishment Request, we must *not* re-use the existing conn, but allocate a second
+ * conn for the same bsub. The previous conn will be Clear'ed by the MSC as soon as it receives the L3 Complete
+ * message == the CM Re-Establishment Request.
+ */
+ if (bsub && !(pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_REEST_REQ))
conn = bsc_conn_by_bsub(bsub);
if (!conn) {
@@ -447,7 +439,10 @@ int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_chan
paged_from_msc = NULL;
paging_reasons = BSC_PAGING_NONE;
if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP) {
- paging_request_stop(&paged_from_msc, &paging_reasons, bts, conn->bsub);
+ /* It only makes sense to attempt to find a pending paging request if the subscriber from the
+ * Paging Response can be identified (bsub != NULL). */
+ if (conn->bsub)
+ paging_request_stop(&paged_from_msc, &paging_reasons, bts, conn->bsub);
if (!paged_from_msc) {
/* This looks like an unsolicited Paging Response. It is required to pick any MSC, because any
* MT-CSFB calls were Paged by the MSC via SGs, and hence are not listed in the BSC. */
@@ -455,12 +450,12 @@ int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_chan
"%s Unsolicited Paging Response, possibly an MT-CSFB call.\n",
osmo_mobile_identity_to_str_c(OTC_SELECT, &mi));
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_PAGING_NO_ACTIVE_PAGING]);
- rate_ctr_inc(&bsc_gsmnet->bsc_ctrs->ctr[BSC_CTR_PAGING_NO_ACTIVE_PAGING]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_PAGING_NO_ACTIVE_PAGING));
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->bsc_ctrs, BSC_CTR_PAGING_NO_ACTIVE_PAGING));
} else if (is_msc_usable(paged_from_msc, is_emerg)) {
LOG_COMPL_L3(pdisc, mtype, LOGL_DEBUG, "%s matches earlier Paging from msc %d\n",
osmo_mobile_identity_to_str_c(OTC_SELECT, &mi), paged_from_msc->nr);
- rate_ctr_inc(&paged_from_msc->msc_ctrs->ctr[MSC_CTR_MSCPOOL_SUBSCR_PAGED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(paged_from_msc->msc_ctrs, MSC_CTR_MSCPOOL_SUBSCR_PAGED));
} else {
LOG_COMPL_L3(pdisc, mtype, LOGL_DEBUG,
"%s matches earlier Paging from msc %d, but this MSC is not connected\n",
@@ -539,11 +534,13 @@ int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_chan
early_exit:
if (release_lchan)
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
+ lchan_release(lchan, true, true, RSL_ERR_EQUIPMENT_FAIL,
+ gscon_last_eutran_plmn(conn));
log_set_context(LOG_CTX_BSC_SUBSCR, NULL);
return rc;
}
+
/*! MS->BSC/MSC: Um L3 message. */
void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
{
@@ -556,8 +553,9 @@ void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct ms
parse_powercap(conn, msg);
- /* Store link_id in msg->cb */
- OBSC_LINKID_CB(msg) = link_id;
+ /* convert RSL link ID to DLCI, store in msg->cb */
+ OBSC_LINKID_CB(msg) = RSL_LINK_ID2DLCI(link_id);
+
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MO_DTAP, msg);
done:
log_set_context(LOG_CTX_BSC_SUBSCR, NULL);
@@ -590,10 +588,20 @@ void bsc_cm_update(struct gsm_subscriber_connection *conn,
}
conn_update_ms_power_class(conn, rc8);
+ if (cm3 != NULL && cm3_len > 0) {
+ rc = gsm48_decode_classmark3(&conn->cm3, cm3, cm3_len);
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_NOTICE, "Unable to decode classmark3 during CM Update.\n");
+ memset(&conn->cm3, 0, sizeof(conn->cm3));
+ conn->cm3_valid = false;
+ } else
+ conn->cm3_valid = true;
+ }
+
if (!msc_connected(conn))
return;
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_CLASSMARK_UPDATE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_CLASSMARK_UPDATE));
resp = gsm0808_create_classmark_update(cm2, cm2_len, cm3, cm3_len);
rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
if (rc != 0)
diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c
index 1152783ae..012149a36 100644
--- a/src/osmo-bsc/gsm_data.c
+++ b/src/osmo-bsc/gsm_data.c
@@ -62,16 +62,13 @@ void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr,
struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
struct gsm_bts *start_bts)
{
- int i;
struct gsm_bts *bts;
int skip = 0;
if (start_bts)
skip = 1;
- for (i = 0; i < net->num_bts; i++) {
- bts = gsm_bts_num(net, i);
-
+ llist_for_each_entry(bts, &net->bts_list, list) {
if (skip) {
if (start_bts == bts)
skip = 0;
@@ -110,19 +107,21 @@ struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_typ
uint8_t bsic)
{
struct gsm_bts_model *model = bts_model_find(type);
+ struct gsm_bts_sm *bts_sm;
struct gsm_bts *bts;
if (!model && type != GSM_BTS_TYPE_UNKNOWN)
return NULL;
- bts = gsm_bts_alloc(net, net->num_bts);
- if (!bts)
+ bts_sm = gsm_bts_sm_alloc(net, net->num_bts);
+ if (!bts_sm)
return NULL;
+ bts = bts_sm->bts[0];
net->num_bts++;
bts->type = type;
- bts->model = model;
+ gsm_set_bts_model(bts, model);
bts->bsic = bsic;
llist_add_tail(&bts->list, &net->bts_list);
@@ -153,6 +152,7 @@ void gsm_abis_mo_reset(struct gsm_abis_mo *mo)
{
mo->nm_state.operational = NM_OPSTATE_NULL;
mo->nm_state.availability = NM_AVSTATE_POWER_OFF;
+ mo->nm_state.administrative = NM_STATE_LOCKED;
}
void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
@@ -169,7 +169,7 @@ void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
const struct value_string gsm_chreq_descs[] = {
{ GSM_CHREQ_REASON_EMERG, "emergency call" },
{ GSM_CHREQ_REASON_PAG, "answer to paging" },
- { GSM_CHREQ_REASON_CALL, "call re-establishment" },
+ { GSM_CHREQ_REASON_CALL, "call (re-)establishment" },
{ GSM_CHREQ_REASON_LOCATION_UPD,"Location updating" },
{ GSM_CHREQ_REASON_PDCH, "one phase packet access" },
{ GSM_CHREQ_REASON_OTHER, "other" },
@@ -184,31 +184,22 @@ const struct value_string gsm_pchant_names[] = {
{ GSM_PCHAN_TCH_H, "TCH/H" },
{ GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" },
{ GSM_PCHAN_PDCH, "PDCH" },
- { GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" },
+ { GSM_PCHAN_TCH_F_PDCH, "DYNAMIC/IPACCESS" },
{ GSM_PCHAN_UNKNOWN, "UNKNOWN" },
{ GSM_PCHAN_CCCH_SDCCH4_CBCH, "CCCH+SDCCH4+CBCH" },
{ GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH8+CBCH" },
- { GSM_PCHAN_TCH_F_TCH_H_PDCH, "TCH/F_TCH/H_PDCH" },
- { 0, NULL }
-};
-
-const struct value_string gsm_pchan_ids[] = {
- { GSM_PCHAN_NONE, "NONE" },
- { GSM_PCHAN_CCCH, "CCCH" },
- { GSM_PCHAN_CCCH_SDCCH4,"CCCH_SDCCH4" },
- { GSM_PCHAN_TCH_F, "TCH_F" },
- { GSM_PCHAN_TCH_H, "TCH_H" },
- { GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" },
- { GSM_PCHAN_PDCH, "PDCH" },
- { GSM_PCHAN_TCH_F_PDCH, "TCH_F_PDCH" },
- { GSM_PCHAN_UNKNOWN, "UNKNOWN" },
- { GSM_PCHAN_CCCH_SDCCH4_CBCH, "CCCH_SDCCH4_CBCH" },
- { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH8_CBCH" },
- { GSM_PCHAN_TCH_F_TCH_H_PDCH, "TCH_F_TCH_H_PDCH" },
+ { GSM_PCHAN_OSMO_DYN, "DYNAMIC/OSMOCOM" },
+ /* make get_string_value() return GSM_PCHAN_TCH_F_PDCH for both "DYNAMIC/IPACCESS" and "TCH/F_PDCH" */
+ { GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" },
+ /* make get_string_value() return GSM_PCHAN_OSMO_DYN for both "DYNAMIC/OSMOCOM" and "TCH/F_TCH/H_SDCCH8_PDCH" */
+ { GSM_PCHAN_OSMO_DYN, "TCH/F_TCH/H_SDCCH8_PDCH" },
+ /* When adding items here, you must also add matching items to gsm_pchant_descs[]! */
{ 0, NULL }
};
-const struct value_string gsm_pchant_descs[13] = {
+/* VTY command descriptions. These have to be in the same order as gsm_pchant_names[], so that the automatic VTY command
+ * composition in bts_trx_vty_init() works out. */
+const struct value_string gsm_pchant_descs[] = {
{ GSM_PCHAN_NONE, "Physical Channel not configured" },
{ GSM_PCHAN_CCCH, "FCCH + SCH + BCCH + CCCH (Comb. IV)" },
{ GSM_PCHAN_CCCH_SDCCH4,
@@ -217,14 +208,24 @@ const struct value_string gsm_pchant_descs[13] = {
{ GSM_PCHAN_TCH_H, "2 TCH/H + 2 FACCH/H + 2 SACCH (Comb. II)" },
{ GSM_PCHAN_SDCCH8_SACCH8C, "8 SDCCH + 4 SACCH (Comb. VII)" },
{ GSM_PCHAN_PDCH, "Packet Data Channel for GPRS/EDGE" },
- { GSM_PCHAN_TCH_F_PDCH, "Dynamic TCH/F or GPRS PDCH" },
+ { GSM_PCHAN_TCH_F_PDCH, "Dynamic TCH/F or GPRS PDCH"
+ " (dynamic/ipaccess is an alias for tch/f_pdch)" },
{ GSM_PCHAN_UNKNOWN, "Unknown / Unsupported channel combination" },
{ GSM_PCHAN_CCCH_SDCCH4_CBCH, "FCCH + SCH + BCCH + CCCH + CBCH + 3 SDCCH + 2 SACCH (Comb. V)" },
{ GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "7 SDCCH + 4 SACCH + CBCH (Comb. VII)" },
- { GSM_PCHAN_TCH_F_TCH_H_PDCH, "Dynamic TCH/F or TCH/H or GPRS PDCH" },
+ { GSM_PCHAN_OSMO_DYN, "Dynamic TCH/F or TCH/H or SDCCH/8 or GPRS PDCH"
+ " (dynamic/osmocom is an alias for tch/f_tch/h_sdcch8_pdch)" },
+ /* These duplicate entries are needed to provide a description for both the DYNAMIC/... aliases and their
+ * explicit versions 'TCH/F_PDCH' / 'TCH/F_TCH/H_SDCCH8_PDCH', see bts_trx_vty_init() */
+ { GSM_PCHAN_TCH_F_PDCH, "Dynamic TCH/F or GPRS PDCH"
+ " (dynamic/ipaccess is an alias for tch/f_pdch)" },
+ { GSM_PCHAN_OSMO_DYN, "Dynamic TCH/F or TCH/H or SDCCH/8 or GPRS PDCH"
+ " (dynamic/osmocom is an alias for tch/f_tch/h_sdcch8_pdch)" },
{ 0, NULL }
};
+osmo_static_assert(ARRAY_SIZE(gsm_pchant_names) == ARRAY_SIZE(gsm_pchant_descs), _pchan_vty_docs);
+
const char *gsm_pchan_name(enum gsm_phys_chan_config c)
{
return get_value_string(gsm_pchant_names, c);
@@ -235,12 +236,6 @@ enum gsm_phys_chan_config gsm_pchan_parse(const char *name)
return get_string_value(gsm_pchant_names, name);
}
-/* TODO: move to libosmocore, next to gsm_chan_t_names? */
-const char *gsm_lchant_name(enum gsm_chan_t c)
-{
- return get_value_string(gsm_chan_t_names, c);
-}
-
static const struct value_string chreq_names[] = {
{ GSM_CHREQ_REASON_EMERG, "EMERGENCY" },
{ GSM_CHREQ_REASON_PAG, "PAGING" },
@@ -331,16 +326,6 @@ char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts)
return ts2str;
}
-char *gsm_lchan_name_compute(const struct gsm_lchan *lchan)
-{
- struct gsm_bts_trx_ts *ts = lchan->ts;
-
- snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr);
-
- return ts2str;
-}
-
/* obtain the MO structure for a given object instance */
static inline struct gsm_abis_mo *
gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class,
@@ -377,7 +362,7 @@ gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class,
mo = &trx->ts[obj_inst->ts_nr].mo;
break;
case NM_OC_SITE_MANAGER:
- mo = &bts->site_mgr.mo;
+ mo = &bts->site_mgr->mo;
break;
case NM_OC_BS11:
switch (obj_inst->bts_nr) {
@@ -409,15 +394,15 @@ gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class,
mo = &bts->bs11.envabtse[obj_inst->trx_nr].mo;
break;
case NM_OC_GPRS_NSE:
- mo = &bts->gprs.nse.mo;
+ mo = &bts->site_mgr->gprs.nse.mo;
break;
case NM_OC_GPRS_CELL:
mo = &bts->gprs.cell.mo;
break;
case NM_OC_GPRS_NSVC:
- if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
+ if (obj_inst->trx_nr >= ARRAY_SIZE(bts->site_mgr->gprs.nsvc))
return NULL;
- mo = &bts->gprs.nsvc[obj_inst->trx_nr].mo;
+ mo = &bts->site_mgr->gprs.nsvc[obj_inst->trx_nr].mo;
break;
}
return mo;
@@ -473,43 +458,53 @@ gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class,
obj = &trx->ts[obj_inst->ts_nr];
break;
case NM_OC_SITE_MANAGER:
- obj = &bts->site_mgr;
+ obj = bts->site_mgr;
break;
case NM_OC_GPRS_NSE:
- obj = &bts->gprs.nse;
+ obj = &bts->site_mgr->gprs.nse;
break;
case NM_OC_GPRS_CELL:
obj = &bts->gprs.cell;
break;
case NM_OC_GPRS_NSVC:
- if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
+ if (obj_inst->trx_nr >= ARRAY_SIZE(bts->site_mgr->gprs.nsvc))
return NULL;
- obj = &bts->gprs.nsvc[obj_inst->trx_nr];
+ obj = &bts->site_mgr->gprs.nsvc[obj_inst->trx_nr];
break;
}
return obj;
}
/* See Table 10.5.25 of GSM04.08 */
-uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
- uint8_t ts_nr, uint8_t lchan_nr)
+int gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
+ uint8_t ts_nr, uint8_t lchan_nr, bool vamos_is_secondary)
{
uint8_t cbits, chan_nr;
switch (pchan) {
case GSM_PCHAN_TCH_F:
case GSM_PCHAN_TCH_F_PDCH:
- OSMO_ASSERT(lchan_nr == 0);
- cbits = 0x01;
+ if (lchan_nr != 0)
+ return -EINVAL;
+ if (vamos_is_secondary)
+ cbits = ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Bm_ACCHs;
+ else
+ cbits = 0x01;
break;
case GSM_PCHAN_PDCH:
- OSMO_ASSERT(lchan_nr == 0);
+ if (lchan_nr != 0)
+ return -EINVAL;
cbits = RSL_CHAN_OSMO_PDCH >> 3;
break;
case GSM_PCHAN_TCH_H:
- OSMO_ASSERT(lchan_nr < 2);
- cbits = 0x02;
- cbits += lchan_nr;
+ if (lchan_nr >= 2)
+ return -EINVAL;
+ if (vamos_is_secondary)
+ cbits = ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Lm_ACCHs(lchan_nr);
+ else {
+ cbits = 0x02;
+ cbits += lchan_nr;
+ }
break;
case GSM_PCHAN_CCCH_SDCCH4:
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
@@ -520,20 +515,22 @@ uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
*/
if (lchan_nr == CCCH_LCHAN)
chan_nr = 0;
- else
- OSMO_ASSERT(lchan_nr < 4);
+ else if (lchan_nr >= 4)
+ return -EINVAL;
cbits = 0x04;
cbits += lchan_nr;
break;
case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
- OSMO_ASSERT(lchan_nr < 8);
+ if (lchan_nr >= 8)
+ return -EINVAL;
cbits = 0x08;
cbits += lchan_nr;
break;
default:
case GSM_PCHAN_CCCH:
- OSMO_ASSERT(lchan_nr == 0);
+ if (lchan_nr != 0)
+ return -EINVAL;
cbits = 0x10;
break;
}
@@ -543,11 +540,43 @@ uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
return chan_nr;
}
-uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan)
+/* For RSL, to talk to osmo-bts, we introduce Osmocom specific channel number cbits to indicate VAMOS secondary lchans.
+ * However, in RR, which is sent to the MS, these special cbits must not be sent, but their "normal" equivalent; for RR
+ * messages, pass allow_osmo_cbits = false. */
+int gsm_lchan_and_pchan2chan_nr(const struct gsm_lchan *lchan, enum gsm_phys_chan_config pchan, bool allow_osmo_cbits)
{
- /* Note: non-standard Osmocom style dyn TS PDCH mode chan_nr is only used within
- * rsl_tx_dyn_ts_pdch_act_deact(). */
- return gsm_pchan2chan_nr(lchan->ts->pchan_is, lchan->ts->nr, lchan->nr);
+ int rc;
+ uint8_t lchan_nr = lchan->nr;
+
+ /* Take care that we never send Osmocom specific cbits to non-Osmo BTS. */
+ if (allow_osmo_cbits && lchan->vamos.is_secondary
+ && lchan->ts->trx->bts->model->type != GSM_BTS_TYPE_OSMOBTS) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "Cannot address VAMOS shadow lchan on this BTS type: %s\n",
+ get_value_string(bts_type_names, lchan->ts->trx->bts->model->type));
+ return -ENOTSUP;
+ }
+ if (allow_osmo_cbits && lchan->ts->trx->bts->model->type != GSM_BTS_TYPE_OSMOBTS)
+ allow_osmo_cbits = false;
+
+ /* The VAMOS lchans are behind the primary ones in the ts->lchan[] array. They keep their lchan->nr as in the
+ * array, but on the wire they are the "shadow" lchans for the primary lchans. For example, for TCH/F, there is
+ * a primary ts->lchan[0] and a VAMOS ts->lchan[1]. Still, the VAMOS lchan should send chan_nr = 0. */
+ if (lchan->vamos.is_secondary)
+ lchan_nr -= lchan->ts->max_primary_lchans;
+ rc = gsm_pchan2chan_nr(pchan, lchan->ts->nr, lchan_nr,
+ allow_osmo_cbits ? lchan->vamos.is_secondary : false);
+ /* Log an error so that we don't need to add logging to each caller of this function */
+ if (rc < 0)
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "Error encoding Channel Number: pchan %s ts %u ss %u%s\n",
+ gsm_pchan_name(lchan->ts->pchan_from_config), lchan->ts->nr, lchan_nr,
+ lchan->vamos.is_secondary ? " (VAMOS shadow)" : "");
+ return rc;
+}
+
+int gsm_lchan2chan_nr(const struct gsm_lchan *lchan, bool allow_osmo_cbits)
+{
+ return gsm_lchan_and_pchan2chan_nr(lchan, lchan->ts->pchan_is, allow_osmo_cbits);
}
static const uint8_t subslots_per_pchan[] = {
@@ -561,12 +590,12 @@ static const uint8_t subslots_per_pchan[] = {
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8,
/* Dyn TS: maximum allowed subslots */
- [GSM_PCHAN_TCH_F_TCH_H_PDCH] = 2,
+ [GSM_PCHAN_OSMO_DYN] = 8,
[GSM_PCHAN_TCH_F_PDCH] = 1,
};
-/*! According to ts->pchan and possibly ts->dyn_pchan, return the number of
- * logical channels available in the timeslot. */
+/*! Return the maximum number of logical channels that may be used in a timeslot of the given physical channel
+ * configuration. */
uint8_t pchan_subslots(enum gsm_phys_chan_config pchan)
{
if (pchan < 0 || pchan >= ARRAY_SIZE(subslots_per_pchan))
@@ -574,6 +603,30 @@ uint8_t pchan_subslots(enum gsm_phys_chan_config pchan)
return subslots_per_pchan[pchan];
}
+static const uint8_t subslots_per_pchan_vamos[] = {
+ [GSM_PCHAN_NONE] = 0,
+ [GSM_PCHAN_CCCH] = 0,
+ [GSM_PCHAN_PDCH] = 0,
+ [GSM_PCHAN_CCCH_SDCCH4] = 0,
+ /* VAMOS: on a TCH/F, there may be a TCH/H shadow */
+ [GSM_PCHAN_TCH_F] = 2,
+ [GSM_PCHAN_TCH_H] = 2,
+ [GSM_PCHAN_SDCCH8_SACCH8C] = 0,
+ [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 0,
+ [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 0,
+ [GSM_PCHAN_OSMO_DYN] = 0,
+ [GSM_PCHAN_TCH_F_PDCH] = 2,
+};
+
+/* Return the maximum number of VAMOS secondary lchans that may be used in a timeslot of the given physical channel
+ * configuration. */
+uint8_t pchan_subslots_vamos(enum gsm_phys_chan_config pchan)
+{
+ if (pchan < 0 || pchan >= ARRAY_SIZE(subslots_per_pchan_vamos))
+ return 0;
+ return subslots_per_pchan_vamos[pchan];
+}
+
static bool pchan_is_tch(enum gsm_phys_chan_config pchan)
{
switch (pchan) {
@@ -596,17 +649,18 @@ struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn) {
return conn->lchan->ts->trx->bts;
}
-static void _chan_desc_fill_tail(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan)
+static void _chan_desc_fill_tail(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan,
+ uint8_t tsc)
{
if (!lchan->ts->hopping.enabled) {
uint16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
- cd->h0.tsc = gsm_ts_tsc(lchan->ts);
+ cd->h0.tsc = tsc;
cd->h0.h = 0;
cd->h0.spare = 0;
cd->h0.arfcn_high = arfcn >> 8;
cd->h0.arfcn_low = arfcn & 0xff;
} else {
- cd->h1.tsc = gsm_ts_tsc(lchan->ts);
+ cd->h1.tsc = tsc;
cd->h1.h = 1;
cd->h1.maio_high = lchan->ts->hopping.maio >> 2;
cd->h1.maio_low = lchan->ts->hopping.maio & 0x03;
@@ -614,21 +668,30 @@ static void _chan_desc_fill_tail(struct gsm48_chan_desc *cd, const struct gsm_lc
}
}
-void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
- const struct gsm_lchan *lchan)
+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)
{
- cd->chan_nr = gsm_lchan2chan_nr(lchan);
- _chan_desc_fill_tail(cd, lchan);
+ int chan_nr = gsm_lchan_and_pchan2chan_nr(lchan, pchan, allow_osmo_cbits);
+ if (chan_nr < 0) {
+ /* Log an error so that we don't need to add logging to each caller of this function */
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "Error encoding Channel Number: pchan %s ts %u ss %u%s (rc = %d)\n",
+ gsm_pchan_name(pchan), lchan->ts->nr, lchan->nr,
+ lchan->vamos.is_secondary ? " (VAMOS shadow)" : "", chan_nr);
+ return chan_nr;
+ }
+ cd->chan_nr = chan_nr;
+ _chan_desc_fill_tail(cd, lchan, tsc);
+ return 0;
}
-/* like gsm48_lchan2chan_desc() above, but use ts->pchan_from_config to
- * return a channel description based on what is configured, rather than
- * what the current state of the pchan type is */
-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)
{
- cd->chan_nr = gsm_pchan2chan_nr(lchan->ts->pchan_from_config, lchan->ts->nr, lchan->nr);
- _chan_desc_fill_tail(cd, lchan);
+ return gsm48_lchan_and_pchan2chan_desc(cd, lchan, lchan->ts->pchan_is, tsc, allow_osmo_cbits);
}
uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts)
@@ -642,7 +705,7 @@ uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts)
bool nm_is_running(const struct gsm_nm_state *s) {
if (s->operational != NM_OPSTATE_ENABLED)
return false;
- if ((s->availability != NM_AVSTATE_OK) && (s->availability != 0xff))
+ if (s->availability != NM_AVSTATE_OK)
return false;
if (s->administrative != NM_STATE_UNLOCKED)
return false;
@@ -674,6 +737,8 @@ enum gsm_phys_chan_config gsm_pchan_by_lchan_type(enum gsm_chan_t type)
return GSM_PCHAN_TCH_F;
case GSM_LCHAN_TCH_H:
return GSM_PCHAN_TCH_H;
+ case GSM_LCHAN_SDCCH:
+ return GSM_PCHAN_SDCCH8_SACCH8C;
case GSM_LCHAN_NONE:
case GSM_LCHAN_PDTCH:
/* TODO: so far lchan->type is NONE in PDCH mode. PDTCH is only
@@ -685,6 +750,22 @@ enum gsm_phys_chan_config gsm_pchan_by_lchan_type(enum gsm_chan_t type)
}
}
+enum channel_rate chan_t_to_chan_rate(enum gsm_chan_t chan_t)
+{
+ switch (chan_t) {
+ case GSM_LCHAN_SDCCH:
+ return CH_RATE_SDCCH;
+ case GSM_LCHAN_TCH_F:
+ return CH_RATE_FULL;
+ case GSM_LCHAN_TCH_H:
+ return CH_RATE_HALF;
+ default:
+ /* For other channel types, the channel_rate value is never used. It is fine to return an invalid value,
+ * and callers don't actually need to check for this. */
+ return -1;
+ }
+}
+
/* Can the timeslot in principle be used as this PCHAN kind? */
bool ts_is_capable_of_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan)
{
@@ -698,11 +779,12 @@ bool ts_is_capable_of_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config
return false;
}
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
switch (pchan) {
case GSM_PCHAN_TCH_F:
case GSM_PCHAN_TCH_H:
case GSM_PCHAN_PDCH:
+ case GSM_PCHAN_SDCCH8_SACCH8C:
return true;
default:
return false;
@@ -770,11 +852,12 @@ bool ts_is_capable_of_lchant(struct gsm_bts_trx_ts *ts, enum gsm_chan_t type)
return false;
}
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
switch (type) {
case GSM_LCHAN_TCH_F:
case GSM_LCHAN_TCH_H:
case GSM_LCHAN_PDTCH:
+ case GSM_LCHAN_SDCCH:
return true;
default:
return false;
@@ -816,10 +899,8 @@ bool ts_is_capable_of_lchant(struct gsm_bts_trx_ts *ts, enum gsm_chan_t type)
bool ts_is_usable(const struct gsm_bts_trx_ts *ts)
{
- if (!trx_is_usable(ts->trx)) {
- LOGP(DRLL, LOGL_DEBUG, "%s not usable\n", gsm_trx_name(ts->trx));
+ if (!trx_is_usable(ts->trx))
return false;
- }
if (!ts->fi)
return false;
@@ -838,6 +919,11 @@ bool ts_is_usable(const struct gsm_bts_trx_ts *ts)
void conn_update_ms_power_class(struct gsm_subscriber_connection *conn, uint8_t power_class)
{
struct gsm_bts *bts = conn_get_bts(conn);
+
+ /* MS Power class remains the same => do nothing */
+ if (power_class == conn->ms_power_class)
+ return;
+
LOGP(DRLL, LOGL_DEBUG, "MS Power class update: %" PRIu8 " -> %" PRIu8 "\n",
conn->ms_power_class, power_class);
@@ -849,65 +935,27 @@ void conn_update_ms_power_class(struct gsm_subscriber_connection *conn, uint8_t
lchan_update_ms_power_ctrl_level(conn->lchan, bts->ms_max_power);
}
-void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm)
-{
- struct gsm_bts *bts = lchan->ts->trx->bts;
- struct gsm_subscriber_connection *conn = lchan->conn;
- int max_pwr_dbm_pwclass, new_pwr;
- bool send_pwr_ctrl_msg = false;
-
- LOG_LCHAN(lchan, LOGL_DEBUG,
- "MS Power level update requested: %d dBm\n", ms_power_dbm);
-
- if (!conn)
- goto ms_power_default;
-
- if (conn->ms_power_class == 0)
- goto ms_power_default;
-
- if ((max_pwr_dbm_pwclass = (int)ms_class_gmsk_dbm(bts->band, conn->ms_power_class)) < 0) {
- LOG_LCHAN(lchan, LOGL_INFO,
- "Failed getting max ms power for power class %" PRIu8
- " on band %s, providing default max ms power\n",
- conn->ms_power_class, gsm_band_name(bts->band));
- goto ms_power_default;
- }
-
- /* Current configured max pwr is above maximum one allowed on
- current band + ms power class, so use that one. */
- if (ms_power_dbm > max_pwr_dbm_pwclass)
- ms_power_dbm = max_pwr_dbm_pwclass;
-
-ms_power_default:
- if ((new_pwr = ms_pwr_ctl_lvl(bts->band, ms_power_dbm)) < 0) {
- LOG_LCHAN(lchan, LOGL_INFO,
- "Failed getting max ms power level %d on band %s,"
- " providing default max ms power\n",
- ms_power_dbm, gsm_band_name(bts->band));
- return;
- }
-
- LOG_LCHAN(lchan, LOGL_DEBUG,
- "MS Power level update (power class %" PRIu8 "): %" PRIu8 " -> %d\n",
- conn ? conn->ms_power_class : 0, lchan->ms_power, new_pwr);
-
- /* If chan was already activated and max ms_power changes (due to power
- classmark received), send an MS Power Control message */
- if (lchan->activate.activ_ack && new_pwr != lchan->ms_power)
- send_pwr_ctrl_msg = true;
-
- lchan->ms_power = new_pwr;
+const struct value_string lchan_activate_mode_names[] = {
+ OSMO_VALUE_STRING(ACTIVATE_FOR_NONE),
+ OSMO_VALUE_STRING(ACTIVATE_FOR_MS_CHANNEL_REQUEST),
+ OSMO_VALUE_STRING(ACTIVATE_FOR_ASSIGNMENT),
+ OSMO_VALUE_STRING(ACTIVATE_FOR_HANDOVER),
+ OSMO_VALUE_STRING(ACTIVATE_FOR_VTY),
+ {}
+};
- if (send_pwr_ctrl_msg)
- rsl_chan_ms_power_ctrl(lchan);
-}
+const struct value_string lchan_modify_for_names[] = {
+ OSMO_VALUE_STRING(MODIFY_FOR_NONE),
+ OSMO_VALUE_STRING(MODIFY_FOR_ASSIGNMENT),
+ OSMO_VALUE_STRING(MODIFY_FOR_VTY),
+ {}
+};
-const struct value_string lchan_activate_mode_names[] = {
- OSMO_VALUE_STRING(FOR_NONE),
- OSMO_VALUE_STRING(FOR_MS_CHANNEL_REQUEST),
- OSMO_VALUE_STRING(FOR_ASSIGNMENT),
- OSMO_VALUE_STRING(FOR_HANDOVER),
- OSMO_VALUE_STRING(FOR_VTY),
+const struct value_string assign_for_names[] = {
+ OSMO_VALUE_STRING(ASSIGN_FOR_NONE),
+ OSMO_VALUE_STRING(ASSIGN_FOR_BSSMAP_REQ),
+ OSMO_VALUE_STRING(ASSIGN_FOR_CONGESTION_RESOLUTION),
+ OSMO_VALUE_STRING(ASSIGN_FOR_VTY),
{}
};
@@ -963,3 +1011,50 @@ enum gsm48_rr_cause bsc_gsm48_rr_cause_from_rsl_cause(uint8_t c)
return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
}
}
+
+/* Default Interference Measurement Parameters */
+const struct gsm_interf_meas_params interf_meas_params_def = {
+ .avg_period = 6, /* 6 SACCH periods */
+ .bounds_dbm = {
+ 115, /* 0: -115 dBm */
+ 109, /* X1: -109 dBm */
+ 103, /* X2: -103 dBm */
+ 97, /* X3: -97 dBm */
+ 91, /* X4: -91 dBm */
+ 85, /* X5: -85 dBm */
+ },
+};
+
+enum rsl_cmod_spd chan_mode_to_rsl_cmod_spd(enum gsm48_chan_mode chan_mode)
+{
+ switch (gsm48_chan_mode_to_non_vamos(chan_mode)) {
+ case GSM48_CMODE_SIGN:
+ return RSL_CMOD_SPD_SIGN;
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_SPEECH_AMR:
+ return RSL_CMOD_SPD_SPEECH;
+ case GSM48_CMODE_DATA_14k5:
+ case GSM48_CMODE_DATA_12k0:
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ return RSL_CMOD_SPD_DATA;
+ default:
+ return -EINVAL;
+ }
+}
+
+int gsm_audio_support_cmp(const struct gsm_audio_support *a, const struct gsm_audio_support *b)
+{
+ int rc;
+ if (a == b)
+ return 0;
+ if (!a)
+ return -1;
+ if (!b)
+ return 1;
+ rc = OSMO_CMP(a->hr, b->hr);
+ if (rc)
+ return rc;
+ return OSMO_CMP(a->ver, b->ver);
+}
diff --git a/src/osmo-bsc/handover_ctrl.c b/src/osmo-bsc/handover_ctrl.c
new file mode 100644
index 000000000..139d0382b
--- /dev/null
+++ b/src/osmo-bsc/handover_ctrl.c
@@ -0,0 +1,216 @@
+/* OsmoBSC handover control interface implementation */
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier <pmaier@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdbool.h>
+#include <talloc.h>
+#include <osmocom/bsc/vty.h>
+#include <osmocom/bsc/handover_cfg.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/handover_decision_2.h>
+#include <osmocom/ctrl/control_cmd.h>
+
+/* In handover_cfg.h the config items are described in VTY syntax. To be able to
+ * use those here in the CTRL interface, we parse the config arguments like the
+ * VTY would. (the value specification may be in the form of "<from-to>" or
+ * "A|B|C|..." */
+static bool verify_vty_cmd_arg(void *ctx, const char *range, const char *value)
+{
+ bool success;
+ char *range_tok;
+ char *valid_val;
+
+ /* "default" value is always a valid value */
+ if (strcmp(value, "default") == 0)
+ return true;
+
+ /* Try to check for a range first since it is the most common case */
+ if (range[0] == '<') {
+ if (vty_cmd_range_match(range, value))
+ return true;
+ else
+ return false;
+ }
+
+ /* Try to tokenize the string to check for distintinct values */
+ success = false;
+ range_tok = talloc_zero_size(ctx, strlen(range) + 1);
+ memcpy(range_tok, range, strlen(range));
+ valid_val = strtok(range_tok, "|");
+ while (valid_val != NULL) {
+ if (strcmp(valid_val, value) == 0) {
+ success = true;
+ break;
+ }
+ valid_val = strtok(NULL, "|");
+ }
+
+ talloc_free(range_tok);
+ return success;
+}
+
+/* NOTE: The following macro scheme has been designed for using it in the VTY
+ * code. However, for the most part it also works for CTRL interface code as
+ * well. */
+#define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, VTY_CMD_PREFIX, VTY_CMD, VTY_CMD_ARG, VTY_ARG_EVAL, VTY_WRITE_FMT, VTY_WRITE_CONV, VTY6) \
+CTRL_CMD_DEFINE(NAME, VTY_CMD_PREFIX VTY_CMD); \
+static int get_##NAME(struct ctrl_cmd *cmd, void *_data) \
+{ \
+ struct gsm_network *net = cmd->node; \
+ struct handover_cfg *ho = net->ho; \
+ TYPE val; \
+ if (ho_isset_##NAME(ho)) { \
+ val = ho_get_##NAME(ho); \
+ cmd->reply = talloc_asprintf(cmd, VTY_WRITE_FMT, VTY_WRITE_CONV(val)); \
+ } else \
+ cmd->reply = talloc_asprintf(cmd, "%s", #DEFAULT_VAL); \
+ return CTRL_CMD_REPLY; \
+} \
+static int set_##NAME(struct ctrl_cmd *cmd, void *_data) \
+{ \
+ struct gsm_network *net = cmd->node; \
+ struct handover_cfg *ho = net->ho; \
+ TYPE value; \
+ if (strcmp(cmd->value, "default") == 0) \
+ value = VTY_ARG_EVAL(#DEFAULT_VAL); \
+ else \
+ value = VTY_ARG_EVAL(cmd->value); \
+ ho_set_##NAME(ho, value); \
+ return get_##NAME(cmd, _data); \
+} \
+static int verify_##NAME(struct ctrl_cmd *cmd, const char *value, void *_data) \
+{ \
+ if (verify_vty_cmd_arg(cmd, VTY_CMD_ARG, value) != true) \
+ return -1; \
+ return 0; \
+} \
+CTRL_CMD_DEFINE(bts_##NAME, VTY_CMD_PREFIX VTY_CMD); \
+static int get_bts_##NAME(struct ctrl_cmd *cmd, void *_data) \
+{ \
+ struct gsm_bts *bts = cmd->node; \
+ struct handover_cfg *ho = bts->ho; \
+ TYPE val; \
+ if (ho_isset_##NAME(ho)) { \
+ val = ho_get_##NAME(ho); \
+ cmd->reply = talloc_asprintf(cmd, VTY_WRITE_FMT, VTY_WRITE_CONV(val)); \
+ } else { \
+ cmd->reply = talloc_asprintf(cmd, "%s", #DEFAULT_VAL); \
+ } \
+ return CTRL_CMD_REPLY; \
+} \
+static int set_bts_##NAME(struct ctrl_cmd *cmd, void *_data) \
+{ \
+ struct gsm_bts *bts = cmd->node; \
+ struct handover_cfg *ho = bts->ho; \
+ TYPE value; \
+ if (strcmp(cmd->value, "default") == 0) \
+ value = VTY_ARG_EVAL(#DEFAULT_VAL); \
+ else \
+ value = VTY_ARG_EVAL(cmd->value); \
+ ho_set_##NAME(ho, value); \
+ return get_bts_##NAME(cmd, _data); \
+} \
+static int verify_bts_##NAME(struct ctrl_cmd *cmd, const char *value, void *_data) \
+{ \
+ return verify_##NAME(cmd, value, _data); \
+} \
+
+/* Expand the above macro using the definitions from handover_cfg.h */
+HO_CFG_ALL_MEMBERS
+#undef HO_CFG_ONE_MEMBER
+
+CTRL_CMD_DEFINE(congestion_check_interval, "handover2 congestion-check");
+static int get_congestion_check_interval(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_network *net = cmd->node;
+ if (net->hodec2.congestion_check_interval_s > 0)
+ cmd->reply = talloc_asprintf(cmd, "%u", net->hodec2.congestion_check_interval_s);
+ else
+ cmd->reply = "disabled";
+ return CTRL_CMD_REPLY;
+}
+
+static int set_congestion_check_interval(struct ctrl_cmd *cmd, void *_data)
+{
+ struct gsm_network *net = cmd->node;
+ int value;
+
+ /* Trigger congestion check and leave without changing anything */
+ if (strcmp(cmd->value, "now") == 0) {
+ hodec2_congestion_check(net);
+ return get_congestion_check_interval(cmd, _data);
+ }
+
+ if (strcmp(cmd->value, "disabled") == 0)
+ value = 0;
+ else
+ value = atoi(cmd->value);
+ hodec2_on_change_congestion_check_interval(net, value);
+ return get_congestion_check_interval(cmd, _data);
+}
+
+static int verify_congestion_check_interval(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (strcmp(value, "disabled") == 0)
+ return 0;
+ if (strcmp(value, "now") == 0)
+ return 0;
+ if (verify_vty_cmd_arg(cmd, "<1-999>", value))
+ return 0;
+ return -1;
+}
+
+/* Filter name member in cmd for illegal '/' characters */
+static struct ctrl_cmd_element *filter_name(void *ctx,
+ struct ctrl_cmd_element *cmd)
+{
+ unsigned int i;
+ char *name;
+
+ if (osmo_separated_identifiers_valid(cmd->name, " -"))
+ return cmd;
+
+ name = talloc_strdup(ctx, cmd->name);
+ for (i = 0; i < strlen(name); i++) {
+ if (name[i] == '/')
+ name[i] = '-';
+ }
+
+ cmd->name = name;
+ return cmd;
+}
+
+int bsc_ho_ctrl_cmds_install(void *ctx)
+{
+ int rc = 0;
+
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_congestion_check_interval);
+
+#define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, VTY0, VTY1, VTY2, VTY_ARG_EVAL, VTY4, VTY5, VTY6) \
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, filter_name(ctx, &cmd_##NAME)); \
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, filter_name(ctx, &cmd_bts_##NAME)); \
+
+HO_CFG_ALL_MEMBERS
+#undef HO_CFG_ONE_MEMBER
+
+ return rc;
+}
diff --git a/src/osmo-bsc/handover_decision.c b/src/osmo-bsc/handover_decision.c
index 7eb8f31e0..2fb466cbe 100644
--- a/src/osmo-bsc/handover_decision.c
+++ b/src/osmo-bsc/handover_decision.c
@@ -201,8 +201,7 @@ static void attempt_handover(struct gsm_meas_rep *mr)
req = (struct handover_out_req){
.from_hodec_id = HODEC1,
.old_lchan = mr->lchan,
- .target_nik = {
- .from_bts = bts->nr,
+ .target_cell_ab = {
.arfcn = best_cell->arfcn,
.bsic = best_cell->bsic,
},
@@ -215,7 +214,6 @@ static void attempt_handover(struct gsm_meas_rep *mr)
static void on_measurement_report(struct gsm_meas_rep *mr)
{
struct gsm_bts *bts = mr->lchan->ts->trx->bts;
- enum meas_rep_field dlev, dqual;
int av_rxlev;
unsigned int pwr_interval;
@@ -232,24 +230,16 @@ static void on_measurement_report(struct gsm_meas_rep *mr)
return;
}
- if (mr->flags & MEAS_REP_F_DL_DTX) {
- dlev = MEAS_REP_DL_RXLEV_SUB;
- dqual = MEAS_REP_DL_RXQUAL_SUB;
- } else {
- dlev = MEAS_REP_DL_RXLEV_FULL;
- dqual = MEAS_REP_DL_RXQUAL_FULL;
- }
-
/* parse actual neighbor cell info */
if (mr->num_cell > 0 && mr->num_cell < 7)
process_meas_neigh(mr);
- av_rxlev = get_meas_rep_avg(mr->lchan, dlev,
+ av_rxlev = get_meas_rep_avg(mr->lchan, TDMA_MEAS_FIELD_RXLEV, TDMA_MEAS_DIR_DL, TDMA_MEAS_SET_AUTO,
ho_get_hodec1_rxlev_avg_win(bts->ho));
/* Interference HO */
if (rxlev2dbm(av_rxlev) > -85 &&
- meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5)) {
+ meas_rep_n_out_of_m_be(mr->lchan, TDMA_MEAS_FIELD_RXQUAL, TDMA_MEAS_DIR_DL, TDMA_MEAS_SET_AUTO, 3, 4, 5)) {
LOGPC(DHO, LOGL_INFO, "HO cause: Interference HO av_rxlev=%d dBm\n",
rxlev2dbm(av_rxlev));
attempt_handover(mr);
@@ -257,7 +247,7 @@ static void on_measurement_report(struct gsm_meas_rep *mr)
}
/* Bad Quality */
- if (meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5)) {
+ if (meas_rep_n_out_of_m_be(mr->lchan, TDMA_MEAS_FIELD_RXQUAL, TDMA_MEAS_DIR_DL, TDMA_MEAS_SET_AUTO, 3, 4, 5)) {
LOGPC(DHO, LOGL_INFO, "HO cause: Bad Quality av_rxlev=%d dBm\n", rxlev2dbm(av_rxlev));
attempt_handover(mr);
return;
diff --git a/src/osmo-bsc/handover_decision_2.c b/src/osmo-bsc/handover_decision_2.c
index c818dbbde..073e013d9 100644
--- a/src/osmo-bsc/handover_decision_2.c
+++ b/src/osmo-bsc/handover_decision_2.c
@@ -24,9 +24,12 @@
#include <stdbool.h>
#include <errno.h>
+#include <limits.h>
+#include <math.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/assignment_fsm.h>
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/handover_decision.h>
#include <osmocom/bsc/handover_decision_2.h>
@@ -38,6 +41,8 @@
#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/timeslot_fsm.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/lchan_select.h>
+#include <osmocom/bsc/chan_counts.h>
#define LOGPHOBTS(bts, level, fmt, args...) \
LOGP(DHODEC, level, "(BTS %u) " fmt, bts->nr, ## args)
@@ -48,8 +53,8 @@
lchan->ts->trx->nr, \
lchan->ts->nr, \
lchan->nr, \
- gsm_lchant_name(lchan->type), \
- gsm48_chan_mode_name(lchan->tch_mode), \
+ gsm_chan_t_name(lchan->type), \
+ gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), \
bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
## args)
@@ -59,8 +64,8 @@
lchan->ts->trx->nr, \
lchan->ts->nr, \
lchan->nr, \
- gsm_lchant_name(lchan->type), \
- gsm48_chan_mode_name(lchan->tch_mode), \
+ gsm_chan_t_name(lchan->type), \
+ gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), \
new_bts->nr, \
bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
## args)
@@ -71,18 +76,18 @@
lchan->ts->trx->nr, \
lchan->ts->nr, \
lchan->nr, \
- gsm_lchant_name(lchan->type), \
- gsm48_chan_mode_name(lchan->tch_mode), \
+ gsm_chan_t_name(lchan->type), \
+ gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), \
gsm0808_cell_id_list_name(remote_cil), \
bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
## args)
#define LOGPHOCAND(candidate, level, fmt, args...) do {\
- if ((candidate)->bts) \
- LOGPHOLCHANTOBTS((candidate)->lchan, (candidate)->bts, level, fmt, ## args); \
- else if ((candidate)->cil) \
- LOGPHOLCHANTOREMOTE((candidate)->lchan, (candidate)->cil, level, fmt, ## args); \
- } while(0)
+ if ((candidate)->target.bts) \
+ LOGPHOLCHANTOBTS((candidate)->current.lchan, (candidate)->target.bts, level, fmt, ## args); \
+ else if ((candidate)->target.cell_ids.id_list_len) \
+ LOGPHOLCHANTOREMOTE((candidate)->current.lchan, &(candidate)->target.cell_ids, level, fmt, ## args); \
+ } while (0)
#define REQUIREMENT_A_TCHF 0x01
@@ -98,12 +103,39 @@
#define REQUIREMENT_C_MASK (REQUIREMENT_C_TCHF | REQUIREMENT_C_TCHH)
struct ho_candidate {
- struct gsm_lchan *lchan; /* candidate for whom */
- struct neighbor_ident_key nik; /* neighbor ARFCN+BSIC */
- struct gsm_bts *bts; /* target BTS in local BSS */
- const struct gsm0808_cell_id_list2 *cil; /* target cells in remote BSS */
uint8_t requirements; /* what is fulfilled */
- int avg; /* average RX level */
+ struct {
+ struct gsm_lchan *lchan;
+ struct gsm_bts *bts;
+ int rxlev;
+ /* free/min-free for the current TCH kind, same as either free_tch_f or free_tch_h below */
+ int free_tch;
+ int min_free_tch;
+ /* free/min-free for the two TCH kinds, to calculate F<->H cross effects for dynamic timeslots */
+ int free_tchf;
+ int min_free_tchf;
+ int free_tchh;
+ int min_free_tchh;
+ /* Effects of freeing a dynamic timeslot, i.e. turning it into PDCH mode and making available more free
+ * TCH: */
+ int lchan_frees_tchf;
+ int lchan_frees_tchh;
+ } current;
+ struct {
+ struct cell_ab ab; /* neighbor ARFCN+BSIC */
+ struct gsm0808_cell_id_list2 cell_ids; /* target cells in remote BSS */
+ struct gsm_bts *bts;
+ int rxlev;
+ int rxlev_afs_bias;
+ int free_tchf;
+ int min_free_tchf;
+ int free_tchh;
+ int min_free_tchh;
+ /* Effects of occupying a dynamic timeslot, i.e. turning from PDCH into a specific TCH kind, and
+ * reducing the number of free TCH for both TCH/F and TCH/H: */
+ int next_tchf_reduces_tchh;
+ int next_tchh_reduces_tchf;
+ } target;
};
enum ho_reason {
@@ -136,6 +168,24 @@ static enum ho_reason global_ho_reason;
static void congestion_check_cb(void *arg);
+static bool is_upgrade_to_tchf(const struct ho_candidate *c, uint8_t for_requirement)
+{
+ return c->current.lchan
+ && (c->current.lchan->type == GSM_LCHAN_TCH_H)
+ && ((c->requirements & for_requirement) & REQUIREMENT_TCHF_MASK);
+}
+
+static unsigned int ts_usage_count(struct gsm_bts_trx_ts *ts)
+{
+ struct gsm_lchan *lchan;
+ unsigned int count = 0;
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
+ if (lchan_state_is(lchan, LCHAN_ST_ESTABLISHED))
+ count++;
+ }
+ return count;
+}
+
/* This function gets called on ho2 init, whenever the congestion check interval is changed, and also
* when the timer has fired to trigger again after the next congestion check timeout. */
static void reinit_congestion_timer(struct gsm_network *net)
@@ -174,49 +224,6 @@ void hodec2_on_change_congestion_check_interval(struct gsm_network *net, unsigne
reinit_congestion_timer(net);
}
-static void _conn_penalty_time_add(struct gsm_subscriber_connection *conn,
- const void *for_object,
- int penalty_time)
-{
- if (!for_object) {
- LOGP(DHODEC, LOGL_ERROR, "%s Unable to set Handover-2 penalty timer:"
- " no target cell pointer\n",
- bsc_subscr_name(conn->bsub));
- return;
- }
-
- if (!conn->hodec2.penalty_timers) {
- conn->hodec2.penalty_timers = penalty_timers_init(conn);
- OSMO_ASSERT(conn->hodec2.penalty_timers);
- }
-
- penalty_timers_add(conn->hodec2.penalty_timers, for_object, penalty_time);
-}
-
-static void nik_penalty_time_add(struct gsm_subscriber_connection *conn,
- struct neighbor_ident_key *nik,
- int penalty_time)
-{
- _conn_penalty_time_add(conn,
- neighbor_ident_get(conn->network->neighbor_bss_cells, nik),
- penalty_time);
-}
-
-static void bts_penalty_time_add(struct gsm_subscriber_connection *conn,
- struct gsm_bts *bts,
- int penalty_time)
-{
- _conn_penalty_time_add(conn, bts, penalty_time);
-}
-
-static unsigned int conn_penalty_time_remaining(struct gsm_subscriber_connection *conn,
- const void *for_object)
-{
- if (!conn->hodec2.penalty_timers)
- return 0;
- return penalty_timers_remaining(conn->hodec2.penalty_timers, for_object);
-}
-
/* did we get a RXLEV for a given cell in the given report? Mark matches as MRC_F_PROCESSED. */
static struct gsm_meas_rep_cell *cell_in_rep(struct gsm_meas_rep *mr, uint16_t arfcn, uint8_t bsic)
{
@@ -235,6 +242,35 @@ static struct gsm_meas_rep_cell *cell_in_rep(struct gsm_meas_rep *mr, uint16_t a
return NULL;
}
+static int current_rxlev(struct gsm_lchan *lchan)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ return get_meas_rep_avg(lchan, TDMA_MEAS_FIELD_RXLEV, TDMA_MEAS_DIR_DL, ho_get_hodec2_tdma_meas_set(bts->ho),
+ ho_get_hodec2_rxlev_avg_win(bts->ho));
+}
+
+static int current_rxqual(struct gsm_lchan *lchan)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ return get_meas_rep_avg(lchan, TDMA_MEAS_FIELD_RXQUAL, TDMA_MEAS_DIR_DL, ho_get_hodec2_tdma_meas_set(bts->ho),
+ ho_get_hodec2_rxqual_avg_win(bts->ho));
+}
+
+static bool is_low_rxlev(int rxlev_current, struct handover_cfg *neigh_cfg)
+{
+ return rxlev_current >= 0
+ && rxlev2dbm(rxlev_current) < ho_get_hodec2_min_rxlev(neigh_cfg);
+}
+
+static bool is_low_rxqual(int rxqual_current, struct handover_cfg *neigh_cfg)
+{
+ /* min_rxqual is actually a bit of a misnomer, low quality is a high number. So the "min" refers to the minimum
+ * acceptable level of quality, and "min or better" here means "rxqual number must be SMALLER-or-equal than the
+ * min-rxqual setting". */
+ return rxqual_current >= 0
+ && rxqual_current > ho_get_hodec2_min_rxqual(neigh_cfg);
+}
+
/* obtain averaged rxlev for given neighbor */
static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window)
{
@@ -364,6 +400,26 @@ static bool codec_type_is_supported(struct gsm_subscriber_connection *conn,
return false;
}
+#define LOAD_PRECISION 6
+
+/* Return a number representing overload, i.e. the fraction of lchans used above the congestion threshold.
+ * Think of it as a percentage of used lchans above congestion, just represented in a fixed-point fraction with N
+ * decimal digits of fractional part. If there is no congestion (free_tch >= min_free_tch), return 0.
+ */
+static int32_t load_above_congestion(int free_tch, int min_free_tch)
+{
+ int32_t v;
+ OSMO_ASSERT(free_tch >= 0);
+ /* Avoid division by zero when no congestion threshold is set, and return zero overload when there is no
+ * congestion. */
+ if (free_tch >= min_free_tch)
+ return 0;
+ v = min_free_tch - free_tch;
+ v *= pow(10, LOAD_PRECISION);
+ v /= min_free_tch;
+ return v;
+}
+
/*
* Check what requirements the given cell fulfills.
* A bit mask of fulfilled requirements is returned.
@@ -416,113 +472,116 @@ static bool codec_type_is_supported(struct gsm_subscriber_connection *conn,
* * The number of free slots are checked for TCH/F and TCH/H slot types
* individually.
*/
-static uint8_t check_requirements(struct gsm_lchan *lchan, struct gsm_bts *bts, int tchf_count, int tchh_count)
+static void check_requirements(struct ho_candidate *c)
{
- int count;
uint8_t requirement = 0;
unsigned int penalty_time;
- struct gsm_bts *current_bts = lchan->ts->trx->bts;
+ int32_t current_overbooked;
+ struct gsm0808_cell_id target_cell_id;
+ c->requirements = 0;
/* Requirement A */
/* the handover/assignment must not be disabled */
- if (current_bts == bts) {
- if (!ho_get_hodec2_as_active(bts->ho)) {
- LOGPHOLCHAN(lchan, LOGL_DEBUG, "Assignment disabled\n");
- return 0;
+ if (c->current.bts == c->target.bts) {
+ if (!ho_get_hodec2_as_active(c->target.bts->ho)) {
+ LOGPHOLCHAN(c->current.lchan, LOGL_DEBUG, "Assignment disabled\n");
+ return;
}
} else {
- if (!ho_get_ho_active(bts->ho)) {
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
+ if (!ho_get_ho_active(c->target.bts->ho)) {
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
"not a candidate, handover is disabled in target BTS\n");
- return 0;
+ return;
}
}
/* the handover penalty timer must not run for this bts */
- penalty_time = conn_penalty_time_remaining(lchan->conn, bts);
+ gsm_bts_cell_id(&target_cell_id, c->target.bts);
+ penalty_time = penalty_timers_remaining(&c->current.lchan->conn->hodec2.penalty_timers, &target_cell_id);
if (penalty_time) {
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, target BTS still in penalty time"
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG, "not a candidate, target BTS still in penalty time"
" (%u seconds left)\n", penalty_time);
- return 0;
+ return;
}
/* compatibility check for codecs.
* if so, the candidates for full rate and half rate are selected */
- switch (lchan->tch_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(c->current.lchan->current_ch_mode_rate.chan_mode)) {
case GSM48_CMODE_SPEECH_V1:
- switch (lchan->type) {
+ switch (c->current.lchan->type) {
case GSM_LCHAN_TCH_F: /* mandatory */
requirement |= REQUIREMENT_A_TCHF;
break;
case GSM_LCHAN_TCH_H:
- if (!bts->codec.hr) {
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
+ if (!c->target.bts->codec.hr) {
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
"tch_mode='%s' type='%s' not supported\n",
get_value_string(gsm48_chan_mode_names,
- lchan->tch_mode),
- gsm_lchant_name(lchan->type));
+ c->current.lchan->current_ch_mode_rate.chan_mode),
+ gsm_chan_t_name(c->current.lchan->type));
break;
}
- if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR1))
+ if (codec_type_is_supported(c->current.lchan->conn, GSM0808_SCT_HR1))
requirement |= REQUIREMENT_A_TCHH;
break;
default:
- LOGPHOLCHAN(lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
- get_value_string(gsm48_chan_mode_names, lchan->tch_mode));
- return 0;
+ LOGPHOLCHAN(c->current.lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
+ get_value_string(gsm48_chan_mode_names,
+ c->current.lchan->current_ch_mode_rate.chan_mode));
+ return;
}
break;
case GSM48_CMODE_SPEECH_EFR:
- if (!bts->codec.efr) {
- LOGPHOBTS(bts, LOGL_DEBUG, "EFR not supported\n");
+ if (!c->target.bts->codec.efr) {
+ LOGPHOBTS(c->target.bts, LOGL_DEBUG, "EFR not supported\n");
break;
}
- if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR2))
+ if (codec_type_is_supported(c->current.lchan->conn, GSM0808_SCT_FR2))
requirement |= REQUIREMENT_A_TCHF;
break;
case GSM48_CMODE_SPEECH_AMR:
- if (!bts->codec.amr) {
- LOGPHOBTS(bts, LOGL_DEBUG, "AMR not supported\n");
+ if (!c->target.bts->codec.amr) {
+ LOGPHOBTS(c->target.bts, LOGL_DEBUG, "AMR not supported\n");
break;
}
- if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR3))
+ if (codec_type_is_supported(c->current.lchan->conn, GSM0808_SCT_FR3))
requirement |= REQUIREMENT_A_TCHF;
- if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR3))
+ if (codec_type_is_supported(c->current.lchan->conn, GSM0808_SCT_HR3))
requirement |= REQUIREMENT_A_TCHH;
break;
default:
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "Not even considering: src is not a SPEECH mode lchan\n");
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG, "Not even considering: src is not a SPEECH mode lchan\n");
/* FIXME: should allow handover of non-speech lchans */
- return 0;
+ return;
}
/* no candidate, because new cell is incompatible */
if (!requirement) {
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, because codec of MS and BTS are incompatible\n");
- return 0;
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG, "not a candidate, because codec of MS and BTS are incompatible\n");
+ return;
}
/* remove slot types that are not available */
- if (!tchf_count && requirement & REQUIREMENT_A_TCHF) {
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
+ if (!c->target.free_tchf && (requirement & REQUIREMENT_A_TCHF)) {
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
"removing TCH/F, since all TCH/F lchans are in use\n");
requirement &= ~(REQUIREMENT_A_TCHF);
}
- if (!tchh_count && requirement & REQUIREMENT_A_TCHH) {
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
+ if (!c->target.free_tchh && (requirement & REQUIREMENT_A_TCHH)) {
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
"removing TCH/H, since all TCH/H lchans are in use\n");
requirement &= ~(REQUIREMENT_A_TCHH);
}
if (!requirement) {
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, because no suitable slots available\n");
- return 0;
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG, "not a candidate, because no suitable slots available\n");
+ return;
}
/* omit same channel type on same BTS (will not change anything) */
- if (bts == current_bts) {
- switch (lchan->type) {
+ if (c->target.bts == c->current.bts) {
+ switch (c->current.lchan->type) {
case GSM_LCHAN_TCH_F:
requirement &= ~(REQUIREMENT_A_TCHF);
break;
@@ -534,9 +593,9 @@ static uint8_t check_requirements(struct gsm_lchan *lchan, struct gsm_bts *bts,
}
if (!requirement) {
- LOGPHOLCHAN(lchan, LOGL_DEBUG,
+ LOGPHOLCHAN(c->current.lchan, LOGL_DEBUG,
"Reassignment within cell not an option, no differing channel types available\n");
- return 0;
+ return;
}
}
@@ -544,26 +603,26 @@ static uint8_t check_requirements(struct gsm_lchan *lchan, struct gsm_bts *bts,
// This was useful in osmo-nitb. We're in osmo-bsc now and have no idea whether the osmo-msc does
// internal or external call control. Maybe a future config switch wants to add this behavior?
/* Built-in call control requires equal codec rates. Remove rates that are not equal. */
- if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
- && current_bts->network->mncc_recv != mncc_sock_from_cc) {
- switch (lchan->type) {
+ if (gsm48_chan_mode_to_non_vamos(c->current.lchan->tch_mode) == GSM48_CMODE_SPEECH_AMR
+ && c->current.bts->network->mncc_recv != mncc_sock_from_cc) {
+ switch (c->current.lchan->type) {
case GSM_LCHAN_TCH_F:
if ((requirement & REQUIREMENT_A_TCHF)
- && !!memcmp(&current_bts->mr_full, &bts->mr_full,
+ && !!memcmp(&c->current.bts->mr_full, &c->target.bts->mr_full,
sizeof(struct amr_multirate_conf)))
requirement &= ~(REQUIREMENT_A_TCHF);
if ((requirement & REQUIREMENT_A_TCHH)
- && !!memcmp(&current_bts->mr_full, &bts->mr_half,
+ && !!memcmp(&c->current.bts->mr_full, &c->target.bts->mr_half,
sizeof(struct amr_multirate_conf)))
requirement &= ~(REQUIREMENT_A_TCHH);
break;
case GSM_LCHAN_TCH_H:
if ((requirement & REQUIREMENT_A_TCHF)
- && !!memcmp(&current_bts->mr_half, &bts->mr_full,
+ && !!memcmp(&c->current.bts->mr_half, &c->target.bts->mr_full,
sizeof(struct amr_multirate_conf)))
requirement &= ~(REQUIREMENT_A_TCHF);
if ((requirement & REQUIREMENT_A_TCHH)
- && !!memcmp(&current_bts->mr_half, &bts->mr_half,
+ && !!memcmp(&c->current.bts->mr_half, &c->target.bts->mr_half,
sizeof(struct amr_multirate_conf)))
requirement &= ~(REQUIREMENT_A_TCHH);
break;
@@ -572,20 +631,20 @@ static uint8_t check_requirements(struct gsm_lchan *lchan, struct gsm_bts *bts,
}
if (!requirement) {
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
"not a candidate, cannot provide identical codec rate\n");
- return 0;
+ return;
}
}
#endif
/* the maximum number of unsynchronized handovers must no be exceeded */
- if (current_bts != bts
- && bts_handover_count(bts, HO_SCOPE_ALL) >= ho_get_hodec2_ho_max(bts->ho)) {
- LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
+ if (c->current.bts != c->target.bts
+ && bts_handover_count(c->target.bts, HO_SCOPE_ALL) >= ho_get_hodec2_ho_max(c->target.bts->ho)) {
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
"not a candidate, number of allowed handovers (%d) would be exceeded\n",
- ho_get_hodec2_ho_max(bts->ho));
- return 0;
+ ho_get_hodec2_ho_max(c->target.bts->ho));
+ return;
}
/* Requirement B */
@@ -593,88 +652,177 @@ static uint8_t check_requirements(struct gsm_lchan *lchan, struct gsm_bts *bts,
/* the minimum free timeslots that are defined for this cell must
* be maintained _after_ handover/assignment */
if (requirement & REQUIREMENT_A_TCHF) {
- if (tchf_count - 1 >= ho_get_hodec2_tchf_min_slots(bts->ho))
+ if ((c->target.free_tchf - 1) >= c->target.min_free_tchf
+ && (!c->target.next_tchf_reduces_tchh
+ || (c->target.free_tchh - c->target.next_tchf_reduces_tchh) >= c->target.min_free_tchh))
requirement |= REQUIREMENT_B_TCHF;
}
if (requirement & REQUIREMENT_A_TCHH) {
- if (tchh_count - 1 >= ho_get_hodec2_tchh_min_slots(bts->ho))
+ if ((c->target.free_tchh - 1) >= c->target.min_free_tchh
+ && (!c->target.next_tchh_reduces_tchf
+ || (c->target.free_tchf - c->target.next_tchh_reduces_tchf) >= c->target.min_free_tchf))
requirement |= REQUIREMENT_B_TCHH;
}
/* Requirement C */
- /* the nr of free timeslots of the target cell must be >= the
- * free slots of the current cell _after_ handover/assignment */
- count = bts_count_free_ts(current_bts,
- (lchan->type == GSM_LCHAN_TCH_H) ?
- GSM_PCHAN_TCH_H : GSM_PCHAN_TCH_F);
+ /* the load percentage above congestion on the target cell *after* HO must be < the load percentage above
+ * congestion on the current cell, hence the - 1 on the target. */
+ current_overbooked = load_above_congestion(c->current.free_tch, c->current.min_free_tch);
if (requirement & REQUIREMENT_A_TCHF) {
- if (tchf_count - 1 >= count + 1)
+ bool ok;
+ int32_t target_overbooked;
+ int target_free_tchf_after_ho;
+
+ /* To evaluate whether a handover improves or worsens congestion on TCH/F, first figure out how many
+ * TCH/F lchans will be occupied on the target after the handover. If the target is a different cell,
+ * then we obviously reduce by one TCH/F. If source and target cell are the same (re-assignment), then
+ * the source lchan may also free a TCH/F at the same time. Add up all of these effects to figure out
+ * the congestion percentages before and after handover. */
+ target_free_tchf_after_ho = c->target.free_tchf - 1;
+ if (c->current.bts == c->target.bts)
+ target_free_tchf_after_ho += c->current.lchan_frees_tchf;
+ target_overbooked = load_above_congestion(target_free_tchf_after_ho, c->target.min_free_tchf);
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
+ "current overbooked = %s%%, TCH/F target overbooked after HO = %s%%\n",
+ osmo_int_to_float_str_c(OTC_SELECT, current_overbooked, LOAD_PRECISION - 2),
+ osmo_int_to_float_str_c(OTC_SELECT, target_overbooked, LOAD_PRECISION - 2));
+ ok = target_overbooked < current_overbooked;
+ /* Look at dynamic timeslot effects on TCH/H: */
+ if (ok && c->target.next_tchf_reduces_tchh) {
+ /* Looking at the current TCH type and the target cell's TCH/F alone, congestion balancing
+ * should happen. However, what if the target TCH/F is a dynamic timeslot -- would that cause
+ * congestion on TCH/H above the current cell's TCH/H congestion? */
+ int32_t current_tchh_overbooked = load_above_congestion(c->current.free_tchh,
+ c->current.min_free_tchh);
+ int32_t target_tchh_overbooked;
+ int target_free_tchh_after_ho = c->target.free_tchh - c->target.next_tchf_reduces_tchh;
+ /* If this is a re-assignment within the same cell, and if the current candidate would free a
+ * dynamic timeslot, then the target-overbooking after HO is reduced again by the freed dynamic
+ * TS. */
+ if (c->current.bts == c->target.bts)
+ target_free_tchh_after_ho += c->current.lchan_frees_tchh;
+ target_tchh_overbooked = load_above_congestion(target_free_tchh_after_ho,
+ c->target.min_free_tchh);
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
+ "dyn TS: current TCH/H overbooked = %s%%, TCH/H target overbooked after HO = %s%%\n",
+ osmo_int_to_float_str_c(OTC_SELECT, current_tchh_overbooked, LOAD_PRECISION - 2),
+ osmo_int_to_float_str_c(OTC_SELECT, target_tchh_overbooked, LOAD_PRECISION - 2));
+ /* For the current TCH kind, a handover should only happen if things actually get better
+ * (condition is '<'). For dynamic timeslot cross effects TCH/F->TCH/H, it is fine to not make
+ * it worse. Hence the smaller-or-equal condition. */
+ ok = target_tchh_overbooked <= current_tchh_overbooked;
+ }
+ if (ok)
requirement |= REQUIREMENT_C_TCHF;
}
if (requirement & REQUIREMENT_A_TCHH) {
- if (tchh_count - 1 >= count + 1)
+ bool ok;
+ int32_t target_overbooked;
+ int target_free_tchh_after_ho;
+
+ /* To evaluate whether a handover improves or worsens congestion on TCH/H, first figure out how many
+ * TCH/H lchans will be occupied on the target after the handover. If the target is a different cell,
+ * then we obviously reduce by one TCH/H. If source and target cell are the same (re-assignment), then
+ * the source lchan may also free one or two TCH/H at the same time. Add up all of these effects to
+ * figure out the congestion percentages before and after handover. */
+ target_free_tchh_after_ho = c->target.free_tchh - 1;
+ if (c->current.bts == c->target.bts)
+ target_free_tchh_after_ho += c->current.lchan_frees_tchh;
+ target_overbooked = load_above_congestion(target_free_tchh_after_ho, c->target.min_free_tchh);
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
+ "current overbooked = %s%%, TCH/H target overbooked after HO = %s%%\n",
+ osmo_int_to_float_str_c(OTC_SELECT, current_overbooked, LOAD_PRECISION - 2),
+ osmo_int_to_float_str_c(OTC_SELECT, target_overbooked, LOAD_PRECISION - 2));
+ ok = target_overbooked < current_overbooked;
+ /* Look at dynamic timeslot effects on TCH/F: */
+ if (ok && c->target.next_tchh_reduces_tchf) {
+ /* Looking at the current TCH type and the target cell's TCH/H alone, congestion balancing
+ * should happen. However, what if the target TCH/H is a dynamic timeslot -- would that cause
+ * congestion on TCH/F above the current cell's TCH/F congestion? */
+ int32_t current_tchf_overbooked = load_above_congestion(c->current.free_tchf,
+ c->current.min_free_tchf);
+ int32_t target_tchf_overbooked;
+ int target_free_tchf_after_ho = c->target.free_tchf - c->target.next_tchh_reduces_tchf;
+ /* If this is a re-assignment within the same cell, and if the current candidate would free a
+ * dynamic timeslot, then the target-overbooking after HO is reduced again by the freed dynamic
+ * TS. */
+ if (c->current.bts == c->target.bts)
+ target_free_tchf_after_ho += c->current.lchan_frees_tchf;
+ target_tchf_overbooked = load_above_congestion(target_free_tchf_after_ho,
+ c->target.min_free_tchf);
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
+ "dyn TS: current TCH/F overbooked = %s%%, TCH/F target overbooked after HO = %s%%\n",
+ osmo_int_to_float_str_c(OTC_SELECT, current_tchf_overbooked, LOAD_PRECISION - 2),
+ osmo_int_to_float_str_c(OTC_SELECT, target_tchf_overbooked, LOAD_PRECISION - 2));
+ /* For the current TCH kind, a handover should only happen if things actually get better
+ * (condition is '<'). For dynamic timeslot cross effects TCH/H->TCH/F, it is fine to not make
+ * it worse. Hence the smaller-or-equal condition. */
+ ok = target_tchf_overbooked <= current_tchf_overbooked;
+ }
+ if (ok)
requirement |= REQUIREMENT_C_TCHH;
}
/* return mask of fulfilled requirements */
- return requirement;
+ c->requirements = requirement;
}
-static uint8_t check_requirements_remote_bss(struct gsm_lchan *lchan,
- const struct gsm0808_cell_id_list2 *cil)
+static void check_requirements_remote_bss(struct ho_candidate *c)
{
uint8_t requirement = 0;
unsigned int penalty_time;
+ c->requirements = 0;
/* Requirement A */
/* the handover penalty timer must not run for this bts */
- penalty_time = conn_penalty_time_remaining(lchan->conn, cil);
+ penalty_time = penalty_timers_remaining_list(&c->current.lchan->conn->hodec2.penalty_timers, &c->target.cell_ids);
if (penalty_time) {
- LOGPHOLCHANTOREMOTE(lchan, cil, LOGL_DEBUG,
+ LOGPHOLCHANTOREMOTE(c->current.lchan, &c->target.cell_ids, LOGL_DEBUG,
"not a candidate, target BSS still in penalty time"
" (%u seconds left)\n", penalty_time);
- return 0;
+ return;
}
/* compatibility check for codecs -- we have no notion of what the remote BSS supports. We can
* only assume that a handover would work, and use only the local requirements. */
- switch (lchan->tch_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(c->current.lchan->current_ch_mode_rate.chan_mode)) {
case GSM48_CMODE_SPEECH_V1:
- switch (lchan->type) {
+ switch (c->current.lchan->type) {
case GSM_LCHAN_TCH_F: /* mandatory */
requirement |= REQUIREMENT_A_TCHF;
break;
case GSM_LCHAN_TCH_H:
- if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR1))
+ if (codec_type_is_supported(c->current.lchan->conn, GSM0808_SCT_HR1))
requirement |= REQUIREMENT_A_TCHH;
break;
default:
- LOGPHOLCHAN(lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
- get_value_string(gsm48_chan_mode_names, lchan->tch_mode));
- return 0;
+ LOGPHOLCHAN(c->current.lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
+ get_value_string(gsm48_chan_mode_names,
+ c->current.lchan->current_ch_mode_rate.chan_mode));
+ return;
}
break;
case GSM48_CMODE_SPEECH_EFR:
- if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR2))
+ if (codec_type_is_supported(c->current.lchan->conn, GSM0808_SCT_FR2))
requirement |= REQUIREMENT_A_TCHF;
break;
case GSM48_CMODE_SPEECH_AMR:
- if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR3))
+ if (codec_type_is_supported(c->current.lchan->conn, GSM0808_SCT_FR3))
requirement |= REQUIREMENT_A_TCHF;
- if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR3))
+ if (codec_type_is_supported(c->current.lchan->conn, GSM0808_SCT_HR3))
requirement |= REQUIREMENT_A_TCHH;
break;
default:
- LOGPHOLCHAN(lchan, LOGL_DEBUG, "Not even considering: src is not a SPEECH mode lchan\n");
+ LOGPHOLCHAN(c->current.lchan, LOGL_DEBUG, "Not even considering: src is not a SPEECH mode lchan\n");
/* FIXME: should allow handover of non-speech lchans */
- return 0;
+ return;
}
if (!requirement) {
- LOGPHOLCHAN(lchan, LOGL_ERROR, "lchan doesn't fit its own requirements??\n");
- return 0;
+ LOGPHOLCHAN(c->current.lchan, LOGL_ERROR, "lchan doesn't fit its own requirements??\n");
+ return;
}
/* Requirement B and C */
@@ -687,38 +835,34 @@ static uint8_t check_requirements_remote_bss(struct gsm_lchan *lchan,
requirement |= REQUIREMENT_B_TCHH | REQUIREMENT_C_TCHH;
/* return mask of fulfilled requirements */
- return requirement;
+ c->requirements = requirement;
}
/* Trigger handover or assignment depending on the target BTS */
static int trigger_local_ho_or_as(struct ho_candidate *c, uint8_t requirements)
{
- struct gsm_lchan *lchan = c->lchan;
- struct gsm_bts *new_bts = c->bts;
- struct handover_out_req req;
- struct gsm_bts *current_bts = lchan->ts->trx->bts;
int afs_bias = 0;
bool full_rate = false;
/* afs_bias becomes > 0, if AFS is used and is improved */
- if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
- afs_bias = ho_get_hodec2_afs_bias_rxlev(new_bts->ho);
+ if (gsm48_chan_mode_to_non_vamos(c->current.lchan->current_ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR)
+ afs_bias = ho_get_hodec2_afs_bias_rxlev(c->target.bts->ho);
/* select TCH rate, prefer TCH/F if AFS is improved */
- switch (lchan->type) {
+ switch (c->current.lchan->type) {
case GSM_LCHAN_TCH_F:
/* keep on full rate, if TCH/F is a candidate */
if ((requirements & REQUIREMENT_TCHF_MASK)) {
- if (current_bts == new_bts) {
- LOGPHOLCHAN(lchan, LOGL_INFO, "Not performing assignment: Already on target type\n");
- return 0;
+ if (c->current.bts == c->target.bts) {
+ LOGPHOLCHAN(c->current.lchan, LOGL_INFO, "Not performing assignment: Already on target type\n");
+ return -EALREADY;
}
full_rate = true;
break;
}
/* change to half rate */
if (!(requirements & REQUIREMENT_TCHH_MASK)) {
- LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_ERROR,
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_ERROR,
"neither TCH/F nor TCH/H requested, aborting ho/as\n");
return -EINVAL;
}
@@ -737,38 +881,53 @@ static int trigger_local_ho_or_as(struct ho_candidate *c, uint8_t requirements)
}
/* keep on half rate */
if (!(requirements & REQUIREMENT_TCHH_MASK)) {
- LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_ERROR,
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_ERROR,
"neither TCH/F nor TCH/H requested, aborting ho/as\n");
return -EINVAL;
}
- if (current_bts == new_bts) {
- LOGPHOLCHAN(lchan, LOGL_INFO, "Not performing assignment: Already on target type\n");
- return 0;
+ if (c->current.bts == c->target.bts) {
+ LOGPHOLCHAN(c->current.lchan, LOGL_INFO, "Not performing assignment: Already on target type\n");
+ return -EALREADY;
}
break;
default:
- LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_ERROR, "lchan is neither TCH/F nor TCH/H, aborting ho/as\n");
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_ERROR, "c->current.lchan is neither TCH/F nor TCH/H, aborting ho/as\n");
return -EINVAL;
}
/* trigger handover or assignment */
- if (current_bts == new_bts)
- LOGPHOLCHAN(lchan, LOGL_NOTICE, "Triggering assignment to %s, due to %s\n",
+ if (c->current.bts == c->target.bts) {
+ LOGPHOLCHAN(c->current.lchan, LOGL_NOTICE, "Triggering assignment to %s, due to %s\n",
full_rate ? "TCH/F" : "TCH/H",
ho_reason_name(global_ho_reason));
- else
- LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_INFO,
+ return reassignment_request_to_chan_type(ASSIGN_FOR_CONGESTION_RESOLUTION, c->current.lchan,
+ full_rate? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H);
+ } else {
+ struct handover_out_req req = {
+ .from_hodec_id = HODEC2,
+ .old_lchan = c->current.lchan,
+ .new_lchan_type = full_rate? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H,
+ };
+ bts_cell_ab(&req.target_cell_ab, c->target.bts);
+ LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_INFO,
"Triggering handover to %s, due to %s\n",
full_rate ? "TCH/F" : "TCH/H",
ho_reason_name(global_ho_reason));
-
- req = (struct handover_out_req){
- .from_hodec_id = HODEC2,
- .old_lchan = lchan,
- .target_nik = *bts_ident_key(new_bts),
- .new_lchan_type = full_rate? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H,
- };
- handover_request(&req);
+ handover_request(&req);
+
+ /* Apply penalty timer hodec2_penalty_low_rxqual_ho */
+ if (global_ho_reason == HO_REASON_INTERFERENCE
+ || global_ho_reason == HO_REASON_BAD_QUALITY) {
+ struct gsm0808_cell_id bts_id;
+ struct gsm_subscriber_connection *conn = c->current.lchan->conn;
+ int timeout = ho_get_hodec2_penalty_low_rxqual_ho(c->current.bts->ho);
+ gsm_bts_cell_id(&bts_id, c->current.bts);
+ LOGPHOCAND(c, LOGL_DEBUG, "Applying penalty-time low-rxqual-ho %d s on bts %u (%s), reason: %s\n",
+ timeout, c->current.bts->nr, gsm0808_cell_id_name_c(OTC_SELECT, &bts_id),
+ ho_reason_name(global_ho_reason));
+ penalty_timers_add(conn, &conn->hodec2.penalty_timers, &bts_id, timeout);
+ }
+ }
return 0;
}
@@ -776,14 +935,14 @@ static int trigger_remote_bss_ho(struct ho_candidate *c, uint8_t requirements)
{
struct handover_out_req req;
- LOGPHOLCHANTOREMOTE(c->lchan, c->cil, LOGL_INFO,
+ LOGPHOLCHANTOREMOTE(c->current.lchan, &c->target.cell_ids, LOGL_INFO,
"Triggering inter-BSC handover, due to %s\n",
ho_reason_name(global_ho_reason));
req = (struct handover_out_req){
.from_hodec_id = HODEC2,
- .old_lchan = c->lchan,
- .target_nik = c->nik,
+ .old_lchan = c->current.lchan,
+ .target_cell_ab = c->target.ab,
};
handover_request(&req);
return 0;
@@ -791,7 +950,7 @@ static int trigger_remote_bss_ho(struct ho_candidate *c, uint8_t requirements)
static int trigger_ho(struct ho_candidate *c, uint8_t requirements)
{
- if (c->bts)
+ if (c->target.bts)
return trigger_local_ho_or_as(c, requirements);
else
return trigger_remote_bss_ho(c, requirements);
@@ -812,59 +971,134 @@ static int trigger_ho(struct ho_candidate *c, uint8_t requirements)
" less-or-equal congestion"))
/* verbosely log about a handover candidate */
-static inline void debug_candidate(struct ho_candidate *candidate,
- int8_t rxlev, int tchf_count, int tchh_count)
+static inline void debug_candidate(struct ho_candidate *candidate)
{
- struct gsm_lchan *lchan = candidate->lchan;
-
#define HO_CANDIDATE_FMT(tchx, TCHX) "TCH/" #TCHX "={free %d (want %d), " REQUIREMENTS_FMT "}"
#define HO_CANDIDATE_ARGS(tchx, TCHX) \
- tch##tchx##_count, ho_get_hodec2_tch##tchx##_min_slots(candidate->bts->ho), \
+ candidate->target.free_tch##tchx, candidate->target.min_free_tch##tchx, \
REQUIREMENTS_ARGS(candidate->requirements, TCHX)
- if (!candidate->bts && !candidate->cil)
- LOGPHOLCHAN(lchan, LOGL_DEBUG, "Empty candidate\n");
- if (candidate->bts && candidate->cil)
- LOGPHOLCHAN(lchan, LOGL_ERROR, "Invalid candidate: both local- and remote-BSS target\n");
+ if (!candidate->target.bts && !candidate->target.cell_ids.id_list_len)
+ LOGPHOLCHAN(candidate->current.lchan, LOGL_DEBUG, "Empty candidate\n");
+ if (candidate->target.bts && candidate->target.cell_ids.id_list_len)
+ LOGPHOLCHAN(candidate->current.lchan, LOGL_ERROR, "Invalid candidate: both local- and remote-BSS target\n");
- if (candidate->cil)
- LOGPHOLCHANTOREMOTE(lchan, candidate->cil, LOGL_DEBUG,
- "RX level %d -> %d\n",
- rxlev2dbm(rxlev), rxlev2dbm(candidate->avg));
+ if (candidate->target.cell_ids.id_list_len)
+ LOGPHOLCHANTOREMOTE(candidate->current.lchan, &candidate->target.cell_ids, LOGL_DEBUG,
+ "RX level %d dBm -> %d dBm\n",
+ rxlev2dbm(candidate->current.rxlev), rxlev2dbm(candidate->target.rxlev));
- if (candidate->bts == lchan->ts->trx->bts)
- LOGPHOLCHANTOBTS(lchan, candidate->bts, LOGL_DEBUG,
- "RX level %d; "
+ if (candidate->target.bts == candidate->current.bts)
+ LOGPHOLCHANTOBTS(candidate->current.lchan, candidate->target.bts, LOGL_DEBUG,
+ "RX level %d dBm; "
HO_CANDIDATE_FMT(f, F) "; " HO_CANDIDATE_FMT(h, H) "\n",
- rxlev2dbm(candidate->avg),
+ rxlev2dbm(candidate->current.rxlev),
HO_CANDIDATE_ARGS(f, F), HO_CANDIDATE_ARGS(h, H));
- else if (candidate->bts)
- LOGPHOLCHANTOBTS(lchan, candidate->bts, LOGL_DEBUG,
- "RX level %d -> %d; "
+ else if (candidate->target.bts)
+ LOGPHOLCHANTOBTS(candidate->current.lchan, candidate->target.bts, LOGL_DEBUG,
+ "RX level %d dBm -> %d dBm; "
HO_CANDIDATE_FMT(f, F) "; " HO_CANDIDATE_FMT(h, H) "\n",
- rxlev2dbm(rxlev), rxlev2dbm(candidate->avg),
+ rxlev2dbm(candidate->current.rxlev), rxlev2dbm(candidate->target.rxlev),
HO_CANDIDATE_ARGS(f, F), HO_CANDIDATE_ARGS(h, H));
}
+static void candidate_set_free_tch(struct ho_candidate *c)
+{
+ struct chan_counts *bts_counts;
+ struct gsm_lchan *next_lchan;
+
+ bts_counts = &c->current.bts->chan_counts;
+ c->current.free_tchf = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ c->current.min_free_tchf = ho_get_hodec2_tchf_min_slots(c->current.bts->ho);
+ c->current.free_tchh = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
+ c->current.min_free_tchh = ho_get_hodec2_tchh_min_slots(c->current.bts->ho);
+
+ switch (c->current.lchan->ts->pchan_is) {
+ case GSM_PCHAN_TCH_F:
+ c->current.free_tch = c->current.free_tchf;
+ c->current.min_free_tch = c->current.min_free_tchf;
+ c->current.lchan_frees_tchf = 1;
+ if (c->current.lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN)
+ c->current.lchan_frees_tchh = 2;
+ else
+ c->current.lchan_frees_tchh = 0;
+ break;
+ case GSM_PCHAN_TCH_H:
+ c->current.free_tch = c->current.free_tchh;
+ c->current.min_free_tch = c->current.min_free_tchh;
+ c->current.lchan_frees_tchh = 1;
+ /* Freeing one of two TCH/H does not free a dyn TS and would not free a TCH/F. It has to be the last
+ * TCH/H of a dynamic timeslot that is freed to get a new TCH/F in the current cell from the handover.
+ * Hence the ts_usage_count() condition. */
+ if (c->current.lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN
+ && ts_usage_count(c->current.lchan->ts) == 1)
+ c->current.lchan_frees_tchf = 1;
+ else
+ c->current.lchan_frees_tchf = 0;
+ break;
+ default:
+ break;
+ }
+
+ /* For inter-BSC handover, the target BTS is in a different BSC and hence NULL here. */
+ if (c->target.bts) {
+ bts_counts = &c->target.bts->chan_counts;
+ c->target.free_tchf = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ c->target.min_free_tchf = ho_get_hodec2_tchf_min_slots(c->target.bts->ho);
+ c->target.free_tchh = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
+ c->target.min_free_tchh = ho_get_hodec2_tchh_min_slots(c->target.bts->ho);
+
+ /* Would the next TCH/F lchan occupy a dynamic timeslot that currently counts for free TCH/H timeslots?
+ */
+ next_lchan = lchan_avail_by_type(c->target.bts, GSM_LCHAN_TCH_F,
+ SELECT_FOR_HANDOVER, NULL, false);
+ if (next_lchan && next_lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN)
+ c->target.next_tchf_reduces_tchh = 2;
+ else
+ c->target.next_tchf_reduces_tchh = 0;
+
+ /* Would the next TCH/H lchan occupy a dynamic timeslot that currently counts for free TCH/F timeslots?
+ * Note that a dyn TS already in TCH/H mode (half occupied) would not reduce free TCH/F. */
+ next_lchan = lchan_avail_by_type(c->target.bts, GSM_LCHAN_TCH_H,
+ SELECT_FOR_HANDOVER, NULL, false);
+ if (next_lchan && next_lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN
+ && next_lchan->ts->pchan_is != GSM_PCHAN_TCH_H)
+ c->target.next_tchh_reduces_tchf = 1;
+ else
+ c->target.next_tchh_reduces_tchf = 0;
+ } else {
+
+ c->target.free_tchf = 0;
+ c->target.min_free_tchf = 0;
+ c->target.next_tchh_reduces_tchf = 0;
+ c->target.free_tchh = 0;
+ c->target.min_free_tchh = 0;
+ c->target.next_tchf_reduces_tchh = 0;
+ }
+}
+
/* add candidate for re-assignment within the current cell */
static void collect_assignment_candidate(struct gsm_lchan *lchan, struct ho_candidate *clist,
- unsigned int *candidates, int av_rxlev)
+ unsigned int *candidates, int rxlev_current)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
- int tchf_count, tchh_count;
struct ho_candidate c;
- tchf_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_F);
- tchh_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_H);
-
c = (struct ho_candidate){
- .lchan = lchan,
- .bts = bts,
- .requirements = check_requirements(lchan, bts, tchf_count, tchh_count),
- .avg = av_rxlev,
+ .current = {
+ .lchan = lchan,
+ .bts = bts,
+ .rxlev = rxlev_current,
+ },
+ .target = {
+ .bts = bts,
+ .rxlev = rxlev_current, /* same cell, same rxlev */
+ },
};
+ candidate_set_free_tch(&c);
+ check_requirements(&c);
- debug_candidate(&c, 0, tchf_count, tchh_count);
+ debug_candidate(&c);
if (!c.requirements)
return;
@@ -876,22 +1110,17 @@ static void collect_assignment_candidate(struct gsm_lchan *lchan, struct ho_cand
/* add candidates for handover to all neighbor cells */
static void collect_handover_candidate(struct gsm_lchan *lchan, struct neigh_meas_proc *nmp,
struct ho_candidate *clist, unsigned int *candidates,
- bool include_weaker_rxlev, int av_rxlev,
+ bool include_weaker_rxlev, int rxlev_current,
int *neighbors_count)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
- int tchf_count = 0;
- int tchh_count = 0;
struct gsm_bts *neighbor_bts;
- const struct gsm0808_cell_id_list2 *neighbor_cil;
- struct neighbor_ident_key ni = {
- .from_bts = bts->nr,
+ struct gsm0808_cell_id_list2 neighbor_cil;
+ struct cell_ab target_ab = {
.arfcn = nmp->arfcn,
.bsic = nmp->bsic,
};
- int avg;
struct ho_candidate c;
- int min_rxlev;
struct handover_cfg *neigh_cfg;
/* skip empty slots */
@@ -910,9 +1139,9 @@ static void collect_handover_candidate(struct gsm_lchan *lchan, struct neigh_mea
}
find_handover_target_cell(&neighbor_bts, &neighbor_cil,
- lchan->conn, &ni, false);
+ lchan->conn, &target_ab, false);
- if (!neighbor_bts && !neighbor_cil) {
+ if (!neighbor_bts && !neighbor_cil.id_list_len) {
LOGPHOBTS(bts, LOGL_DEBUG, "no neighbor ARFCN %u BSIC %u configured for this cell\n",
nmp->arfcn, nmp->bsic);
return;
@@ -928,51 +1157,51 @@ static void collect_handover_candidate(struct gsm_lchan *lchan, struct neigh_mea
* instead assume the local BTS' config to apply. */
neigh_cfg = (neighbor_bts ? : bts)->ho;
- /* calculate average rxlev for this cell over the window */
- avg = neigh_meas_avg(nmp, ho_get_hodec2_rxlev_neigh_avg_win(bts->ho));
-
c = (struct ho_candidate){
- .lchan = lchan,
- .avg = avg,
- .nik = ni,
- .bts = neighbor_bts,
- .cil = neighbor_cil,
+ .current = {
+ .lchan = lchan,
+ .bts = bts,
+ .rxlev = rxlev_current,
+ },
+ .target = {
+ .ab = target_ab,
+ .bts = neighbor_bts,
+ .cell_ids = neighbor_cil,
+ .rxlev = neigh_meas_avg(nmp, ho_get_hodec2_rxlev_neigh_avg_win(bts->ho)),
+ },
};
+ candidate_set_free_tch(&c);
/* Heed rxlev hysteresis only if the RXLEV/RXQUAL/TA levels of the MS aren't critically bad and
* we're just looking for an improvement. If levels are critical, we desperately need a handover
* and thus skip the hysteresis check. */
if (!include_weaker_rxlev) {
- unsigned int pwr_hyst = ho_get_hodec2_pwr_hysteresis(bts->ho);
- if (avg <= (av_rxlev + pwr_hyst)) {
+ int pwr_hyst = ho_get_hodec2_pwr_hysteresis(bts->ho);
+ if ((c.target.rxlev - c.current.rxlev) <= pwr_hyst) {
LOGPHOCAND(&c, LOGL_DEBUG,
- "Not a candidate, because RX level (%d) is lower"
- " or equal than current RX level (%d) + hysteresis (%d)\n",
- rxlev2dbm(avg), rxlev2dbm(av_rxlev), pwr_hyst);
+ "Not a candidate, because RX level (%d dBm) is lower"
+ " or equal than current RX level (%d dBm) + hysteresis (%d)\n",
+ rxlev2dbm(c.target.rxlev), rxlev2dbm(c.current.rxlev), pwr_hyst);
return;
}
}
/* if the minimum level is not reached.
* In case of a remote-BSS, use the current BTS' configuration. */
- min_rxlev = ho_get_hodec2_min_rxlev(neigh_cfg);
- if (rxlev2dbm(avg) < min_rxlev) {
+ if (is_low_rxlev(c.target.rxlev, neigh_cfg)) {
LOGPHOCAND(&c, LOGL_DEBUG,
- "Not a candidate, because RX level (%d) is lower"
- " than the minimum required RX level (%d)\n",
- rxlev2dbm(avg), min_rxlev);
+ "Not a candidate, because RX level (%d dBm) is lower"
+ " than the minimum required RX level (%d dBm)\n",
+ rxlev2dbm(c.target.rxlev), ho_get_hodec2_min_rxlev(neigh_cfg));
return;
}
if (neighbor_bts) {
- tchf_count = bts_count_free_ts(neighbor_bts, GSM_PCHAN_TCH_F);
- tchh_count = bts_count_free_ts(neighbor_bts, GSM_PCHAN_TCH_H);
- c.requirements = check_requirements(lchan, neighbor_bts, tchf_count,
- tchh_count);
+ check_requirements(&c);
} else
- c.requirements = check_requirements_remote_bss(lchan, neighbor_cil);
+ check_requirements_remote_bss(&c);
- debug_candidate(&c, av_rxlev, tchf_count, tchh_count);
+ debug_candidate(&c);
if (!c.requirements)
return;
@@ -983,45 +1212,45 @@ static void collect_handover_candidate(struct gsm_lchan *lchan, struct neigh_mea
static void collect_candidates_for_lchan(struct gsm_lchan *lchan,
struct ho_candidate *clist, unsigned int *candidates,
- int *_av_rxlev, bool include_weaker_rxlev)
+ int *_rxlev_current, bool include_weaker_rxlev)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
- int av_rxlev;
+ int rxlev_current;
bool assignment;
bool handover;
int neighbors_count = 0;
- unsigned int rxlev_avg_win = ho_get_hodec2_rxlev_avg_win(bts->ho);
OSMO_ASSERT(candidates);
- /* calculate average rxlev for this cell over the window */
- av_rxlev = get_meas_rep_avg(lchan,
- ho_get_hodec2_full_tdma(bts->ho) ?
- MEAS_REP_DL_RXLEV_FULL : MEAS_REP_DL_RXLEV_SUB,
- rxlev_avg_win);
- if (_av_rxlev)
- *_av_rxlev = av_rxlev;
+ rxlev_current = current_rxlev(lchan);
+ if (_rxlev_current)
+ *_rxlev_current = rxlev_current;
/* in case there is no measurement report (yet) */
- if (av_rxlev < 0) {
+ if (rxlev_current < 0) {
LOGPHOLCHAN(lchan, LOGL_DEBUG, "Not collecting candidates, not enough measurements"
" (got %d, want %u)\n",
- lchan->meas_rep_count, rxlev_avg_win);
+ lchan->meas_rep_count, ho_get_hodec2_rxlev_avg_win(bts->ho));
return;
}
assignment = ho_get_hodec2_as_active(bts->ho);
handover = ho_get_ho_active(bts->ho);
- if (assignment)
- collect_assignment_candidate(lchan, clist, candidates, av_rxlev);
+ /* See if re-assignment within the same cell can resolve congestion.
+ * But: when TCH/F has low rxlev, do not re-assign. If a low rxlev TCH/F were re-assigned to TCH/H, we would
+ * subsequently oscillate back to TCH/F due to low rxlev. So skip TCH/F with low rxlev. */
+ if (assignment
+ && !(lchan->type == GSM_LCHAN_TCH_F
+ && (is_low_rxlev(rxlev_current, bts->ho) || is_low_rxqual(current_rxqual(lchan), bts->ho))))
+ collect_assignment_candidate(lchan, clist, candidates, rxlev_current);
if (handover) {
int i;
for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) {
collect_handover_candidate(lchan, &lchan->neigh_meas[i],
clist, candidates,
- include_weaker_rxlev, av_rxlev, &neighbors_count);
+ include_weaker_rxlev, rxlev_current, &neighbors_count);
}
}
}
@@ -1077,12 +1306,12 @@ static void collect_candidates_for_lchan(struct gsm_lchan *lchan,
* If minimum RXLEV, minimum RXQUAL or maximum TA are exceeded, the caller should pass
* include_weaker_rxlev=true so that handover is performed despite congestion.
*/
-static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_rxlev)
+static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_rxlev, bool request_upgrade_to_tch_f)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
- int ahs = (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
+ int ahs = (gsm48_chan_mode_to_non_vamos(lchan->current_ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR
&& lchan->type == GSM_LCHAN_TCH_H);
- int av_rxlev;
+ int rxlev_current;
struct ho_candidate clist[1 + ARRAY_SIZE(lchan->neigh_meas)];
unsigned int candidates = 0;
int i;
@@ -1098,7 +1327,7 @@ static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_r
return 0;
}
- collect_candidates_for_lchan(lchan, clist, &candidates, &av_rxlev, include_weaker_rxlev);
+ collect_candidates_for_lchan(lchan, clist, &candidates, &rxlev_current, include_weaker_rxlev);
/* If assignment is disabled and no neighbor cell report exists, or no neighbor cell qualifies,
* we may not even have any candidates. */
@@ -1117,14 +1346,15 @@ static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_r
continue;
/* Only consider Local-BSS cells */
- if (!clist[i].bts)
+ if (!clist[i].target.bts)
continue;
- better = clist[i].avg - av_rxlev;
- /* Apply AFS bias? */
+ better = clist[i].target.rxlev - clist[i].current.rxlev;
+ /* Apply AFS bias? Skip AFS bias for all intra-cell candidates. */
afs_bias = 0;
- if (ahs && (clist[i].requirements & REQUIREMENT_B_TCHF))
- afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
+ if (clist[i].target.bts != bts
+ && ahs && (clist[i].requirements & REQUIREMENT_B_TCHF))
+ afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].target.bts->ho);
better += afs_bias;
if (better > best_better_db) {
best_cand = &clist[i];
@@ -1136,7 +1366,7 @@ static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_r
/* perform handover, if there is a candidate */
if (best_cand) {
LOGPHOCAND(best_cand, LOGL_INFO, "Best candidate, RX level %d%s\n",
- rxlev2dbm(best_cand->avg),
+ rxlev2dbm(best_cand->target.rxlev),
best_applied_afs_bias ? " (applied AHS -> AFS rxlev bias)" : "");
return trigger_ho(best_cand, best_cand->requirements & REQUIREMENT_B_MASK);
}
@@ -1150,14 +1380,15 @@ static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_r
continue;
/* Only consider Local-BSS cells */
- if (!clist[i].bts)
+ if (!clist[i].target.bts)
continue;
- better = clist[i].avg - av_rxlev;
- /* Apply AFS bias? */
+ better = clist[i].target.rxlev - clist[i].current.rxlev;
+ /* Apply AFS bias? Skip AFS bias for all intra-cell candidates. */
afs_bias = 0;
- if (ahs && (clist[i].requirements & REQUIREMENT_C_TCHF))
- afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
+ if (clist[i].target.bts != bts
+ && ahs && (clist[i].requirements & REQUIREMENT_C_TCHF))
+ afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].target.bts->ho);
better += afs_bias;
if (better > best_better_db) {
best_cand = &clist[i];
@@ -1169,7 +1400,7 @@ static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_r
/* perform handover, if there is a candidate */
if (best_cand) {
LOGPHOCAND(best_cand, LOGL_INFO, "Best candidate, RX level %d%s\n",
- rxlev2dbm(best_cand->avg),
+ rxlev2dbm(best_cand->target.rxlev),
best_applied_afs_bias? " (applied AHS -> AFS rxlev bias)" : "");
return trigger_ho(best_cand, best_cand->requirements & REQUIREMENT_C_MASK);
}
@@ -1190,15 +1421,19 @@ static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_r
if (!(clist[i].requirements & REQUIREMENT_A_MASK))
continue;
- better = clist[i].avg - av_rxlev;
- /* Apply AFS bias?
+ better = clist[i].target.rxlev - clist[i].current.rxlev;
+ /* Apply AFS bias? Skip AFS bias for all intra-cell candidates.
* (never to remote-BSS neighbors, since we will not change the lchan type for those.) */
afs_bias = 0;
if (ahs && (clist[i].requirements & REQUIREMENT_A_TCHF)
- && clist[i].bts)
- afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
+ && clist[i].target.bts && clist[i].target.bts != bts)
+ afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].target.bts->ho);
better += afs_bias;
- if (better > best_better_db) {
+ if (better > best_better_db
+ || (better >= best_better_db /* Upgrade from TCH/H to TCH/F: allow for equal rxlev */
+ && request_upgrade_to_tch_f
+ && is_upgrade_to_tchf(&clist[i], REQUIREMENT_A_MASK))) {
+
best_cand = &clist[i];
best_better_db = better;
best_applied_afs_bias = afs_bias? true : false;
@@ -1207,10 +1442,22 @@ static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_r
/* perform handover, if there is a candidate */
if (best_cand) {
+ int rc;
LOGPHOCAND(best_cand, LOGL_INFO, "Best candidate: RX level %d%s\n",
- rxlev2dbm(best_cand->avg),
+ rxlev2dbm(best_cand->target.rxlev),
best_applied_afs_bias ? " (applied AHS -> AFS rxlev bias)" : "");
- return trigger_ho(best_cand, best_cand->requirements & REQUIREMENT_A_MASK);
+ rc = trigger_ho(best_cand, best_cand->requirements & REQUIREMENT_A_MASK);
+
+ /* After upgrading TCH/H to TCH/F due to bad RxQual, start penalty timer to avoid re-assignment within
+ * the same cell again, to avoid oscillation from RxQual noise combined with congestion resolution. */
+ if (!rc && best_cand->target.bts == best_cand->current.bts
+ && is_upgrade_to_tchf(best_cand, REQUIREMENT_A_MASK)) {
+ struct gsm0808_cell_id bts_id;
+ gsm_bts_cell_id(&bts_id, best_cand->target.bts);
+ penalty_timers_add(lchan->conn, &lchan->conn->hodec2.penalty_timers, &bts_id,
+ ho_get_hodec2_penalty_low_rxqual_as(bts->ho));
+ }
+ return rc;
}
/* Damn, all is congested, has too low RXLEV or cannot service the voice call due to codec
@@ -1276,14 +1523,8 @@ static void on_measurement_report(struct gsm_meas_rep *mr)
}
/* get average levels. if not enough measurements yet, value is < 0 */
- av_rxlev = get_meas_rep_avg(lchan,
- ho_get_hodec2_full_tdma(bts->ho) ?
- MEAS_REP_DL_RXLEV_FULL : MEAS_REP_DL_RXLEV_SUB,
- ho_get_hodec2_rxlev_avg_win(bts->ho));
- av_rxqual = get_meas_rep_avg(lchan,
- ho_get_hodec2_full_tdma(bts->ho) ?
- MEAS_REP_DL_RXQUAL_FULL : MEAS_REP_DL_RXQUAL_SUB,
- ho_get_hodec2_rxqual_avg_win(bts->ho));
+ av_rxlev = current_rxlev(lchan);
+ av_rxqual = current_rxqual(lchan);
if (av_rxlev < 0 && av_rxqual < 0) {
LOGPHOLCHAN(lchan, LOGL_INFO, "Skipping, Not enough recent measurements\n");
return;
@@ -1291,7 +1532,7 @@ static void on_measurement_report(struct gsm_meas_rep *mr)
/* improve levels in case of AFS, if defined */
if (lchan->type == GSM_LCHAN_TCH_F
- && lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ && gsm48_chan_mode_to_non_vamos(lchan->current_ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
int av_rxlev_was = av_rxlev;
int av_rxqual_was = av_rxqual;
int rxlev_bias = ho_get_hodec2_afs_bias_rxlev(bts->ho);
@@ -1323,31 +1564,34 @@ static void on_measurement_report(struct gsm_meas_rep *mr)
global_ho_reason = HO_REASON_BAD_QUALITY;
LOGPHOLCHAN(lchan, LOGL_INFO, "Trying handover/assignment due to bad quality\n");
}
- find_alternative_lchan(lchan, true);
+ find_alternative_lchan(lchan, true, true);
return;
}
/* Low Level */
- if (av_rxlev >= 0 && rxlev2dbm(av_rxlev) < ho_get_hodec2_min_rxlev(bts->ho)) {
+ if (is_low_rxlev(av_rxlev, bts->ho)) {
global_ho_reason = HO_REASON_LOW_RXLEVEL;
LOGPHOLCHAN(lchan, LOGL_NOTICE, "RX level is TOO LOW: %d < %d\n",
rxlev2dbm(av_rxlev), ho_get_hodec2_min_rxlev(bts->ho));
- find_alternative_lchan(lchan, true);
+ find_alternative_lchan(lchan, true, true);
return;
}
/* Max Distance */
if (lchan->meas_rep_count > 0
- && lchan->rqd_ta > ho_get_hodec2_max_distance(bts->ho)) {
+ && lchan->last_ta > ho_get_hodec2_max_distance(bts->ho)) {
+ struct gsm0808_cell_id bts_id;
global_ho_reason = HO_REASON_MAX_DISTANCE;
LOGPHOLCHAN(lchan, LOGL_NOTICE, "TA is TOO HIGH: %u > %d\n",
- lchan->rqd_ta, ho_get_hodec2_max_distance(bts->ho));
+ lchan->last_ta, ho_get_hodec2_max_distance(bts->ho));
/* start penalty timer to prevent coming back too
* early. it must be started before selecting a better cell,
* so there is no assignment selected, due to running
* penalty timer. */
- bts_penalty_time_add(lchan->conn, bts, ho_get_hodec2_penalty_max_dist(bts->ho));
- find_alternative_lchan(lchan, true);
+ gsm_bts_cell_id(&bts_id, bts);
+ penalty_timers_add(lchan->conn, &lchan->conn->hodec2.penalty_timers, &bts_id,
+ ho_get_hodec2_penalty_max_dist(bts->ho));
+ find_alternative_lchan(lchan, true, false);
return;
}
@@ -1358,10 +1602,123 @@ static void on_measurement_report(struct gsm_meas_rep *mr)
/* try handover to a better cell */
if (av_rxlev >= 0 && (mr->nr % pwr_interval) == 0) {
global_ho_reason = HO_REASON_BETTER_CELL;
- find_alternative_lchan(lchan, false);
+ find_alternative_lchan(lchan, false, false);
}
}
+static bool lchan_is_on_dynamic_ts(struct gsm_lchan *lchan)
+{
+ return lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN
+ || lchan->ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH;
+}
+
+/* Given two candidates, pick the one that should rather be moved during handover.
+ * Return the better candidate in out-parameters best_cand and best_avg_db.
+ */
+static struct ho_candidate *pick_better_lchan_to_move(struct ho_candidate *a,
+ struct ho_candidate *b,
+ uint8_t for_requirement)
+{
+ int a_rxlev_change;
+ int b_rxlev_change;
+ struct ho_candidate *ret = a;
+
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+
+ a_rxlev_change = a->target.rxlev - a->current.rxlev;
+ b_rxlev_change = b->target.rxlev - b->current.rxlev;
+
+ /* Typically, a congestion related handover reduces RXLEV. If there is a candidate that actually improves RXLEV,
+ * prefer that, because it pre-empts a likely handover due to measurement results later. Also favor unchanged
+ * RXLEV over a loss of RXLEV (favor staying within the same cell over moving to a worse cell). */
+ if (a_rxlev_change >= 0 && a_rxlev_change > b_rxlev_change)
+ return a;
+ if (b_rxlev_change >= 0 && b_rxlev_change > a_rxlev_change)
+ return b;
+
+ if (a_rxlev_change < 0 && b_rxlev_change < 0) {
+ /* For handover that reduces RXLEV, favor the highest resulting RXLEV, AFS bias applied. */
+ int a_rxlev = a->target.rxlev + a->target.rxlev_afs_bias;
+ int b_rxlev = b->target.rxlev + b->target.rxlev_afs_bias;
+
+ if (a_rxlev > b_rxlev)
+ return a;
+ if (b_rxlev > a_rxlev)
+ return b;
+ /* There is no target RXLEV difference between the two candidates. Let other factors influence the
+ * choice. */
+ }
+
+ /* Prefer picking a dynamic timeslot: free PDCH and allow more timeslot type flexibility for further
+ * congestion resolution. */
+ if (lchan_is_on_dynamic_ts(b->current.lchan)) {
+ unsigned int ac, bc;
+
+ if (!lchan_is_on_dynamic_ts(a->current.lchan))
+ return b;
+
+ /* Both are dynamic timeslots. Prefer one that completely (or to a higher degree) frees its
+ * timeslot. */
+ ac = ts_usage_count(a->current.lchan->ts);
+ bc = ts_usage_count(b->current.lchan->ts);
+ if (bc < ac)
+ return b;
+ if (ac < bc)
+ return a;
+ /* (If both are dynamic timeslots, favor moving the later dynamic timeslot. That is a vague preference
+ * for later dynamic TS to become PDCH and join up with plain PDCH that follow it -- not actually clear
+ * whether that helps, and depends on user's TS config. No harm done either way.) */
+ ret = b;
+ }
+
+ /* When upgrading TCH/H to TCH/F, favor moving a TCH/H with lower current rxlev, because presumably that
+ * one benefits more from a higher bandwidth. */
+ if (is_upgrade_to_tchf(a, for_requirement) && is_upgrade_to_tchf(b, for_requirement)) {
+ if (b->current.rxlev < a->current.rxlev)
+ return b;
+ if (a->current.rxlev < b->current.rxlev)
+ return a;
+ }
+
+ return ret;
+}
+
+static struct ho_candidate *pick_best_candidate(struct ho_candidate *clist, int clist_len,
+ uint8_t for_requirement)
+{
+ struct ho_candidate *result = NULL;
+ int i;
+
+ for (i = 0; i < clist_len; i++) {
+ struct ho_candidate *c = &clist[i];
+
+ /* For multiple passes of congestion resolution, already handovered candidates are marked by lchan =
+ * NULL. (though at the time of writing, multiple passes of congestion resolution are DISABLED.) */
+ if (!c->current.lchan)
+ continue;
+
+ /* Omit remote BSS */
+ if (!c->target.bts)
+ continue;
+
+ if (!(c->requirements & for_requirement))
+ continue;
+
+ /* improve AHS */
+ if (is_upgrade_to_tchf(c, for_requirement))
+ c->target.rxlev_afs_bias = ho_get_hodec2_afs_bias_rxlev(c->target.bts->ho);
+ else
+ c->target.rxlev_afs_bias = 0;
+
+ result = pick_better_lchan_to_move(result, c, for_requirement);
+ }
+
+ return result;
+}
+
/*
* Handover/assignment check after timer timeout:
*
@@ -1419,13 +1776,9 @@ static int bts_resolve_congestion(struct gsm_bts *bts, int tchf_congestion, int
int i, j;
struct ho_candidate *clist;
unsigned int candidates;
- struct ho_candidate *best_cand = NULL, *worst_cand = NULL;
- struct gsm_lchan *delete_lchan = NULL;
- unsigned int best_avg_db, worst_avg_db;
- int avg;
+ struct ho_candidate *best_cand = NULL;
int rc = 0;
int any_ho = 0;
- int is_improved = 0;
if (tchf_congestion < 0)
tchf_congestion = 0;
@@ -1511,70 +1864,27 @@ static int bts_resolve_congestion(struct gsm_bts *bts, int tchf_congestion, int
LOGPHOCAND(&clist[i], LOGL_DEBUG, "#%d: req={TCH/F:" REQUIREMENTS_FMT ", TCH/H:" REQUIREMENTS_FMT "} avg-rxlev=%d dBm\n",
i, REQUIREMENTS_ARGS(clist[i].requirements, F),
REQUIREMENTS_ARGS(clist[i].requirements, H),
- rxlev2dbm(clist[i].avg));
+ rxlev2dbm(clist[i].target.rxlev));
}
}
#if 0
next_b1:
#endif
- /* select best candidate that fulfills requirement B,
- * omit change from AHS to AFS */
- best_avg_db = 0;
- for (i = 0; i < candidates; i++) {
- /* delete subscriber that just have handovered */
- if (clist[i].lchan == delete_lchan)
- clist[i].lchan = NULL;
- /* omit all subscribers that are handovered */
- if (!clist[i].lchan)
- continue;
-
- /* Do not resolve congestion towards remote BSS, which would cause oscillation if the
- * remote BSS is also congested. */
- /* TODO: attempt inter-BSC HO if no local cells qualify, and rely on the remote BSS to
- * deny receiving the handover if it also considers itself congested. Maybe do that only
- * when the cell is absolutely full, i.e. not only min-free-slots. (x) */
- if (!clist[i].bts)
- continue;
-
- if (!(clist[i].requirements & REQUIREMENT_B_MASK))
- continue;
- /* omit assignment from AHS to AFS */
- if (clist[i].lchan->ts->trx->bts == clist[i].bts
- && clist[i].lchan->type == GSM_LCHAN_TCH_H
- && (clist[i].requirements & REQUIREMENT_B_TCHF))
- continue;
- /* omit candidates that will not solve/reduce congestion */
- if (clist[i].lchan->type == GSM_LCHAN_TCH_F
- && tchf_congestion <= 0)
- continue;
- if (clist[i].lchan->type == GSM_LCHAN_TCH_H
- && tchh_congestion <= 0)
- continue;
-
- avg = clist[i].avg;
- /* improve AHS */
- if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
- && clist[i].lchan->type == GSM_LCHAN_TCH_H
- && (clist[i].requirements & REQUIREMENT_B_TCHF)) {
- avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
- is_improved = 1;
- } else
- is_improved = 0;
- LOGPHOCAND(&clist[i], LOGL_DEBUG, "candidate %d: avg=%d best_avg_db=%d\n",
- i, avg, best_avg_db);
- if (avg > best_avg_db) {
- best_cand = &clist[i];
- best_avg_db = avg;
- }
- }
-
- /* perform handover, if there is a candidate */
+ /* select best candidate that does not cause congestion in the target.
+ * Do not resolve congestion towards remote BSS, which would cause oscillation if the remote BSS is also
+ * congested.
+ * Treating specially below: upgrading TCH/H to TCH/F within the same cell, so omit here.
+ */
+ /* TODO: attempt inter-BSC HO if no local cells qualify, and rely on the remote BSS to
+ * deny receiving the handover if it also considers itself congested. Maybe do that only
+ * when the cell is absolutely full, i.e. not only min-free-slots. (x) */
+ best_cand = pick_best_candidate(clist, candidates, REQUIREMENT_B_MASK);
if (best_cand) {
any_ho = 1;
LOGPHOCAND(best_cand, LOGL_DEBUG, "Best candidate: RX level %d%s\n",
- rxlev2dbm(best_cand->avg),
- is_improved ? " (applied AHS->AFS bias)" : "");
+ rxlev2dbm(best_cand->target.rxlev),
+ best_cand->target.rxlev_afs_bias ? " (applied AHS->AFS bias)" : "");
trigger_ho(best_cand, best_cand->requirements & REQUIREMENT_B_MASK);
#if 0
/* if there is still congestion, mark lchan as deleted
@@ -1598,132 +1908,17 @@ next_b1:
}
#if 0
-next_b2:
-#endif
- /* select worst candidate that fulfills requirement B,
- * select candidates that change from AHS to AFS only */
- if (tchh_congestion > 0) {
- /* since this will only check half rate channels, it will
- * only need to be checked, if tchh is congested */
- worst_avg_db = 999;
- for (i = 0; i < candidates; i++) {
- /* delete subscriber that just have handovered */
- if (clist[i].lchan == delete_lchan)
- clist[i].lchan = NULL;
- /* omit all subscribers that are handovered */
- if (!clist[i].lchan)
- continue;
-
- /* Do not resolve congestion towards remote BSS, which would cause oscillation if
- * the remote BSS is also congested. */
- /* TODO: see (x) above */
- if (!clist[i].bts)
- continue;
-
- if (!(clist[i].requirements & REQUIREMENT_B_MASK))
- continue;
- /* omit all but assignment from AHS to AFS */
- if (clist[i].lchan->ts->trx->bts != clist[i].bts
- || clist[i].lchan->type != GSM_LCHAN_TCH_H
- || !(clist[i].requirements & REQUIREMENT_B_TCHF))
- continue;
-
- avg = clist[i].avg;
- /* improve AHS */
- if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
- && clist[i].lchan->type == GSM_LCHAN_TCH_H) {
- avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
- is_improved = 1;
- } else
- is_improved = 0;
- if (avg < worst_avg_db) {
- worst_cand = &clist[i];
- worst_avg_db = avg;
- }
- }
- }
-
- /* perform handover, if there is a candidate */
- if (worst_cand) {
- any_ho = 1;
- LOGPHOCAND(worst_cand, LOGL_INFO, "Worst candidate: RX level %d from TCH/H -> TCH/F%s\n",
- rxlev2dbm(worst_cand->avg),
- is_improved ? " (applied AHS -> AFS rxlev bias)" : "");
- trigger_ho(worst_cand, worst_cand->requirements & REQUIREMENT_B_MASK);
-#if 0
- /* if there is still congestion, mark lchan as deleted
- * and redo this process */
- tchh_congestion--;
- if (tchh_congestion > 0) {
- delete_lchan = worst_cand->lchan;
- best_cand = NULL;
- goto next_b2;
- }
-#else
- /* must exit here, because triggering handover/assignment
- * will cause change in requirements. more check for this
- * bts is performed in the next iteration.
- */
-#endif
- goto exit;
- }
-
-#if 0
next_c1:
#endif
- /* select best candidate that fulfills requirement C,
- * omit change from AHS to AFS */
- best_avg_db = 0;
- for (i = 0; i < candidates; i++) {
- /* delete subscriber that just have handovered */
- if (clist[i].lchan == delete_lchan)
- clist[i].lchan = NULL;
- /* omit all subscribers that are handovered */
- if (!clist[i].lchan)
- continue;
-
- /* Do not resolve congestion towards remote BSS, which would cause oscillation if
- * the remote BSS is also congested. */
- /* TODO: see (x) above */
- if (!clist[i].bts)
- continue;
-
- if (!(clist[i].requirements & REQUIREMENT_C_MASK))
- continue;
- /* omit assignment from AHS to AFS */
- if (clist[i].lchan->ts->trx->bts == clist[i].bts
- && clist[i].lchan->type == GSM_LCHAN_TCH_H
- && (clist[i].requirements & REQUIREMENT_C_TCHF))
- continue;
- /* omit candidates that will not solve/reduce congestion */
- if (clist[i].lchan->type == GSM_LCHAN_TCH_F
- && tchf_congestion <= 0)
- continue;
- if (clist[i].lchan->type == GSM_LCHAN_TCH_H
- && tchh_congestion <= 0)
- continue;
-
- avg = clist[i].avg;
- /* improve AHS */
- if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
- && clist[i].lchan->type == GSM_LCHAN_TCH_H
- && (clist[i].requirements & REQUIREMENT_C_TCHF)) {
- avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
- is_improved = 1;
- } else
- is_improved = 0;
- if (avg > best_avg_db) {
- best_cand = &clist[i];
- best_avg_db = avg;
- }
- }
-
- /* perform handover, if there is a candidate */
+ /* Select best candidate that balances congestion.
+ * Again no remote BSS.
+ * Again no TCH/H -> F upgrades within the same cell. */
+ best_cand = pick_best_candidate(clist, candidates, REQUIREMENT_C_MASK);
if (best_cand) {
any_ho = 1;
LOGPHOCAND(best_cand, LOGL_INFO, "Best candidate: RX level %d%s\n",
- rxlev2dbm(best_cand->avg),
- is_improved ? " (applied AHS -> AFS rxlev bias)" : "");
+ rxlev2dbm(best_cand->target.rxlev),
+ best_cand->target.rxlev_afs_bias ? " (applied AHS -> AFS rxlev bias)" : "");
trigger_ho(best_cand, best_cand->requirements & REQUIREMENT_C_MASK);
#if 0
/* if there is still congestion, mark lchan as deleted
@@ -1746,84 +1941,7 @@ next_c1:
goto exit;
}
- LOGPHOBTS(bts, LOGL_DEBUG, "Did not find a best candidate that fulfills requirement C"
- " (omitting change from AHS to AFS)\n");
-
-#if 0
-next_c2:
-#endif
- /* select worst candidate that fulfills requirement C,
- * select candidates that change from AHS to AFS only */
- if (tchh_congestion > 0) {
- /* since this will only check half rate channels, it will
- * only need to be checked, if tchh is congested */
- worst_avg_db = 999;
- for (i = 0; i < candidates; i++) {
- /* delete subscriber that just have handovered */
- if (clist[i].lchan == delete_lchan)
- clist[i].lchan = NULL;
- /* omit all subscribers that are handovered */
- if (!clist[i].lchan)
- continue;
-
- /* Do not resolve congestion towards remote BSS, which would cause oscillation if
- * the remote BSS is also congested. */
- /* TODO: see (x) above */
- if (!clist[i].bts)
- continue;
-
- if (!(clist[i].requirements & REQUIREMENT_C_MASK))
- continue;
- /* omit all but assignment from AHS to AFS */
- if (clist[i].lchan->ts->trx->bts != clist[i].bts
- || clist[i].lchan->type != GSM_LCHAN_TCH_H
- || !(clist[i].requirements & REQUIREMENT_C_TCHF))
- continue;
-
- avg = clist[i].avg;
- /* improve AHS */
- if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
- && clist[i].lchan->type == GSM_LCHAN_TCH_H) {
- avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho);
- is_improved = 1;
- } else
- is_improved = 0;
- LOGP(DHODEC, LOGL_DEBUG, "candidate %d: avg=%d worst_avg_db=%d\n", i, avg,
- worst_avg_db);
- if (avg < worst_avg_db) {
- worst_cand = &clist[i];
- worst_avg_db = avg;
- }
- }
- }
-
- /* perform handover, if there is a candidate */
- if (worst_cand) {
- any_ho = 1;
- LOGPHOCAND(worst_cand, LOGL_INFO, "Worst candidate: RX level %d from TCH/H -> TCH/F%s\n",
- rxlev2dbm(worst_cand->avg),
- is_improved ? " (applied AHS -> AFS rxlev bias)" : "");
- trigger_ho(worst_cand, worst_cand->requirements & REQUIREMENT_C_MASK);
-#if 0
- /* if there is still congestion, mark lchan as deleted
- * and redo this process */
- tchh_congestion--;
- if (tchh_congestion > 0) {
- delete_lchan = worst_cand->lchan;
- worst_cand = NULL;
- goto next_c2;
- }
-#else
- /* must exit here, because triggering handover/assignment
- * will cause change in requirements. more check for this
- * bts is performed in the next iteration.
- */
-#endif
- goto exit;
- }
- LOGPHOBTS(bts, LOGL_DEBUG, "Did not find a worst candidate that fulfills requirement C,"
- " selecting candidates that change from AHS to AFS only\n");
-
+ LOGPHOBTS(bts, LOGL_DEBUG, "Did not find a best candidate that fulfills requirement C\n");
exit:
/* free array */
@@ -1843,8 +1961,9 @@ exit:
static void bts_congestion_check(struct gsm_bts *bts)
{
+ struct chan_counts *bts_counts;
int min_free_tchf, min_free_tchh;
- int tchf_count, tchh_count;
+ int free_tchf, free_tchh;
global_ho_reason = HO_REASON_CONGESTION;
@@ -1870,19 +1989,20 @@ static void bts_congestion_check(struct gsm_bts *bts)
return;
}
- tchf_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_F);
- tchh_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_H);
+ bts_counts = &bts->chan_counts;
+ free_tchf = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ free_tchh = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
LOGPHOBTS(bts, LOGL_INFO, "Congestion check: (free/want-free) TCH/F=%d/%d TCH/H=%d/%d\n",
- tchf_count, min_free_tchf, tchh_count, min_free_tchh);
+ free_tchf, min_free_tchf, free_tchh, min_free_tchh);
/* only check BTS if congested */
- if (tchf_count >= min_free_tchf && tchh_count >= min_free_tchh) {
+ if (free_tchf >= min_free_tchf && free_tchh >= min_free_tchh) {
LOGPHOBTS(bts, LOGL_DEBUG, "Not congested\n");
return;
}
LOGPHOBTS(bts, LOGL_DEBUG, "Attempting to resolve congestion...\n");
- bts_resolve_congestion(bts, min_free_tchf - tchf_count, min_free_tchh - tchh_count);
+ bts_resolve_congestion(bts, min_free_tchf - free_tchf, min_free_tchh - free_tchh);
}
void hodec2_congestion_check(struct gsm_network *net)
@@ -1903,7 +2023,6 @@ static void congestion_check_cb(void *arg)
static void on_handover_end(struct gsm_subscriber_connection *conn, enum handover_result result)
{
struct gsm_bts *old_bts = NULL;
- struct gsm_bts *new_bts = NULL;
int penalty;
struct handover *ho = &conn->ho;
@@ -1913,8 +2032,6 @@ static void on_handover_end(struct gsm_subscriber_connection *conn, enum handove
if (conn->lchan)
old_bts = conn->lchan->ts->trx->bts;
- if (ho->new_lchan)
- new_bts = ho->new_lchan->ts->trx->bts;
/* Only interested in handovers within this BSS or going out into another BSS. Incoming handovers
* from another BSS are accounted for in the other BSS. */
@@ -1941,11 +2058,7 @@ static void on_handover_end(struct gsm_subscriber_connection *conn, enum handove
LOG_HO(conn, LOGL_NOTICE, "Failed, starting penalty timer (%d s)\n", penalty);
conn->hodec2.failures = 0;
-
- if (new_bts)
- bts_penalty_time_add(conn, new_bts, penalty);
- else
- nik_penalty_time_add(conn, &ho->target_cell, penalty);
+ penalty_timers_add_list(conn, &conn->hodec2.penalty_timers, &ho->target_cell_ids, penalty);
}
static struct handover_decision_callbacks hodec2_callbacks = {
diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c
index 573f249a1..e8581c587 100644
--- a/src/osmo-bsc/handover_fsm.c
+++ b/src/osmo-bsc/handover_fsm.c
@@ -45,6 +45,8 @@
#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lcs_loc_req.h>
+#include <osmocom/bsc/bsc_stats.h>
+#include <osmocom/bsc/lchan.h>
#define LOG_FMT_BTS "bts %u lac-ci %u-%u arfcn-bsic %d-%d"
#define LOG_ARGS_BTS(bts) \
@@ -59,9 +61,9 @@
lchan ? lchan->ts->trx->bts->nr : 0, \
lchan ? lchan->ts->trx->nr : 0, \
lchan ? lchan->ts->nr : 0, \
- lchan ? gsm_lchant_name(lchan->type) : "?", \
+ lchan ? gsm_chan_t_name(lchan->type) : "?", \
lchan ? lchan->nr : 0, \
- lchan ? gsm48_chan_mode_name(lchan->tch_mode) : "?"
+ lchan ? gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode) : "?"
#define LOG_FMT_TO_LCHAN "%u-%u-%u-%s%s%s-%u"
#define LOG_ARGS_TO_LCHAN(lchan) \
@@ -88,8 +90,8 @@
LOG_HO(conn, LOGL_DEBUG, "(BSC) incrementing rate counter: %s %s\n", \
bsc_ctr_description[counter].name, \
bsc_ctr_description[counter].description); \
- rate_ctr_inc(&conn->network->bsc_ctrs->ctr[counter]); \
- } while(0)
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->network->bsc_ctrs, counter)); \
+ } while (0)
/* Assume presence of local var 'conn' as struct gsm_subscriber_connection.
* Handles bts == NULL gracefully
@@ -102,15 +104,18 @@
bts_ctr_description[counter].name, \
bts_ctr_description[counter].description); \
if (bts) \
- rate_ctr_inc(&bts->bts_ctrs->ctr[counter]); \
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, counter)); \
else \
- rate_ctr_inc(&conn->network->bts_unknown_ctrs->ctr[counter]); \
- } while(0)
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->network->bts_unknown_ctrs, counter)); \
+ } while (0)
+/* Count handover result on both bts and bsc level.
+ * Call with 'counter' being the counter name without the "BSC_"/"BTS_" part,
+ * e.g. ho_count(conn_get_bts(conn), CTR_HANDOVER_ATTEMPTED); */
#define ho_count(bts, counter) do { \
- ho_count_bsc(BSC_##counter); \
- ho_count_bts(bts, BTS_##counter); \
-} while(0)
+ ho_count_bsc(BSC_##counter); \
+ ho_count_bts(bts, BTS_##counter); \
+ } while (0)
static uint8_t g_next_ho_ref = 1;
@@ -134,7 +139,7 @@ const char *handover_status(struct gsm_subscriber_connection *conn)
"("LOG_FMT_FROM_LCHAN") --HO-> ("LOG_FMT_BTS",%s) " LOG_FMT_HO_SCOPE,
LOG_ARGS_FROM_LCHAN(conn->lchan),
LOG_ARGS_BTS(ho->new_bts),
- gsm_lchant_name(ho->new_lchan_type),
+ gsm_chan_t_name(ho->new_lchan_type),
LOG_ARGS_HO_SCOPE(conn));
else
snprintf(buf, sizeof(buf),
@@ -146,7 +151,7 @@ const char *handover_status(struct gsm_subscriber_connection *conn)
snprintf(buf, sizeof(buf),
"("LOG_FMT_FROM_LCHAN") --HO-> (%s) " LOG_FMT_HO_SCOPE,
LOG_ARGS_FROM_LCHAN(conn->lchan),
- neighbor_ident_key_name(&ho->target_cell),
+ cell_ab_to_str_c(OTC_SELECT, &ho->target_cell_ab),
LOG_ARGS_HO_SCOPE(conn));
else if (ho->scope & HO_INTER_BSC_IN) {
@@ -163,14 +168,14 @@ const char *handover_status(struct gsm_subscriber_connection *conn)
ho->inter_bsc_in.cell_id_serving_name,
ho->inter_bsc_in.cell_id_target_name,
LOG_ARGS_BTS(ho->new_bts),
- gsm_lchant_name(ho->new_lchan_type),
+ gsm_chan_t_name(ho->new_lchan_type),
LOG_ARGS_HO_SCOPE(conn));
else
snprintf(buf, sizeof(buf),
"(remote:%s) --HO-> (local:%s,%s) " LOG_FMT_HO_SCOPE,
ho->inter_bsc_in.cell_id_serving_name,
ho->inter_bsc_in.cell_id_target_name,
- gsm_lchant_name(ho->new_lchan_type),
+ gsm_chan_t_name(ho->new_lchan_type),
LOG_ARGS_HO_SCOPE(conn));
} else
snprintf(buf, sizeof(buf), LOG_FMT_HO_SCOPE, LOG_ARGS_HO_SCOPE(conn));
@@ -188,11 +193,11 @@ struct gsm_subscriber_connection *ho_fi_conn(struct osmo_fsm_inst *fi)
}
static const struct osmo_tdef_state_timeout ho_fsm_timeouts[32] = {
- [HO_ST_WAIT_LCHAN_ACTIVE] = { .T = 23042 },
- [HO_ST_WAIT_MGW_ENDPOINT_TO_MSC] = { .T = 23042 },
- [HO_ST_WAIT_RR_HO_DETECT] = { .T = 23042 },
- [HO_ST_WAIT_RR_HO_COMPLETE] = { .T = 23042 },
- [HO_ST_WAIT_LCHAN_ESTABLISHED] = { .T = 23042 },
+ [HO_ST_WAIT_LCHAN_ACTIVE] = { /* Guarded by X5 + X6 in lchan_fsm_timeouts */ },
+ [HO_ST_WAIT_MGW_ENDPOINT_TO_MSC] = { .T = -9 },
+ [HO_ST_WAIT_RR_HO_DETECT] = { .T = 3103 },
+ [HO_ST_WAIT_RR_HO_COMPLETE] = { .keep_timer = true }, /* Keep T3103 */
+ [HO_ST_WAIT_LCHAN_ESTABLISHED] = { /* Guarded by T3101 in lchan_fsm_timeouts */ },
[HO_OUT_ST_WAIT_HO_COMMAND] = { .T = 7 },
[HO_OUT_ST_WAIT_CLEAR] = { .T = 8 },
};
@@ -211,15 +216,15 @@ static const struct osmo_tdef_state_timeout ho_fsm_timeouts[32] = {
LOG_HO(conn, LOGL_ERROR, "Handover failed in state %s, %s: " fmt "\n", \
osmo_fsm_inst_state_name(conn->fi), handover_result_name(result), ## args); \
handover_end(conn, result); \
- } while(0)
+ } while (0)
#define ho_success() do { \
LOG_HO(conn, LOGL_DEBUG, "Handover succeeded\n"); \
handover_end(conn, HO_RESULT_OK); \
- } while(0)
+ } while (0)
/* issue handover to a cell identified by ARFCN and BSIC */
-void handover_request(struct handover_out_req *req)
+int handover_request(struct handover_out_req *req)
{
struct gsm_subscriber_connection *conn;
OSMO_ASSERT(req->old_lchan);
@@ -227,12 +232,9 @@ void handover_request(struct handover_out_req *req)
conn = req->old_lchan->conn;
OSMO_ASSERT(conn && conn->fi);
- /* Make sure the handover target neighbor_ident_key contains the correct source bts nr */
- req->target_nik.from_bts = req->old_lchan->ts->trx->bts->nr;
-
/* To make sure we're allowed to start a handover, go through a gscon event dispatch. If that is accepted, the
* same req is passed to handover_start(). */
- osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_HANDOVER_START, req);
+ return osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_HANDOVER_START, req);
}
/* Check that ho has old_lchan and/or new_lchan and conn pointers match.
@@ -275,7 +277,8 @@ static void handover_reset(struct gsm_subscriber_connection *conn)
struct osmo_mgcpc_ep_ci *ci;
if (conn->ho.new_lchan)
/* New lchan was activated but never passed to a conn */
- lchan_release(conn->ho.new_lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
+ lchan_release(conn->ho.new_lchan, false, true, RSL_ERR_EQUIPMENT_FAIL,
+ NULL);
ci = conn->ho.created_ci_for_msc;
if (ci) {
@@ -290,7 +293,7 @@ static void handover_reset(struct gsm_subscriber_connection *conn)
};
}
-void handover_fsm_init()
+static __attribute__((constructor)) void handover_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&ho_fsm) == 0);
}
@@ -315,10 +318,10 @@ void handover_start(struct handover_out_req *req)
OSMO_ASSERT(req && req->old_lchan && req->old_lchan->conn);
struct gsm_subscriber_connection *conn = req->old_lchan->conn;
- const struct neighbor_ident_key *search_for = &req->target_nik;
+ const struct cell_ab *search_for = &req->target_cell_ab;
struct handover *ho = &conn->ho;
struct gsm_bts *local_target_cell = NULL;
- const struct gsm0808_cell_id_list2 *remote_target_cell = NULL;
+ struct gsm0808_cell_id_list2 remote_target_cells = {};
if (conn->ho.fi) {
LOG_HO(conn, LOGL_ERROR, "Handover requested while another handover is ongoing; Ignore\n");
@@ -335,9 +338,9 @@ void handover_start(struct handover_out_req *req)
ho->from_hodec_id = req->from_hodec_id;
ho->new_lchan_type = req->new_lchan_type == GSM_LCHAN_NONE ?
req->old_lchan->type : req->new_lchan_type;
- ho->target_cell = req->target_nik;
+ ho->target_cell_ab = req->target_cell_ab;
- if (find_handover_target_cell(&local_target_cell, &remote_target_cell,
+ if (find_handover_target_cell(&local_target_cell, &remote_target_cells,
conn, search_for, true)) {
handover_end(conn, HO_RESULT_ERROR);
return;
@@ -349,8 +352,8 @@ void handover_start(struct handover_out_req *req)
return;
}
- if (remote_target_cell) {
- handover_start_inter_bsc_out(conn, remote_target_cell);
+ if (remote_target_cells.id_list_len) {
+ handover_start_inter_bsc_out(conn, &remote_target_cells);
return;
}
@@ -375,8 +378,12 @@ static void handover_start_intra_bsc(struct gsm_subscriber_connection *conn)
ho->scope = (ho->new_bts == bts) ? HO_INTRA_CELL : HO_INTRA_BSC;
ho->ho_ref = g_next_ho_ref++;
ho->async = true;
+ gsm_bts_cell_id_list(&ho->target_cell_ids, ho->new_bts);
- ho->new_lchan = lchan_select_by_type(ho->new_bts, ho->new_lchan_type);
+ ho->new_lchan = lchan_select_by_type(ho->new_bts,
+ ho->new_lchan_type,
+ SELECT_FOR_HANDOVER,
+ NULL);
if (ho->scope & HO_INTRA_CELL) {
ho_count(bts, CTR_INTRA_CELL_HO_ATTEMPTED);
@@ -384,12 +391,13 @@ static void handover_start_intra_bsc(struct gsm_subscriber_connection *conn)
} else {
ho_count(bts, CTR_INTRA_BSC_HO_ATTEMPTED);
ho_fsm_update_id(fi, "intraBSC");
+ ho_count_bts(ho->new_bts, BTS_CTR_INCOMING_INTRA_BSC_HO_ATTEMPTED);
}
if (!ho->new_lchan) {
ho_fail(HO_RESULT_FAIL_NO_CHANNEL,
"No %s lchan available on BTS %u",
- gsm_lchant_name(ho->new_lchan_type), ho->new_bts->nr);
+ gsm_chan_t_name(ho->new_lchan_type), ho->new_bts->nr);
return;
}
LOG_HO(conn, LOGL_DEBUG, "Selected lchan %s\n", gsm_lchan_name(ho->new_lchan));
@@ -397,20 +405,53 @@ static void handover_start_intra_bsc(struct gsm_subscriber_connection *conn)
ho_fsm_state_chg(HO_ST_WAIT_LCHAN_ACTIVE);
info = (struct lchan_activate_info){
- .activ_for = FOR_HANDOVER,
+ .activ_for = ACTIVATE_FOR_HANDOVER,
.for_conn = conn,
- .chan_mode = conn->lchan->tch_mode,
+ .ch_mode_rate = conn->lchan->current_ch_mode_rate,
.encr = conn->lchan->encr,
.requires_voice_stream = conn->lchan->mgw_endpoint_ci_bts ? true : false,
.msc_assigned_cic = conn->ho.inter_bsc_in.msc_assigned_cic,
.re_use_mgw_endpoint_from_lchan = conn->lchan,
.wait_before_switching_rtp = true,
- .s15_s0 = conn->lchan->activate.info.s15_s0,
};
+ info.ch_mode_rate.chan_rate = chan_t_to_chan_rate(ho->new_lchan->type);
+
+ /* For intra-cell handover, we know the accurate Timing Advance from the previous lchan. For inter-cell
+ * handover, no Timing Advance for the new cell is known, so leave it unset. */
+ if (ho->new_bts == bts) {
+ info.ta = conn->lchan->last_ta;
+ info.ta_known = true;
+ }
lchan_activate(ho->new_lchan, &info);
}
+/* 3GPP TS 48.008 § 3.2.2.58 Old BSS to New BSS Information */
+static void parse_old2new_bss_info(struct gsm_subscriber_connection *conn,
+ const uint8_t* data, uint16_t len,
+ struct handover_in_req *req)
+{
+ /* § 3.2.2.58: Information contained here shall take precedence over
+ duplicate information from Information Elements in the HANDOVER
+ REQUEST as long as the coding is understood by the new BSS */
+ /* § 3.2.2.58: <<Reception of an erroneous "Old BSS to New BSS
+ information" shall not cause a rejection of the HANDOVER REQUEST
+ message; the "Old BSS to New BSS information" information element
+ shall be discarded and the handover resource allocation procedure
+ shall continue>>. See also 3.1.19.7. */
+ struct tlv_parsed tp;
+ if (tlv_parse(&tp, &gsm0808_old_bss_to_new_bss_info_att_tlvdef, data, len, 0, 0) <= 0) {
+ LOG_HO(conn, LOGL_NOTICE, "Failed to parse IE \"Old BSS to New BSS information\"\n");
+ return;
+ }
+
+ if (TLVP_VAL(&tp, GSM0808_FE_IE_LAST_USED_EUTRAN_PLMN_ID)) {
+ req->last_eutran_plmn_valid = true;
+ osmo_plmn_from_bcd(TLVP_VAL(&tp, GSM0808_FE_IE_LAST_USED_EUTRAN_PLMN_ID),
+ &req->last_eutran_plmn);
+ }
+}
+
/* 3GPP TS 48.008 § 3.2.1.8 Handover Request */
static bool parse_ho_request(struct gsm_subscriber_connection *conn, const struct msgb *msg,
struct handover_in_req *req)
@@ -422,6 +463,7 @@ static bool parse_ho_request(struct gsm_subscriber_connection *conn, const struc
int payload_length;
bool aoip = gscon_is_aoip(conn);
bool sccplite = gscon_is_sccplite(conn);
+ bool has_a54 = false;
if ((aoip && sccplite) || !(aoip || sccplite)) {
LOG_HO(conn, LOGL_ERROR, "Received BSSMAP Handover Request, but conn is not"
@@ -452,6 +494,16 @@ static bool parse_ho_request(struct gsm_subscriber_connection *conn, const struc
LOG_HO(conn, LOGL_ERROR, "Failed to parse Encryption Information IE\n");
return false;
}
+ req->ei_as_bitmask = *e->val;
+
+ if ((e = TLVP_GET(tp, GSM0808_IE_KC_128))) {
+ if (e->len != 16) {
+ LOG_HO(conn, LOGL_ERROR, "Invalid length in Kc128 IE: %u bytes (expected 16)\n", e->len);
+ return false;
+ }
+ memcpy(req->kc128, e->val, 16);
+ req->kc128_present = true;
+ }
if ((e = TLVP_GET(tp, GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1))) {
if (e->len != sizeof(req->classmark.classmark1)) {
@@ -481,9 +533,10 @@ static bool parse_ho_request(struct gsm_subscriber_connection *conn, const struc
req->chosen_encr_alg);
}
- LOG_HO(conn, LOGL_DEBUG, "Handover Request encryption info: chosen=A5/%u key=%s\n",
- (req->chosen_encr_alg ? : 1) - 1, req->ei.key_len?
- osmo_hexdump_nospc(req->ei.key, req->ei.key_len) : "none");
+ LOG_HO(conn, LOGL_DEBUG, "Handover Request encryption info: chosen=A5/%u key=%s kc128=%s\n",
+ (req->chosen_encr_alg ? : 1) - 1,
+ req->ei.key_len ? osmo_hexdump_nospc(req->ei.key, req->ei.key_len) : "none",
+ has_a54 ? osmo_hexdump_nospc(req->kc128, 16) : "none");
if (TLVP_PRESENT(tp, GSM0808_IE_AOIP_TRASP_ADDR)) {
int rc;
@@ -551,6 +604,27 @@ static bool parse_ho_request(struct gsm_subscriber_connection *conn, const struc
return false;
}
+ if ((e = TLVP_GET(tp, GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION))) {
+ parse_old2new_bss_info(conn, e->val, e->len, req);
+ }
+
+ /* Decode "Codec List (MSC Preferred)". First set len = 0 to empty the list. (For inter-BSC incoming handover,
+ * there can't possibly be a list here already, because the conn has just now been created; just do ensure
+ * sanity.) */
+ conn->codec_list = (struct gsm0808_speech_codec_list){};
+ if ((e = TLVP_GET(tp, GSM0808_IE_SPEECH_CODEC_LIST))) {
+ if (gsm0808_dec_speech_codec_list(&conn->codec_list, e->val, e->len) < 0) {
+ LOG_HO(conn, LOGL_ERROR, "incoming inter-BSC Handover: HO Request:"
+ " Unable to decode Codec List (MSC Preferred)\n");
+ return false;
+ }
+ }
+ if (aoip && !conn->codec_list.len) {
+ LOG_HO(conn, LOGL_ERROR, "incoming inter-BSC Handover: HO Request:"
+ " Invalid or empty Codec List (MSC Preferred)\n");
+ return false;
+ }
+
/* A lot of IEs remain ignored... */
return true;
@@ -558,7 +632,7 @@ static bool parse_ho_request(struct gsm_subscriber_connection *conn, const struc
static bool chan_mode_is_tch(enum gsm48_chan_mode mode)
{
- switch (mode) {
+ switch (gsm48_chan_mode_to_non_vamos(mode)) {
case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_EFR:
case GSM48_CMODE_SPEECH_AMR:
@@ -578,6 +652,7 @@ void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
int match_idx;
struct osmo_fsm_inst *fi;
struct channel_mode_and_rate ch_mode_rate = {};
+ int chosen_a5_n;
handover_fsm_alloc(conn);
@@ -593,7 +668,7 @@ void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
ho_fsm_update_id(fi, "interBSCin");
if (!parse_ho_request(conn, ho_request_msg, req)) {
- ho_fail(HO_RESULT_ERROR, "Invalid Handover Request message from MSC\n");
+ ho_fail(HO_RESULT_ERROR, "Invalid Handover Request message from MSC");
return;
}
@@ -625,7 +700,10 @@ void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
ch_mode_rate.chan_rate == CH_RATE_FULL ? "full-rate" : "half-rate",
gsm0808_channel_type_name(&req->ct));
- lchan = lchan_select_by_chan_mode(bts, ch_mode_rate.chan_mode, ch_mode_rate.chan_rate);
+ lchan = lchan_select_by_chan_mode(bts,
+ ch_mode_rate.chan_mode,
+ ch_mode_rate.chan_rate,
+ SELECT_FOR_HANDOVER, NULL);
if (!lchan) {
LOG_HO(conn, LOGL_DEBUG, "BTS %u has no matching free channels\n", bts->nr);
continue;
@@ -658,26 +736,37 @@ void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
ho_fsm_state_chg(HO_ST_WAIT_LCHAN_ACTIVE);
info = (struct lchan_activate_info){
- .activ_for = FOR_HANDOVER,
+ .activ_for = ACTIVATE_FOR_HANDOVER,
.for_conn = conn,
- .chan_mode = ch_mode_rate.chan_mode,
- .s15_s0 = ch_mode_rate.s15_s0,
+ .ch_mode_rate = ch_mode_rate,
.requires_voice_stream = chan_mode_is_tch(ch_mode_rate.chan_mode),
.msc_assigned_cic = req->msc_assigned_cic,
};
- if (req->chosen_encr_alg) {
- info.encr.alg_id = req->chosen_encr_alg;
- if (info.encr.alg_id > 1 && !req->ei.key_len) {
- ho_fail(HO_RESULT_ERROR, "Chosen Encryption Algorithm (Serving) reflects A5/%u"
- " but there is no key (Encryption Information)", info.encr.alg_id - 1);
+ /* Figure out the encryption algorithm */
+ chosen_a5_n = select_best_cipher(req->ei_as_bitmask, bsc_gsmnet->a5_encryption_mask);
+ if (chosen_a5_n < 0) {
+ ho_fail(HO_RESULT_FAIL_RR_HO_FAIL,
+ "There is no A5 encryption mode that both BSC and MSC permit: MSC 0x%x & BSC 0x%x = 0",
+ req->ei_as_bitmask, bsc_gsmnet->a5_encryption_mask);
+ return;
+ }
+ if (chosen_a5_n > 0 && !req->ei.key_len) {
+ /* There is no key. Is A5/0 permitted? */
+ if ((req->ei_as_bitmask & bsc_gsmnet->a5_encryption_mask & 0x1) == 0x1) {
+ chosen_a5_n = 0;
+ } else {
+ ho_fail(HO_RESULT_ERROR,
+ "Encryption is required, but there is no key (Encryption Information)");
return;
}
}
- if (req->ei.key_len) {
+ /* Put encryption info in the chan activation info */
+ info.encr.alg_a5_n = chosen_a5_n;
+ if (chosen_a5_n > 0) {
if (req->ei.key_len > sizeof(info.encr.key)) {
- ho_fail(HO_RESULT_ERROR, "Encryption Information IE key length is too large: %u\n",
+ ho_fail(HO_RESULT_ERROR, "Encryption Information IE key length is too large: %u",
req->ei.key_len);
return;
}
@@ -685,9 +774,39 @@ void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
info.encr.key_len = req->ei.key_len;
}
+ if (req->kc128_present) {
+ memcpy(info.encr.kc128, req->kc128, 16);
+ info.encr.kc128_present = true;
+ }
+
+ if (req->last_eutran_plmn_valid) {
+ conn->fast_return.allowed = ho->new_bts->srvcc_fast_return_allowed;
+ conn->fast_return.last_eutran_plmn_valid = true;
+ memcpy(&conn->fast_return.last_eutran_plmn, &req->last_eutran_plmn,
+ sizeof(conn->fast_return.last_eutran_plmn));
+ ho_count(ho->new_bts, CTR_SRVCC_ATTEMPTED);
+ }
+
lchan_activate(ho->new_lchan, &info);
}
+/* Create functions result_counter_{BSC,BTS}_{HANDOVER,...}(), to evaluate the handover result and return
+ * BSC_CTR_HANDOVER_ATTEMPTED,
+ * BSC_CTR_HANDOVER_COMPLETED,
+ * BSC_CTR_HANDOVER_STOPPED,
+ * BSC_CTR_HANDOVER_NO_CHANNEL,
+ * BSC_CTR_HANDOVER_TIMEOUT,
+ * BSC_CTR_HANDOVER_FAILED,
+ * BSC_CTR_HANDOVER_ERROR,
+ * or
+ * BTS_CTR_HANDOVER_ATTEMPTED,
+ * BTS_CTR_HANDOVER_COMPLETED,
+ * BTS_CTR_HANDOVER_STOPPED,
+ * BTS_CTR_HANDOVER_NO_CHANNEL,
+ * BTS_CTR_HANDOVER_TIMEOUT,
+ * BTS_CTR_HANDOVER_FAILED,
+ * BTS_CTR_HANDOVER_ERROR,
+ */
#define FUNC_RESULT_COUNTER(obj, name) \
static int result_counter_##obj##_##name(enum handover_result result) \
{ \
@@ -713,6 +832,7 @@ FUNC_RESULT_COUNTER(BSC, INTRA_CELL_HO)
FUNC_RESULT_COUNTER(BSC, INTRA_BSC_HO)
FUNC_RESULT_COUNTER(BSC, INTER_BSC_HO_IN)
+/* INTRA_BSC_HO_OUT does not have a NO_CHANNEL result, so can't do this with FUNC_RESULT_COUNTER() macro. */
static int result_counter_BSC_INTER_BSC_HO_OUT(enum handover_result result) {
switch (result) {
case HO_RESULT_OK:
@@ -748,8 +868,10 @@ static int result_counter_bsc(enum handover_scope scope, enum handover_result re
FUNC_RESULT_COUNTER(BTS, HANDOVER)
FUNC_RESULT_COUNTER(BTS, INTRA_CELL_HO)
FUNC_RESULT_COUNTER(BTS, INTRA_BSC_HO)
+FUNC_RESULT_COUNTER(BTS, INCOMING_INTRA_BSC_HO)
FUNC_RESULT_COUNTER(BTS, INTER_BSC_HO_IN)
+/* INTRA_BSC_HO_OUT does not have a NO_CHANNEL result, so can't do this with FUNC_RESULT_COUNTER() macro. */
static int result_counter_BTS_INTER_BSC_HO_OUT(enum handover_result result) {
switch (result) {
case HO_RESULT_OK:
@@ -782,6 +904,9 @@ static int result_counter_bts(enum handover_scope scope, enum handover_result re
}
}
+FUNC_RESULT_COUNTER(BSC, SRVCC)
+FUNC_RESULT_COUNTER(BTS, SRVCC)
+
static void send_handover_performed(struct gsm_subscriber_connection *conn)
{
struct gsm_lchan *lchan = conn->lchan;
@@ -807,7 +932,7 @@ static void send_handover_performed(struct gsm_subscriber_connection *conn)
};
/* Chosen Channel 3.2.2.33 */
- ho_perf_params.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode);
+ ho_perf_params.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode);
if (!ho_perf_params.chosen_channel) {
LOG_HO(conn, LOGL_ERROR, "Failed to generate Chosen Channel IE, can't send HANDOVER PERFORMED!\n");
return;
@@ -815,19 +940,20 @@ static void send_handover_performed(struct gsm_subscriber_connection *conn)
ho_perf_params.chosen_channel_present = true;
/* Chosen Encryption Algorithm 3.2.2.44 */
- ho_perf_params.chosen_encr_alg = lchan->encr.alg_id;
+ ho_perf_params.chosen_encr_alg = ALG_A5_NR_TO_BSSAP(lchan->encr.alg_a5_n);
ho_perf_params.chosen_encr_alg_present = true;
if (ho->new_lchan->activate.info.requires_voice_stream) {
/* Speech Version (chosen) 3.2.2.51 */
- ho_perf_params.speech_version_chosen = gsm0808_permitted_speech(lchan->type, lchan->tch_mode);
+ ho_perf_params.speech_version_chosen = gsm0808_permitted_speech(lchan->type,
+ lchan->current_ch_mode_rate.chan_mode);
ho_perf_params.speech_version_chosen_present = true;
/* Speech Codec (chosen) 3.2.2.104 */
if (gscon_is_aoip(conn)) {
/* Extrapolate speech codec from speech mode */
gsm0808_speech_codec_from_chan_type(&sc, ho_perf_params.speech_version_chosen);
- sc.cfg = conn->lchan->ch_mode_rate.s15_s0;
+ sc.cfg = conn->lchan->current_ch_mode_rate.s15_s0;
memcpy(&ho_perf_params.speech_codec_chosen, &sc, sizeof(sc));
ho_perf_params.speech_codec_chosen_present = true;
}
@@ -839,7 +965,7 @@ static void send_handover_performed(struct gsm_subscriber_connection *conn)
return;
}
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_HANDOVER_PERFORMED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_HANDOVER_PERFORMED));
rc = gscon_sigtran_send(conn, msg);
if (rc < 0) {
LOG_HO(conn, LOGL_ERROR, "message sending failed, can't send HANDOVER PERFORMED!\n");
@@ -877,7 +1003,7 @@ void handover_end(struct gsm_subscriber_connection *conn, enum handover_result r
result = bsc_tx_bssmap_ho_complete(conn, ho->new_lchan);
}
/* Not 'else': above checks may still result in HO_RESULT_ERROR. */
- if (result == HO_RESULT_ERROR) {
+ if (result != HO_RESULT_OK) {
/* Return a BSSMAP Handover Failure, as described in 3GPP TS 48.008 3.1.5.2.2
* "Handover Resource Allocation Failure" */
bsc_tx_bssmap_ho_failure(conn);
@@ -903,7 +1029,8 @@ void handover_end(struct gsm_subscriber_connection *conn, enum handover_result r
/* 3GPP TS 48.008 3.1.5.3.3 "Abnormal Conditions": if neither MS reports
* HO Failure nor the MSC sends a Clear Command, we should release the
* dedicated radio resources and send a Clear Request to the MSC. */
- lchan_release(conn->lchan, true, true, GSM48_RR_CAUSE_ABNORMAL_TIMER);
+ lchan_release(conn->lchan, true, true, GSM48_RR_CAUSE_ABNORMAL_TIMER,
+ gscon_last_eutran_plmn(conn));
/* Once the channel release is through, the BSSMAP Clear will follow. */
break;
}
@@ -933,13 +1060,30 @@ void handover_end(struct gsm_subscriber_connection *conn, enum handover_result r
ho_count_bsc(result_counter_bsc(ho->scope, result));
ho_count_bts(bts, result_counter_BTS_HANDOVER(result));
ho_count_bts(bts, result_counter_bts(ho->scope, result));
+ /* For inter-cell HO, also increment the "INCOMING" counters on the target BTS. */
+ if (ho->scope & HO_INTRA_BSC)
+ ho_count_bts(ho->new_bts, result_counter_BTS_INCOMING_INTRA_BSC_HO(result));
+ if (ho->scope & HO_INTER_BSC_IN && conn->fast_return.last_eutran_plmn_valid) {
+ /* From outside local BSC and with Last EUTRAN PLMN Id => SRVCC */
+ ho_count_bsc(result_counter_BSC_SRVCC(result));
+ ho_count_bts(bts, result_counter_BTS_SRVCC(result));
+ }
LOG_HO(conn, LOGL_INFO, "Result: %s\n", handover_result_name(result));
if (ho->new_lchan && result == HO_RESULT_OK) {
+ struct gsm_bts *bts;
+
gscon_change_primary_lchan(conn, conn->ho.new_lchan);
ho->new_lchan = NULL;
+ bts = conn_get_bts(conn);
+ 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);
+ }
+
/* If a Perform Location Request (LCS) is busy, inform the SMLC that there is a new lchan */
if (conn->lcs.loc_req)
osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_HANDOVER_PERFORMED, NULL);
@@ -950,7 +1094,7 @@ void handover_end(struct gsm_subscriber_connection *conn, enum handover_result r
/* Detach the new_lchan last, so we can still see it in above logging */
if (ho->new_lchan) {
/* Release new lchan, it didn't work out */
- lchan_release(ho->new_lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
+ lchan_release(ho->new_lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
ho->new_lchan = NULL;
}
@@ -1079,6 +1223,7 @@ static void ho_fsm_wait_rr_ho_detect_onenter(struct osmo_fsm_inst *fi, uint32_t
struct handover *ho = &conn->ho;
struct msgb *rr_ho_cmd = gsm48_make_ho_cmd(ho->new_lchan,
+ ho->scope, ho->async,
ho->new_lchan->ms_power,
ho->ho_ref);
if (!rr_ho_cmd) {
diff --git a/src/osmo-bsc/handover_logic.c b/src/osmo-bsc/handover_logic.c
index ade330d46..1e1b6c319 100644
--- a/src/osmo-bsc/handover_logic.c
+++ b/src/osmo-bsc/handover_logic.c
@@ -112,7 +112,7 @@ int bts_handover_count(struct gsm_bts *bts, int ho_scopes)
if (!nm_is_running(&ts->mo.nm_state))
continue;
- ts_for_each_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
if (!lchan->conn)
continue;
if (!lchan->conn->ho.fi)
@@ -126,7 +126,7 @@ int bts_handover_count(struct gsm_bts *bts, int ho_scopes)
return count;
}
-/* Find out a handover target cell for the given neighbor_ident_key,
+/* Find out a handover target cell for the given arfcn_bsic,
* and make sure there are no ambiguous matches.
* Given a source BTS and a target ARFCN+BSIC, find which cell is the right handover target.
* ARFCN+BSIC may be re-used within and/or across BSS, so make sure that only those cells that are explicitly
@@ -138,22 +138,20 @@ int bts_handover_count(struct gsm_bts *bts, int ho_scopes)
* to be found.
*/
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 gsm_network *net = conn->network;
- struct gsm_bts *from_bts;
struct gsm_bts *local_target_cell = NULL;
- const struct gsm0808_cell_id_list2 *remote_target_cell = NULL;
- struct gsm_bts_ref *neigh;
bool ho_active;
bool as_active;
+ struct gsm_bts *from_bts = conn->lchan->ts->trx->bts;
+ *remote_target_cells = (struct gsm0808_cell_id_list2){};
if (local_target_cell_p)
*local_target_cell_p = NULL;
- if (remote_target_cell_p)
- *remote_target_cell_p = NULL;
if (!search_for) {
if (log_errors)
@@ -161,7 +159,6 @@ int find_handover_target_cell(struct gsm_bts **local_target_cell_p,
return -EINVAL;
}
- from_bts = gsm_bts_num(net, search_for->from_bts);
if (!from_bts) {
if (log_errors)
LOG_HO(conn, LOGL_ERROR, "Handover without source cell\n");
@@ -174,12 +171,11 @@ int find_handover_target_cell(struct gsm_bts **local_target_cell_p,
if (!ho_active && !as_active) {
if (log_errors)
LOG_HO(conn, LOGL_ERROR, "Cannot start Handover: Handover and Assignment disabled for this source cell (%s)\n",
- neighbor_ident_key_name(search_for));
+ cell_ab_to_str_c(OTC_SELECT, search_for));
return -EINVAL;
}
- if (llist_empty(&from_bts->local_neighbors)
- && !neighbor_ident_bts_entry_exists(from_bts->nr)) {
+ if (llist_empty(&from_bts->neighbors)) {
/* No explicit neighbor entries exist for this BTS. Hence apply the legacy default behavior that all
* local cells are neighbors. */
struct gsm_bts *bts;
@@ -192,15 +188,16 @@ int find_handover_target_cell(struct gsm_bts **local_target_cell_p,
for (i = 0; i < 2; i++) {
bool exact_match = !i;
llist_for_each_entry(bts, &net->bts_list, list) {
- struct neighbor_ident_key bts_key = *bts_ident_key(bts);
- if (neighbor_ident_key_match(&bts_key, search_for, exact_match)) {
+ struct cell_ab bts_ab;
+ bts_cell_ab(&bts_ab, bts);
+ if (cell_ab_match(&bts_ab, search_for, exact_match)) {
if (local_target_cell) {
if (log_errors)
LOG_HO(conn, LOGL_ERROR,
"NEIGHBOR CONFIGURATION ERROR: Multiple local cells match %s"
" (BTS %d and BTS %d)."
" Aborting Handover because of ambiguous network topology.\n",
- neighbor_ident_key_name(search_for),
+ cell_ab_to_str_c(OTC_SELECT, search_for),
local_target_cell->nr, bts->nr);
return -EINVAL;
}
@@ -214,7 +211,7 @@ int find_handover_target_cell(struct gsm_bts **local_target_cell_p,
if (!local_target_cell) {
if (log_errors)
LOG_HO(conn, LOGL_ERROR, "Cannot Handover, no cell matches %s\n",
- neighbor_ident_key_name(search_for));
+ cell_ab_to_str_c(OTC_SELECT, search_for));
return -EINVAL;
}
@@ -222,14 +219,14 @@ int find_handover_target_cell(struct gsm_bts **local_target_cell_p,
if (log_errors)
LOG_HO(conn, LOGL_ERROR,
"Cannot start re-assignment, Assignment disabled for this cell (%s)\n",
- neighbor_ident_key_name(search_for));
+ cell_ab_to_str_c(OTC_SELECT, search_for));
return -EINVAL;
}
if (local_target_cell != from_bts && !ho_active) {
if (log_errors)
LOG_HO(conn, LOGL_ERROR,
"Cannot start Handover, Handover disabled for this cell (%s)\n",
- neighbor_ident_key_name(search_for));
+ cell_ab_to_str_c(OTC_SELECT, search_for));
return -EINVAL;
}
@@ -243,96 +240,64 @@ int find_handover_target_cell(struct gsm_bts **local_target_cell_p,
LOG_HO(conn, LOGL_DEBUG, "There are explicit neighbors configured for this cell\n");
- /* Iterate explicit local neighbor cells */
- llist_for_each_entry(neigh, &from_bts->local_neighbors, entry) {
- struct gsm_bts *neigh_bts = neigh->bts;
- struct neighbor_ident_key neigh_bts_key = *bts_ident_key(neigh_bts);
- neigh_bts_key.from_bts = from_bts->nr;
-
- LOG_HO(conn, LOGL_DEBUG, "Local neighbor %s\n", neighbor_ident_key_name(&neigh_bts_key));
-
- if (!neighbor_ident_key_match(&neigh_bts_key, search_for, true)) {
- LOG_HO(conn, LOGL_DEBUG, "Doesn't match %s\n", neighbor_ident_key_name(search_for));
- continue;
- }
-
- if (local_target_cell) {
- if (log_errors)
- LOG_HO(conn, LOGL_ERROR,
- "NEIGHBOR CONFIGURATION ERROR: Multiple BTS match %s (BTS %d and BTS %d)."
- " Aborting Handover because of ambiguous network topology.\n",
- neighbor_ident_key_name(search_for), local_target_cell->nr, neigh_bts->nr);
- return -EINVAL;
- }
-
- local_target_cell = neigh_bts;
+ if (resolve_neighbors(&local_target_cell, remote_target_cells, from_bts, search_for, log_errors)) {
+ LOG_HO(conn, LOGL_ERROR, "Cannot handover BTS %u -> %s: neighbor unknown\n",
+ from_bts->nr, cell_ab_to_str_c(OTC_SELECT, search_for));
+ return -ENOENT;
}
- /* Any matching remote-BSS neighbor cell? */
- remote_target_cell = neighbor_ident_get(net->neighbor_bss_cells, search_for);
+ /* We have found possibly a local_target_cell (when != NULL), and / or remote_target_cells (when .id_list_len >
+ * 0). Figure out what to do with them. */
- if (remote_target_cell)
- LOG_HO(conn, LOGL_DEBUG, "Found remote target cell %s\n",
- gsm0808_cell_id_list_name(remote_target_cell));
+ if (remote_target_cells->id_list_len)
+ LOG_HO(conn, LOGL_DEBUG, "Found remote target cell(s) %s\n",
+ gsm0808_cell_id_list_name_c(OTC_SELECT, remote_target_cells));
- if (local_target_cell && remote_target_cell) {
+ if (local_target_cell && remote_target_cells->id_list_len) {
if (log_errors)
- LOG_HO(conn, LOGL_ERROR, "NEIGHBOR CONFIGURATION ERROR: Both a local and a remote-BSS cell match %s"
- " (BTS %d and remote %s)."
+ LOG_HO(conn, LOGL_ERROR, "NEIGHBOR CONFIGURATION ERROR: Both a local and a remote-BSS cell"
+ " match BTS %u -> %s (BTS %d and remote %s)."
" Aborting Handover because of ambiguous network topology.\n",
- neighbor_ident_key_name(search_for), local_target_cell->nr,
- gsm0808_cell_id_list_name(remote_target_cell));
+ from_bts->nr, cell_ab_to_str_c(OTC_SELECT, search_for), local_target_cell->bts_nr,
+ gsm0808_cell_id_list_name_c(OTC_SELECT, remote_target_cells));
return -EINVAL;
}
if (local_target_cell == from_bts && !as_active) {
if (log_errors)
LOG_HO(conn, LOGL_ERROR,
- "Cannot start re-assignment, Assignment disabled for this cell (%s)\n",
- neighbor_ident_key_name(search_for));
+ "Cannot start re-assignment, Assignment disabled for this cell (BTS %u)\n",
+ from_bts->nr);
return -EINVAL;
}
if (((local_target_cell && local_target_cell != from_bts)
- || remote_target_cell)
+ || remote_target_cells->id_list_len)
&& !ho_active) {
if (log_errors)
LOG_HO(conn, LOGL_ERROR,
- "Cannot start Handover, Handover disabled for this cell (%s)\n",
- neighbor_ident_key_name(search_for));
+ "Cannot start Handover, Handover disabled for this cell (BTS %u -> %s)\n",
+ from_bts->bts_nr, cell_ab_to_str_c(OTC_SELECT, search_for));
return -EINVAL;
}
+ /* Return the result. After above checks, only one of local or remote cell has been found. */
if (local_target_cell) {
if (local_target_cell_p)
*local_target_cell_p = local_target_cell;
return 0;
}
- if (remote_target_cell) {
- if (remote_target_cell_p)
- *remote_target_cell_p = remote_target_cell;
+ if (remote_target_cells->id_list_len)
return 0;
- }
if (log_errors)
LOG_HO(conn, LOGL_ERROR, "Cannot handover %s: neighbor unknown\n",
- neighbor_ident_key_name(search_for));
+ cell_ab_to_str_c(OTC_SELECT, search_for));
return -ENODEV;
}
-struct neighbor_ident_key *bts_ident_key(const struct gsm_bts *bts)
-{
- static struct neighbor_ident_key key;
- key = (struct neighbor_ident_key){
- .from_bts = NEIGHBOR_IDENT_KEY_ANY_BTS,
- .arfcn = bts->c0->arfcn,
- .bsic = bts->bsic,
- };
- return &key;
-}
-
static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
diff --git a/src/osmo-bsc/handover_vty.c b/src/osmo-bsc/handover_vty.c
index 81313e251..afc12d9d1 100644
--- a/src/osmo-bsc/handover_vty.c
+++ b/src/osmo-bsc/handover_vty.c
@@ -47,10 +47,10 @@ static struct handover_cfg *ho_cfg_from_vty(struct vty *vty)
VTY_CMD_PREFIX, VTY_CMD, VTY_CMD_ARG, VTY_ARG_EVAL, \
VTY_WRITE_FMT, VTY_WRITE_CONV, \
VTY_DOC) \
-DEFUN(cfg_ho_##NAME, cfg_ho_##NAME##_cmd, \
- VTY_CMD_PREFIX VTY_CMD " (" VTY_CMD_ARG "|default)", \
- VTY_DOC \
- "Use default (" #DEFAULT_VAL "), remove explicit setting on this node\n") \
+DEFUN_ATTR(cfg_ho_##NAME, cfg_ho_##NAME##_cmd, \
+ VTY_CMD_PREFIX VTY_CMD " (" VTY_CMD_ARG "|default)", \
+ VTY_DOC \
+ "Use default (" #DEFAULT_VAL "), remove explicit setting on this node\n", CMD_ATTR_IMMEDIATE) \
{ \
struct handover_cfg *ho = ho_cfg_from_vty(vty); \
const char *val = argv[0]; \
@@ -104,15 +104,16 @@ static inline const char *congestion_check_interval2a(int val)
return str;
}
-DEFUN(cfg_net_ho_congestion_check_interval, cfg_net_ho_congestion_check_interval_cmd,
- "handover2 congestion-check (disabled|<1-999>|now)",
- HO_CFG_STR_HANDOVER2
- "Configure congestion check interval\n"
- "Disable congestion checking, do not handover based on cell load. Note: there is one global congestion check"
- " interval, i.e. contrary to other handover2 settings, this is not configurable per individual cell.\n"
- "Congestion check interval in seconds (default "
- OSMO_STRINGIFY_VAL(HO_CFG_CONGESTION_CHECK_DEFAULT) ")\n"
- "Manually trigger a congestion check to run right now\n")
+DEFUN_ATTR(cfg_net_ho_congestion_check_interval, cfg_net_ho_congestion_check_interval_cmd,
+ "handover2 congestion-check (disabled|<1-999>|now)",
+ HO_CFG_STR_HANDOVER2
+ "Configure congestion check interval\n"
+ "Disable congestion checking, do not handover based on cell load. Note: there is one global congestion check"
+ " interval, i.e. contrary to other handover2 settings, this is not configurable per individual cell.\n"
+ "Congestion check interval in seconds (default "
+ OSMO_STRINGIFY_VAL(HO_CFG_CONGESTION_CHECK_DEFAULT) ")\n"
+ "Manually trigger a congestion check to run right now\n",
+ CMD_ATTR_IMMEDIATE)
{
if (!strcmp(argv[0], "now")) {
hodec2_congestion_check(gsmnet_from_vty(vty));
@@ -169,7 +170,7 @@ HODEC1_CFG_ALL_MEMBERS
#undef HO_CFG_ONE_MEMBER
}
-void ho_vty_init()
+void ho_vty_init(void)
{
ho_vty_init_cmds(GSMNET_NODE);
install_element(GSMNET_NODE, &cfg_net_ho_congestion_check_interval_cmd);
diff --git a/src/osmo-bsc/lb.c b/src/osmo-bsc/lb.c
index 77fa1165b..6f3cfac7c 100644
--- a/src/osmo-bsc/lb.c
+++ b/src/osmo-bsc/lb.c
@@ -30,6 +30,7 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
#include <osmocom/bsc/lcs_loc_req.h>
+#include <osmocom/bsc/bssmap_reset.h>
static struct gsm_subscriber_connection *get_bsc_conn_by_lb_conn_id(int conn_id)
{
@@ -45,8 +46,7 @@ static struct gsm_subscriber_connection *get_bsc_conn_by_lb_conn_id(int conn_id)
}
/* Send reset to SMLC */
-int bssmap_le_tx_reset()
- // TODO use this -- patch coming up
+int bssmap_le_tx_reset(void)
{
struct osmo_ss7_instance *ss7;
struct msgb *msg;
@@ -58,18 +58,23 @@ int bssmap_le_tx_reset()
},
};
- ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc.cs7_instance);
+ if (!bsc_gsmnet->smlc->sccp_user) {
+ LOGP(DRESET, LOGL_DEBUG, "Not sending RESET to SMLC, Lb link down\n");
+ return -1;
+ }
+
+ ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc->cs7_instance);
OSMO_ASSERT(ss7);
- LOGP(DLCS, LOGL_NOTICE, "Sending RESET to SMLC: %s\n", osmo_sccp_addr_name(ss7, &bsc_gsmnet->smlc.smlc_addr));
+ LOGP(DRESET, LOGL_INFO, "Sending RESET to SMLC: %s\n", osmo_sccp_addr_name(ss7, &bsc_gsmnet->smlc->smlc_addr));
msg = osmo_bssap_le_enc(&reset);
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_UDT_RESET]);
- return osmo_sccp_tx_unitdata_msg(bsc_gsmnet->smlc.sccp_user, &bsc_gsmnet->smlc.bsc_addr,
- &bsc_gsmnet->smlc.smlc_addr, msg);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_UDT_RESET));
+ return osmo_sccp_tx_unitdata_msg(bsc_gsmnet->smlc->sccp_user, &bsc_gsmnet->smlc->bsc_addr,
+ &bsc_gsmnet->smlc->smlc_addr, msg);
}
/* Send reset-ack to SMLC */
-int bssmap_le_tx_reset_ack()
+int bssmap_le_tx_reset_ack(void)
{
struct osmo_ss7_instance *ss7;
struct msgb *msg;
@@ -80,35 +85,14 @@ int bssmap_le_tx_reset_ack()
},
};
- ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc.cs7_instance);
+ ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc->cs7_instance);
OSMO_ASSERT(ss7);
- LOGP(DLCS, LOGL_NOTICE, "Tx RESET ACK to SMLC: %s\n", osmo_sccp_addr_name(ss7, &bsc_gsmnet->smlc.smlc_addr));
+ LOGP(DRESET, LOGL_NOTICE, "Sending RESET ACK to SMLC: %s\n", osmo_sccp_addr_name(ss7, &bsc_gsmnet->smlc->smlc_addr));
msg = osmo_bssap_le_enc(&reset_ack);
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK]);
- return osmo_sccp_tx_unitdata_msg(bsc_gsmnet->smlc.sccp_user, &bsc_gsmnet->smlc.bsc_addr,
- &bsc_gsmnet->smlc.smlc_addr, msg);
-}
-
-static int bssmap_le_handle_reset(const struct bssmap_le_pdu *pdu)
-{
- struct gsm_subscriber_connection *conn;
- int rc;
-
- /* Abort all ongoing Location Requests */
- llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry)
- lcs_loc_req_reset(conn);
-
- rc = bssmap_le_tx_reset_ack();
- if (!rc)
- bsc_gsmnet->smlc.ready = true;
- return rc;
-}
-
-static int bssmap_le_handle_reset_ack()
-{
- bsc_gsmnet->smlc.ready = true;
- return 0;
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK));
+ return osmo_sccp_tx_unitdata_msg(bsc_gsmnet->smlc->sccp_user, &bsc_gsmnet->smlc->bsc_addr,
+ &bsc_gsmnet->smlc->smlc_addr, msg);
}
static int handle_unitdata_from_smlc(const struct osmo_sccp_addr *smlc_addr, struct msgb *msg,
@@ -116,22 +100,22 @@ static int handle_unitdata_from_smlc(const struct osmo_sccp_addr *smlc_addr, str
{
struct osmo_ss7_instance *ss7;
struct bssap_le_pdu bssap_le;
- struct osmo_bssap_le_err *err;
- struct rate_ctr *ctr = bsc_gsmnet->smlc.ctrs->ctr;
+ struct osmo_bssap_le_err *err = NULL;
+ struct rate_ctr_group *ctrg = bsc_gsmnet->smlc->ctrs;
ss7 = osmo_sccp_get_ss7(osmo_sccp_get_sccp(scu));
OSMO_ASSERT(ss7);
- if (osmo_sccp_addr_cmp(smlc_addr, &bsc_gsmnet->smlc.smlc_addr, OSMO_SCCP_ADDR_T_MASK)) {
+ if (osmo_sccp_addr_cmp(smlc_addr, &bsc_gsmnet->smlc->smlc_addr, OSMO_SCCP_ADDR_T_MASK)) {
LOGP(DLCS, LOGL_ERROR, "Rx BSSMAP-LE UnitData from unknown remote address: %s\n",
osmo_sccp_addr_name(ss7, smlc_addr));
- rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UNKNOWN_PEER]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_UNKNOWN_PEER));
return -EINVAL;
}
if (osmo_bssap_le_dec(&bssap_le, &err, msg, msg)) {
LOGP(DLCS, LOGL_ERROR, "Rx BSSAP-LE UnitData with error: %s\n", err->logmsg);
- rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG));
return -EINVAL;
}
@@ -142,15 +126,17 @@ static int handle_unitdata_from_smlc(const struct osmo_sccp_addr *smlc_addr, str
switch (bssap_le.bssmap_le.msg_type) {
case BSSMAP_LE_MSGT_RESET:
- rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UDT_RESET]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_UDT_RESET));
LOGP(DLCS, LOGL_NOTICE, "RESET from SMLC: %s\n", osmo_sccp_addr_name(ss7, smlc_addr));
- return bssmap_le_handle_reset(&bssap_le.bssmap_le);
+ return osmo_fsm_inst_dispatch(bsc_gsmnet->smlc->bssmap_reset->fi, BSSMAP_RESET_EV_RX_RESET, NULL);
+
case BSSMAP_LE_MSGT_RESET_ACK:
- rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK));
LOGP(DLCS, LOGL_NOTICE, "RESET-ACK from SMLC: %s\n", osmo_sccp_addr_name(ss7, smlc_addr));
- return bssmap_le_handle_reset_ack();
+ return osmo_fsm_inst_dispatch(bsc_gsmnet->smlc->bssmap_reset->fi, BSSMAP_RESET_EV_RX_RESET_ACK, NULL);
+
default:
- rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG));
LOGP(DLCS, LOGL_ERROR, "Rx unimplemented UDT message type %s\n",
osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));
return -EINVAL;
@@ -255,23 +241,23 @@ static int lb_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg
return -EINVAL;
}
- conn_id = bsc_sccp_inst_next_conn_id(bsc_gsmnet->smlc.sccp);
+ conn_id = bsc_sccp_inst_next_conn_id(bsc_gsmnet->smlc->sccp);
if (conn_id < 0) {
LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR, "Unable to allocate SCCP Connection ID for BSSMAP-LE to SMLC\n");
return -ENOSPC;
}
conn->lcs.lb.conn_id = conn_id;
- ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc.cs7_instance);
+ ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc->cs7_instance);
OSMO_ASSERT(ss7);
LOGPFSMSL(conn->fi, DLCS, LOGL_INFO, "Opening new SCCP connection (id=%i) to SMLC: %s\n", conn_id,
- osmo_sccp_addr_name(ss7, &bsc_gsmnet->smlc.smlc_addr));
+ osmo_sccp_addr_name(ss7, &bsc_gsmnet->smlc->smlc_addr));
- rc = osmo_sccp_tx_conn_req_msg(bsc_gsmnet->smlc.sccp_user, conn_id, &bsc_gsmnet->smlc.bsc_addr,
- &bsc_gsmnet->smlc.smlc_addr, msg);
+ rc = osmo_sccp_tx_conn_req_msg(bsc_gsmnet->smlc->sccp_user, conn_id, &bsc_gsmnet->smlc->bsc_addr,
+ &bsc_gsmnet->smlc->smlc_addr, msg);
if (rc >= 0)
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_SUCCESS]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_SUCCESS));
else
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_ERR_SEND]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_ERR_SEND));
if (rc >= 0)
conn->lcs.lb.state = SUBSCR_SCCP_ST_WAIT_CONN_CONF;
@@ -282,7 +268,7 @@ void lb_close_conn(struct gsm_subscriber_connection *conn)
{
if (conn->lcs.lb.state == SUBSCR_SCCP_ST_NONE)
return;
- osmo_sccp_tx_disconn(bsc_gsmnet->smlc.sccp_user, conn->lcs.lb.conn_id, &bsc_gsmnet->smlc.bsc_addr, 0);
+ osmo_sccp_tx_disconn(bsc_gsmnet->smlc->sccp_user, conn->lcs.lb.conn_id, &bsc_gsmnet->smlc->bsc_addr, 0);
conn->lcs.lb.state = SUBSCR_SCCP_ST_NONE;
}
@@ -294,6 +280,15 @@ int lb_send(struct gsm_subscriber_connection *conn, const struct bssap_le_pdu *b
OSMO_ASSERT(conn);
+ if (!bssmap_reset_is_conn_ready(bsc_gsmnet->smlc->bssmap_reset)) {
+ LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR, "Lb link to SMLC is not ready (no RESET-ACK), cannot send %s\n",
+ osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));
+ /* If the remote side was lost, make sure that the SCCP conn is discarded in the local state and towards
+ * the STP. */
+ lb_close_conn(conn);
+ return -EINVAL;
+ }
+
msg = osmo_bssap_le_enc(bssap_le);
if (!msg) {
LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR, "Failed to encode %s\n",
@@ -307,11 +302,11 @@ int lb_send(struct gsm_subscriber_connection *conn, const struct bssap_le_pdu *b
}
LOGPFSMSL(conn->fi, DLCS, LOGL_DEBUG, "Tx %s\n", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));
- rc = osmo_sccp_tx_data_msg(bsc_gsmnet->smlc.sccp_user, conn->lcs.lb.conn_id, msg);
+ rc = osmo_sccp_tx_data_msg(bsc_gsmnet->smlc->sccp_user, conn->lcs.lb.conn_id, msg);
if (rc >= 0)
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_SUCCESS]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_SUCCESS));
else
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_ERR_SEND]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_ERR_SEND));
count_tx:
if (rc < 0)
@@ -319,24 +314,24 @@ count_tx:
switch (bssap_le->bssmap_le.msg_type) {
case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_REQUEST]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_REQUEST));
break;
case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT:
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_ABORT]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_ABORT));
break;
case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
switch (bssap_le->bssmap_le.conn_oriented_info.apdu.msg_type) {
case BSSLAP_MSGT_TA_RESPONSE:
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_RESPONSE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_RESPONSE));
break;
case BSSLAP_MSGT_REJECT:
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_REJECT]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_REJECT));
break;
case BSSLAP_MSGT_RESET:
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_RESET]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_RESET));
break;
case BSSLAP_MSGT_ABORT:
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_ABORT]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_ABORT));
break;
default:
break;
@@ -357,8 +352,65 @@ count_tx:
#define DEFAULT_ASP_LOCAL_IP "localhost"
#define DEFAULT_ASP_REMOTE_IP "localhost"
-/* Initialize Lb interface to SMLC */
-int lb_init()
+void lb_cancel_all(void)
+{
+ struct gsm_subscriber_connection *conn;
+ llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry)
+ lcs_loc_req_reset(conn);
+};
+
+void lb_reset_link_up(void *data)
+{
+ LOGP(DLCS, LOGL_INFO, "Lb link ready\n");
+}
+
+void lb_reset_link_lost(void *data)
+{
+ struct gsm_subscriber_connection *conn;
+ LOGP(DLCS, LOGL_INFO, "Lb link down\n");
+
+ /* Abort all ongoing Location Requests */
+ llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry)
+ lcs_loc_req_reset(conn);
+};
+
+void lb_reset_tx_reset(void *data)
+{
+ bssmap_le_tx_reset();
+}
+
+void lb_reset_tx_reset_ack(void *data)
+{
+ bssmap_le_tx_reset_ack();
+}
+
+static void lb_start_reset_fsm(void)
+{
+ struct bssmap_reset_cfg cfg = {
+ .conn_cfm_failure_threshold = 3,
+ .ops = {
+ .tx_reset = lb_reset_tx_reset,
+ .tx_reset_ack = lb_reset_tx_reset_ack,
+ .link_up = lb_reset_link_up,
+ .link_lost = lb_reset_link_lost,
+ },
+ };
+
+ if (bsc_gsmnet->smlc->bssmap_reset) {
+ LOGP(DLCS, LOGL_ERROR, "will not allocate a second reset FSM for Lb\n");
+ return;
+ }
+
+ bsc_gsmnet->smlc->bssmap_reset = bssmap_reset_alloc(bsc_gsmnet, "Lb", &cfg);
+}
+
+static void lb_stop_reset_fsm(void)
+{
+ bssmap_reset_term_and_free(bsc_gsmnet->smlc->bssmap_reset);
+ bsc_gsmnet->smlc->bssmap_reset = NULL;
+}
+
+static int lb_start(void)
{
uint32_t default_pc;
struct osmo_ss7_instance *cs7_inst = NULL;
@@ -367,15 +419,21 @@ int lb_init()
char inst_name[32];
const char *smlc_name = "smlc";
- if (!bsc_gsmnet->smlc.cs7_instance_valid) {
- bsc_gsmnet->smlc.cs7_instance = 0;
+ /* Already set up? */
+ if (bsc_gsmnet->smlc->sccp_user)
+ return -EALREADY;
+
+ LOGP(DLCS, LOGL_INFO, "Starting Lb link\n");
+
+ if (!bsc_gsmnet->smlc->cs7_instance_valid) {
+ bsc_gsmnet->smlc->cs7_instance = 0;
}
- cs7_inst = osmo_ss7_instance_find_or_create(tall_bsc_ctx, bsc_gsmnet->smlc.cs7_instance);
+ cs7_inst = osmo_ss7_instance_find_or_create(tall_bsc_ctx, bsc_gsmnet->smlc->cs7_instance);
OSMO_ASSERT(cs7_inst);
/* If unset, use default SCCP address for the SMLC */
- if (!bsc_gsmnet->smlc.smlc_addr.presence)
- osmo_sccp_make_addr_pc_ssn(&bsc_gsmnet->smlc.smlc_addr,
+ if (!bsc_gsmnet->smlc->smlc_addr.presence)
+ osmo_sccp_make_addr_pc_ssn(&bsc_gsmnet->smlc->smlc_addr,
osmo_ss7_pointcode_parse(NULL, SMLC_DEFAULT_PC),
OSMO_SCCP_SSN_SMLC_BSSAP_LE);
@@ -392,41 +450,98 @@ int lb_init()
0, DEFAULT_ASP_REMOTE_IP);
if (!sccp)
return -EINVAL;
- bsc_gsmnet->smlc.sccp = sccp;
+ bsc_gsmnet->smlc->sccp = sccp;
/* If unset, use default local SCCP address */
- if (!bsc_gsmnet->smlc.bsc_addr.presence)
- osmo_sccp_local_addr_by_instance(&bsc_gsmnet->smlc.bsc_addr, sccp,
+ if (!bsc_gsmnet->smlc->bsc_addr.presence)
+ osmo_sccp_local_addr_by_instance(&bsc_gsmnet->smlc->bsc_addr, sccp,
OSMO_SCCP_SSN_BSC_BSSAP_LE);
- if (!osmo_sccp_check_addr(&bsc_gsmnet->smlc.bsc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) {
+ if (!osmo_sccp_check_addr(&bsc_gsmnet->smlc->bsc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) {
LOGP(DLCS, LOGL_ERROR,
"%s %s: invalid local (BSC) SCCP address: %s\n",
- inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc.bsc_addr));
+ inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc->bsc_addr));
return -EINVAL;
}
- if (!osmo_sccp_check_addr(&bsc_gsmnet->smlc.smlc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) {
+ if (!osmo_sccp_check_addr(&bsc_gsmnet->smlc->smlc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) {
LOGP(DLCS, LOGL_ERROR,
"%s %s: invalid remote (SMLC) SCCP address: %s\n",
- inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc.smlc_addr));
+ inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc->smlc_addr));
return -EINVAL;
}
LOGP(DLCS, LOGL_NOTICE, "Lb: %s %s: local (BSC) SCCP address: %s\n",
- inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc.bsc_addr));
+ inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc->bsc_addr));
LOGP(DLCS, LOGL_NOTICE, "Lb: %s %s: remote (SMLC) SCCP address: %s\n",
- inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc.smlc_addr));
-
- /* Bind SCCP user. */
- bsc_gsmnet->smlc.sccp_user = osmo_sccp_user_find(sccp, bsc_gsmnet->smlc.bsc_addr.ssn, bsc_gsmnet->smlc.bsc_addr.pc);
- LOGP(DLCS, LOGL_NOTICE, "%s %s: %s\n", inst_name, smlc_name,
- bsc_gsmnet->smlc.sccp_user ? "user already bound for this SCCP instance" : "binding SCCP user");
- if (!bsc_gsmnet->smlc.sccp_user)
- bsc_gsmnet->smlc.sccp_user = osmo_sccp_user_bind(sccp, smlc_name, sccp_sap_up, bsc_gsmnet->smlc.bsc_addr.ssn);
- if (!bsc_gsmnet->smlc.sccp_user)
+ inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc->smlc_addr));
+
+ bsc_gsmnet->smlc->sccp_user = osmo_sccp_user_bind(sccp, smlc_name, sccp_sap_up, bsc_gsmnet->smlc->bsc_addr.ssn);
+ if (!bsc_gsmnet->smlc->sccp_user)
return -EINVAL;
+ lb_start_reset_fsm();
+ return 0;
+}
+
+static int lb_stop(void)
+{
+ /* Not set up? */
+ if (!bsc_gsmnet->smlc->sccp_user)
+ return -EALREADY;
+
+ LOGP(DLCS, LOGL_INFO, "Shutting down Lb link\n");
+
+ lb_cancel_all();
+ lb_stop_reset_fsm();
+ osmo_sccp_user_unbind(bsc_gsmnet->smlc->sccp_user);
+ bsc_gsmnet->smlc->sccp_user = NULL;
+ return 0;
+}
+
+int lb_start_or_stop(void)
+{
+ int rc;
+ if (bsc_gsmnet->smlc->enable) {
+ rc = lb_start();
+ switch (rc) {
+ case 0:
+ /* all is fine */
+ break;
+ case -EALREADY:
+ /* no need to log about anything */
+ break;
+ default:
+ LOGP(DLCS, LOGL_ERROR, "Failed to start Lb interface (rc=%d)\n", rc);
+ break;
+ }
+ } else {
+ rc = lb_stop();
+ switch (rc) {
+ case 0:
+ /* all is fine */
+ break;
+ case -EALREADY:
+ /* no need to log about anything */
+ break;
+ default:
+ LOGP(DLCS, LOGL_ERROR, "Failed to stop Lb interface (rc=%d)\n", rc);
+ break;
+ }
+ }
+ return rc;
+}
+
+static void smlc_vty_init(void);
+
+int lb_init(void)
+{
+ OSMO_ASSERT(!bsc_gsmnet->smlc);
+ bsc_gsmnet->smlc = talloc_zero(bsc_gsmnet, struct smlc_config);
+ OSMO_ASSERT(bsc_gsmnet->smlc);
+ bsc_gsmnet->smlc->ctrs = rate_ctr_group_alloc(bsc_gsmnet, &smlc_ctrg_desc, 0);
+
+ smlc_vty_init();
return 0;
}
@@ -447,6 +562,30 @@ static struct cmd_node smlc_node = {
1,
};
+DEFUN(cfg_smlc_enable, cfg_smlc_enable_cmd,
+ "enable",
+ "Start up Lb interface connection to the remote SMLC\n")
+{
+ bsc_gsmnet->smlc->enable = true;
+ if (vty->type != VTY_FILE) {
+ if (lb_start_or_stop())
+ vty_out(vty, "%% Error: failed to enable Lb interface%s", VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_smlc_no_enable, cfg_smlc_no_enable_cmd,
+ "no enable",
+ NO_STR "Stop Lb interface connection to the remote SMLC\n")
+{
+ bsc_gsmnet->smlc->enable = false;
+ if (vty->type != VTY_FILE) {
+ if (lb_start_or_stop())
+ vty_out(vty, "%% Error: failed to disable Lb interface%s", VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
static void enforce_ssn(struct vty *vty, struct osmo_sccp_addr *addr, enum osmo_sccp_ssn want_ssn)
{
if (addr->presence & OSMO_SCCP_ADDR_T_SSN) {
@@ -460,6 +599,28 @@ static void enforce_ssn(struct vty *vty, struct osmo_sccp_addr *addr, enum osmo_
addr->ssn = want_ssn;
}
+/* Prevent mixing addresses from different CS7 instances */
+bool smlc_set_cs7_instance(struct vty *vty, const char *from_vty_cmd, const char *from_addr,
+ struct osmo_ss7_instance *ss7)
+{
+ if (bsc_gsmnet->smlc->cs7_instance_valid) {
+ if (bsc_gsmnet->smlc->cs7_instance != ss7->cfg.id) {
+ LOGP(DLCS, LOGL_ERROR,
+ "%s: expecting address from cs7 instance %u, but '%s' is from %u\n",
+ from_vty_cmd, bsc_gsmnet->smlc->cs7_instance, from_addr, ss7->cfg.id);
+ vty_out(vty, "Error:"
+ " %s: expecting address from cs7 instance %u, but '%s' is from %u%s",
+ from_vty_cmd, bsc_gsmnet->smlc->cs7_instance, from_addr, ss7->cfg.id, VTY_NEWLINE);
+ return false;
+ }
+ } else {
+ bsc_gsmnet->smlc->cs7_instance = ss7->cfg.id;
+ bsc_gsmnet->smlc->cs7_instance_valid = true;
+ LOGP(DLCS, LOGL_NOTICE, "Lb interface is using cs7 instance %u\n", bsc_gsmnet->smlc->cs7_instance);
+ }
+ return true;
+}
+
DEFUN(cfg_smlc_cs7_bsc_addr,
cfg_smlc_cs7_bsc_addr_cmd,
"bsc-addr NAME",
@@ -468,25 +629,17 @@ DEFUN(cfg_smlc_cs7_bsc_addr,
const char *bsc_addr_name = argv[0];
struct osmo_ss7_instance *ss7;
- ss7 = osmo_sccp_addr_by_name(&bsc_gsmnet->smlc.bsc_addr, bsc_addr_name);
+ ss7 = osmo_sccp_addr_by_name(&bsc_gsmnet->smlc->bsc_addr, bsc_addr_name);
if (!ss7) {
vty_out(vty, "Error: No such SCCP addressbook entry: '%s'%s", bsc_addr_name, VTY_NEWLINE);
return CMD_ERR_INCOMPLETE;
}
- /* Prevent mixing addresses from different CS7 instances */
- if (bsc_gsmnet->smlc.cs7_instance_valid
- && bsc_gsmnet->smlc.cs7_instance != ss7->cfg.id) {
- vty_out(vty,
- "Error: SCCP addressbook entry from mismatching CS7 instance: '%s'%s",
- bsc_addr_name, VTY_NEWLINE);
+ if (!smlc_set_cs7_instance(vty, "smlc / bsc-addr", bsc_addr_name, ss7))
return CMD_WARNING;
- }
- bsc_gsmnet->smlc.cs7_instance = ss7->cfg.id;
- bsc_gsmnet->smlc.cs7_instance_valid = true;
- enforce_ssn(vty, &bsc_gsmnet->smlc.bsc_addr, OSMO_SCCP_SSN_BSC_BSSAP_LE);
- bsc_gsmnet->smlc.bsc_addr_name = talloc_strdup(bsc_gsmnet, bsc_addr_name);
+ enforce_ssn(vty, &bsc_gsmnet->smlc->bsc_addr, OSMO_SCCP_SSN_BSC_BSSAP_LE);
+ bsc_gsmnet->smlc->bsc_addr_name = talloc_strdup(bsc_gsmnet, bsc_addr_name);
return CMD_SUCCESS;
}
@@ -498,39 +651,40 @@ DEFUN(cfg_smlc_cs7_smlc_addr,
const char *smlc_addr_name = argv[0];
struct osmo_ss7_instance *ss7;
- ss7 = osmo_sccp_addr_by_name(&bsc_gsmnet->smlc.smlc_addr, smlc_addr_name);
+ ss7 = osmo_sccp_addr_by_name(&bsc_gsmnet->smlc->smlc_addr, smlc_addr_name);
if (!ss7) {
vty_out(vty, "Error: No such SCCP addressbook entry: '%s'%s", smlc_addr_name, VTY_NEWLINE);
return CMD_ERR_INCOMPLETE;
}
- /* Prevent mixing addresses from different CS7/SS7 instances */
- if (bsc_gsmnet->smlc.cs7_instance_valid) {
- if (bsc_gsmnet->smlc.cs7_instance != ss7->cfg.id) {
- vty_out(vty,
- "Error: SCCP addressbook entry from mismatching CS7 instance: '%s'%s",
- smlc_addr_name, VTY_NEWLINE);
- return CMD_ERR_INCOMPLETE;
- }
- }
+ if (!smlc_set_cs7_instance(vty, "smlc / smlc-addr", smlc_addr_name, ss7))
+ return CMD_WARNING;
- bsc_gsmnet->smlc.cs7_instance = ss7->cfg.id;
- bsc_gsmnet->smlc.cs7_instance_valid = true;
- enforce_ssn(vty, &bsc_gsmnet->smlc.smlc_addr, OSMO_SCCP_SSN_SMLC_BSSAP_LE);
- bsc_gsmnet->smlc.smlc_addr_name = talloc_strdup(bsc_gsmnet, smlc_addr_name);
+ enforce_ssn(vty, &bsc_gsmnet->smlc->smlc_addr, OSMO_SCCP_SSN_SMLC_BSSAP_LE);
+ bsc_gsmnet->smlc->smlc_addr_name = talloc_strdup(bsc_gsmnet, smlc_addr_name);
return CMD_SUCCESS;
}
static int config_write_smlc(struct vty *vty)
{
+ /* Nothing to write? */
+ if (!(bsc_gsmnet->smlc->enable
+ || bsc_gsmnet->smlc->bsc_addr_name
+ || bsc_gsmnet->smlc->smlc_addr_name))
+ return 0;
+
vty_out(vty, "smlc%s", VTY_NEWLINE);
- if (bsc_gsmnet->smlc.bsc_addr_name) {
+
+ if (bsc_gsmnet->smlc->enable)
+ vty_out(vty, " enable%s", VTY_NEWLINE);
+
+ if (bsc_gsmnet->smlc->bsc_addr_name) {
vty_out(vty, " bsc-addr %s%s",
- bsc_gsmnet->smlc.bsc_addr_name, VTY_NEWLINE);
+ bsc_gsmnet->smlc->bsc_addr_name, VTY_NEWLINE);
}
- if (bsc_gsmnet->smlc.smlc_addr_name) {
+ if (bsc_gsmnet->smlc->smlc_addr_name) {
vty_out(vty, " smlc-addr %s%s",
- bsc_gsmnet->smlc.smlc_addr_name, VTY_NEWLINE);
+ bsc_gsmnet->smlc->smlc_addr_name, VTY_NEWLINE);
}
return 0;
@@ -550,6 +704,8 @@ void smlc_vty_init(void)
install_element(CONFIG_NODE, &cfg_smlc_cmd);
install_node(&smlc_node, config_write_smlc);
+ install_element(SMLC_NODE, &cfg_smlc_enable_cmd);
+ install_element(SMLC_NODE, &cfg_smlc_no_enable_cmd);
install_element(SMLC_NODE, &cfg_smlc_cs7_bsc_addr_cmd);
install_element(SMLC_NODE, &cfg_smlc_cs7_smlc_addr_cmd);
}
diff --git a/src/osmo-bsc/lchan.c b/src/osmo-bsc/lchan.c
new file mode 100644
index 000000000..262e2a5c3
--- /dev/null
+++ b/src/osmo-bsc/lchan.c
@@ -0,0 +1,150 @@
+/* (C) 2022 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * (C) 2008-2018 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/bsc/lchan.h>
+#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bts_trx.h>
+#include <osmocom/bsc/abis_rsl.h>
+
+void lchan_init(struct gsm_lchan *lchan, struct gsm_bts_trx_ts *ts, unsigned int nr)
+{
+ lchan->ts = ts;
+ lchan->nr = nr;
+ lchan->type = GSM_LCHAN_NONE;
+
+ lchan_update_name(lchan);
+}
+
+void lchan_update_name(struct gsm_lchan *lchan)
+{
+ struct gsm_bts_trx_ts *ts = lchan->ts;
+ if (lchan->name)
+ talloc_free(lchan->name);
+ lchan->name = talloc_asprintf(ts->trx, "(bts=%d,trx=%d,ts=%d,ss=%s%d)",
+ ts->trx->bts->nr, ts->trx->nr, ts->nr,
+ lchan->vamos.is_secondary ? "shadow" : "",
+ lchan->nr - (lchan->vamos.is_secondary ? ts->max_primary_lchans : 0));
+}
+
+/* If the lchan is currently active, return the duration since activation in milliseconds.
+ * Otherwise return 0. */
+uint64_t gsm_lchan_active_duration_ms(const struct gsm_lchan *lchan)
+{
+ struct timespec now, elapsed;
+
+ if (lchan->active_start.tv_sec == 0 && lchan->active_start.tv_nsec == 0)
+ return 0;
+
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecsub(&now, &lchan->active_start, &elapsed);
+
+ return elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
+}
+
+/* For a VAMOS secondary shadow lchan, return its primary lchan. If the lchan is not a secondary lchan, return NULL. */
+struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos)
+{
+ struct gsm_lchan *lchan_primary;
+ if (!lchan_vamos || !lchan_vamos->vamos.is_secondary)
+ return NULL;
+ /* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
+ * relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
+ * have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
+ lchan_primary = (struct gsm_lchan *)lchan_vamos - lchan_vamos->ts->max_primary_lchans;
+ if (!lchan_primary->fi)
+ return NULL;
+ return lchan_primary;
+}
+
+/* For a primary lchan, return its VAMOS secondary shadow lchan. If the lchan is not a primary lchan, return NULL. */
+struct gsm_lchan *gsm_lchan_primary_to_vamos(const struct gsm_lchan *lchan_primary)
+{
+ struct gsm_lchan *lchan_vamos;
+ if (!lchan_primary || lchan_primary->vamos.is_secondary)
+ return NULL;
+ /* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
+ * relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
+ * have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
+ lchan_vamos = (struct gsm_lchan *)lchan_primary + lchan_primary->ts->max_primary_lchans;
+ if (!lchan_vamos->fi)
+ return NULL;
+ return lchan_vamos;
+}
+
+void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ struct gsm_subscriber_connection *conn = lchan->conn;
+ int max_pwr_dbm_pwclass, new_pwr;
+ bool send_pwr_ctrl_msg = false;
+
+ LOG_LCHAN(lchan, LOGL_DEBUG,
+ "MS Power level update requested: %d dBm\n", ms_power_dbm);
+
+ if (!conn)
+ goto ms_power_default;
+
+ if (conn->ms_power_class == 0)
+ goto ms_power_default;
+
+ if ((max_pwr_dbm_pwclass = (int)ms_class_gmsk_dbm(bts->band, conn->ms_power_class)) < 0) {
+ LOG_LCHAN(lchan, LOGL_INFO,
+ "Failed getting max ms power for power class %" PRIu8
+ " on band %s, providing default max ms power\n",
+ conn->ms_power_class, gsm_band_name(bts->band));
+ goto ms_power_default;
+ }
+
+ /* Current configured max pwr is above maximum one allowed on
+ current band + ms power class, so use that one. */
+ if (ms_power_dbm > max_pwr_dbm_pwclass)
+ ms_power_dbm = max_pwr_dbm_pwclass;
+
+ms_power_default:
+ if ((new_pwr = ms_pwr_ctl_lvl(bts->band, ms_power_dbm)) < 0) {
+ LOG_LCHAN(lchan, LOGL_INFO,
+ "Failed getting max ms power level %d on band %s,"
+ " providing default max ms power\n",
+ ms_power_dbm, gsm_band_name(bts->band));
+ return;
+ }
+
+ LOG_LCHAN(lchan, LOGL_DEBUG,
+ "MS Power level update (power class %" PRIu8 "): %" PRIu8 " -> %d\n",
+ conn ? conn->ms_power_class : 0, lchan->ms_power, new_pwr);
+
+ /* If chan was already activated and max ms_power changes (due to power
+ classmark received), send an MS Power Control message */
+ if (lchan->activate.activ_ack && new_pwr != lchan->ms_power)
+ send_pwr_ctrl_msg = true;
+
+ lchan->ms_power = new_pwr;
+
+ if (send_pwr_ctrl_msg)
+ rsl_chan_ms_power_ctrl(lchan);
+}
diff --git a/src/osmo-bsc/lchan_fsm.c b/src/osmo-bsc/lchan_fsm.c
index e97c1baeb..05fcea75b 100644
--- a/src/osmo-bsc/lchan_fsm.c
+++ b/src/osmo-bsc/lchan_fsm.c
@@ -40,6 +40,8 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
+#include <osmocom/bsc/lchan.h>
static struct osmo_fsm lchan_fsm;
@@ -66,30 +68,63 @@ bool lchan_may_receive_data(struct gsm_lchan *lchan)
}
}
-void lchan_set_last_error(struct gsm_lchan *lchan, const char *fmt, ...)
+static void lchan_on_mode_modify_success(struct gsm_lchan *lchan)
{
- va_list ap;
- /* This dance allows using an existing error reason in above fmt */
- char *last_error_was = lchan->last_error;
- lchan->last_error = NULL;
+ lchan->modify.concluded = true;
- if (fmt) {
- va_start(ap, fmt);
- lchan->last_error = talloc_vasprintf(lchan->ts->trx, fmt, ap);
- va_end(ap);
+ switch (lchan->modify.info.modify_for) {
- LOG_LCHAN(lchan, LOGL_ERROR, "%s\n", lchan->last_error);
+ case MODIFY_FOR_ASSIGNMENT:
+ osmo_fsm_inst_dispatch(lchan->conn->assignment.fi, ASSIGNMENT_EV_LCHAN_MODIFIED, lchan);
+ break;
+
+ default:
+ break;
}
+}
+
+#define lchan_on_mode_modify_failure(lchan, modify_for, for_conn) \
+ _lchan_on_mode_modify_failure(lchan, modify_for, for_conn, \
+ __FILE__, __LINE__)
+static void _lchan_on_mode_modify_failure(struct gsm_lchan *lchan, enum lchan_modify_for modify_for,
+ struct gsm_subscriber_connection *for_conn,
+ const char *file, int line)
+{
+ if (lchan->modify.concluded)
+ return;
+ lchan->modify.concluded = true;
+
+ switch (modify_for) {
+
+ case MODIFY_FOR_ASSIGNMENT:
+ LOG_LCHAN(lchan, LOGL_NOTICE, "Signalling Assignment FSM of error (%s)\n",
+ lchan->last_error ? : "unknown error");
+ if (!for_conn) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "lchan Channel Mode Modify failed, "
+ "but modify request has no conn\n");
+ break;
+ }
+ _osmo_fsm_inst_dispatch(for_conn->assignment.fi, ASSIGNMENT_EV_LCHAN_ERROR, lchan,
+ file, line);
+ return;
+
+ case MODIFY_FOR_VTY:
+ LOG_LCHAN(lchan, LOGL_ERROR, "VTY user invoked lchan Channel Mode Modify failed (%s)\n",
+ lchan->last_error ? : "unknown error");
+ break;
- if (last_error_was)
- talloc_free(last_error_was);
+ default:
+ LOG_LCHAN(lchan, LOGL_ERROR, "lchan Channel Mode Modify failed (%s)\n",
+ lchan->last_error ? : "unknown error");
+ break;
+ }
}
/* The idea here is that we must not require to change any lchan state in order to deny a request. */
#define lchan_on_activation_failure(lchan, for_conn, activ_for) \
_lchan_on_activation_failure(lchan, for_conn, activ_for, \
__FILE__, __LINE__)
-static void _lchan_on_activation_failure(struct gsm_lchan *lchan, enum lchan_activate_mode activ_for,
+static void _lchan_on_activation_failure(struct gsm_lchan *lchan, enum lchan_activate_for activ_for,
struct gsm_subscriber_connection *for_conn,
const char *file, int line)
{
@@ -99,7 +134,7 @@ static void _lchan_on_activation_failure(struct gsm_lchan *lchan, enum lchan_act
switch (activ_for) {
- case FOR_MS_CHANNEL_REQUEST:
+ case ACTIVATE_FOR_MS_CHANNEL_REQUEST:
if (!lchan->activate.immediate_assignment_sent) {
/* Failure before Immediate Assignment message, send a reject. */
LOG_LCHAN(lchan, LOGL_NOTICE, "Tx Immediate Assignment Reject (%s)\n",
@@ -111,14 +146,26 @@ static void _lchan_on_activation_failure(struct gsm_lchan *lchan, enum lchan_act
* lchan_on_activation_failure(), no additional action or logging needed. */
break;
- case FOR_ASSIGNMENT:
+ case ACTIVATE_FOR_ASSIGNMENT:
LOG_LCHAN(lchan, LOGL_NOTICE, "Signalling Assignment FSM of error (%s)\n",
lchan->last_error ? : "unknown error");
- _osmo_fsm_inst_dispatch(for_conn->assignment.fi, ASSIGNMENT_EV_LCHAN_ERROR, lchan,
- file, line);
- return;
+ if (!for_conn) {
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "lchan activation for Assignment failed, but activation request has"
+ " no conn\n");
+ break;
+ }
+ if (!for_conn->assignment.fi) {
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "lchan activation for Assignment failed, but conn has no ongoing"
+ " assignment procedure\n");
+ break;
+ }
+ _osmo_fsm_inst_dispatch(for_conn->assignment.fi, ASSIGNMENT_EV_LCHAN_ERROR,
+ lchan, file, line);
+ break;
- case FOR_HANDOVER:
+ case ACTIVATE_FOR_HANDOVER:
LOG_LCHAN(lchan, LOGL_NOTICE, "Signalling Handover FSM of error (%s)\n",
lchan->last_error ? : "unknown error");
if (!for_conn) {
@@ -136,11 +183,15 @@ static void _lchan_on_activation_failure(struct gsm_lchan *lchan, enum lchan_act
_osmo_fsm_inst_dispatch(for_conn->ho.fi, HO_EV_LCHAN_ERROR, lchan, file, line);
break;
- case FOR_VTY:
+ case ACTIVATE_FOR_VTY:
LOG_LCHAN(lchan, LOGL_ERROR, "VTY user invoked lchan activation failed (%s)\n",
lchan->last_error ? : "unknown error");
break;
+ case ACTIVATE_FOR_MODE_MODIFY_RTP:
+ lchan_on_mode_modify_failure(lchan, lchan->modify.info.modify_for, for_conn);
+ break;
+
default:
LOG_LCHAN(lchan, LOGL_ERROR, "lchan activation failed (%s)\n",
lchan->last_error ? : "unknown error");
@@ -154,25 +205,42 @@ static void lchan_on_fully_established(struct gsm_lchan *lchan)
return;
lchan->activate.concluded = true;
+ /* Set active state timekeeping markers. */
+ osmo_clock_gettime(CLOCK_MONOTONIC, &lchan->active_start);
+ lchan->active_stored = lchan->active_start;
+
+ /* Increment rate counters tracking fully established lchans. */
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_H:
+ case GSM_LCHAN_TCH_F:
+ rate_ctr_inc(rate_ctr_group_get_ctr(lchan->ts->trx->bts->bts_ctrs, BTS_CTR_CHAN_TCH_FULLY_ESTABLISHED));
+ break;
+ case GSM_LCHAN_SDCCH:
+ rate_ctr_inc(rate_ctr_group_get_ctr(lchan->ts->trx->bts->bts_ctrs, BTS_CTR_CHAN_SDCCH_FULLY_ESTABLISHED));
+ break;
+ default:
+ break;
+ }
+
switch (lchan->activate.info.activ_for) {
- case FOR_MS_CHANNEL_REQUEST:
+ case ACTIVATE_FOR_MS_CHANNEL_REQUEST:
/* No signalling to do here, MS is free to use the channel, and should go on to connect
* to the MSC and establish a subscriber connection. */
break;
- case FOR_ASSIGNMENT:
+ case ACTIVATE_FOR_ASSIGNMENT:
if (!lchan->conn) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for assignment succeeded, but lchan has no conn:"
" cannot trigger appropriate actions. Release.\n");
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
break;
}
if (!lchan->conn->assignment.fi) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for assignment succeeded, but lchan has no"
" assignment ongoing: cannot trigger appropriate actions. Release.\n");
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
break;
}
osmo_fsm_inst_dispatch(lchan->conn->assignment.fi, ASSIGNMENT_EV_LCHAN_ESTABLISHED,
@@ -182,18 +250,18 @@ static void lchan_on_fully_established(struct gsm_lchan *lchan)
* will try to roll back a modified RTP connection. */
break;
- case FOR_HANDOVER:
+ case ACTIVATE_FOR_HANDOVER:
if (!lchan->conn) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for handover succeeded, but lchan has no conn\n");
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
break;
}
if (!lchan->conn->ho.fi) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for handover succeeded, but lchan has no"
" handover ongoing\n");
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
break;
}
osmo_fsm_inst_dispatch(lchan->conn->ho.fi, HO_EV_LCHAN_ESTABLISHED, lchan);
@@ -202,6 +270,10 @@ static void lchan_on_fully_established(struct gsm_lchan *lchan)
* we will try to roll back a modified RTP connection. */
break;
+ case ACTIVATE_FOR_MODE_MODIFY_RTP:
+ lchan_on_mode_modify_success(lchan);
+ break;
+
default:
LOG_LCHAN(lchan, LOGL_NOTICE, "lchan %s fully established\n",
lchan_activate_mode_name(lchan->activate.info.activ_for));
@@ -210,13 +282,15 @@ static void lchan_on_fully_established(struct gsm_lchan *lchan)
}
struct osmo_tdef_state_timeout lchan_fsm_timeouts[32] = {
- [LCHAN_ST_WAIT_TS_READY] = { .T=-5 },
- [LCHAN_ST_WAIT_ACTIV_ACK] = { .T=-6 },
- [LCHAN_ST_WAIT_RLL_RTP_ESTABLISH] = { .T=3101 },
- [LCHAN_ST_WAIT_RLL_RTP_RELEASED] = { .T=3109 },
- [LCHAN_ST_WAIT_BEFORE_RF_RELEASE] = { .T=3111 },
- [LCHAN_ST_WAIT_RF_RELEASE_ACK] = { .T=3111 },
- [LCHAN_ST_WAIT_AFTER_ERROR] = { .T=-3111 },
+ [LCHAN_ST_WAIT_TS_READY] = { .T = -5 },
+ [LCHAN_ST_WAIT_ACTIV_ACK] = { .T = -6 },
+ [LCHAN_ST_WAIT_RLL_RTP_ESTABLISH] = { .T = 3101 },
+ [LCHAN_ST_WAIT_RLL_RTP_RELEASED] = { .T = 3109 },
+ [LCHAN_ST_WAIT_BEFORE_RF_RELEASE] = { .T = 3111 },
+ [LCHAN_ST_WAIT_RF_RELEASE_ACK] = { .T = 3111 },
+ [LCHAN_ST_WAIT_AFTER_ERROR] = { .T = -3111 },
+ [LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK] = { .T = -13 },
+ [LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK] = { .T = -14 },
};
/* Transition to a state, using the T timer defined in lchan_fsm_timeouts.
@@ -238,7 +312,7 @@ struct osmo_tdef_state_timeout lchan_fsm_timeouts[32] = {
const uint32_t state_chg = STATE_CHG; \
LOG_LCHAN(_lchan, LOGL_DEBUG, "Handling failure, will then transition to state %s\n", \
osmo_fsm_state_name(fsm, state_chg)); \
- lchan_set_last_error(_lchan, "lchan %s in state %s: " fmt, \
+ LCHAN_SET_LAST_ERROR(_lchan, "lchan %s in state %s: " fmt, \
_lchan->activate.concluded ? "failure" : "allocation failed", \
osmo_fsm_state_name(fsm, state_was), ## args); \
lchan_on_activation_failure(_lchan, _lchan->activate.info.activ_for, _lchan->conn); \
@@ -247,10 +321,10 @@ struct osmo_tdef_state_timeout lchan_fsm_timeouts[32] = {
else \
LOG_LCHAN(_lchan, LOGL_DEBUG, "After failure handling, already in state %s\n", \
osmo_fsm_state_name(fsm, state_chg)); \
- } while(0)
+ } while (0)
/* Which state to transition to when lchan_fail() is called in a given state. */
-uint32_t lchan_fsm_on_error[34] = {
+uint32_t lchan_fsm_on_error[32] = {
[LCHAN_ST_UNUSED] = LCHAN_ST_UNUSED,
[LCHAN_ST_WAIT_TS_READY] = LCHAN_ST_UNUSED,
[LCHAN_ST_WAIT_ACTIV_ACK] = LCHAN_ST_BORKEN,
@@ -261,8 +335,8 @@ uint32_t lchan_fsm_on_error[34] = {
[LCHAN_ST_WAIT_RF_RELEASE_ACK] = LCHAN_ST_BORKEN,
[LCHAN_ST_WAIT_AFTER_ERROR] = LCHAN_ST_UNUSED,
[LCHAN_ST_BORKEN] = LCHAN_ST_BORKEN,
- [LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK] = LCHAN_ST_BORKEN,
- [LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK] = LCHAN_ST_BORKEN,
+ [LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK] = LCHAN_ST_WAIT_RF_RELEASE_ACK,
+ [LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK] = LCHAN_ST_WAIT_RF_RELEASE_ACK,
};
#define lchan_fail(fmt, args...) lchan_fail_to(lchan_fsm_on_error[fi->state], fmt, ## args)
@@ -273,6 +347,16 @@ void lchan_activate(struct gsm_lchan *lchan, struct lchan_activate_info *info)
OSMO_ASSERT(lchan && info);
+ if ((info->vamos || lchan->vamos.is_secondary)
+ && !osmo_bts_has_feature(&lchan->ts->trx->bts->features, BTS_FEAT_VAMOS)) {
+ lchan->last_error = talloc_strdup(lchan->ts->trx, "VAMOS related channel activation requested,"
+ " but BTS does not support VAMOS");
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "VAMOS related channel activation requested, but BTS %u does not support VAMOS\n",
+ lchan->ts->trx->bts->nr);
+ goto abort;
+ }
+
if (!lchan_state_is(lchan, LCHAN_ST_UNUSED))
goto abort;
@@ -281,7 +365,7 @@ void lchan_activate(struct gsm_lchan *lchan, struct lchan_activate_info *info)
switch (info->activ_for) {
- case FOR_ASSIGNMENT:
+ case ACTIVATE_FOR_ASSIGNMENT:
if (!info->for_conn
|| !info->for_conn->fi) {
LOG_LCHAN(lchan, LOGL_ERROR, "Activation requested, but no conn\n");
@@ -298,7 +382,7 @@ void lchan_activate(struct gsm_lchan *lchan, struct lchan_activate_info *info)
}
break;
- case FOR_HANDOVER:
+ case ACTIVATE_FOR_HANDOVER:
if (!info->for_conn
|| !info->for_conn->fi) {
LOG_LCHAN(lchan, LOGL_ERROR, "Activation requested, but no conn\n");
@@ -340,23 +424,50 @@ abort:
/* Remain in state UNUSED */
}
-static void lchan_fsm_update_id(struct gsm_lchan *lchan)
+void lchan_mode_modify(struct gsm_lchan *lchan, struct lchan_modify_info *info)
+{
+ OSMO_ASSERT(lchan && info);
+
+ if ((info->vamos || lchan->vamos.is_secondary)
+ && !osmo_bts_has_feature(&lchan->ts->trx->bts->features, BTS_FEAT_VAMOS)) {
+ lchan->last_error = talloc_strdup(lchan->ts->trx, "VAMOS related Channel Mode Modify requested,"
+ " but BTS does not support VAMOS");
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "VAMOS related Channel Mode Modify requested, but BTS %u does not support VAMOS\n",
+ lchan->ts->trx->bts->nr);
+ lchan_on_mode_modify_failure(lchan, info->modify_for, lchan->conn);
+ return;
+ }
+
+ /* To make sure that the lchan is actually allowed to initiate Mode Modify, feed through an FSM event. */
+ if (osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_REQUEST_MODE_MODIFY, info)) {
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "Channel Mode Modify requested, but cannot dispatch LCHAN_EV_REQUEST_MODE_MODIFY event\n");
+ lchan_on_mode_modify_failure(lchan, info->modify_for, lchan->conn);
+ }
+}
+
+void lchan_fsm_update_id(struct gsm_lchan *lchan)
{
- osmo_fsm_inst_update_id_f(lchan->fi, "%u-%u-%u-%s-%u",
- lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
- gsm_pchan_id(lchan->ts->pchan_on_init), lchan->nr);
+ lchan_update_name(lchan);
+ if (!lchan->fi)
+ return;
+ osmo_fsm_inst_update_id_f_sanitize(lchan->fi, '_', "%u-%u-%u-%s-%s%u",
+ lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
+ gsm_pchan_name(lchan->ts->pchan_on_init),
+ lchan->vamos.is_secondary ? "shadow" : "",
+ lchan->nr - (lchan->vamos.is_secondary ? lchan->ts->max_primary_lchans : 0));
if (lchan->fi_rtp)
osmo_fsm_inst_update_id_f(lchan->fi_rtp, lchan->fi->id);
}
-extern void lchan_rtp_fsm_init();
-
-void lchan_fsm_init()
+static __attribute__((constructor)) void lchan_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&lchan_fsm) == 0);
- lchan_rtp_fsm_init();
}
+static void lchan_reset(struct gsm_lchan *lchan);
+
void lchan_fsm_alloc(struct gsm_lchan *lchan)
{
OSMO_ASSERT(lchan->ts);
@@ -368,6 +479,7 @@ void lchan_fsm_alloc(struct gsm_lchan *lchan)
lchan->fi->priv = lchan;
lchan_fsm_update_id(lchan);
LOGPFSML(lchan->fi, LOGL_DEBUG, "new lchan\n");
+ lchan_reset(lchan);
}
/* Clear volatile state of the lchan. Clear all except
@@ -381,8 +493,11 @@ static void lchan_reset(struct gsm_lchan *lchan)
{
LOG_LCHAN(lchan, LOGL_DEBUG, "Clearing lchan state\n");
- if (lchan->conn)
- gscon_forget_lchan(lchan->conn, lchan);
+ if (lchan->conn) {
+ struct gsm_subscriber_connection *conn = lchan->conn;
+ lchan_forget_conn(lchan);
+ gscon_forget_lchan(conn, lchan);
+ }
if (lchan->rqd_ref) {
talloc_free(lchan->rqd_ref);
@@ -407,6 +522,10 @@ static void lchan_reset(struct gsm_lchan *lchan)
.last_error = lchan->last_error,
.release.rr_cause = GSM48_RR_CAUSE_NORMAL,
+
+ .tsc_set = 1,
+ .interf_dbm = INTERF_DBM_UNKNOWN,
+ .interf_band = INTERF_BAND_UNKNOWN,
};
}
@@ -415,6 +534,7 @@ static void lchan_fsm_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
struct gsm_bts *bts = lchan->ts->trx->bts;
lchan_reset(lchan);
+ chan_counts_ts_update(lchan->ts);
osmo_fsm_inst_dispatch(lchan->ts->fi, TS_EV_LCHAN_UNUSED, lchan);
/* Poll the channel request queue, so that waiting calls can make use of the lchan that just
@@ -422,6 +542,12 @@ static void lchan_fsm_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta
abis_rsl_chan_rqd_queue_poll(bts);
}
+static void lchan_fsm_cbch_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ chan_counts_ts_update(lchan->ts);
+}
+
static void lchan_fsm_wait_after_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
@@ -434,101 +560,87 @@ static void lchan_fsm_wait_after_error_onenter(struct osmo_fsm_inst *fi, uint32_
}
/* Configure the multirate setting on this channel. */
-static int lchan_mr_config(struct gsm_lchan *lchan, uint16_t s15_s0)
+static int mr_config_filter(struct gsm48_multi_rate_conf *mr_conf_result,
+ bool full_rate,
+ const struct amr_multirate_conf *amr_mrc,
+ const struct gsm48_multi_rate_conf *mr_filter_msc,
+ uint16_t s15_s0,
+ const struct gsm_lchan *lchan_for_logging)
{
- struct gsm48_multi_rate_conf mr_conf;
- bool full_rate = (lchan->type == GSM_LCHAN_TCH_F);
- struct gsm_bts *bts = lchan->ts->trx->bts;
- struct amr_multirate_conf *mr;
int rc;
- int rc_rate;
- struct gsm48_multi_rate_conf mr_conf_filtered;
- const struct gsm48_multi_rate_conf *mr_conf_bts;
+ struct gsm48_multi_rate_conf *mr_filter_bts = (struct gsm48_multi_rate_conf*)amr_mrc->gsm48_ie;
/* Generate mr conf struct from S15-S0 bits */
- if (gsm48_mr_cfg_from_gsm0808_sc_cfg(&mr_conf, s15_s0) < 0) {
- LOG_LCHAN(lchan, LOGL_ERROR,
+ if (gsm48_mr_cfg_from_gsm0808_sc_cfg(mr_conf_result, s15_s0) < 0) {
+ LOG_LCHAN(lchan_for_logging, LOGL_ERROR,
"can not determine multirate configuration, S15-S0 (%04x) are ambiguous!\n", s15_s0);
return -EINVAL;
}
/* Do not include 12.2 kbps rate when S1 is set. */
- if (lchan->type == GSM_LCHAN_TCH_H && (s15_s0 & GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20)) {
- /* See also 3GPP TS 28.062, chapter 7.11.3.1.3: "In case this Configuration
- * "Config-NB-Code = 1" is signalled in the TFO Negotiation for the HR_AMR
- * Codec Type,then it shall be assumed that AMR mode 12.2 kbps is (of course)
- * not included. */
- mr_conf.m12_2 = 0;
+ if ((!full_rate) && (s15_s0 & GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20)) {
+ /* See also 3GPP TS 28.062, chapter 7.11.3.1.3:
+ *
+ * In case this Configuration "Config-NB-Code = 1" is signalled in the TFO Negotiation for the HR_AMR
+ * Codec Type, then it shall be assumed that AMR mode 12.2 kbps is (of course) not included.
+ *
+ * Further below, we log an error if 12k2 is included for a TCH/H lchan: removing this here ensures that
+ * we don't log that error for GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20 on a TCH/H lchan. */
+ mr_conf_result->m12_2 = 0;
}
- /* There are two different active sets, depending on the channel rate,
- * make sure the appropate one is selected. */
- if (full_rate)
- mr = &bts->mr_full;
- else
- mr = &bts->mr_half;
-
- if (lchan->activate.info.activ_for == FOR_VTY)
- /* If the channel is activated manually from VTY, then there is no
- * conn attached to the lchan, also no MSC is involved. Since this
- * option is for debugging and the codec choice is an intentional
- * decision by the VTY user, we do not filter the mr_conf. */
- memcpy(&mr_conf_filtered, &mr_conf, sizeof(mr_conf_filtered));
- else {
- /* The VTY allows to forbid certain codec rates. Unfortunately we can
- * not articulate all of the prohibitions on through S0-S15 on the A
- * interface. To ensure that the VTY settings are observed we create
- * a manipulated copy of the mr_conf that ensures forbidden codec rates
- * are not used in the multirate configuration IE. */
- rc_rate = calc_amr_rate_intersection(&mr_conf_filtered, &lchan->conn->sccp.msc->amr_conf, &mr_conf);
- if (rc_rate < 0) {
- LOG_LCHAN(lchan, LOGL_ERROR,
+ if (mr_filter_msc) {
+ rc = calc_amr_rate_intersection(mr_conf_result, mr_filter_msc, mr_conf_result);
+ if (rc < 0) {
+ LOG_LCHAN(lchan_for_logging, LOGL_ERROR,
"can not encode multirate configuration (invalid amr rate setting, MSC)\n");
return -EINVAL;
}
}
- /* The two last codec rates which are defined for AMR do only work with
- * full rate channels. We will pinch off those rates für half-rate
- * channels to ensure they are not included accidently. */
- if (!full_rate) {
- if (mr_conf_filtered.m10_2 || mr_conf_filtered.m12_2)
- LOG_LCHAN(lchan, LOGL_ERROR, "ignoring unsupported amr codec rates\n");
- mr_conf_filtered.m10_2 = 0;
- mr_conf_filtered.m12_2 = 0;
+ rc = calc_amr_rate_intersection(mr_conf_result, mr_filter_bts, mr_conf_result);
+ if (rc < 0) {
+ LOG_LCHAN(lchan_for_logging, LOGL_ERROR,
+ "can not encode multirate configuration (invalid amr rate setting, BTS)\n");
+ return -EINVAL;
}
- /* Ensure that the resulting filtered conf is coherent with the
- * configuration that is set for the BTS and the specified rate.
- * if the channel activation was triggerd by the VTY, do not
- * filter anything (see also comment above) */
- if (lchan->activate.info.activ_for != FOR_VTY) {
- mr_conf_bts = (struct gsm48_multi_rate_conf *)mr->gsm48_ie;
- rc_rate = calc_amr_rate_intersection(&mr_conf_filtered, mr_conf_bts, &mr_conf_filtered);
- if (rc_rate < 0) {
- LOG_LCHAN(lchan, LOGL_ERROR,
- "can not encode multirate configuration (invalid amr rate setting, BTS)\n");
- return -EINVAL;
- }
- }
+ /* Set the ICMI according to the BTS. Above gsm48_mr_cfg_from_gsm0808_sc_cfg() always sets ICMI = 1, which
+ * carried through all of the above rate intersections. */
+ mr_conf_result->icmi = mr_filter_bts->icmi;
+ mr_conf_result->smod = mr_filter_bts->smod;
- /* Proceed with the generation of the multirate configuration IE
- * (MS and BTS) */
- rc = gsm48_multirate_config(lchan->mr_ms_lv, &mr_conf_filtered, mr->ms_mode, mr->num_modes);
- if (rc != 0) {
- LOG_LCHAN(lchan, LOGL_ERROR, "can not encode multirate configuration (MS)\n");
- return -EINVAL;
- }
- rc = gsm48_multirate_config(lchan->mr_bts_lv, &mr_conf_filtered, mr->bts_mode, mr->num_modes);
- if (rc != 0) {
- LOG_LCHAN(lchan, LOGL_ERROR, "can not encode multirate configuration (BTS)\n");
- return -EINVAL;
+ /* 10k2 and 12k2 only work for full rate */
+ if (!full_rate) {
+ if (mr_conf_result->m10_2 || mr_conf_result->m12_2)
+ LOG_LCHAN(lchan_for_logging, LOGL_ERROR,
+ "half rate lchan: ignoring unsupported AMR codec rates 10k2 and 12k2\n");
+ mr_conf_result->m10_2 = 0;
+ mr_conf_result->m12_2 = 0;
}
- lchan->s15_s0 = s15_s0;
return 0;
}
+/* Configure the multirate setting on this channel. */
+static int lchan_mr_config(struct gsm48_multi_rate_conf *mr_conf, const struct gsm_lchan *lchan, uint16_t s15_s0)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ bool full_rate = lchan->type == GSM_LCHAN_TCH_F;
+ struct amr_multirate_conf *amr_mrc = full_rate ? &bts->mr_full : &bts->mr_half;
+ struct gsm48_multi_rate_conf *mr_filter_msc = NULL;
+
+ /* If activated for VTY, there may not be a conn indicating an MSC AMR configuration. */
+ if (lchan->conn && lchan->conn->sccp.msc)
+ mr_filter_msc = &lchan->conn->sccp.msc->amr_conf;
+
+ return mr_config_filter(mr_conf,
+ full_rate,
+ amr_mrc, mr_filter_msc,
+ s15_s0,
+ lchan);
+}
+
static void lchan_fsm_unused(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct lchan_activate_info *info = data;
@@ -540,10 +652,16 @@ static void lchan_fsm_unused(struct osmo_fsm_inst *fi, uint32_t event, void *dat
OSMO_ASSERT(info);
OSMO_ASSERT(!lchan->conn);
OSMO_ASSERT(!lchan->mgw_endpoint_ci_bts);
- lchan_set_last_error(lchan, NULL);
+ if (lchan->last_error)
+ talloc_free(lchan->last_error);
+ lchan->last_error = NULL;
lchan->release.requested = false;
lchan->activate.info = *info;
+ /* To avoid confusion, invalidate info.chreq_reason value if it isn't for a CHREQ */
+ if (lchan->activate.info.activ_for != ACTIVATE_FOR_MS_CHANNEL_REQUEST)
+ lchan->activate.info.chreq_reason = -1;
+
lchan->activate.concluded = false;
lchan_fsm_state_chg(LCHAN_ST_WAIT_TS_READY);
break;
@@ -553,6 +671,44 @@ static void lchan_fsm_unused(struct osmo_fsm_inst *fi, uint32_t event, void *dat
}
}
+static int lchan_activate_set_ch_mode_rate_and_mr_config(struct gsm_lchan *lchan)
+{
+ struct osmo_fsm_inst *fi = lchan->fi;
+ lchan->activate.ch_mode_rate = lchan->activate.info.ch_mode_rate;
+ lchan->activate.ch_mode_rate.chan_mode = (lchan->activate.info.vamos
+ ? gsm48_chan_mode_to_vamos(lchan->activate.info.ch_mode_rate.chan_mode)
+ : gsm48_chan_mode_to_non_vamos(lchan->activate.info.ch_mode_rate.chan_mode));
+ if (lchan->activate.ch_mode_rate.chan_mode < 0) {
+ lchan_fail("Invalid chan_mode: %s", gsm48_chan_mode_name(lchan->activate.info.ch_mode_rate.chan_mode));
+ return -EINVAL;
+ }
+
+ if (gsm48_chan_mode_to_non_vamos(lchan->activate.ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ if (lchan_mr_config(&lchan->activate.mr_conf_filtered, lchan, lchan->activate.ch_mode_rate.s15_s0) < 0) {
+ lchan_fail("Can not generate multirate configuration IE");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int lchan_send_imm_ass(struct osmo_fsm_inst *fi)
+{
+ int rc;
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ rc = rsl_tx_imm_assignment(lchan);
+ if (rc) {
+ lchan_fail("Failed to Tx RR Immediate Assignment message (rc=%d %s)",
+ rc, strerror(-rc));
+ return rc;
+ }
+ LOG_LCHAN(lchan, LOGL_DEBUG, "Tx RR Immediate Assignment\n");
+ lchan->activate.immediate_assignment_sent = true;
+ return rc;
+}
+
+static void post_activ_ack_accept_preliminary_settings(struct gsm_lchan *lchan);
+
static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
@@ -560,55 +716,48 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
struct osmo_mgcpc_ep_ci *use_mgwep_ci;
struct gsm_lchan *old_lchan = lchan->activate.info.re_use_mgw_endpoint_from_lchan;
struct lchan_activate_info *info = &lchan->activate.info;
- int ms_power_dbm;
+ int ms_power_dbm = bts->ms_max_power;
if (lchan->release.requested) {
lchan_fail("Release requested while activating");
return;
}
+ chan_counts_ts_update(lchan->ts);
+
lchan->conn = info->for_conn;
/* If there is a previous lchan, and the new lchan is on the same cell as previous one,
- * take over power and TA values. Otherwise, use max power and zero TA. */
+ * take over MS power values. Otherwise, use configured MS max power. */
if (old_lchan && old_lchan->ts->trx->bts == bts) {
- ms_power_dbm = ms_pwr_dbm(bts->band, old_lchan->ms_power);
- lchan_update_ms_power_ctrl_level(lchan, ms_power_dbm >= 0 ? ms_power_dbm : bts->ms_max_power);
- lchan->bs_power = old_lchan->bs_power;
- lchan->rqd_ta = old_lchan->rqd_ta;
- } else {
- lchan_update_ms_power_ctrl_level(lchan, bts->ms_max_power);
- /* Upon last entering the UNUSED state, from lchan_reset():
- * - bs_power is still zero, 0dB reduction, output power = Pn.
- * - TA is still zero, to be determined by RACH. */
+ if ((ms_power_dbm = ms_pwr_dbm(bts->band, old_lchan->ms_power)) == 0)
+ ms_power_dbm = bts->ms_max_power;
}
-
- if (info->chan_mode == GSM48_CMODE_SPEECH_AMR) {
- if (lchan_mr_config(lchan, info->s15_s0) < 0) {
- lchan_fail("Can not generate multirate configuration IE\n");
- return;
- }
+ lchan_update_ms_power_ctrl_level(lchan, ms_power_dbm);
+
+ /* Default BS Power reduction value (in dB) */
+ lchan->bs_power_db = (bts->bs_power_ctrl.mode == GSM_PWR_CTRL_MODE_DYN_BTS) ?
+ bts->bs_power_ctrl.bs_power_max_db :
+ bts->bs_power_ctrl.bs_power_val_db;
+ /* BS Power Control is generally not allowed on the BCCH/CCCH carrier.
+ * However, we allow it in the BCCH carrier power reduction mode of operation. */
+ if (lchan->ts->trx == bts->c0) {
+ lchan->bs_power_db = OSMO_MIN(lchan->ts->c0_max_power_red_db,
+ lchan->bs_power_db);
}
- switch (info->chan_mode) {
-
- case GSM48_CMODE_SIGN:
- lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
- lchan->tch_mode = GSM48_CMODE_SIGN;
- break;
+ if (lchan_activate_set_ch_mode_rate_and_mr_config(lchan))
+ return;
- case GSM48_CMODE_SPEECH_V1:
- case GSM48_CMODE_SPEECH_EFR:
- case GSM48_CMODE_SPEECH_AMR:
- lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
- lchan->tch_mode = info->chan_mode;
- break;
+ /* If enabling VAMOS mode and no specific TSC Set was selected, make sure to select a sane TSC Set by
+ * default: Set 1 for the primary and Set 2 for the shadow lchan. For non-VAMOS lchans, TSC Set 1. */
+ if (lchan->activate.info.tsc_set.present)
+ lchan->activate.tsc_set = lchan->activate.info.tsc_set.val;
+ else
+ lchan->activate.tsc_set = lchan->vamos.is_secondary ? 2 : 1;
- default:
- lchan_fail("Not implemented: cannot activate for chan mode %s",
- gsm48_chan_mode_name(info->chan_mode));
- return;
- }
+ /* Use the TSC provided in the activation request, if any. Otherwise use the timeslot's configured TSC. */
+ lchan->activate.tsc = lchan->activate.info.tsc.present ? lchan->activate.info.tsc.val : gsm_ts_tsc(lchan->ts);
use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);
@@ -619,9 +768,9 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
lchan->activate.info.requires_voice_stream ?
(use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new")
: "none",
- gsm_lchant_name(lchan->type),
- gsm48_chan_mode_name(lchan->tch_mode),
- (lchan->activate.info.encr.alg_id ? : 1)-1,
+ gsm_chan_t_name(lchan->type),
+ gsm48_chan_mode_name(lchan->activate.ch_mode_rate.chan_mode),
+ lchan->activate.info.encr.alg_a5_n,
lchan->activate.info.encr.key_len ? osmo_hexdump_nospc(lchan->activate.info.encr.key,
lchan->activate.info.encr.key_len) : "none");
@@ -632,6 +781,18 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
/* Prepare an MGW endpoint CI if appropriate. */
if (lchan->activate.info.requires_voice_stream)
lchan_rtp_fsm_start(lchan);
+
+ if (lchan->activate.info.imm_ass_time == IMM_ASS_TIME_PRE_TS_ACK) {
+ /* Send the Immediate Assignment even before the timeslot is ready, saving a dyn TS timeslot roundtrip
+ * on Abis (experimental).
+ *
+ * Until the Channel Activation ACK is received, various values still are preliminary and hence live in
+ * lchan->activate.*. We're doing things early here and need e.g. an accurate lchan->tsc, so already
+ * copy the preliminary values from lchan->activate.* into the operative places in lchan->* prematurely.
+ */
+ post_activ_ack_accept_preliminary_settings(lchan);
+ lchan_send_imm_ass(fi);
+ }
}
static void lchan_fsm_wait_ts_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -652,7 +813,7 @@ static void lchan_fsm_wait_ts_ready(struct osmo_fsm_inst *fi, uint32_t event, vo
return;
}
- lchan_fail("Failed to setup RTP stream: %s in state %s\n",
+ lchan_fail("Failed to setup RTP stream: %s in state %s",
osmo_fsm_event_name(fi->fsm, event),
osmo_fsm_inst_state_name(fi));
return;
@@ -675,24 +836,46 @@ static void lchan_fsm_wait_activ_ack_onenter(struct osmo_fsm_inst *fi, uint32_t
}
switch (lchan->activate.info.activ_for) {
- case FOR_MS_CHANNEL_REQUEST:
+ case ACTIVATE_FOR_MS_CHANNEL_REQUEST:
act_type = RSL_ACT_INTRA_IMM_ASS;
break;
- case FOR_HANDOVER:
+ case ACTIVATE_FOR_HANDOVER:
act_type = lchan->conn->ho.async ? RSL_ACT_INTER_ASYNC : RSL_ACT_INTER_SYNC;
ho_ref = lchan->conn->ho.ho_ref;
break;
+ case ACTIVATE_FOR_ASSIGNMENT:
default:
- case FOR_ASSIGNMENT:
act_type = RSL_ACT_INTRA_NORM_ASS;
break;
}
+ /* rsl_tx_chan_activ() and build_encr_info() access lchan->encr, make sure it reflects the values requested for
+ * activation.
+ * TODO: rather leave it in lchan->activate.info.encr until the ACK is received, which means that
+ * rsl_tx_chan_activ() should use lchan->activate.info.encr and build_encr_info() should be passed encr as an
+ * explicit argument. */
lchan->encr = lchan->activate.info.encr;
rc = rsl_tx_chan_activ(lchan, act_type, ho_ref);
- if (rc)
+ if (rc) {
lchan_fail_to(LCHAN_ST_UNUSED, "Tx Chan Activ failed: %s (%d)", strerror(-rc), rc);
+ return;
+ }
+
+ if (lchan->activate.info.ta_known)
+ lchan->last_ta = lchan->activate.info.ta;
+
+ if (lchan->activate.info.imm_ass_time == IMM_ASS_TIME_PRE_CHAN_ACK) {
+ /* Send the Immediate Assignment directly after the Channel Activation request, saving one Abis
+ * roundtrip between ChanRqd and Imm Ass.
+ *
+ * Until the Channel Activation ACK is received, various values still are preliminary and hence live in
+ * lchan->activate.*. We're doing things early here and need e.g. an accurate lchan->tsc, so already
+ * copy the preliminary values from lchan->activate.* into the operative places in lchan->* prematurely.
+ */
+ post_activ_ack_accept_preliminary_settings(lchan);
+ lchan_send_imm_ass(fi);
+ }
}
static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi);
@@ -751,10 +934,23 @@ static void lchan_fsm_wait_activ_ack(struct osmo_fsm_inst *fi, uint32_t event, v
}
}
+static void post_activ_ack_accept_preliminary_settings(struct gsm_lchan *lchan)
+{
+ lchan->current_ch_mode_rate = lchan->activate.ch_mode_rate;
+ lchan->current_mr_conf = lchan->activate.mr_conf_filtered;
+ lchan->vamos.enabled = lchan->activate.info.vamos;
+ lchan->tsc_set = lchan->activate.tsc_set;
+ lchan->tsc = lchan->activate.tsc;
+}
+
static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)
{
- int rc;
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+
+ post_activ_ack_accept_preliminary_settings(lchan);
+ LOG_LCHAN(lchan, LOGL_INFO, "Rx Activ ACK %s\n",
+ gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode));
+
if (lchan->release.requested) {
lchan_fail_to(LCHAN_ST_WAIT_RF_RELEASE_ACK, "Release requested while activating");
return;
@@ -762,51 +958,49 @@ static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)
switch (lchan->activate.info.activ_for) {
- case FOR_MS_CHANNEL_REQUEST:
- rc = rsl_tx_imm_assignment(lchan);
- if (rc) {
- lchan_fail("Failed to Tx RR Immediate Assignment message (rc=%d %s)\n",
- rc, strerror(-rc));
- return;
+ case ACTIVATE_FOR_MS_CHANNEL_REQUEST:
+ if (lchan->activate.info.imm_ass_time == IMM_ASS_TIME_POST_CHAN_ACK) {
+ if (lchan_send_imm_ass(fi)) {
+ /* lchan_fail() was already called in lchan_send_imm_ass() */
+ return;
+ }
}
- LOG_LCHAN(lchan, LOGL_DEBUG, "Tx RR Immediate Assignment\n");
- lchan->activate.immediate_assignment_sent = true;
break;
- case FOR_ASSIGNMENT:
+ case ACTIVATE_FOR_ASSIGNMENT:
if (!lchan->conn) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for assignment succeeded, but lchan has no conn:"
" cannot trigger appropriate actions. Release.\n");
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
- break;
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
+ return;
}
if (!lchan->conn->assignment.fi) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for assignment succeeded, but lchan has no"
" assignment ongoing: cannot trigger appropriate actions. Release.\n");
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
- break;
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
+ return;
}
/* After the Chan Activ Ack, the MS expects to receive an RR Assignment Command.
* Let the assignment_fsm handle that. */
osmo_fsm_inst_dispatch(lchan->conn->assignment.fi, ASSIGNMENT_EV_LCHAN_ACTIVE, lchan);
break;
- case FOR_HANDOVER:
+ case ACTIVATE_FOR_HANDOVER:
if (!lchan->conn) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for handover succeeded, but lchan has no conn:"
" cannot trigger appropriate actions. Release.\n");
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
- break;
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
+ return;
}
if (!lchan->conn->ho.fi) {
LOG_LCHAN(lchan, LOGL_ERROR,
"lchan activation for handover succeeded, but lchan has no"
" handover ongoing: cannot trigger appropriate actions. Release.\n");
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
- break;
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
+ return;
}
/* After the Chan Activ Ack of the new lchan, send the MS an RR Handover Command on the
* old channel. The handover_fsm handles that. */
@@ -833,7 +1027,7 @@ static void lchan_fsm_wait_rll_rtp_establish_onenter(struct osmo_fsm_inst *fi, u
/* When activating a channel for VTY, skip waiting for activity from
* lchan_rtp_fsm, but only if no voice stream is required. */
- if (lchan->activate.info.activ_for == FOR_VTY &&
+ if (lchan->activate.info.activ_for == ACTIVATE_FOR_VTY &&
!lchan->activate.info.requires_voice_stream) {
lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);
}
@@ -846,8 +1040,13 @@ static void lchan_fsm_wait_rll_rtp_establish(struct osmo_fsm_inst *fi, uint32_t
case LCHAN_EV_RLL_ESTABLISH_IND:
if (!lchan->activate.info.requires_voice_stream
- || lchan_rtp_established(lchan))
+ || lchan_rtp_established(lchan)) {
+ LOG_LCHAN(lchan, LOGL_DEBUG,
+ "%s\n",
+ (lchan->activate.info.requires_voice_stream ?
+ "RTP already established earlier" : "no voice stream required"));
lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);
+ }
return;
case LCHAN_EV_RTP_READY:
@@ -863,7 +1062,7 @@ static void lchan_fsm_wait_rll_rtp_establish(struct osmo_fsm_inst *fi, uint32_t
return;
}
- lchan_fail("Failed to setup RTP stream: %s in state %s\n",
+ lchan_fail("Failed to setup RTP stream: %s in state %s",
osmo_fsm_event_name(fi->fsm, event),
osmo_fsm_inst_state_name(fi));
return;
@@ -876,7 +1075,7 @@ static void lchan_fsm_wait_rll_rtp_establish(struct osmo_fsm_inst *fi, uint32_t
static void lchan_fsm_wait_rr_chan_mode_modify_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
- gsm48_lchan_modify(lchan, lchan->activate.info.chan_mode);
+ gsm48_lchan_modify(lchan, lchan->modify.ch_mode_rate.chan_mode);
}
static void lchan_fsm_wait_rr_chan_mode_modify_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -888,7 +1087,7 @@ static void lchan_fsm_wait_rr_chan_mode_modify_ack(struct osmo_fsm_inst *fi, uin
return;
case LCHAN_EV_RR_CHAN_MODE_MODIFY_ERROR:
- lchan_fail("Failed to change channel mode on the MS side: %s in state %s\n",
+ lchan_fail("Failed to change channel mode on the MS side: %s in state %s",
osmo_fsm_event_name(fi->fsm, event),
osmo_fsm_inst_state_name(fi));
return;
@@ -905,7 +1104,7 @@ static void lchan_fsm_wait_rsl_chan_mode_modify_ack_onenter(struct osmo_fsm_inst
rc = rsl_chan_mode_modify_req(lchan);
if (rc < 0) {
- lchan_fail("Failed to send rsl message to change the channel mode on the BTS side: state %s\n",
+ lchan_fail("Failed to send rsl message to change the channel mode on the BTS side: state %s",
osmo_fsm_inst_state_name(fi));
}
}
@@ -916,14 +1115,37 @@ static void lchan_fsm_wait_rsl_chan_mode_modify_ack(struct osmo_fsm_inst *fi, ui
switch (event) {
case LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK:
- if (lchan->activate.info.requires_voice_stream)
+ /* The Channel Mode Modify was ACKed, now the requested values become the accepted and used values. */
+ lchan->current_ch_mode_rate = lchan->modify.ch_mode_rate;
+ lchan->current_mr_conf = lchan->modify.mr_conf_filtered;
+ lchan->tsc_set = lchan->modify.tsc_set;
+ lchan->tsc = lchan->modify.tsc;
+ lchan->vamos.enabled = lchan->modify.info.vamos;
+
+ if (lchan->modify.info.requires_voice_stream
+ && !lchan->fi_rtp) {
+ /* Continue with RTP stream establishing as done in lchan_activate(). Place the requested values in
+ * lchan->activate.info and continue with voice stream setup. */
+ lchan->activate.info = (struct lchan_activate_info){
+ .activ_for = ACTIVATE_FOR_MODE_MODIFY_RTP,
+ .for_conn = lchan->conn,
+ .ch_mode_rate = lchan->modify.ch_mode_rate,
+ .requires_voice_stream = true,
+ .msc_assigned_cic = lchan->modify.info.msc_assigned_cic,
+ };
+ if (lchan_activate_set_ch_mode_rate_and_mr_config(lchan))
+ return;
+
+ lchan->activate.concluded = false;
lchan_fsm_state_chg(LCHAN_ST_WAIT_RLL_RTP_ESTABLISH);
- else
+ } else {
lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);
+ lchan_on_mode_modify_success(lchan);
+ }
return;
case LCHAN_EV_RSL_CHAN_MODE_MODIFY_NACK:
- lchan_fail("Failed to change channel mode on the BTS side: %s in state %s\n",
+ lchan_fail("Failed to change channel mode on the BTS side: %s in state %s",
osmo_fsm_event_name(fi->fsm, event),
osmo_fsm_inst_state_name(fi));
return;
@@ -996,11 +1218,17 @@ static void handle_rll_rel_ind_or_conf(struct osmo_fsm_inst *fi, uint32_t event,
lchan->sapis[sapi] = LCHAN_SAPI_UNUSED;
rll_indication(lchan, link_id, BSC_RLLR_IND_REL_IND);
- /* Releasing SAPI 0 means the conn becomes invalid; but not if the link_id contains a TCH flag.
- * (TODO: is this the correct interpretation?) */
+ /* Releasing SAPI 0 means the conn becomes invalid; but not if the link_id contains a SACCH flag. */
if (lchan->conn && sapi == 0 && !(link_id & 0xc0)) {
LOG_LCHAN(lchan, LOGL_DEBUG, "lchan is releasing\n");
gscon_lchan_releasing(lchan->conn, lchan);
+
+ /* if SAPI=0 is gone, it makes no sense if other SAPIs are still around,
+ * this is not a valid configuration and we should forget about them.
+ * This is particularly relevant in case of Ericsson RBS6000, which doesn't
+ * seem to send a RLL_REL_IND for SAPI=3 if there was already one for SAPI=0 */
+ for_each_active_sapi(sapi, 1, lchan)
+ lchan->sapis[sapi] = LCHAN_SAPI_UNUSED;
}
/* The caller shall check whether all SAPIs are released and cause a state chg */
@@ -1009,7 +1237,7 @@ static void handle_rll_rel_ind_or_conf(struct osmo_fsm_inst *fi, uint32_t event,
static void lchan_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
- struct lchan_activate_info *info;
+ struct lchan_modify_info *modif_info;
struct osmo_mgcpc_ep_ci *use_mgwep_ci;
switch (event) {
@@ -1032,50 +1260,57 @@ static void lchan_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void
return;
}
- lchan_fail("RTP stream closed unexpectedly: %s in state %s\n",
+ lchan_fail("RTP stream closed unexpectedly: %s in state %s",
osmo_fsm_event_name(fi->fsm, event),
osmo_fsm_inst_state_name(fi));
return;
case LCHAN_EV_REQUEST_MODE_MODIFY:
+ modif_info = data;
+ lchan->modify.info = *modif_info;
+ lchan->modify.concluded = false;
+
+ use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);
- /* FIXME: Add missing implementation to handle an already existing RTP voice stream on MODE MODIFY.
- * there may be transitions from VOICE to SIGNALLING and also from VOICE to VOICE with a different
- * codec. */
- if (lchan->fi_rtp) {
- lchan_fail("MODE MODIFY not implemented when RTP voice stream is already active (VOICE => SIGNALLING, VOICE/CODEC_A => VOICE/CODEC_B)\n");
+ lchan->modify.ch_mode_rate = lchan->modify.info.ch_mode_rate;
+ lchan->modify.ch_mode_rate.chan_mode = (lchan->modify.info.vamos
+ ? gsm48_chan_mode_to_vamos(lchan->modify.info.ch_mode_rate.chan_mode)
+ : gsm48_chan_mode_to_non_vamos(lchan->modify.info.ch_mode_rate.chan_mode));
+ if (lchan->modify.ch_mode_rate.chan_mode < 0) {
+ lchan_fail("Invalid chan_mode: %s", gsm48_chan_mode_name(lchan->modify.info.ch_mode_rate.chan_mode));
return;
}
- info = data;
- lchan->activate.info = *info;
- use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);
-
- if (info->chan_mode == GSM48_CMODE_SPEECH_AMR) {
- if (lchan_mr_config(lchan, info->s15_s0) < 0) {
- lchan_fail("Can not generate multirate configuration IE\n");
+ if (gsm48_chan_mode_to_non_vamos(modif_info->ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ if (lchan_mr_config(&lchan->modify.mr_conf_filtered, lchan, modif_info->ch_mode_rate.s15_s0)
+ < 0) {
+ lchan_fail("Can not generate multirate configuration IE");
return;
}
}
+ /* If enabling VAMOS mode and no specific TSC Set was selected, make sure to select a sane TSC Set by
+ * default: Set 1 for the primary and Set 2 for the shadow lchan. For non-VAMOS lchans, TSC Set 1. */
+ if (lchan->modify.info.tsc_set.present)
+ lchan->modify.tsc_set = lchan->modify.info.tsc_set.val;
+ else
+ lchan->modify.tsc_set = lchan->vamos.is_secondary ? 2 : 1;
+
+ /* Use the TSC provided in the modification request, if any. Otherwise use the timeslot's configured
+ * TSC. */
+ lchan->modify.tsc = lchan->modify.info.tsc.present ? lchan->modify.info.tsc.val : gsm_ts_tsc(lchan->ts);
+
LOG_LCHAN(lchan, LOGL_INFO,
- "Modification requested: %s voice=%s MGW-ci=%s type=%s tch-mode=%s encr-alg=A5/%u ck=%s\n",
- lchan_activate_mode_name(lchan->activate.info.activ_for),
- lchan->activate.info.requires_voice_stream ? "yes" : "no",
- lchan->activate.info.requires_voice_stream ?
+ "Modification requested: %s voice=%s MGW-ci=%s type=%s tch-mode=%s tsc=%d/%u\n",
+ lchan_modify_for_name(lchan->modify.info.modify_for),
+ lchan->modify.info.requires_voice_stream ? "yes" : "no",
+ lchan->modify.info.requires_voice_stream ?
(use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new")
: "none",
- gsm_lchant_name(lchan->type),
- gsm48_chan_mode_name(lchan->tch_mode),
- (lchan->activate.info.encr.alg_id ? : 1) - 1,
- lchan->activate.info.encr.key_len ? osmo_hexdump_nospc(lchan->activate.info.encr.key,
- lchan->activate.info.encr.key_len) : "none");
-
- /* While the mode is changed the lchan is virtually "not activated", at least
- * from the FSM implementations perspective */
- lchan->activate.concluded = false;
+ gsm_chan_t_name(lchan->type),
+ gsm48_chan_mode_name(lchan->modify.ch_mode_rate.chan_mode),
+ lchan->modify.tsc_set, lchan->modify.tsc);
- /* Initiate mode modification, start with the MS side (RR) */
lchan_fsm_state_chg(LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK);
return;
@@ -1186,8 +1421,9 @@ static void lchan_fsm_wait_rf_release_ack_onenter(struct osmo_fsm_inst *fi, uint
* lchan_reset(), we make sure it does. But in case of releases from error handling, the
* conn might as well notice now already that its lchan is becoming unusable. */
if (lchan->conn) {
- gscon_forget_lchan(lchan->conn, lchan);
+ struct gsm_subscriber_connection *conn = lchan->conn;
lchan_forget_conn(lchan);
+ gscon_forget_lchan(conn, lchan);
}
rc = rsl_tx_rf_chan_release(lchan);
@@ -1212,6 +1448,13 @@ static void lchan_fsm_wait_rf_release_ack(struct osmo_fsm_inst *fi, uint32_t eve
/* ignore late lchan_rtp_fsm release events */
return;
+ case LCHAN_EV_RLL_REL_IND:
+ /* let's just ignore this. We are already logging the fact
+ * that this message was received inside abis_rsl.c. There can
+ * be any number of reasons why the radio link layer failed.
+ */
+ return;
+
default:
OSMO_ASSERT(false);
}
@@ -1220,6 +1463,7 @@ static void lchan_fsm_wait_rf_release_ack(struct osmo_fsm_inst *fi, uint32_t eve
static void lchan_fsm_borken_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ struct gsm_bts *bts = lchan->ts->trx->bts;
enum bts_counter_id ctr;
switch (prev_state) {
case LCHAN_ST_UNUSED:
@@ -1243,23 +1487,25 @@ static void lchan_fsm_borken_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta
default:
ctr = BTS_CTR_LCHAN_BORKEN_FROM_UNKNOWN;
}
- rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[ctr]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, ctr));
if (prev_state != LCHAN_ST_BORKEN)
- osmo_stat_item_inc(lchan->ts->trx->bts->bts_statg->items[BTS_STAT_LCHAN_BORKEN], 1);
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_LCHAN_BORKEN), 1);
/* The actual action besides all the beancounting above */
lchan_reset(lchan);
+ chan_counts_ts_update(lchan->ts);
}
static void lchan_fsm_borken(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ struct gsm_bts *bts = lchan->ts->trx->bts;
switch (event) {
case LCHAN_EV_RSL_CHAN_ACTIV_ACK:
/* A late Chan Activ ACK? Release. */
- rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK]);
- osmo_stat_item_dec(lchan->ts->trx->bts->bts_statg->items[BTS_STAT_LCHAN_BORKEN], 1);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK));
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_LCHAN_BORKEN), 1);
lchan->release.in_error = true;
lchan->release.rsl_error_cause = RSL_ERR_INTERWORKING;
lchan->release.rr_cause = bsc_gsm48_rr_cause_from_rsl_cause(lchan->release.rsl_error_cause);
@@ -1268,33 +1514,24 @@ static void lchan_fsm_borken(struct osmo_fsm_inst *fi, uint32_t event, void *dat
case LCHAN_EV_RSL_CHAN_ACTIV_NACK:
/* A late Chan Activ NACK? Ok then, unused. */
- rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK]);
- osmo_stat_item_dec(lchan->ts->trx->bts->bts_statg->items[BTS_STAT_LCHAN_BORKEN], 1);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK));
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_LCHAN_BORKEN), 1);
lchan_fsm_state_chg(LCHAN_ST_UNUSED);
return;
case LCHAN_EV_RSL_RF_CHAN_REL_ACK:
/* A late Release ACK? */
- rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK]);
- osmo_stat_item_dec(lchan->ts->trx->bts->bts_statg->items[BTS_STAT_LCHAN_BORKEN], 1);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK));
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_LCHAN_BORKEN), 1);
lchan->release.in_error = true;
lchan->release.rsl_error_cause = RSL_ERR_INTERWORKING;
lchan->release.rr_cause = bsc_gsm48_rr_cause_from_rsl_cause(lchan->release.rsl_error_cause);
lchan_fsm_state_chg(LCHAN_ST_WAIT_AFTER_ERROR);
- /* TODO: we used to do this only for sysmobts:
- int do_free = is_sysmobts_v2(ts->trx->bts);
- LOGP(DRSL, LOGL_NOTICE,
- "%s CHAN REL ACK for broken channel. %s.\n",
- gsm_lchan_name(lchan),
- do_free ? "Releasing it" : "Keeping it broken");
- if (do_free)
- do_lchan_free(lchan);
- * Clarify the reason. If a BTS sends a RF Chan Rel ACK, we can consider it released,
- * independently from the BTS model, right?? */
return;
case LCHAN_EV_RTP_RELEASED:
case LCHAN_EV_RTP_ERROR:
+ case LCHAN_EV_RLL_REL_IND:
return;
default:
@@ -1320,6 +1557,7 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
},
[LCHAN_ST_CBCH] = {
.name = "CBCH",
+ .onenter = lchan_fsm_cbch_onenter,
.out_state_mask = 0
| S(LCHAN_ST_UNUSED)
,
@@ -1382,8 +1620,9 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
| S(LCHAN_EV_RR_CHAN_MODE_MODIFY_ERROR)
,
.out_state_mask = 0
- | S(LCHAN_ST_BORKEN)
| S(LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK)
+ | S(LCHAN_ST_WAIT_RF_RELEASE_ACK)
+ | S(LCHAN_ST_BORKEN)
,
},
[LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK] = {
@@ -1395,8 +1634,10 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
| S(LCHAN_EV_RSL_CHAN_MODE_MODIFY_NACK)
,
.out_state_mask = 0
- | S(LCHAN_ST_BORKEN)
+ | S(LCHAN_ST_ESTABLISHED)
| S(LCHAN_ST_WAIT_RLL_RTP_ESTABLISH)
+ | S(LCHAN_ST_WAIT_RF_RELEASE_ACK)
+ | S(LCHAN_ST_BORKEN)
,
},
[LCHAN_ST_ESTABLISHED] = {
@@ -1452,6 +1693,7 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
.action = lchan_fsm_wait_rf_release_ack,
.in_event_mask = 0
| S(LCHAN_EV_RSL_RF_CHAN_REL_ACK)
+ | S(LCHAN_EV_RLL_REL_IND) /* ignore late REL_IND of SAPI[0] */
| S(LCHAN_EV_RTP_RELEASED) /* ignore late lchan_rtp_fsm release events */
,
.out_state_mask = 0
@@ -1480,6 +1722,7 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
| S(LCHAN_EV_RSL_RF_CHAN_REL_ACK)
| S(LCHAN_EV_RTP_ERROR)
| S(LCHAN_EV_RTP_RELEASED)
+ | S(LCHAN_EV_RLL_REL_IND)
,
.out_state_mask = 0
| S(LCHAN_ST_WAIT_RF_RELEASE_ACK)
@@ -1507,6 +1750,7 @@ static const struct value_string lchan_fsm_event_names[] = {
OSMO_VALUE_STRING(LCHAN_EV_RR_CHAN_MODE_MODIFY_ERROR),
OSMO_VALUE_STRING(LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK),
OSMO_VALUE_STRING(LCHAN_EV_RSL_CHAN_MODE_MODIFY_NACK),
+ OSMO_VALUE_STRING(LCHAN_EV_REQUEST_MODE_MODIFY),
{}
};
@@ -1515,8 +1759,15 @@ static void lchan_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event,
switch (event) {
case LCHAN_EV_TS_ERROR:
+ {
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ if (fi->state == LCHAN_ST_BORKEN) {
+ rate_ctr_inc(rate_ctr_group_get_ctr(lchan->ts->trx->bts->bts_ctrs, BTS_CTR_LCHAN_BORKEN_EV_TS_ERROR));
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(lchan->ts->trx->bts->bts_statg, BTS_STAT_LCHAN_BORKEN), 1);
+ }
lchan_fail_to(LCHAN_ST_UNUSED, "LCHAN_EV_TS_ERROR");
return;
+ }
case LCHAN_EV_RLL_ERR_IND:
/* let's just ignore this. We are already logging the
@@ -1554,15 +1805,23 @@ static int lchan_fsm_timer_cb(struct osmo_fsm_inst *fi)
lchan->release.in_error = true;
lchan->release.rsl_error_cause = RSL_ERR_INTERWORKING;
lchan->release.rr_cause = bsc_gsm48_rr_cause_from_rsl_cause(lchan->release.rsl_error_cause);
- lchan_fail("Timeout");
+ if (fi->state == LCHAN_ST_WAIT_RLL_RTP_ESTABLISH) {
+ lchan_fail("Timeout (rll_ready=%s,voice_require=%s,voice_ready=%s)",
+ (lchan->sapis[0] != LCHAN_SAPI_UNUSED) ? "yes" : "no",
+ lchan->activate.info.requires_voice_stream ? "yes" : "no",
+ lchan_rtp_established(lchan) ? "yes" : "no");
+ } else {
+ lchan_fail("Timeout");
+ }
return 0;
}
}
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)
{
- if (!lchan || !lchan->fi)
+ if (!lchan || !lchan->fi || lchan->fi->state == LCHAN_ST_UNUSED)
return;
if (lchan->release.in_release_handler)
@@ -1574,6 +1833,10 @@ void lchan_release(struct gsm_lchan *lchan, bool do_rr_release,
lchan->release.in_error = err;
lchan->release.do_rr_release = do_rr_release;
lchan->release.rr_cause = cause_rr;
+ if (last_eutran_plmn) {
+ lchan->release.last_eutran_plmn_valid = true;
+ memcpy(&lchan->release.last_eutran_plmn, last_eutran_plmn, sizeof(*last_eutran_plmn));
+ }
/* States waiting for events will notice the desire to release when done waiting, so it is enough
* to mark for release. */
@@ -1616,8 +1879,8 @@ static void lchan_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
if (lchan->fi->state == LCHAN_ST_BORKEN) {
- rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN]);
- osmo_stat_item_dec(lchan->ts->trx->bts->bts_statg->items[BTS_STAT_LCHAN_BORKEN], 1);
+ rate_ctr_inc(rate_ctr_group_get_ctr(lchan->ts->trx->bts->bts_ctrs, BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN));
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(lchan->ts->trx->bts->bts_statg, BTS_STAT_LCHAN_BORKEN), 1);
}
lchan_reset(lchan);
if (lchan->last_error) {
diff --git a/src/osmo-bsc/lchan_rtp_fsm.c b/src/osmo-bsc/lchan_rtp_fsm.c
index cd195d021..4cfec5421 100644
--- a/src/osmo-bsc/lchan_rtp_fsm.c
+++ b/src/osmo-bsc/lchan_rtp_fsm.c
@@ -31,6 +31,7 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/lchan.h>
static struct osmo_fsm lchan_rtp_fsm;
@@ -43,10 +44,10 @@ struct gsm_lchan *lchan_rtp_fi_lchan(struct osmo_fsm_inst *fi)
}
struct osmo_tdef_state_timeout lchan_rtp_fsm_timeouts[32] = {
- [LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE] = { .T=-9 },
- [LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK] = { .T=-7 },
- [LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK] = { .T=-8 },
- [LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = { .T=-10 },
+ [LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE] = { .T = -9 },
+ [LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK] = { .T = -7 },
+ [LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK] = { .T = -8 },
+ [LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = { .T = -10 },
};
/* Transition to a state, using the T timer defined in lchan_rtp_fsm_timeouts.
@@ -63,13 +64,13 @@ struct osmo_tdef_state_timeout lchan_rtp_fsm_timeouts[32] = {
#define lchan_rtp_fail(fmt, args...) do { \
struct gsm_lchan *_lchan = fi->priv; \
uint32_t state_was = fi->state; \
- lchan_set_last_error(_lchan, "lchan-rtp failure in state %s: " fmt, \
+ LCHAN_SET_LAST_ERROR(_lchan, "lchan-rtp failure in state %s: " fmt, \
osmo_fsm_state_name(fi->fsm, state_was), ## args); \
osmo_fsm_inst_dispatch(_lchan->fi, LCHAN_EV_RTP_ERROR, 0); \
- } while(0)
+ } while (0)
/* Called from lchan_fsm_init(), does not need to be visible in lchan_rtp_fsm.h */
-void lchan_rtp_fsm_init()
+static __attribute__((constructor)) void lchan_rtp_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&lchan_rtp_fsm) == 0);
}
@@ -137,9 +138,10 @@ static void lchan_rtp_fsm_wait_mgw_endpoint_available_onenter(struct osmo_fsm_in
uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
+ struct gsm_bts *bts = lchan->ts->trx->bts;
struct osmo_mgcpc_ep *mgwep;
struct osmo_mgcpc_ep_ci *use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);
- struct mgcp_conn_peer crcx_info = {};
+ struct mgcp_conn_peer crcx_info;
if (!is_ipaccess_bts(lchan->ts->trx->bts)) {
LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "Audio link to-BTS via E1, skipping IPACC\n");
@@ -162,14 +164,36 @@ static void lchan_rtp_fsm_wait_mgw_endpoint_available_onenter(struct osmo_fsm_in
lchan->mgw_endpoint_ci_bts = osmo_mgcpc_ep_ci_add(mgwep, "to-BTS");
+ crcx_info = (struct mgcp_conn_peer){
+ .ptime = 20,
+ .x_osmo_osmux_cid = -1, /* -1 is wildcard, .x_osmo_osmux_use set below */
+ };
if (lchan->conn) {
crcx_info.call_id = lchan->conn->sccp.conn_id;
if (lchan->conn->sccp.msc)
crcx_info.x_osmo_ign = lchan->conn->sccp.msc->x_osmo_ign;
}
- crcx_info.ptime = 20;
mgcp_pick_codec(&crcx_info, lchan, true);
+ /* Set up Osmux use in MGW according to configured policy */
+ bool amr_picked = mgcp_codec_is_picked(&crcx_info, CODEC_AMR_8000_1);
+ switch (bts->use_osmux) {
+ case OSMUX_USAGE_OFF:
+ crcx_info.x_osmo_osmux_use = false;
+ break;
+ case OSMUX_USAGE_ON:
+ crcx_info.x_osmo_osmux_use = amr_picked;
+ break;
+ case OSMUX_USAGE_ONLY:
+ if (!amr_picked) {
+ lchan_rtp_fail("Only AMR codec can be used when configured with policy 'osmux only'."
+ " Check your configuration.");
+ return;
+ }
+ crcx_info.x_osmo_osmux_use = true;
+ break;
+ }
+
osmo_mgcpc_ep_ci_request(lchan->mgw_endpoint_ci_bts, MGCP_VERB_CRCX, &crcx_info,
fi, LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE, LCHAN_RTP_EV_MGW_ENDPOINT_ERROR,
0);
@@ -178,11 +202,26 @@ static void lchan_rtp_fsm_wait_mgw_endpoint_available_onenter(struct osmo_fsm_in
static void lchan_rtp_fsm_wait_mgw_endpoint_available(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
+ struct gsm_bts *bts = lchan->ts->trx->bts;
switch (event) {
case LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE:
LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "MGW endpoint: %s\n",
osmo_mgcpc_ep_ci_name(lchan_use_mgw_endpoint_ci_bts(lchan)));
+ if (osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(lchan->mgw_endpoint_ci_bts,
+ &lchan->abis_ip.osmux.local_cid)) {
+ if (bts->use_osmux == OSMUX_USAGE_OFF) {
+ lchan_rtp_fail("Got Osmux CID from MGW but we didn't ask for it");
+ return;
+ }
+ lchan->abis_ip.osmux.use = true;
+ } else {
+ if (bts->use_osmux == OSMUX_USAGE_ONLY) {
+ lchan_rtp_fail("Got no Osmux CID from MGW but Osmux is mandatory");
+ return;
+ }
+ lchan->abis_ip.osmux.use = false;
+ }
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_LCHAN_READY);
return;
@@ -268,20 +307,20 @@ static void lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter(struct osmo_fsm_inst *fi,
return;
}
- val = ipacc_speech_mode(lchan->tch_mode, lchan->type);
+ val = ipacc_speech_mode(lchan->activate.ch_mode_rate.chan_mode, lchan->type);
if (val < 0) {
- lchan_rtp_fail("Cannot determine Abis/IP speech mode for tch_mode=%s type=%s\n",
- get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
- gsm_lchant_name(lchan->type));
+ lchan_rtp_fail("Cannot determine Abis/IP speech mode for tch_mode=%s type=%s",
+ get_value_string(gsm48_chan_mode_names, lchan->activate.ch_mode_rate.chan_mode),
+ gsm_chan_t_name(lchan->type));
return;
}
lchan->abis_ip.speech_mode = val;
- val = ipacc_payload_type(lchan->tch_mode, lchan->type);
+ val = ipacc_payload_type(lchan->activate.ch_mode_rate.chan_mode, lchan->type);
if (val < 0) {
- lchan_rtp_fail("Cannot determine Abis/IP payload type for tch_mode=%s type=%s\n",
- get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
- gsm_lchant_name(lchan->type));
+ lchan_rtp_fail("Cannot determine Abis/IP payload type for tch_mode=%s type=%s",
+ get_value_string(gsm48_chan_mode_names, lchan->activate.ch_mode_rate.chan_mode),
+ gsm_chan_t_name(lchan->type));
return;
}
lchan->abis_ip.rtp_payload = val;
@@ -411,9 +450,16 @@ static void connect_mgw_endpoint_to_lchan(struct osmo_fsm_inst *fi,
struct in_addr addr;
const char *addr_str;
+ if (lchan->abis_ip.osmux.use && !lchan->abis_ip.osmux.remote_cid_present) {
+ lchan_rtp_fail("BTS didn't provide any remote Osmux CID for the call");
+ return;
+ }
+
mdcx_info = (struct mgcp_conn_peer){
.port = to_lchan->abis_ip.bound_port,
.ptime = 20,
+ .x_osmo_osmux_use = lchan->abis_ip.osmux.use,
+ .x_osmo_osmux_cid = lchan->abis_ip.osmux.remote_cid,
};
mgcp_pick_codec(&mdcx_info, to_lchan, true);
@@ -522,6 +568,12 @@ static void lchan_rtp_fsm_ready(struct osmo_fsm_inst *fi, uint32_t event, void *
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_ROLLBACK);
return;
+ case LCHAN_RTP_EV_READY_TO_SWITCH_RTP:
+ /* Ignore / silence an "event not permitted" error. In case of an inter-BSC incoming handover, there is
+ * no previous lchan to be switched over, and we are already in this state when the usual handover code
+ * path emits this event. */
+ return;
+
default:
OSMO_ASSERT(false);
}
@@ -704,6 +756,7 @@ static const struct osmo_fsm_state lchan_rtp_fsm_states[] = {
| S(LCHAN_RTP_EV_ESTABLISHED)
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
+ | S(LCHAN_RTP_EV_READY_TO_SWITCH_RTP)
,
.out_state_mask = 0
| S(LCHAN_RTP_ST_ESTABLISHED)
@@ -796,7 +849,7 @@ static struct osmo_fsm lchan_rtp_fsm = {
/* Depending on the channel mode and rate, return the codec type that is signalled towards the MGW. */
static enum mgcp_codecs chan_mode_to_mgcp_codec(enum gsm48_chan_mode chan_mode, bool full_rate)
{
- switch (chan_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(chan_mode)) {
case GSM48_CMODE_SPEECH_V1:
if (full_rate)
return CODEC_GSM_8000_1;
@@ -834,14 +887,14 @@ static int chan_mode_to_mgcp_bss_pt(enum mgcp_codecs codec)
void mgcp_pick_codec(struct mgcp_conn_peer *verb_info, const struct gsm_lchan *lchan, bool bss_side)
{
- enum mgcp_codecs codec = chan_mode_to_mgcp_codec(lchan->tch_mode,
+ enum mgcp_codecs codec = chan_mode_to_mgcp_codec(lchan->activate.ch_mode_rate.chan_mode,
lchan->type == GSM_LCHAN_TCH_H? false : true);
int custom_pt;
if (codec < 0) {
LOG_LCHAN(lchan, LOGL_ERROR,
"Unable to determine MGCP codec type for %s in chan-mode %s\n",
- gsm_lchant_name(lchan->type), gsm48_chan_mode_name(lchan->tch_mode));
+ gsm_chan_t_name(lchan->type), gsm48_chan_mode_name(lchan->activate.ch_mode_rate.chan_mode));
verb_info->codecs_len = 0;
return;
}
@@ -853,8 +906,8 @@ void mgcp_pick_codec(struct mgcp_conn_peer *verb_info, const struct gsm_lchan *l
custom_pt = chan_mode_to_mgcp_bss_pt(codec);
if (bss_side && custom_pt > 0) {
verb_info->ptmap[0].codec = codec;
- verb_info->ptmap[0].pt = custom_pt;
- verb_info->ptmap_len = 1;
+ verb_info->ptmap[0].pt = custom_pt;
+ verb_info->ptmap_len = 1;
}
/* AMR requires additional parameters to be set up (framing mode) */
@@ -866,7 +919,7 @@ void mgcp_pick_codec(struct mgcp_conn_peer *verb_info, const struct gsm_lchan *l
if (bss_side && verb_info->codecs[0] == CODEC_AMR_8000_1) {
/* FIXME: At the moment all BTSs we support are using the
* octet-aligned payload format. However, in the future
- * we may support BTSs that are using bandwith-efficient
+ * we may support BTSs that are using bandwidth-efficient
* format. In this case we will have to add functionality
* that distinguishes by the BTS model which mode to use. */
verb_info->param.amr_octet_aligned = true;
@@ -875,3 +928,8 @@ void mgcp_pick_codec(struct mgcp_conn_peer *verb_info, const struct gsm_lchan *l
verb_info->param.amr_octet_aligned = lchan->conn->sccp.msc->amr_octet_aligned;
}
}
+
+bool mgcp_codec_is_picked(const struct mgcp_conn_peer *verb_info, enum mgcp_codecs codec)
+{
+ return verb_info->codecs[0] == codec;
+}
diff --git a/src/osmo-bsc/lchan_select.c b/src/osmo-bsc/lchan_select.c
index 6d3caaced..2388e2449 100644
--- a/src/osmo-bsc/lchan_select.c
+++ b/src/osmo-bsc/lchan_select.c
@@ -2,7 +2,7 @@
*
* (C) 2008 by Harald Welte <laforge@gnumonks.org>
* (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * (C) 2018-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -21,6 +21,8 @@
*
*/
+#include <stdlib.h>
+
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
@@ -30,148 +32,332 @@
#include <osmocom/bsc/lchan_select.h>
#include <osmocom/bsc/bts.h>
-static struct gsm_lchan *
-_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan,
- enum gsm_phys_chan_config as_pchan)
+struct lchan_select_ts_list {
+ struct gsm_bts_trx_ts **list;
+ unsigned int num;
+};
+
+const struct value_string lchan_select_reason_names[] = {
+ OSMO_VALUE_STRING(SELECT_FOR_MS_CHAN_REQ),
+ OSMO_VALUE_STRING(SELECT_FOR_ASSIGNMENT),
+ OSMO_VALUE_STRING(SELECT_FOR_HANDOVER),
+ {0, NULL}
+};
+
+static struct gsm_lchan *pick_better_lchan(struct gsm_lchan *a, struct gsm_lchan *b)
+{
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+ /* comparing negative dBm values: smaller value means less interference. */
+ if (b->interf_dbm < a->interf_dbm)
+ return b;
+ return a;
+}
+
+static struct gsm_lchan *_lc_find(struct lchan_select_ts_list *ts_list,
+ enum gsm_phys_chan_config pchan,
+ enum gsm_phys_chan_config as_pchan,
+ bool allow_pchan_switch, bool log)
{
struct gsm_lchan *lchan;
- struct gsm_bts_trx_ts *ts;
- int j, start, stop, dir;
+ struct gsm_lchan *found_lchan = NULL;
-#define LOGPLCHANALLOC(fmt, args...) \
- LOGP(DRLL, LOGL_DEBUG, "looking for lchan %s%s%s: " fmt, \
+#define LOGPLCHANALLOC(fmt, args...) do { \
+ if (log) \
+ LOGP(DRLL, LOGL_DEBUG, "looking for lchan %s%s%s%s: " fmt, \
gsm_pchan_name(pchan), \
pchan == as_pchan ? "" : " as ", \
- pchan == as_pchan ? "" : gsm_pchan_name(as_pchan), ## args)
+ pchan == as_pchan ? "" : gsm_pchan_name(as_pchan), \
+ ((pchan != as_pchan) && !allow_pchan_switch) ? " without pchan switch" : "", \
+ ## args); \
+ } while (0)
- if (!trx_is_usable(trx)) {
- LOGPLCHANALLOC("%s trx not usable\n", gsm_trx_name(trx));
- return NULL;
- }
+ for (unsigned int tn = 0; tn < ts_list->num; tn++) {
+ struct gsm_bts_trx_ts *ts = ts_list->list[tn];
+ int lchans_as_pchan;
- if (trx->bts->chan_alloc_reverse) {
- /* check TS 7..0 */
- start = 7;
- stop = -1;
- dir = -1;
- } else {
- /* check TS 0..7 */
- start = 0;
- stop = 8;
- dir = 1;
- }
-
- for (j = start; j != stop; j += dir) {
- ts = &trx->ts[j];
- if (!ts_is_usable(ts))
- continue;
/* The caller first selects what kind of TS to search in, e.g. looking for exact
- * GSM_PCHAN_TCH_F, or maybe among dynamic GSM_PCHAN_TCH_F_TCH_H_PDCH... */
+ * GSM_PCHAN_TCH_F, or maybe among dynamic GSM_PCHAN_OSMO_DYN... */
if (ts->pchan_on_init != pchan) {
LOGPLCHANALLOC("%s is != %s\n", gsm_ts_and_pchan_name(ts),
gsm_pchan_name(pchan));
continue;
}
/* Next, is this timeslot in or can it be switched to the pchan we want to use it for? */
- if (!ts_usable_as_pchan(ts, as_pchan)) {
- LOGPLCHANALLOC("%s is not usable as %s\n", gsm_ts_and_pchan_name(ts),
- gsm_pchan_name(as_pchan));
+ if (!ts_usable_as_pchan(ts, as_pchan, allow_pchan_switch)) {
+ LOGPLCHANALLOC("%s is not usable as %s%s\n", gsm_ts_and_pchan_name(ts),
+ gsm_pchan_name(as_pchan),
+ allow_pchan_switch ? "" : " without pchan switch");
continue;
}
/* TS is (going to be) in desired pchan mode. Go ahead and check for an available lchan. */
- ts_as_pchan_for_each_lchan(lchan, ts, as_pchan) {
- if (lchan->fi->state == LCHAN_ST_UNUSED) {
- LOGPLCHANALLOC("%s ss=%d is available%s\n",
+ lchans_as_pchan = pchan_subslots(as_pchan);
+ ts_for_n_lchans(lchan, ts, lchans_as_pchan) {
+ struct gsm_lchan *was = found_lchan;
+
+ if (lchan->fi->state != LCHAN_ST_UNUSED) {
+ LOGPLCHANALLOC("%s ss=%d in type=%s,state=%s not suitable\n",
gsm_ts_and_pchan_name(ts), lchan->nr,
- ts->pchan_is != as_pchan ? " after dyn PCHAN change" : "");
- return lchan;
+ gsm_chan_t_name(lchan->type),
+ osmo_fsm_inst_state_name(lchan->fi));
+ continue;
}
- LOGPLCHANALLOC("%s ss=%d in type=%s,state=%s not suitable\n",
- gsm_ts_and_pchan_name(ts), lchan->nr,
- gsm_lchant_name(lchan->type),
- osmo_fsm_inst_state_name(lchan->fi));
+
+ found_lchan = pick_better_lchan(found_lchan, lchan);
+ if (found_lchan != was)
+ LOGPLCHANALLOC("%s ss=%d interf=%u=%ddBm is %s%s\n",
+ gsm_ts_and_pchan_name(ts), lchan->nr,
+ lchan->interf_band, lchan->interf_dbm,
+ was == NULL ? "available" : "better",
+ ts->pchan_is != as_pchan ? ", after dyn PCHAN change" : "");
+ else
+ LOGPLCHANALLOC("%s ss=%d interf=%u=%ddBm is also available but not better\n",
+ gsm_ts_and_pchan_name(ts), lchan->nr,
+ lchan->interf_band, lchan->interf_dbm);
+
+ /* When picking an lchan with least interference, continue to loop across all lchans. When
+ * ignoring interference levels, return the first match. */
+ if (found_lchan && !ts->trx->bts->chan_alloc_avoid_interf)
+ return found_lchan;
}
}
- return NULL;
+ if (found_lchan)
+ LOGPLCHANALLOC("%s ss=%d interf=%ddBm%s is the best pick\n",
+ gsm_ts_and_pchan_name(found_lchan->ts), found_lchan->nr,
+ found_lchan->interf_dbm,
+ found_lchan->ts->pchan_is != as_pchan ? ", after dyn PCHAN change," : "");
+ else
+ LOGPLCHANALLOC("Nothing found\n");
+ return found_lchan;
#undef LOGPLCHANALLOC
}
-static struct gsm_lchan *
-_lc_dyn_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan,
- enum gsm_phys_chan_config dyn_as_pchan)
+static struct gsm_lchan *lc_dyn_find(struct lchan_select_ts_list *ts_list,
+ enum gsm_phys_chan_config pchan,
+ enum gsm_phys_chan_config dyn_as_pchan,
+ bool log)
{
- struct gsm_bts_trx *trx;
- struct gsm_lchan *lc;
+ struct gsm_lchan *lchan;
- if (bts->chan_alloc_reverse) {
- llist_for_each_entry_reverse(trx, &bts->trx_list, list) {
- lc = _lc_find_trx(trx, pchan, dyn_as_pchan);
- if (lc)
- return lc;
- }
- } else {
- llist_for_each_entry(trx, &bts->trx_list, list) {
- lc = _lc_find_trx(trx, pchan, dyn_as_pchan);
- if (lc)
- return lc;
- }
- }
+ /* First find an lchan that needs no change in its timeslot pchan mode.
+ * In particular, this ensures that handover to a dynamic timeslot in TCH/H favors timeslots that are currently
+ * using only one of two TCH/H, so that we don't switch more dynamic timeslots to TCH/H than necessary.
+ * For non-dynamic timeslots, it is not necessary to do a second pass with allow_pchan_switch ==
+ * true, because they never switch anyway. */
+ if ((lchan = _lc_find(ts_list, pchan, dyn_as_pchan, false, log)))
+ return lchan;
+ if ((lchan = _lc_find(ts_list, pchan, dyn_as_pchan, true, log)))
+ return lchan;
return NULL;
}
-static struct gsm_lchan *
-_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
+static struct gsm_lchan *lc_find(struct lchan_select_ts_list *ts_list,
+ enum gsm_phys_chan_config pchan,
+ bool log)
{
- return _lc_dyn_find_bts(bts, pchan, pchan);
+ return _lc_find(ts_list, pchan, pchan, false, log);
}
-struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts,
- enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate)
+enum gsm_chan_t chan_mode_to_chan_type(enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate)
{
- enum gsm_chan_t type;
-
- switch (chan_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(chan_mode)) {
case GSM48_CMODE_SIGN:
switch (chan_rate) {
- case CH_RATE_SDCCH: type = GSM_LCHAN_SDCCH; break;
- case CH_RATE_HALF: type = GSM_LCHAN_TCH_H; break;
- case CH_RATE_FULL: type = GSM_LCHAN_TCH_F; break;
- default: return NULL;
+ case CH_RATE_SDCCH:
+ return GSM_LCHAN_SDCCH;
+ case CH_RATE_HALF:
+ return GSM_LCHAN_TCH_H;
+ case CH_RATE_FULL:
+ return GSM_LCHAN_TCH_F;
+ default:
+ return GSM_LCHAN_NONE;
}
- break;
case GSM48_CMODE_SPEECH_EFR:
/* EFR works over FR channels only */
if (chan_rate != CH_RATE_FULL)
- return NULL;
+ return GSM_LCHAN_NONE;
/* fall through */
case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_AMR:
switch (chan_rate) {
- case CH_RATE_HALF: type = GSM_LCHAN_TCH_H; break;
- case CH_RATE_FULL: type = GSM_LCHAN_TCH_F; break;
- default: return NULL;
+ case CH_RATE_HALF:
+ return GSM_LCHAN_TCH_H;
+ case CH_RATE_FULL:
+ return GSM_LCHAN_TCH_F;
+ default:
+ return GSM_LCHAN_NONE;
}
- break;
default:
- return NULL;
+ return GSM_LCHAN_NONE;
}
+}
+
+static int qsort_func(const void *_a, const void *_b)
+{
+ const struct gsm_bts_trx *trx_a = *(const struct gsm_bts_trx **)_a;
+ const struct gsm_bts_trx *trx_b = *(const struct gsm_bts_trx **)_b;
- return lchan_select_by_type(bts, type);
+ int pwr_a = trx_a->nominal_power - trx_a->max_power_red;
+ int pwr_b = trx_b->nominal_power - trx_b->max_power_red;
+
+ /* Sort in descending order */
+ return pwr_b - pwr_a;
}
-struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type)
+static void populate_ts_list(struct lchan_select_ts_list *ts_list,
+ struct gsm_bts *bts,
+ bool chan_alloc_reverse,
+ bool sort_by_trx_power,
+ bool log)
+{
+ struct gsm_bts_trx **trx_list;
+ struct gsm_bts_trx *trx;
+ unsigned int num = 0;
+
+ /* Allocate an array with pointers to all TRX instances of a BTS */
+ trx_list = talloc_array_ptrtype(bts, trx_list, bts->num_trx);
+ OSMO_ASSERT(trx_list != NULL);
+
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ trx_list[trx->nr] = trx;
+
+ /* Sort by TRX power in descending order (if needed) */
+ if (sort_by_trx_power)
+ qsort(&trx_list[0], bts->num_trx, sizeof(trx), &qsort_func);
+
+ for (unsigned int trxn = 0; trxn < bts->num_trx; trxn++) {
+ trx = trx_list[trxn];
+ for (unsigned int tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ if (ts_is_usable(ts))
+ ts_list->list[num++] = ts;
+ else if (log)
+ LOGP(DRLL, LOGL_DEBUG, "%s is not usable\n", gsm_ts_name(ts));
+ }
+ }
+
+ talloc_free(trx_list);
+ ts_list->num = num;
+
+ /* Reverse the timeslot list if required */
+ if (chan_alloc_reverse) {
+ for (unsigned int tn = 0; tn < num / 2; tn++) {
+ struct gsm_bts_trx_ts *temp = ts_list->list[tn];
+ ts_list->list[tn] = ts_list->list[num - tn - 1];
+ ts_list->list[num - tn - 1] = temp;
+ }
+ }
+}
+
+static bool chan_alloc_ass_dynamic_reverse(struct gsm_bts *bts,
+ void *ctx, bool log)
+{
+ const struct load_counter *ll = &bts->c0->lchan_load;
+ const struct gsm_lchan *old_lchan = ctx;
+ unsigned int lchan_load;
+ int avg_ul_rxlev;
+
+ OSMO_ASSERT(old_lchan != NULL);
+ OSMO_ASSERT(old_lchan->ts->trx->bts == bts);
+
+#define LOG_COND(fmt, args...) do { \
+ if (log) \
+ LOG_LCHAN(old_lchan, LOGL_DEBUG, fmt, ## args); \
+ } while (0)
+
+ /* Condition a) Channel load on the C0 (BCCH carrier) */
+ lchan_load = ll->total ? ll->used * 100 / ll->total : 0;
+ if (lchan_load < bts->chan_alloc_dyn_params.c0_chan_load_thresh) {
+ LOG_COND("C0 Channel Load %u%% < thresh %u%% => using ascending order\n",
+ lchan_load, bts->chan_alloc_dyn_params.c0_chan_load_thresh);
+ return false;
+ }
+
+ /* Condition b) average Uplink RxLev */
+ avg_ul_rxlev = get_meas_rep_avg(old_lchan, TDMA_MEAS_FIELD_RXLEV,
+ TDMA_MEAS_DIR_UL, TDMA_MEAS_SET_AUTO,
+ bts->chan_alloc_dyn_params.ul_rxlev_avg_num);
+ if (avg_ul_rxlev < 0) {
+ LOG_COND("Unknown AVG UL RxLev => using ascending order\n");
+ return false;
+ }
+ if (avg_ul_rxlev < bts->chan_alloc_dyn_params.ul_rxlev_thresh) {
+ LOG_COND("AVG UL RxLev %u < thresh %u => using ascending order\n",
+ avg_ul_rxlev, bts->chan_alloc_dyn_params.ul_rxlev_thresh);
+ return false;
+ }
+
+ LOG_COND("C0 Channel Load %u%% >= thresh %u%% and "
+ "AVG UL RxLev %u >= thresh %u => using descending order\n",
+ lchan_load, bts->chan_alloc_dyn_params.c0_chan_load_thresh,
+ avg_ul_rxlev, bts->chan_alloc_dyn_params.ul_rxlev_thresh);
+
+#undef LOG_COND
+
+ return true;
+}
+
+struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts,
+ enum gsm48_chan_mode chan_mode,
+ enum channel_rate chan_rate,
+ enum lchan_select_reason reason,
+ void *ctx)
+{
+ enum gsm_chan_t type = chan_mode_to_chan_type(chan_mode, chan_rate);
+ if (type == GSM_LCHAN_NONE)
+ return NULL;
+ return lchan_select_by_type(bts, type, reason, ctx);
+}
+
+struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts,
+ enum gsm_chan_t type,
+ enum lchan_select_reason reason,
+ void *ctx, bool log)
{
struct gsm_lchan *lchan = NULL;
enum gsm_phys_chan_config first, first_cbch, second, second_cbch;
+ struct lchan_select_ts_list ts_list;
+ bool sort_by_trx_power = false;
+ bool chan_alloc_reverse = false;
- LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_avail_by_type(%s)\n", gsm_lchant_name(type));
+ if (log) {
+ LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_avail_by_type(type=%s, reason=%s)\n",
+ gsm_chan_t_name(type), lchan_select_reason_name(reason));
+ }
+
+ switch (reason) {
+ case SELECT_FOR_MS_CHAN_REQ:
+ chan_alloc_reverse = bts->chan_alloc_chan_req_reverse;
+ break;
+ case SELECT_FOR_ASSIGNMENT:
+ if (bts->chan_alloc_assignment_dynamic) {
+ chan_alloc_reverse = chan_alloc_ass_dynamic_reverse(bts, ctx, log);
+ sort_by_trx_power = bts->chan_alloc_dyn_params.sort_by_trx_power;
+ } else {
+ chan_alloc_reverse = bts->chan_alloc_assignment_reverse;
+ }
+ break;
+ case SELECT_FOR_HANDOVER:
+ chan_alloc_reverse = bts->chan_alloc_handover_reverse;
+ break;
+ }
+
+ /* Allocate an array with pointers to all timeslots of a BTS */
+ ts_list.list = talloc_array_ptrtype(bts, ts_list.list, bts->num_trx * 8);
+ if (OSMO_UNLIKELY(ts_list.list == NULL))
+ return NULL;
+
+ /* Populate this array with the actual pointers */
+ populate_ts_list(&ts_list, bts, chan_alloc_reverse, sort_by_trx_power, log);
switch (type) {
case GSM_LCHAN_SDCCH:
- if (bts->chan_alloc_reverse) {
+ if (chan_alloc_reverse) {
first = GSM_PCHAN_SDCCH8_SACCH8C;
first_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
second = GSM_PCHAN_CCCH_SDCCH4;
@@ -183,71 +369,79 @@ struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type)
second_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
}
- lchan = _lc_find_bts(bts, first);
+ lchan = lc_find(&ts_list, first, log);
if (lchan == NULL)
- lchan = _lc_find_bts(bts, first_cbch);
+ lchan = lc_find(&ts_list, first_cbch, log);
if (lchan == NULL)
- lchan = _lc_find_bts(bts, second);
+ lchan = lc_find(&ts_list, second, log);
if (lchan == NULL)
- lchan = _lc_find_bts(bts, second_cbch);
+ lchan = lc_find(&ts_list, second_cbch, log);
+ /* No dedicated SDCCH available -- try fully dynamic
+ * TCH/F_TCH/H_SDCCH8_PDCH if BTS supports it: */
+ if (lchan == NULL && osmo_bts_has_feature(&bts->features, BTS_FEAT_DYN_TS_SDCCH8))
+ lchan = lc_dyn_find(&ts_list, GSM_PCHAN_OSMO_DYN,
+ GSM_PCHAN_SDCCH8_SACCH8C, log);
break;
case GSM_LCHAN_TCH_F:
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
+ lchan = lc_find(&ts_list, GSM_PCHAN_TCH_F, log);
/* If we don't have TCH/F available, try dynamic TCH/F_PDCH */
- if (!lchan) {
- lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_PDCH,
- GSM_PCHAN_TCH_F);
- /* TCH/F_PDCH used as TCH/F -- here, type is already
- * set to GSM_LCHAN_TCH_F, but for clarity's sake... */
- if (lchan)
- type = GSM_LCHAN_TCH_F;
- }
+ if (!lchan)
+ lchan = lc_dyn_find(&ts_list, GSM_PCHAN_TCH_F_PDCH,
+ GSM_PCHAN_TCH_F, log);
/* Try fully dynamic TCH/F_TCH/H_PDCH as TCH/F... */
- if (!lchan && bts->network->dyn_ts_allow_tch_f) {
- lchan = _lc_dyn_find_bts(bts,
- GSM_PCHAN_TCH_F_TCH_H_PDCH,
- GSM_PCHAN_TCH_F);
- if (lchan)
- type = GSM_LCHAN_TCH_F;
- }
+ if (!lchan && bts->network->dyn_ts_allow_tch_f)
+ lchan = lc_dyn_find(&ts_list, GSM_PCHAN_OSMO_DYN,
+ GSM_PCHAN_TCH_F, log);
break;
case GSM_LCHAN_TCH_H:
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H);
+ lchan = lc_find(&ts_list, GSM_PCHAN_TCH_H, log);
/* No dedicated TCH/x available -- try fully dynamic
* TCH/F_TCH/H_PDCH */
- if (!lchan) {
- lchan = _lc_dyn_find_bts(bts,
- GSM_PCHAN_TCH_F_TCH_H_PDCH,
- GSM_PCHAN_TCH_H);
- if (lchan)
- type = GSM_LCHAN_TCH_H;
- }
+ if (!lchan)
+ lchan = lc_dyn_find(&ts_list, GSM_PCHAN_OSMO_DYN,
+ GSM_PCHAN_TCH_H, log);
break;
default:
LOG_BTS(bts, DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type);
}
+ talloc_free(ts_list.list);
+
return lchan;
}
/* Return a matching lchan from a specific BTS that is currently available. The next logical step is
* lchan_activate() on it, which would possibly cause dynamic timeslot pchan switching, taken care of by
* the lchan and timeslot FSMs. */
-struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type)
+struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts,
+ enum gsm_chan_t type,
+ enum lchan_select_reason reason,
+ void *ctx)
{
struct gsm_lchan *lchan = NULL;
- lchan = lchan_avail_by_type(bts, type);
+ LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_select_by_type(type=%s, reason=%s)\n",
+ gsm_chan_t_name(type), lchan_select_reason_name(reason));
- LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_select_by_type(%s)\n", gsm_lchant_name(type));
+ lchan = lchan_avail_by_type(bts, type, reason, ctx, true);
- if (lchan) {
- lchan->type = type;
- LOG_LCHAN(lchan, LOGL_INFO, "Selected\n");
- } else
- LOG_BTS(bts, DRLL, LOGL_NOTICE, "Failed to select %s channel\n",
- gsm_lchant_name(type));
+ if (!lchan) {
+ LOG_BTS(bts, DRLL, LOGL_NOTICE, "Failed to select %s channel (%s)\n",
+ gsm_chan_t_name(type), lchan_select_reason_name(reason));
+ return NULL;
+ }
+ lchan_select_set_type(lchan, type);
return lchan;
}
+
+/* Set available lchan to given type. Usually used on lchan obtained with
+ * lchan_avail_by_type. The next logical step is lchan_activate() on it, which
+ * would possibly cause dynamic timeslot pchan switching, taken care of by the
+ * lchan and timeslot FSMs. */
+void lchan_select_set_type(struct gsm_lchan *lchan, enum gsm_chan_t type)
+{
+ lchan->type = type;
+ LOG_LCHAN(lchan, LOGL_INFO, "Selected\n");
+}
diff --git a/src/osmo-bsc/lcs_loc_req.c b/src/osmo-bsc/lcs_loc_req.c
index 5de3676fb..bb0c5e273 100644
--- a/src/osmo-bsc/lcs_loc_req.c
+++ b/src/osmo-bsc/lcs_loc_req.c
@@ -75,13 +75,13 @@ static const struct osmo_tdef_state_timeout lcs_loc_req_fsm_timeouts[32] = {
#define lcs_loc_req_fail(cause, fmt, args...) do { \
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Perform Location Request failed in state %s: " fmt "\n", \
- osmo_fsm_inst_state_name(lcs_loc_req->fi), ## args); \
+ lcs_loc_req ? osmo_fsm_inst_state_name(lcs_loc_req->fi) : "NULL", ## args); \
lcs_loc_req->lcs_cause = (struct lcs_cause_ie){ \
.present = true, \
.cause_val = cause, \
}; \
lcs_loc_req_fsm_state_chg(lcs_loc_req->fi, LCS_LOC_REQ_ST_FAILED); \
- } while(0)
+ } while (0)
static struct lcs_loc_req *lcs_loc_req_alloc(struct osmo_fsm_inst *parent_fi, uint32_t parent_event_term)
{
@@ -104,13 +104,13 @@ static bool parse_bssmap_perf_loc_req(struct lcs_loc_req *lcs_loc_req, struct ms
{
struct tlv_parsed tp_arr[1];
struct tlv_parsed *tp = &tp_arr[0];
- struct tlv_p_entry *e;
+ const struct tlv_p_entry *e;
int payload_length;
#define PARSE_ERR(ERRMSG) do { \
lcs_loc_req_fail(LCS_CAUSE_PROTOCOL_ERROR, "rx BSSMAP Perform Location Request: " ERRMSG); \
return false; \
- } while(0)
+ } while (0)
payload_length = msg->tail - msg->l4h;
if (tlv_parse2(tp_arr, 1, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0) <= 0)
@@ -127,6 +127,29 @@ static bool parse_bssmap_perf_loc_req(struct lcs_loc_req *lcs_loc_req, struct ms
lcs_loc_req->req.cell_id_present = true;
}
+ /* 3GPP TS 49.031, section 10.14 (C) "LCS Client Type" */
+ if (TLVP_PRES_LEN(tp, GSM0808_IE_LCS_CLIENT_TYPE, 1)) {
+ lcs_loc_req->req.client_type = *TLVP_VAL(tp, GSM0808_IE_LCS_CLIENT_TYPE);
+ lcs_loc_req->req.client_type_present = true;
+ } else if (lcs_loc_req->req.location_type.location_information == BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC)
+ PARSE_ERR("Missing LCS Client Type IE");
+
+ /* 3GPP TS 49.031, section 10.15 (O) "LCS Priority" */
+ if (TLVP_PRES_LEN(tp, GSM0808_IE_LCS_PRIORITY, 1)) {
+ lcs_loc_req->req.priority = *TLVP_VAL(tp, GSM0808_IE_LCS_PRIORITY);
+ lcs_loc_req->req.priority_present = true;
+ }
+
+ /* 3GPP TS 49.031, section 10.16 (C) "LCS QoS" */
+ if (TLVP_PRES_LEN(tp, GSM0808_IE_LCS_QOS, sizeof(lcs_loc_req->req.qos))) {
+ size_t qos_len = TLVP_LEN(tp, GSM0808_IE_LCS_QOS);
+ if (qos_len > sizeof(lcs_loc_req->req.qos))
+ qos_len = sizeof(lcs_loc_req->req.qos);
+ memcpy(&lcs_loc_req->req.qos, TLVP_VAL(tp, GSM0808_IE_LCS_QOS), qos_len);
+ lcs_loc_req->req.qos_present = true;
+ } else if (lcs_loc_req->req.location_type.location_information == BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC)
+ PARSE_ERR("Missing LCS QoS IE");
+
if ((e = TLVP_GET(tp, GSM0808_IE_IMSI))) {
if (osmo_mobile_identity_decode(&lcs_loc_req->req.imsi, e->val, e->len, false)
|| lcs_loc_req->req.imsi.type != GSM_MI_TYPE_IMSI)
@@ -139,8 +162,6 @@ static bool parse_bssmap_perf_loc_req(struct lcs_loc_req *lcs_loc_req, struct ms
PARSE_ERR("Failed to parse IMEI IE");
}
- // FIXME LCS QoS IE is mandatory for requesting the location
-
/* A lot of IEs remain ignored... */
return true;
@@ -159,10 +180,7 @@ void lcs_loc_req_start(struct gsm_subscriber_connection *conn, struct msgb *loc_
lcs_loc_req = lcs_loc_req_alloc(conn->fi, GSCON_EV_LCS_LOC_REQ_END);
- *lcs_loc_req = (struct lcs_loc_req){
- .fi = lcs_loc_req->fi,
- .conn = conn,
- };
+ lcs_loc_req->conn = conn;
conn->lcs.loc_req = lcs_loc_req;
if (!parse_bssmap_perf_loc_req(lcs_loc_req, loc_req_msg))
@@ -193,7 +211,7 @@ static int handle_bssmap_le_conn_oriented_info(struct lcs_loc_req *lcs_loc_req,
{
switch (bssmap_le->conn_oriented_info.apdu.msg_type) {
case BSSLAP_MSGT_TA_REQUEST:
- rate_ctr_inc(&bsc_gsmnet->smlc.ctrs->ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_REQUEST]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_REQUEST));
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_DEBUG, "rx BSSLAP TA Request\n");
/* The TA Request message contains only the message type. */
return lcs_ta_req_start(lcs_loc_req);
@@ -208,8 +226,8 @@ int lcs_loc_req_rx_bssmap_le(struct gsm_subscriber_connection *conn, struct msgb
{
struct lcs_loc_req *lcs_loc_req = conn->lcs.loc_req;
struct bssap_le_pdu bssap_le;
- struct osmo_bssap_le_err *err;
- struct rate_ctr *ctr = bsc_gsmnet->smlc.ctrs->ctr;
+ struct osmo_bssap_le_err *err = NULL;
+ struct rate_ctr_group *ctrg = bsc_gsmnet->smlc->ctrs;
if (!lcs_loc_req) {
LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR,
@@ -219,13 +237,13 @@ int lcs_loc_req_rx_bssmap_le(struct gsm_subscriber_connection *conn, struct msgb
if (osmo_bssap_le_dec(&bssap_le, &err, msg, msg)) {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Rx BSSAP-LE message with error: %s\n", err->logmsg);
- rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG));
return -EINVAL;
}
if (bssap_le.discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE) {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Rx BSSAP-LE: discr %d not implemented\n", bssap_le.discr);
- rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG));
return -ENOTSUP;
}
@@ -234,9 +252,9 @@ int lcs_loc_req_rx_bssmap_le(struct gsm_subscriber_connection *conn, struct msgb
switch (bssap_le.bssmap_le.msg_type) {
case BSSMAP_LE_MSGT_PERFORM_LOC_RESP:
if (bssap_le.bssmap_le.perform_loc_resp.location_estimate_present)
- rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS));
else
- rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE));
return osmo_fsm_inst_dispatch(lcs_loc_req->fi, LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE,
&bssap_le.bssmap_le);
@@ -303,6 +321,17 @@ static void lcs_loc_req_wait_loc_resp_onenter(struct osmo_fsm_inst *fi, uint32_t
.cell_id = lcs_loc_req->req.cell_id,
.imsi = lcs_loc_req->req.imsi,
.imei = lcs_loc_req->req.imei,
+
+ .lcs_client_type_present = lcs_loc_req->req.client_type_present,
+ .lcs_client_type = lcs_loc_req->req.client_type,
+
+ .more_items = true,
+
+ .lcs_priority_present = lcs_loc_req->req.priority_present,
+ .lcs_priority = lcs_loc_req->req.priority,
+
+ .lcs_qos_present = lcs_loc_req->req.qos_present,
+ .lcs_qos = lcs_loc_req->req.qos,
},
},
};
@@ -316,7 +345,7 @@ static void lcs_loc_req_wait_loc_resp_onenter(struct osmo_fsm_inst *fi, uint32_t
plr.bssmap_le.perform_loc_req.apdu = (struct bsslap_pdu){
.msg_type = BSSLAP_MSGT_TA_LAYER3,
.ta_layer3 = {
- .ta = lchan->rqd_ta,
+ .ta = lchan->last_ta,
},
};
} else {
@@ -368,11 +397,14 @@ static void lcs_loc_req_handover_performed(struct lcs_loc_req *lcs_loc_req)
.msg_type = BSSLAP_MSGT_RESET,
.reset = {
.cell_id = lchan->ts->trx->bts->cell_identity,
- .ta = lchan->rqd_ta,
+ .ta = lchan->last_ta,
.cause = BSSLAP_CAUSE_INTRA_BSS_HO,
},
};
- gsm48_lchan2chan_desc(&apdu->reset.chan_desc, lchan);
+ if (gsm48_lchan2chan_desc(&apdu->reset.chan_desc, lchan, lchan->tsc, false)) {
+ lcs_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Error encoding Channel Number");
+ return;
+ }
}
lcs_loc_req_send(lcs_loc_req, &bsslap);
@@ -460,9 +492,7 @@ static void lcs_loc_req_got_loc_resp_onenter(struct osmo_fsm_inst *fi, uint32_t
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
"Failed to send Perform Location Response (A-interface)\n");
else
- rate_ctr_inc(&lcs_loc_req->conn->sccp.msc->msc_ctrs->ctr[
- plr.location_estimate_present ? MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS
- : MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(lcs_loc_req->conn->sccp.msc->msc_ctrs, plr.location_estimate_present ? MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS : MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE));
}
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
}
@@ -484,7 +514,8 @@ static void lcs_loc_req_failed_onenter(struct osmo_fsm_inst *fi, uint32_t prev_s
};
/* If we're paging this subscriber for LCS, stop paging. */
- paging_request_cancel(lcs_loc_req->conn->bsub, BSC_PAGING_FOR_LCS);
+ if (lcs_loc_req->conn->bsub)
+ paging_request_cancel(lcs_loc_req->conn->bsub, BSC_PAGING_FOR_LCS);
/* Send Perform Location Abort to SMLC, only if we got started on the Lb */
if (lcs_loc_req->conn->lcs.lb.state == SUBSCR_SCCP_ST_CONNECTED)
@@ -501,7 +532,7 @@ static void lcs_loc_req_failed_onenter(struct osmo_fsm_inst *fi, uint32_t prev_s
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
"Failed to send BSSMAP Perform Location Response (A-interface)\n");
else
- rate_ctr_inc(&lcs_loc_req->conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(lcs_loc_req->conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE));
}
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
}
diff --git a/src/osmo-bsc/lcs_ta_req.c b/src/osmo-bsc/lcs_ta_req.c
index 850474cef..f0bb02eb6 100644
--- a/src/osmo-bsc/lcs_ta_req.c
+++ b/src/osmo-bsc/lcs_ta_req.c
@@ -56,14 +56,14 @@ static const struct osmo_tdef_state_timeout lcs_ta_req_fsm_timeouts[32] = {
osmo_tdef_fsm_inst_state_chg(FI, STATE, \
lcs_ta_req_fsm_timeouts, \
(bsc_gsmnet)->T_defs, \
- 5)
+ -1)
#define lcs_ta_req_fail(cause, fmt, args...) do { \
LOG_LCS_TA_REQ(lcs_ta_req, LOGL_ERROR, "BSSLAP TA Request failed in state %s: " fmt "\n", \
- osmo_fsm_inst_state_name(lcs_ta_req->fi), ## args); \
+ lcs_ta_req ? osmo_fsm_inst_state_name(lcs_ta_req->fi) : "NULL", ## args); \
lcs_ta_req->failure_cause = cause; \
lcs_ta_req_fsm_state_chg(lcs_ta_req->fi, LCS_TA_REQ_ST_FAILED); \
- } while(0)
+ } while (0)
static struct osmo_fsm lcs_ta_req_fsm;
@@ -122,17 +122,6 @@ void lcs_ta_req_wait_ta_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
return;
}
- paging = (struct bsc_paging_params){
- .reason = BSC_PAGING_FOR_LCS,
- .msc = loc_req->conn->sccp.msc,
- .bsub = loc_req->conn->bsub,
- .tmsi = GSM_RESERVED_TMSI,
- .imsi = loc_req->req.imsi,
- .chan_needed = RSL_CHANNEED_ANY,
- };
- if (paging.bsub)
- bsc_subscr_get(paging.bsub, BSUB_USE_PAGING_START);
-
/* Do we already have an active lchan with knowledge of TA? */
lchan = loc_req->conn->lchan;
if (lchan) {
@@ -147,6 +136,17 @@ void lcs_ta_req_wait_ta_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
return;
}
+ paging = (struct bsc_paging_params){
+ .reason = BSC_PAGING_FOR_LCS,
+ .msc = loc_req->conn->sccp.msc,
+ .bsub = loc_req->conn->bsub,
+ .tmsi = GSM_RESERVED_TMSI,
+ .imsi = loc_req->req.imsi,
+ .chan_needed = RSL_CHANNEED_ANY,
+ };
+ if (paging.bsub)
+ bsc_subscr_get(paging.bsub, BSUB_USE_PAGING_START);
+
if (!loc_req->req.cell_id_present) {
LOG_LCS_TA_REQ(lcs_ta_req, LOGL_DEBUG,
"No Cell Identity in BSSMAP Location Request, paging entire BSS\n");
@@ -210,7 +210,7 @@ void lcs_ta_req_got_ta_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
.msg_type = BSSLAP_MSGT_TA_RESPONSE,
.ta_response = {
.cell_id = lchan->ts->trx->bts->cell_identity,
- .ta = lchan->rqd_ta,
+ .ta = lchan->last_ta,
},
},
},
diff --git a/src/osmo-bsc/meas_feed.c b/src/osmo-bsc/meas_feed.c
index 889f6efc3..afd96e18b 100644
--- a/src/osmo-bsc/meas_feed.c
+++ b/src/osmo-bsc/meas_feed.c
@@ -20,6 +20,7 @@
#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/lchan.h>
struct meas_feed_state {
struct osmo_wqueue wqueue;
@@ -119,7 +120,7 @@ static int feed_read_cb(struct osmo_fd *ofd)
char buf[256];
rc = read(ofd->fd, buf, sizeof(buf));
- ofd->fd &= ~OSMO_FD_READ;
+ osmo_fd_read_disable(ofd);
return rc;
}
@@ -159,7 +160,7 @@ int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port)
if (rc < 0)
return rc;
- g_mfs.wqueue.bfd.when &= ~OSMO_FD_READ;
+ osmo_fd_read_disable(&g_mfs.wqueue.bfd);
if (g_mfs.dst_host)
talloc_free(g_mfs.dst_host);
diff --git a/src/osmo-bsc/meas_rep.c b/src/osmo-bsc/meas_rep.c
index 73d9a1f21..776c610df 100644
--- a/src/osmo-bsc/meas_rep.c
+++ b/src/osmo-bsc/meas_rep.c
@@ -31,11 +31,15 @@ static int get_field(const struct gsm_meas_rep *rep,
case MEAS_REP_DL_RXLEV_FULL:
if (!(rep->flags & MEAS_REP_F_DL_VALID))
return -EINVAL;
- return rep->dl.full.rx_lev;
+ /* Add BS Power value to rxlev: improve the RXLEV value by the amount of power that the BTS is reducing
+ * transmission. Note that bs_power is coded as dB, a positive value indicating the amount of power reduction
+ * on the downlink; rxlev is coded in dB, where a higher number means stronger signal. */
+ return rep->dl.full.rx_lev + rep->bs_power_db;
case MEAS_REP_DL_RXLEV_SUB:
if (!(rep->flags & MEAS_REP_F_DL_VALID))
return -EINVAL;
- return rep->dl.sub.rx_lev;
+ /* Apply BS Power as explained above */
+ return rep->dl.sub.rx_lev + rep->bs_power_db;
case MEAS_REP_DL_RXQUAL_FULL:
if (!(rep->flags & MEAS_REP_F_DL_VALID))
return -EINVAL;
@@ -76,9 +80,53 @@ unsigned int calc_initial_idx(unsigned int array_size,
return idx;
}
-/* obtain an average over the last 'num' fields in the meas reps */
+static inline enum meas_rep_field choose_meas_rep_field(enum tdma_meas_field field, enum tdma_meas_dir dir,
+ enum tdma_meas_set set, const struct gsm_meas_rep *meas_rep)
+{
+ if (set == TDMA_MEAS_SET_AUTO) {
+ bool dtx_in_use;
+ dtx_in_use = (meas_rep->flags & ((dir == TDMA_MEAS_DIR_UL) ? MEAS_REP_F_UL_DTX : MEAS_REP_F_DL_DTX));
+ set = (dtx_in_use ? TDMA_MEAS_SET_SUB : TDMA_MEAS_SET_FULL);
+ }
+
+ osmo_static_assert(TDMA_MEAS_FIELD_RXLEV >= 0 && TDMA_MEAS_FIELD_RXLEV <= 1
+ && TDMA_MEAS_FIELD_RXQUAL >= 0 && TDMA_MEAS_FIELD_RXQUAL <= 1
+ && TDMA_MEAS_DIR_UL >= 0 && TDMA_MEAS_DIR_UL <= 1
+ && TDMA_MEAS_DIR_DL >= 0 && TDMA_MEAS_DIR_DL <= 1
+ && TDMA_MEAS_SET_FULL >= 0 && TDMA_MEAS_SET_FULL <= 1
+ && TDMA_MEAS_SET_SUB >= 0 && TDMA_MEAS_SET_SUB <= 1,
+ choose_meas_rep_field__mux_macro_input_ranges);
+#define MUX(FIELD, DIR, SET) ((FIELD) + ((DIR) << 1) + ((SET) << 2))
+
+ switch (MUX(field, dir, set)) {
+ case MUX(TDMA_MEAS_FIELD_RXLEV, TDMA_MEAS_DIR_UL, TDMA_MEAS_SET_FULL):
+ return MEAS_REP_UL_RXLEV_FULL;
+ case MUX(TDMA_MEAS_FIELD_RXLEV, TDMA_MEAS_DIR_UL, TDMA_MEAS_SET_SUB):
+ return MEAS_REP_UL_RXLEV_SUB;
+ case MUX(TDMA_MEAS_FIELD_RXLEV, TDMA_MEAS_DIR_DL, TDMA_MEAS_SET_FULL):
+ return MEAS_REP_DL_RXLEV_FULL;
+ case MUX(TDMA_MEAS_FIELD_RXLEV, TDMA_MEAS_DIR_DL, TDMA_MEAS_SET_SUB):
+ return MEAS_REP_DL_RXLEV_SUB;
+ case MUX(TDMA_MEAS_FIELD_RXQUAL, TDMA_MEAS_DIR_UL, TDMA_MEAS_SET_FULL):
+ return MEAS_REP_UL_RXQUAL_FULL;
+ case MUX(TDMA_MEAS_FIELD_RXQUAL, TDMA_MEAS_DIR_UL, TDMA_MEAS_SET_SUB):
+ return MEAS_REP_UL_RXQUAL_SUB;
+ case MUX(TDMA_MEAS_FIELD_RXQUAL, TDMA_MEAS_DIR_DL, TDMA_MEAS_SET_FULL):
+ return MEAS_REP_DL_RXQUAL_FULL;
+ case MUX(TDMA_MEAS_FIELD_RXQUAL, TDMA_MEAS_DIR_DL, TDMA_MEAS_SET_SUB):
+ return MEAS_REP_DL_RXQUAL_SUB;
+ default:
+ OSMO_ASSERT(false);
+ }
+
+#undef MUX
+}
+
+/* obtain an average over the last 'num' fields in the meas reps. For 'field', pass either DL_RXLEV or DL_RXQUAL, and
+ * by tdma_meas_set, choose between full, subset or automatic choice of set. */
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)
{
unsigned int i, idx;
int avg = 0, valid_num = 0;
@@ -94,7 +142,11 @@ int get_meas_rep_avg(const struct gsm_lchan *lchan,
for (i = 0; i < num; i++) {
int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep);
- int val = get_field(&lchan->meas_rep[j], field);
+ enum meas_rep_field use_field;
+ int val;
+
+ use_field = choose_meas_rep_field(field, dir, set, &lchan->meas_rep[j]);
+ val = get_field(&lchan->meas_rep[j], use_field);
if (val >= 0) {
avg += val;
@@ -110,8 +162,8 @@ int get_meas_rep_avg(const struct gsm_lchan *lchan,
/* 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 i, idx;
int count = 0;
@@ -121,7 +173,11 @@ int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan,
for (i = 0; i < m; i++) {
int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep);
- int val = get_field(&lchan->meas_rep[j], field);
+ enum meas_rep_field use_field;
+ int val;
+
+ use_field = choose_meas_rep_field(field, dir, set, &lchan->meas_rep[j]);
+ val = get_field(&lchan->meas_rep[j], use_field);
if (val >= be) /* implies that val < 0 will not count */
count++;
@@ -132,3 +188,10 @@ int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan,
return 0;
}
+
+const struct value_string tdma_meas_set_names[] = {
+ { TDMA_MEAS_SET_FULL, "full" },
+ { TDMA_MEAS_SET_SUB, "subset" },
+ { TDMA_MEAS_SET_AUTO, "auto" },
+ {}
+};
diff --git a/src/osmo-bsc/neighbor_ident.c b/src/osmo-bsc/neighbor_ident.c
index 4a0cd47ad..3e42c5f2d 100644
--- a/src/osmo-bsc/neighbor_ident.c
+++ b/src/osmo-bsc/neighbor_ident.c
@@ -33,88 +33,247 @@
#include <osmocom/bsc/neighbor_ident.h>
-struct neighbor_ident_list {
- struct llist_head list;
-};
+#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/ctrl/control_if.h>
-struct neighbor_ident {
- struct llist_head entry;
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/debug.h>
- struct neighbor_ident_key key;
- struct gsm0808_cell_id_list2 val;
-};
+void bts_cell_ab(struct cell_ab *arfcn_bsic, const struct gsm_bts *bts)
+{
+ *arfcn_bsic = (struct cell_ab){
+ .arfcn = bts->c0->arfcn,
+ .bsic = bts->bsic,
+ };
+}
+
+/* Find the local gsm_bts pointer that a specific other BTS' neighbor config refers to. Return NULL if there is no such
+ * local cell in this BSS.
+ */
+int resolve_local_neighbor(struct gsm_bts **local_neighbor_p, const struct gsm_bts *from_bts,
+ const struct neighbor *neighbor)
+{
+ struct gsm_bts *bts;
+ struct gsm_bts *bts_exact = NULL;
+ struct gsm_bts *bts_wildcard = NULL;
+ *local_neighbor_p = NULL;
+
+ switch (neighbor->type) {
+ case NEIGHBOR_TYPE_BTS_NR:
+ bts = gsm_bts_num(bsc_gsmnet, neighbor->bts_nr);
+ goto check_bts;
+
+ case NEIGHBOR_TYPE_CELL_ID:
+ /* Find cell id below */
+ break;
+
+ default:
+ return -ENOTSUP;
+ }
+
+ /* NEIGHBOR_TYPE_CELL_ID */
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
+ struct gsm0808_cell_id cell_id;
+ gsm_bts_cell_id(&cell_id, bts);
+
+ if (gsm0808_cell_ids_match(&cell_id, &neighbor->cell_id.id, true)) {
+ if (bts_exact) {
+ LOGP(DHO, LOGL_ERROR,
+ "Neighbor config error: Multiple BTS match %s (BTS %u and BTS %u)\n",
+ gsm0808_cell_id_name_c(OTC_SELECT, &neighbor->cell_id.id),
+ bts_exact->nr, bts->nr);
+ return -EINVAL;
+ } else {
+ bts_exact = bts;
+ }
+ }
+
+ if (!bts_wildcard && gsm0808_cell_ids_match(&cell_id, &neighbor->cell_id.id, false))
+ bts_wildcard = bts;
+ }
-#define APPEND_THING(func, args...) do { \
- int remain = buflen - (pos - buf); \
- int l = func(pos, remain, ##args); \
- if (l < 0 || l > remain) \
- pos = buf + buflen; \
- else \
- pos += l; \
- } while(0)
-#define APPEND_STR(fmt, args...) APPEND_THING(snprintf, fmt, ##args)
+ bts = (bts_exact ? : bts_wildcard);
-const char *_neighbor_ident_key_name(char *buf, size_t buflen, const struct neighbor_ident_key *ni_key)
+check_bts:
+ /* A cell cannot be its own neighbor */
+ if (bts == from_bts) {
+ LOGP(DHO, LOGL_ERROR,
+ "Neighbor config error: BTS %u -> %s: this cell is configured as its own neighbor\n",
+ from_bts->nr, neighbor_to_str_c(OTC_SELECT, neighbor));
+ return -EINVAL;
+ }
+
+ if (!bts)
+ return -ENOENT;
+
+ /* Double check whether ARFCN + BSIC config matches, if present. */
+ if (neighbor->cell_id.ab_present) {
+ struct cell_ab cell_ab;
+ bts_cell_ab(&cell_ab, bts);
+ if (!cell_ab_match(&cell_ab, &neighbor->cell_id.ab, false)) {
+ LOGP(DHO, LOGL_ERROR, "Neighbor config error: Local BTS %d matches %s, but not ARFCN+BSIC %s\n",
+ bts->nr, gsm0808_cell_id_name_c(OTC_SELECT, &neighbor->cell_id.id),
+ cell_ab_to_str_c(OTC_SELECT, &cell_ab));
+ return -EINVAL;
+ }
+ }
+
+ *local_neighbor_p = bts;
+ return 0;
+}
+
+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)
{
- char *pos = buf;
+ struct neighbor *n;
+ struct gsm_bts *local_neighbor = NULL;
+ struct gsm0808_cell_id_list2 remotes = {};
+
+ if (local_neighbor_p)
+ *local_neighbor_p = NULL;
+ if (remote_neighbors)
+ *remote_neighbors = (struct gsm0808_cell_id_list2){ 0 };
+
+ llist_for_each_entry(n, &from_bts->neighbors, entry) {
+ struct gsm_bts *neigh_bts;
+ if (resolve_local_neighbor(&neigh_bts, from_bts, n) == 0) {
+ /* This neighbor entry is a local cell neighbor. Do ARFCN and BSIC match? */
+ struct cell_ab ab;
+ bts_cell_ab(&ab, neigh_bts);
+ if (!cell_ab_match(&ab, target_ab, false))
+ continue;
+
+ /* Found a local cell neighbor that matches the target_ab */
+
+ /* If we already found one, these are ambiguous local neighbors */
+ if (local_neighbor) {
+ if (log_errors)
+ LOGP(DHO, LOGL_ERROR, "Neighbor config error:"
+ " Local BTS %d -> %s resolves to local neighbor BTSes %u *and* %u\n",
+ from_bts->nr, cell_ab_to_str_c(OTC_SELECT, target_ab), local_neighbor->nr,
+ neigh_bts->nr);
+ return -ENOTSUP;
+ }
+ local_neighbor = neigh_bts;
+
+ } else if (n->type == NEIGHBOR_TYPE_CELL_ID && n->cell_id.ab_present) {
+ /* This neighbor entry is a remote-BSS neighbor. There may be multiple remote neighbors,
+ * collect those in a gsm0808_cell_id_list2 (remote_target_cells). A limitation is that all of
+ * them need to be of the same cell id type. */
+ struct gsm0808_cell_id_list2 add_item;
+ int rc;
+
+ if (!cell_ab_match(&n->cell_id.ab, target_ab, false))
+ continue;
+
+ /* Convert the gsm0808_cell_id to a list, so that we can use gsm0808_cell_id_list_add(). */
+ gsm0808_cell_id_to_list(&add_item, &n->cell_id.id);
+ rc = gsm0808_cell_id_list_add(&remotes, &add_item);
+ if (rc < 0) {
+ if (log_errors)
+ LOGP(DHO, LOGL_ERROR, "Neighbor config error:"
+ " Local BTS %d -> %s resolves to remote-BSS neighbor %s;"
+ " Could not store this in neighbors list %s\n",
+ from_bts->nr, cell_ab_to_str_c(OTC_SELECT, target_ab),
+ gsm0808_cell_id_name_c(OTC_SELECT, &n->cell_id.id),
+ gsm0808_cell_id_list_name_c(OTC_SELECT, &remotes));
+ return rc;
+ }
+ }
+ /* else: neighbor entry that does not resolve to anything. */
+ }
- APPEND_STR("BTS ");
- if (ni_key->from_bts == NEIGHBOR_IDENT_KEY_ANY_BTS)
- APPEND_STR("*");
- else if (ni_key->from_bts >= 0 && ni_key->from_bts <= 255)
- APPEND_STR("%d", ni_key->from_bts);
- else
- APPEND_STR("invalid(%d)", ni_key->from_bts);
+ if (local_neighbor_p)
+ *local_neighbor_p = local_neighbor;
+ if (remote_neighbors)
+ *remote_neighbors = remotes;
- APPEND_STR(" to ");
- if (ni_key->bsic == BSIC_ANY)
- APPEND_STR("ARFCN %u (any BSIC)", ni_key->arfcn);
- else
- APPEND_STR("ARFCN %u BSIC %u", ni_key->arfcn, ni_key->bsic & 0x3f);
- return buf;
+ if (!local_neighbor && !remotes.id_list_len)
+ return -ENOENT;
+ return 0;
}
-const char *neighbor_ident_key_name(const struct neighbor_ident_key *ni_key)
+int cell_ab_to_str_buf(char *buf, size_t buflen, const struct cell_ab *cell)
{
- static char buf[64];
- return _neighbor_ident_key_name(buf, sizeof(buf), ni_key);
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ OSMO_STRBUF_PRINTF(sb, "ARFCN-BSIC:%u", cell->arfcn);
+ if (cell->bsic == BSIC_ANY)
+ OSMO_STRBUF_PRINTF(sb, "-any");
+ else {
+ OSMO_STRBUF_PRINTF(sb, "-%u", cell->bsic);
+ if (cell->bsic > 0x3f)
+ OSMO_STRBUF_PRINTF(sb, "[ERANGE>63]");
+ }
+ return sb.chars_needed;
+}
+
+char *cell_ab_to_str_c(void *ctx, const struct cell_ab *cell)
+{
+ OSMO_NAME_C_IMPL(ctx, 64, "ERROR", cell_ab_to_str_buf, cell)
+}
+
+int neighbor_to_str_buf(char *buf, size_t buflen, const struct neighbor *n)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ switch (n->type) {
+ case NEIGHBOR_TYPE_BTS_NR:
+ OSMO_STRBUF_PRINTF(sb, "BTS %u", n->bts_nr);
+ break;
+ case NEIGHBOR_TYPE_CELL_ID:
+ OSMO_STRBUF_APPEND_NOLEN(sb, gsm0808_cell_id_name_buf, &n->cell_id.id);
+ if (n->cell_id.ab_present) {
+ OSMO_STRBUF_PRINTF(sb, " ");
+ OSMO_STRBUF_APPEND(sb, cell_ab_to_str_buf, &n->cell_id.ab);
+ }
+ break;
+ case NEIGHBOR_TYPE_UNSET:
+ OSMO_STRBUF_PRINTF(sb, "UNSET");
+ break;
+ default:
+ OSMO_STRBUF_PRINTF(sb, "INVALID");
+ break;
+ }
+ return sb.chars_needed;
}
-struct neighbor_ident_list *neighbor_ident_init(void *talloc_ctx)
+char *neighbor_to_str_c(void *ctx, const struct neighbor *n)
{
- struct neighbor_ident_list *nil = talloc_zero(talloc_ctx, struct neighbor_ident_list);
- OSMO_ASSERT(nil);
- INIT_LLIST_HEAD(&nil->list);
- return nil;
+ OSMO_NAME_C_IMPL(ctx, 64, "ERROR", neighbor_to_str_buf, n);
}
-void neighbor_ident_free(struct neighbor_ident_list *nil)
+bool neighbor_same(const struct neighbor *a, const struct neighbor *b, bool check_cell_ab)
{
- if (!nil)
- return;
- talloc_free(nil);
+ if (a == b)
+ return true;
+ if (a->type != b->type)
+ return false;
+
+ switch (a->type) {
+ case NEIGHBOR_TYPE_BTS_NR:
+ return a->bts_nr == b->bts_nr;
+
+ case NEIGHBOR_TYPE_CELL_ID:
+ if (check_cell_ab
+ && (a->cell_id.ab_present != b->cell_id.ab_present
+ || !cell_ab_match(&a->cell_id.ab, &b->cell_id.ab, true)))
+ return false;
+ return gsm0808_cell_ids_match(&a->cell_id.id, &b->cell_id.id, true);
+ default:
+ return a->type == b->type;
+ }
}
/* Return true when the entry matches the search_for requirements.
* If exact_match is false, a BSIC_ANY entry acts as wildcard to match any search_for on that ARFCN,
- * and a BSIC_ANY in search_for likewise returns any one entry that matches the ARFCN;
- * also a from_bts == NEIGHBOR_IDENT_KEY_ANY_BTS in either entry or search_for will match.
- * If exact_match is true, only identical bsic values and identical from_bts values return a match.
+ * and a BSIC_ANY in search_for likewise returns any one entry that matches the ARFCN.
+ * If exact_match is true, only identical bsic values return a match.
* Note, typically wildcard BSICs are only in entry, e.g. the user configured list, and search_for
* contains a specific BSIC, e.g. as received from a Measurement Report. */
-bool neighbor_ident_key_match(const struct neighbor_ident_key *entry,
- const struct neighbor_ident_key *search_for,
- bool exact_match)
+bool cell_ab_match(const struct cell_ab *entry,
+ const struct cell_ab *search_for,
+ bool exact_match)
{
- if (exact_match
- && entry->from_bts != search_for->from_bts)
- return false;
-
- if (search_for->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS
- && entry->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS
- && entry->from_bts != search_for->from_bts)
- return false;
-
if (entry->arfcn != search_for->arfcn)
return false;
@@ -127,129 +286,210 @@ bool neighbor_ident_key_match(const struct neighbor_ident_key *entry,
return entry->bsic == search_for->bsic;
}
-static struct neighbor_ident *_neighbor_ident_get(const struct neighbor_ident_list *nil,
- const struct neighbor_ident_key *key,
- bool exact_match)
+bool cell_ab_valid(const struct cell_ab *cell)
{
- struct neighbor_ident *ni;
- struct neighbor_ident *wildcard_match = NULL;
+ if (cell->bsic != BSIC_ANY && cell->bsic > 0x3f)
+ return false;
+ return true;
+}
- /* Do both exact-bsic and wildcard matching in the same iteration:
- * Any exact match returns immediately, while for a wildcard match we still go through all
- * remaining items in case an exact match exists. */
- llist_for_each_entry(ni, &nil->list, entry) {
- if (neighbor_ident_key_match(&ni->key, key, true))
- return ni;
- if (!exact_match) {
- if (neighbor_ident_key_match(&ni->key, key, false))
- wildcard_match = ni;
+int neighbors_check_cfg(void)
+{
+ /* A local neighbor can be configured by BTS number, or by a cell ID. A local neighbor can omit the ARFCN+BSIC,
+ * in which case those are taken from that local BTS config. If a local neighbor has ARFCN+BSIC configured, it
+ * must match the local cell's configuration.
+ *
+ * A remote neighbor must always be a cell ID *and* ARFCN+BSIC.
+ *
+ * Hence any cell ID with ARFCN+BSIC where the cell ID is not found among the local cells is a remote-BSS
+ * neighbor.
+ */
+ struct gsm_bts *bts;
+ bool ok = true;
+
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
+ struct neighbor *neighbor;
+ struct gsm_bts *local_neighbor;
+ llist_for_each_entry(neighbor, &bts->neighbors, entry) {
+ switch (neighbor->type) {
+
+ case NEIGHBOR_TYPE_BTS_NR:
+ if (!gsm_bts_num(bsc_gsmnet, neighbor->bts_nr)) {
+ LOGP(DHO, LOGL_ERROR, "Neighbor Configuration Error:"
+ " BTS %u -> BTS %u: There is no BTS nr %u\n",
+ bts->nr, neighbor->bts_nr, neighbor->bts_nr);
+ ok = false;
+ }
+ break;
+
+ default:
+ switch (resolve_local_neighbor(&local_neighbor, bts, neighbor)) {
+ case 0:
+ break;
+ case -ENOENT:
+ if (!neighbor->cell_id.ab_present) {
+ LOGP(DHO, LOGL_ERROR, "Neighbor Configuration Error:"
+ " BTS %u -> %s: There is no such local neighbor\n",
+ bts->nr, neighbor_to_str_c(OTC_SELECT, neighbor));
+ ok = false;
+ }
+ break;
+ default:
+ /* Error already logged in resolve_local_neighbor() */
+ ok = false;
+ break;
+ }
+ break;
+ }
}
}
- return wildcard_match;
-}
-static void _neighbor_ident_free(struct neighbor_ident *ni)
-{
- llist_del(&ni->entry);
- talloc_free(ni);
+ if (!ok)
+ return -EINVAL;
+ return 0;
}
-bool neighbor_ident_key_valid(const struct neighbor_ident_key *key)
+/* Neighbor Resolution CTRL iface */
+
+CTRL_CMD_DEFINE_RO(neighbor_resolve_cgi_ps_from_lac_ci, "neighbor_resolve_cgi_ps_from_lac_ci");
+
+static int gsm_bts_get_cgi_ps(const struct gsm_bts *bts, struct osmo_cell_global_id_ps *cgi_ps)
{
- if (key->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS
- && (key->from_bts < 0 || key->from_bts > 255))
- return false;
+ if (bts->gprs.mode == BTS_GPRS_NONE)
+ return -ENOTSUP;
- if (key->bsic != BSIC_ANY && key->bsic > 0x3f)
- return false;
- return true;
+ cgi_ps->rai.lac.plmn = bts->network->plmn;
+ cgi_ps->rai.lac.lac = bts->location_area_code;
+ cgi_ps->rai.rac = bts->gprs.rac;
+ cgi_ps->cell_identity = bts->cell_identity;
+
+ return 0;
}
-/*! Add Cell Identifiers to an ARFCN+BSIC entry.
- * Exactly one kind of identifier is allowed per ARFCN+BSIC entry, and any number of entries of that kind
- * may be added up to the capacity of gsm0808_cell_id_list2, by one or more calls to this function. To
- * replace an existing entry, first call neighbor_ident_del(nil, key).
- * \returns number of entries in the resulting identifier list, or negative on error:
- * see gsm0808_cell_id_list_add() for the meaning of returned error codes;
- * return -ENOMEM when the list is not initialized, -ERANGE when the BSIC value is too large. */
-int neighbor_ident_add(struct neighbor_ident_list *nil, const struct neighbor_ident_key *key,
- const struct gsm0808_cell_id_list2 *val)
+/* Attempt resolution of cgi_ps from ARFCN+BSIC of neighbor from BTS identified by LAC+CI */
+int neighbor_address_resolution(const struct gsm_network *net, const struct cell_ab *ab,
+ uint16_t lac, uint16_t cell_id,
+ struct osmo_cell_global_id_ps *res_cgi_ps)
{
- struct neighbor_ident *ni;
- int rc;
+ struct gsm_bts *bts_tmp, *bts_found = NULL;
+ struct osmo_cell_global_id_ps local_cgi_ps;
+ const struct osmo_cell_global_id_ps *cgi_ps = NULL;
+ struct gsm_bts *local_neighbor = NULL;
+ struct gsm0808_cell_id_list2 remote_neighbors = { 0 };
+
+ llist_for_each_entry(bts_tmp, &net->bts_list, list) {
+ if (bts_tmp->location_area_code != lac)
+ continue;
+ if (bts_tmp->cell_identity != cell_id)
+ continue;
+ bts_found = bts_tmp;
+ break;
+ }
+
+ if (!bts_found)
+ goto notfound_err;
- if (!nil)
- return -ENOMEM;
-
- if (!neighbor_ident_key_valid(key))
- return -ERANGE;
-
- ni = _neighbor_ident_get(nil, key, true);
- if (!ni) {
- ni = talloc_zero(nil, struct neighbor_ident);
- OSMO_ASSERT(ni);
- *ni = (struct neighbor_ident){
- .key = *key,
- .val = *val,
- };
- llist_add_tail(&ni->entry, &nil->list);
- return ni->val.id_list_len;
+ LOG_BTS(bts_found, DLINP, LOGL_DEBUG, "Resolving neighbor BTS %u -> %s\n", bts_found->nr,
+ cell_ab_to_str_c(OTC_SELECT, ab));
+
+ if (resolve_neighbors(&local_neighbor, &remote_neighbors, bts_found, ab, true))
+ goto notfound_err;
+
+ /* resolve_neighbors() returns either a local_neighbor or remote_neighbors.
+ * Local-BSS neighbor? */
+ if (local_neighbor) {
+ /* Supporting GPRS? */
+ if (gsm_bts_get_cgi_ps(local_neighbor, &local_cgi_ps) >= 0)
+ cgi_ps = &local_cgi_ps;
}
- rc = gsm0808_cell_id_list_add(&ni->val, val);
+ /* Remote-BSS neighbor?
+ * By spec, there can be multiple remote neighbors for a given ARFCN+BSIC, but so far osmo-bsc enforces only a
+ * single remote neighbor. */
+ if (remote_neighbors.id_list_len
+ && remote_neighbors.id_discr == CELL_IDENT_WHOLE_GLOBAL_PS) {
+ cgi_ps = &remote_neighbors.id_list[0].global_ps;
+ }
- if (rc < 0)
- return rc;
+ /* No neighbor found */
+ if (!cgi_ps)
+ goto notfound_err;
- return ni->val.id_list_len;
-}
+ *res_cgi_ps = *cgi_ps;
+ return 0;
-/*! Find cell identity for given BTS, ARFCN and BSIC, as previously added by neighbor_ident_add().
- */
-const struct gsm0808_cell_id_list2 *neighbor_ident_get(const struct neighbor_ident_list *nil,
- const struct neighbor_ident_key *key)
-{
- struct neighbor_ident *ni;
- if (!nil)
- return NULL;
- ni = _neighbor_ident_get(nil, key, false);
- if (!ni)
- return NULL;
- return &ni->val;
+notfound_err:
+ return -1;
}
-bool neighbor_ident_del(struct neighbor_ident_list *nil, const struct neighbor_ident_key *key)
+static int get_neighbor_resolve_cgi_ps_from_lac_ci(struct ctrl_cmd *cmd, void *data)
{
- struct neighbor_ident *ni;
- if (!nil)
- return false;
- ni = _neighbor_ident_get(nil, key, true);
- if (!ni)
- return false;
- _neighbor_ident_free(ni);
- return true;
+ struct gsm_network *net = (struct gsm_network *)data;
+ char *tmp = NULL, *tok, *saveptr;
+ struct cell_ab ab;
+ unsigned int lac, cell_id;
+ struct osmo_cell_global_id_ps cgi_ps;
+
+ if (!cmd->variable)
+ goto fmt_err;
+
+ tmp = talloc_strdup(cmd, cmd->variable);
+ if (!tmp) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!(tok = strtok_r(tmp, ".", &saveptr)))
+ goto fmt_err;
+ OSMO_ASSERT(strcmp(tok, "neighbor_resolve_cgi_ps_from_lac_ci") == 0);
+
+ if (!(tok = strtok_r(NULL, ".", &saveptr)))
+ goto fmt_err;
+ lac = atoi(tok);
+
+ if (!(tok = strtok_r(NULL, ".", &saveptr)))
+ goto fmt_err;
+ cell_id = atoi(tok);
+
+ if (!(tok = strtok_r(NULL, ".", &saveptr)))
+ goto fmt_err;
+ ab.arfcn = atoi(tok);
+
+ if (!(tok = strtok_r(NULL, "\0", &saveptr)))
+ goto fmt_err;
+ ab.bsic = atoi(tok);
+
+ if (!cell_ab_valid(&ab))
+ goto fmt_err;
+
+ if (neighbor_address_resolution(net, &ab, lac, cell_id, &cgi_ps) < 0)
+ goto notfound_err;
+
+ ctrl_cmd_reply_printf(cmd, "%s", osmo_cgi_ps_name(&cgi_ps));
+ talloc_free(tmp);
+ return CTRL_CMD_REPLY;
+
+notfound_err:
+ talloc_free(tmp);
+ cmd->reply = talloc_strdup(cmd, "No target CGI PS found");
+ return CTRL_CMD_ERROR;
+fmt_err:
+ talloc_free(tmp);
+ cmd->reply = talloc_strdup(cmd, "The format is <src_lac>,<src_cell_id>,<dst_arfcn>,<dst_bsic>");
+ return CTRL_CMD_ERROR;
}
-void neighbor_ident_clear(struct neighbor_ident_list *nil)
+int neighbor_ctrl_cmds_install(struct gsm_network *net)
{
- struct neighbor_ident *ni;
- while ((ni = llist_first_entry_or_null(&nil->list, struct neighbor_ident, entry)))
- _neighbor_ident_free(ni);
+ int rc;
+
+ rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_neighbor_resolve_cgi_ps_from_lac_ci);
+ return rc;
}
-/*! Iterate all neighbor_ident_list entries and call iter_cb for each.
- * If iter_cb returns false, the iteration is stopped. */
-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)
+struct ctrl_handle *neighbor_controlif_setup(struct gsm_network *net)
{
- struct neighbor_ident *ni, *ni_next;
- if (!nil)
- return;
- llist_for_each_entry_safe(ni, ni_next, &nil->list, entry) {
- if (!iter_cb(&ni->key, &ni->val, cb_data))
- return;
- }
+ /* DEPRECATED: see osmobsc-usermanual.pdf, section 16.1.1 Neighbor Address Resolution Service */
+ return ctrl_interface_setup_dynip2(net, net->neigh_ctrl.addr, net->neigh_ctrl.port,
+ NULL, _LAST_CTRL_NODE_NEIGHBOR);
}
diff --git a/src/osmo-bsc/neighbor_ident_ctrl.c b/src/osmo-bsc/neighbor_ident_ctrl.c
new file mode 100644
index 000000000..a9d7b5dc5
--- /dev/null
+++ b/src/osmo-bsc/neighbor_ident_ctrl.c
@@ -0,0 +1,753 @@
+/* CTRL interface implementation to manage identity of neighboring BSS cells for inter-BSC handover. */
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier <pmaier@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <time.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/vty.h>
+
+/* Continue to parse ARFCN and BSIC, which are optional parameters at the end of the parameter string in most of the
+ * commands. The result is ignored when parameter n is set to NULL. */
+static int continue_parse_arfcn_and_bsic(char **saveptr, struct neighbor *n)
+{
+ int arfcn;
+ int bsic;
+ char *tok;
+
+ tok = strtok_r(NULL, "-", saveptr);
+
+ /* No ARFCN and BSIC persent - stop */
+ if (!tok)
+ return 0;
+
+ if (osmo_str_to_int(&arfcn, tok, 10, 0, 1023) < 0)
+ return -EINVAL;
+
+ tok = strtok_r(NULL, "-", saveptr);
+
+ /* When an ARFCN is given, then the BSIC parameter is
+ * mandatory */
+ if (!tok)
+ return -EINVAL;
+
+ if (strcmp(tok, "any") == 0) {
+ bsic = BSIC_ANY;
+ } else {
+ if (osmo_str_to_int(&bsic, tok, 10, 0, 63) < 0)
+ return 1;
+ }
+
+ /* Make sure there are no excess parameters */
+ if (strtok_r(NULL, "-", saveptr))
+ return -EINVAL;
+
+ if (n) {
+ n->cell_id.ab_present = true;
+ n->cell_id.ab.arfcn = arfcn;
+ n->cell_id.ab.bsic = bsic;
+ }
+
+ return 0;
+}
+
+/* This and the following: Add/Remove a BTS as neighbor */
+static int verify_neighbor_bts(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ struct gsm_bts *bts = cmd->node;
+ const int neigh_bts_nr = atoi(value);
+ struct gsm_bts *neigh_bts = gsm_bts_num(bts->network, neigh_bts_nr);
+
+ if (!neigh_bts) {
+ cmd->reply = "Invalid Neighbor BTS number - no such BTS";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int verify_neighbor_bts_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return verify_neighbor_bts(cmd, value, _data);
+}
+
+static int get_neighbor_bts_list(struct ctrl_cmd *cmd, void *data)
+{
+ /* Max. 256 BTS neighbors (as of now, any bts can be its own neighbor per cfg) comma-separated ->
+ * max. 255 commas * + trailing '\0': 256
+ * 10 of those numbers (0...9) are 1-digit numbers: + 10 = 266
+ * 90 of those numbers are 2-digit numbers (10...99): + 90 = 356
+ * 255 - 100 + 1 = 156 are 3-digit numbers (100...255): + 156 = 512 bytes
+ * Double BTS num entries are not possible (check exists and is being tested against in python tests). */
+ char log_buf[512];
+ struct osmo_strbuf reply = { .buf = log_buf,
+ .len = sizeof(log_buf),
+ .pos = log_buf
+ };
+ struct gsm_bts *neighbor_bts, *bts = (struct gsm_bts *)cmd->node;
+ if (!bts) {
+ cmd->reply = "BTS not found";
+ return CTRL_CMD_ERROR;
+ }
+ struct neighbor *n;
+ llist_for_each_entry(n, &bts->neighbors, entry)
+ if (resolve_local_neighbor(&neighbor_bts, bts, n) == 0)
+ OSMO_STRBUF_PRINTF(reply, "%" PRIu8 ",", neighbor_bts->nr);
+ if (reply.buf == reply.pos)
+ cmd->reply = "";
+ else { /* Get rid of trailing comma */
+ reply.pos[-1] = '\0';
+ if (!(cmd->reply = talloc_strdup(cmd, reply.buf)))
+ goto oom;
+ }
+ return CTRL_CMD_REPLY;
+
+oom:
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+}
+
+CTRL_CMD_DEFINE_RO(neighbor_bts_list, "neighbor-bts list");
+
+static int set_neighbor_bts_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ const int bts_nr = atoi(cmd->value);
+ int rc;
+
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_BTS_NR,
+ .bts_nr = bts_nr,
+ };
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<num>"
+ * num: BTS number (0-255) */
+CTRL_CMD_DEFINE_WO(neighbor_bts_add, "neighbor-bts add");
+
+static int verify_neighbor_bts_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return verify_neighbor_bts(cmd, value, _data);
+}
+
+static int set_neighbor_bts_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ const int bts_nr = atoi(cmd->value);
+ int rc;
+
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_BTS_NR,
+ .bts_nr = bts_nr,
+ };
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_bts_del, "neighbor-bts del");
+
+/* This and the following: Add/Remove a LAC as neighbor */
+static int parse_lac(void *ctx, struct neighbor *n, const char *value)
+{
+ char *tmp = NULL, *tok, *saveptr;
+ int rc = 0;
+ int lac;
+
+ if (n)
+ memset(n, 0, sizeof(*n));
+
+ tmp = talloc_strdup(ctx, value);
+ if (!tmp)
+ return -EINVAL;
+
+ /* Parse LAC */
+ tok = strtok_r(tmp, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Optional parameters: ARFCN and BSIC */
+ if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (n) {
+ n->type = NEIGHBOR_TYPE_CELL_ID;
+ n->cell_id.id.id_discr = CELL_IDENT_LAC;
+ n->cell_id.id.id.lac = lac;
+ }
+
+exit:
+ talloc_free(tmp);
+ return rc;
+}
+
+static int verify_neighbor_lac_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_lac(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_lac_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+
+ parse_lac(cmd, &n, cmd->value);
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<lac>[-<arfcn>-<bsic>]"
+ * lac: Location area of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_lac_add, "neighbor-lac add");
+
+static int verify_neighbor_lac_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_lac(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_lac_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+ parse_lac(cmd, &n, cmd->value);
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_lac_del, "neighbor-lac del");
+
+/* This and the following: Add/Remove a LAC-CI as neighbor */
+static int parse_lac_ci(void *ctx, struct neighbor *n, const char *value)
+{
+ char *tmp = NULL, *tok, *saveptr;
+ int rc = 0;
+ int lac;
+ int ci;
+
+ if (n)
+ memset(n, 0, sizeof(*n));
+
+ tmp = talloc_strdup(ctx, value);
+ if (!tmp)
+ return -EINVAL;
+
+ /* Parse LAC */
+ tok = strtok_r(tmp, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse CI */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Optional parameters: ARFCN and BSIC */
+ if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (n) {
+ n->type = NEIGHBOR_TYPE_CELL_ID;
+ n->cell_id.id.id_discr = CELL_IDENT_LAC_AND_CI;
+ n->cell_id.id.id.lac = lac;
+ n->cell_id.id.id.ci = ci;
+ }
+
+exit:
+ talloc_free(tmp);
+ return rc;
+}
+
+static int verify_neighbor_lac_ci_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_lac_ci(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_lac_ci_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+
+ parse_lac_ci(cmd, &n, cmd->value);
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<lac>-<ci>[-<arfcn>-<bsic>]"
+ * lac: Location area of neighbor cell (0-65535)
+ * ci: Cell ID of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_lac_ci_add, "neighbor-lac-ci add");
+
+static int verify_neighbor_lac_ci_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_lac_ci(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_lac_ci_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+ parse_lac_ci(cmd, &n, cmd->value);
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_lac_ci_del, "neighbor-lac-ci del");
+
+/* This and the following: Add/Remove a CGI as neighbor */
+static int parse_cgi(void *ctx, struct neighbor *n, const char *value)
+{
+ char *tmp = NULL, *tok, *saveptr;
+ int rc = 0;
+ uint16_t mcc;
+ uint16_t mnc;
+ bool mnc_3_digits;
+ int lac;
+ int ci;
+
+ if (n)
+ memset(n, 0, sizeof(*n));
+
+ tmp = talloc_strdup(ctx, value);
+ if (!tmp)
+ return -EINVAL;
+
+ /* Parse MCC */
+ tok = strtok_r(tmp, "-", &saveptr);
+ if (tok) {
+ if (osmo_mcc_from_str(tok, &mcc)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse MNC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_mnc_from_str(tok, &mnc, &mnc_3_digits)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse LAC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse CI */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Optional parameters: ARFCN and BSIC */
+ if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (n) {
+ n->type = NEIGHBOR_TYPE_CELL_ID;
+ n->cell_id.id.id_discr = CELL_IDENT_WHOLE_GLOBAL;
+ n->cell_id.id.id.global.lai.lac = lac;
+ n->cell_id.id.id.global.lai.plmn.mcc = mcc;
+ n->cell_id.id.id.global.lai.plmn.mnc = mnc;
+ n->cell_id.id.id.global.lai.plmn.mnc_3_digits = mnc_3_digits;
+ n->cell_id.id.id.global.cell_identity = ci;
+ }
+
+exit:
+ talloc_free(tmp);
+ return rc;
+}
+
+static int verify_neighbor_cgi_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_cgi(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_cgi_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+
+ parse_cgi(cmd, &n, cmd->value);
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"
+ * mcc: Mobile country code of neighbor cell (0-999)
+ * mnc: Mobile network code of neighbor cell (0-999)
+ * lac: Location area of neighbor cell (0-65535)
+ * ci: Cell ID of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_add, "neighbor-cgi add");
+
+static int verify_neighbor_cgi_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_cgi(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_cgi_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+ parse_cgi(cmd, &n, cmd->value);
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_del, "neighbor-cgi del");
+
+/* This and the following: Add/Remove a CGI-PS as neighbor */
+static int parse_cgi_ps(void *ctx, struct neighbor *n, const char *value)
+{
+ char *tmp = NULL, *tok, *saveptr;
+ int rc = 0;
+ uint16_t mcc;
+ uint16_t mnc;
+ bool mnc_3_digits;
+ int lac;
+ int rac;
+ int ci;
+
+ if (n)
+ memset(n, 0, sizeof(*n));
+
+ tmp = talloc_strdup(ctx, value);
+ if (!tmp)
+ return -EINVAL;
+
+ /* Parse MCC */
+ tok = strtok_r(tmp, "-", &saveptr);
+ if (tok) {
+ if (osmo_mcc_from_str(tok, &mcc)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse MNC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_mnc_from_str(tok, &mnc, &mnc_3_digits)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse LAC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse RAC */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&rac, tok, 10, 0, 255) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Parse CI */
+ tok = strtok_r(NULL, "-", &saveptr);
+ if (tok) {
+ if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ } else {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ /* Optional parameters: ARFCN and BSIC */
+ if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (n) {
+ n->type = NEIGHBOR_TYPE_CELL_ID;
+ n->cell_id.id.id_discr = CELL_IDENT_WHOLE_GLOBAL_PS;
+ n->cell_id.id.id.global_ps.rai.lac.lac = lac;
+ n->cell_id.id.id.global_ps.rai.rac = lac;
+ n->cell_id.id.id.global_ps.rai.lac.plmn.mcc = mcc;
+ n->cell_id.id.id.global_ps.rai.lac.plmn.mnc = mnc;
+ n->cell_id.id.id.global_ps.rai.lac.plmn.mnc_3_digits = mnc_3_digits;
+ n->cell_id.id.id.global_ps.cell_identity = ci;
+ }
+
+exit:
+ talloc_free(tmp);
+ return rc;
+}
+
+static int verify_neighbor_cgi_ps_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_cgi_ps(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_cgi_ps_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+
+ parse_cgi_ps(cmd, &n, cmd->value);
+ rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to add neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"
+ * mcc: Mobile country code of neighbor cell (0-999)
+ * mnc: Mobile network code of neighbor cell (0-999)
+ * lac: Location area of neighbor cell (0-65535)
+ * rac: Routing area of neighbor cell (0-65535)
+ * ci: Cell ID of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_ps_add, "neighbor-cgi-ps add");
+
+static int verify_neighbor_cgi_ps_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ if (parse_cgi_ps(cmd, NULL, value))
+ return 1;
+ return 0;
+}
+
+static int set_neighbor_cgi_ps_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ int rc;
+
+ struct neighbor n;
+ parse_cgi_ps(cmd, &n, cmd->value);
+ rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+ if (rc != CMD_SUCCESS) {
+ cmd->reply = "Failed to delete neighbor";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_ps_del, "neighbor-cgi-ps del");
+
+/* This and the following: clear all neighbor cell information */
+static int set_neighbor_clear(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ struct neighbor *neighbor;
+ struct neighbor *neighbor_tmp;
+
+ llist_for_each_entry_safe(neighbor, neighbor_tmp, &bts->neighbors, entry) {
+ llist_del(&neighbor->entry);
+ talloc_free(neighbor);
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(neighbor_clear, "neighbor-clear");
+
+/* Register control interface commands implemented above */
+int neighbor_ident_ctrl_init(void)
+{
+ int rc = 0;
+
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_bts_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_bts_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_ci_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_ci_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_bts_list);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_ps_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_ps_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_clear);
+
+ return rc;
+}
diff --git a/src/osmo-bsc/neighbor_ident_vty.c b/src/osmo-bsc/neighbor_ident_vty.c
index 7feed2a27..7e58e3799 100644
--- a/src/osmo-bsc/neighbor_ident_vty.c
+++ b/src/osmo-bsc/neighbor_ident_vty.c
@@ -23,6 +23,9 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <inttypes.h>
+
+#include <osmocom/ctrl/ports.h>
#include <osmocom/vty/command.h>
#include <osmocom/gsm/gsm0808.h>
@@ -32,43 +35,6 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bts.h>
-static struct gsm_network *g_net = NULL;
-static struct neighbor_ident_list *g_neighbor_cells = NULL;
-
-/* Parse VTY parameters matching NEIGHBOR_IDENT_VTY_KEY_PARAMS. Pass a pointer so that argv[0] is the
- * ARFCN value followed by the BSIC keyword and value. vty *must* reference a BTS_NODE. */
-bool neighbor_ident_vty_parse_key_params(struct vty *vty, const char **argv,
- struct neighbor_ident_key *key)
-{
- struct gsm_bts *bts = vty->index;
-
- OSMO_ASSERT(vty->node == BTS_NODE);
- OSMO_ASSERT(bts);
-
- return neighbor_ident_bts_parse_key_params(vty, bts, argv, key);
-}
-
-/* same as neighbor_ident_vty_parse_key_params() but pass an explicit bts, so it works on any node. */
-bool neighbor_ident_bts_parse_key_params(struct vty *vty, struct gsm_bts *bts, const char **argv,
- struct neighbor_ident_key *key)
-{
- const char *arfcn_str = argv[0];
- const char *bsic_str = argv[1];
-
- OSMO_ASSERT(bts);
-
- *key = (struct neighbor_ident_key){
- .from_bts = bts->nr,
- .arfcn = atoi(arfcn_str),
- };
-
- if (!strcmp(bsic_str, "any"))
- key->bsic = BSIC_ANY;
- else
- key->bsic = atoi(bsic_str);
- return true;
-}
-
#define NEIGHBOR_ADD_CMD "neighbor "
#define NEIGHBOR_DEL_CMD "no neighbor "
#define NEIGHBOR_DOC "Manage local and remote-BSS neighbor cells\n"
@@ -76,64 +42,51 @@ bool neighbor_ident_bts_parse_key_params(struct vty *vty, struct gsm_bts *bts, c
#define NEIGHBOR_DEL_DOC NO_STR "Remove local or remote-BSS neighbor cell\n"
#define LAC_PARAMS "lac <0-65535>"
+#define LAC_ARGC 1
#define LAC_DOC "Neighbor cell by LAC\n" "LAC\n"
#define LAC_CI_PARAMS "lac-ci <0-65535> <0-65535>"
+#define LAC_CI_ARGC 2
#define LAC_CI_DOC "Neighbor cell by LAC and CI\n" "LAC\n" "CI\n"
#define CGI_PARAMS "cgi <0-999> <0-999> <0-65535> <0-65535>"
+#define CGI_ARGC 4
#define CGI_DOC "Neighbor cell by cgi\n" "MCC\n" "MNC\n" "LAC\n" "CI\n"
+#define CGI_PS_PARAMS "cgi-ps <0-999> <0-999> <0-65535> <0-255> <0-65535>"
+#define CGI_PS_ARGC 5
+#define CGI_PS_DOC "Neighbor cell by cgi (Packet Switched, with RAC)\n" "MCC\n" "MNC\n" "LAC\n" "RAC\n" "CI\n"
+
#define LOCAL_BTS_PARAMS "bts <0-255>"
#define LOCAL_BTS_DOC "Neighbor cell by local BTS number\n" "BTS number\n"
-static struct gsm_bts *neighbor_ident_vty_parse_bts_nr(struct vty *vty, const char **argv)
+static int neighbor_ident_vty_parse_lac(struct vty *vty, struct gsm0808_cell_id *cell_id, const char **argv)
{
- const char *bts_nr_str = argv[0];
- struct gsm_bts *bts = gsm_bts_num(g_net, atoi(bts_nr_str));
- if (!bts)
- vty_out(vty, "%% No such BTS: nr = %s%s\n", bts_nr_str, VTY_NEWLINE);
- return bts;
-}
-
-static struct gsm_bts *bts_by_cell_id(struct vty *vty, struct gsm0808_cell_id *cell_id)
-{
- struct gsm_bts *bts = gsm_bts_by_cell_id(g_net, cell_id, 0);
- if (!bts)
- vty_out(vty, "%% No such BTS: %s%s\n", gsm0808_cell_id_name(cell_id), VTY_NEWLINE);
- return bts;
-}
-
-static struct gsm0808_cell_id *neighbor_ident_vty_parse_lac(struct vty *vty, const char **argv)
-{
- static struct gsm0808_cell_id cell_id;
- cell_id = (struct gsm0808_cell_id){
+ *cell_id = (struct gsm0808_cell_id){
.id_discr = CELL_IDENT_LAC,
.id.lac = atoi(argv[0]),
};
- return &cell_id;
+ return 0;
}
-static struct gsm0808_cell_id *neighbor_ident_vty_parse_lac_ci(struct vty *vty, const char **argv)
+static int neighbor_ident_vty_parse_lac_ci(struct vty *vty, struct gsm0808_cell_id *cell_id, const char **argv)
{
- static struct gsm0808_cell_id cell_id;
- cell_id = (struct gsm0808_cell_id){
+ *cell_id = (struct gsm0808_cell_id){
.id_discr = CELL_IDENT_LAC_AND_CI,
.id.lac_and_ci = {
.lac = atoi(argv[0]),
.ci = atoi(argv[1]),
},
};
- return &cell_id;
+ return 0;
}
-static struct gsm0808_cell_id *neighbor_ident_vty_parse_cgi(struct vty *vty, const char **argv)
+static int neighbor_ident_vty_parse_cgi(struct vty *vty, struct gsm0808_cell_id *cell_id, const char **argv)
{
- static struct gsm0808_cell_id cell_id;
- cell_id = (struct gsm0808_cell_id){
+ *cell_id = (struct gsm0808_cell_id){
.id_discr = CELL_IDENT_WHOLE_GLOBAL,
};
- struct osmo_cell_global_id *cgi = &cell_id.id.global;
+ struct osmo_cell_global_id *cgi = &cell_id->id.global;
const char *mcc = argv[0];
const char *mnc = argv[1];
const char *lac = argv[2];
@@ -141,379 +94,388 @@ static struct gsm0808_cell_id *neighbor_ident_vty_parse_cgi(struct vty *vty, con
if (osmo_mcc_from_str(mcc, &cgi->lai.plmn.mcc)) {
vty_out(vty, "%% Error decoding MCC: %s%s", mcc, VTY_NEWLINE);
- return NULL;
+ return -1;
}
if (osmo_mnc_from_str(mnc, &cgi->lai.plmn.mnc, &cgi->lai.plmn.mnc_3_digits)) {
vty_out(vty, "%% Error decoding MNC: %s%s", mnc, VTY_NEWLINE);
- return NULL;
+ return -1;
}
cgi->lai.lac = atoi(lac);
cgi->cell_identity = atoi(ci);
- return &cell_id;
+ return 0;
}
-static int add_local_bts(struct vty *vty, struct gsm_bts *neigh)
+static int neighbor_ident_vty_parse_cgi_ps(struct vty *vty, struct gsm0808_cell_id *cell_id, const char **argv)
{
- int rc;
- struct gsm_bts *bts = vty->index;
- if (vty->node != BTS_NODE) {
- vty_out(vty, "%% Error: cannot add local BTS neighbor, not on BTS node%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (!bts) {
- vty_out(vty, "%% Error: cannot add local BTS neighbor, no BTS on this node%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (!neigh) {
- vty_out(vty, "%% Error: cannot add local BTS neighbor to BTS %u, no such neighbor BTS%s"
- "%% (To add remote-BSS neighbors, pass full ARFCN and BSIC as well)%s",
- bts->nr, VTY_NEWLINE, VTY_NEWLINE);
- return CMD_WARNING;
- }
- rc = gsm_bts_local_neighbor_add(bts, neigh);
- if (rc < 0) {
- vty_out(vty, "%% Error: cannot add local BTS %u as neighbor to BTS %u: %s%s",
- neigh->nr, bts->nr, strerror(-rc), VTY_NEWLINE);
- return CMD_WARNING;
- } else
- vty_out(vty, "%% BTS %u %s local neighbor BTS %u with LAC %u CI %u and ARFCN %u BSIC %u%s",
- bts->nr, rc? "now has" : "already had",
- neigh->nr, neigh->location_area_code, neigh->cell_identity,
- neigh->c0->arfcn, neigh->bsic, VTY_NEWLINE);
- return CMD_SUCCESS;
-}
+ *cell_id = (struct gsm0808_cell_id){
+ .id_discr = CELL_IDENT_WHOLE_GLOBAL_PS,
+ };
+ struct osmo_cell_global_id_ps *cgi_ps = &cell_id->id.global_ps;
+ const char *mcc = argv[0];
+ const char *mnc = argv[1];
+ const char *lac = argv[2];
+ const char *rac = argv[3];
+ const char *ci = argv[4];
-static int del_local_bts(struct vty *vty, struct gsm_bts *neigh)
-{
- int rc;
- struct gsm_bts *bts = vty->index;
- if (vty->node != BTS_NODE) {
- vty_out(vty, "%% Error: cannot remove local BTS neighbor, not on BTS node%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (!bts) {
- vty_out(vty, "%% Error: cannot remove local BTS neighbor, no BTS on this node%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (!neigh) {
- vty_out(vty, "%% Error: cannot remove local BTS neighbor from BTS %u, no such neighbor BTS%s",
- bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
- rc = gsm_bts_local_neighbor_del(bts, neigh);
- if (rc < 0) {
- vty_out(vty, "%% Error: cannot remove local BTS %u neighbor from BTS %u: %s%s",
- neigh->nr, bts->nr, strerror(-rc), VTY_NEWLINE);
- return CMD_WARNING;
+ if (osmo_mcc_from_str(mcc, &cgi_ps->rai.lac.plmn.mcc)) {
+ vty_out(vty, "%% Error decoding MCC: %s%s", mcc, VTY_NEWLINE);
+ return -1;
}
- if (rc == 0)
- vty_out(vty, "%% BTS %u is no neighbor of BTS %u%s",
- neigh->nr, bts->nr, VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-DEFUN(cfg_neighbor_add_bts_nr, cfg_neighbor_add_bts_nr_cmd,
- NEIGHBOR_ADD_CMD LOCAL_BTS_PARAMS,
- NEIGHBOR_ADD_DOC LOCAL_BTS_DOC)
-{
- return add_local_bts(vty, neighbor_ident_vty_parse_bts_nr(vty, argv));
-}
+ if (osmo_mnc_from_str(mnc, &cgi_ps->rai.lac.plmn.mnc, &cgi_ps->rai.lac.plmn.mnc_3_digits)) {
+ vty_out(vty, "%% Error decoding MNC: %s%s", mnc, VTY_NEWLINE);
+ return -1;
+ }
-DEFUN(cfg_neighbor_add_lac, cfg_neighbor_add_lac_cmd,
- NEIGHBOR_ADD_CMD LAC_PARAMS,
- NEIGHBOR_ADD_DOC LAC_DOC)
-{
- return add_local_bts(vty, bts_by_cell_id(vty, neighbor_ident_vty_parse_lac(vty, argv)));
+ cgi_ps->rai.lac.lac = atoi(lac);
+ cgi_ps->rai.rac = atoi(rac);
+ cgi_ps->cell_identity = atoi(ci);
+ return 0;
}
-DEFUN(cfg_neighbor_add_lac_ci, cfg_neighbor_add_lac_ci_cmd,
- NEIGHBOR_ADD_CMD LAC_CI_PARAMS,
- NEIGHBOR_ADD_DOC LAC_CI_DOC)
+void neighbor_ident_vty_parse_arfcn_bsic(struct cell_ab *ab, const char **argv)
{
- return add_local_bts(vty, bts_by_cell_id(vty, neighbor_ident_vty_parse_lac_ci(vty, argv)));
-}
-
-DEFUN(cfg_neighbor_add_cgi, cfg_neighbor_add_cgi_cmd,
- NEIGHBOR_ADD_CMD CGI_PARAMS,
- NEIGHBOR_ADD_DOC CGI_DOC)
-{
- return add_local_bts(vty, bts_by_cell_id(vty, neighbor_ident_vty_parse_cgi(vty, argv)));
-}
+ const char *arfcn_str = argv[0];
+ const char *bsic_str = argv[1];
-bool neighbor_ident_key_matches_bts(const struct neighbor_ident_key *key, struct gsm_bts *bts)
-{
- if (!bts || !key)
- return false;
- return key->arfcn == bts->c0->arfcn
- && (key->bsic == BSIC_ANY || key->bsic == bts->bsic);
+ *ab = (struct cell_ab){
+ .arfcn = atoi(arfcn_str),
+ .bsic = (!strcmp(bsic_str, "any")) ? BSIC_ANY : atoi(bsic_str),
+ };
}
-static int add_remote_or_local_bts(struct vty *vty, const struct gsm0808_cell_id *cell_id,
- const struct neighbor_ident_key *key)
+#define LOGPORVTY(vty, fmt, args...) \
+{ \
+ if (vty) \
+ vty_out(vty, "%% " fmt "%s", ## args, VTY_NEWLINE); \
+ else \
+ LOGP(DLINP, LOGL_NOTICE, fmt "\n", ## args); \
+} while (0) \
+
+/* Add a neighbor from neighborlist. When the parameter *vty is set to NULL all error messages are redirected to the
+ * logtext. */
+int neighbor_ident_add_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n)
{
- int rc;
- struct gsm_bts *local_neigh;
- const struct gsm0808_cell_id_list2 *exists;
- struct gsm0808_cell_id_list2 cil;
- struct gsm_bts *bts = vty->index;
+ struct neighbor *neighbor;
- if (vty->node != BTS_NODE) {
- vty_out(vty, "%% Error: cannot add BTS neighbor, not on BTS node%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (!bts) {
- vty_out(vty, "%% Error: cannot add BTS neighbor, no BTS on this node%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
+ OSMO_ASSERT(bts);
+ OSMO_ASSERT(!vty || (vty->node == BTS_NODE));
+
+ llist_for_each_entry(neighbor, &bts->neighbors, entry) {
+ /* Check against duplicates */
+ if (neighbor_same(neighbor, n, false)) {
+ /* Found a match on Cell ID or BTS number, without ARFCN+BSIC. If they are fully identical, ignore the
+ * duplicate. If the ARFCN+BSIC part differs, it's an error. */
+ LOGPORVTY(vty, "BTS %u already had neighbor %s", bts->nr, neighbor_to_str_c(OTC_SELECT, neighbor));
+ if (!neighbor_same(neighbor, n, true)) {
+ LOGPORVTY(vty, "ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: %s",
+ neighbor_to_str_c(OTC_SELECT, n));
+ return CMD_WARNING;
+ }
+ /* Exact same neighbor again, just ignore. */
+ return CMD_SUCCESS;
+ }
- /* Is there a local BTS that matches the cell_id? */
- local_neigh = gsm_bts_by_cell_id(g_net, cell_id, 0);
- if (local_neigh) {
- /* But do the advertised ARFCN and BSIC match as intended?
- * The user may omit ARFCN and BSIC for local cells, but if they are provided,
- * they need to match. */
- if (!neighbor_ident_key_matches_bts(key, local_neigh)) {
- vty_out(vty, "%% Error: bts %u: neighbor cell id %s indicates local BTS %u,"
- " but it does not match ARFCN+BSIC %s%s",
- bts->nr, gsm0808_cell_id_name(cell_id), local_neigh->nr,
- neighbor_ident_key_name(key), VTY_NEWLINE);
- /* TODO: error out fatally for non-interactive VTY? */
+ /* Allow only one cell ID per remote-BSS neighbor, see OS#3656 */
+ if (n->type == NEIGHBOR_TYPE_CELL_ID
+ && n->cell_id.ab_present && neighbor->cell_id.ab_present
+ && cell_ab_match(&n->cell_id.ab, &neighbor->cell_id.ab, true)) {
+ LOGPORVTY(vty, "Error: only one Cell Identifier entry is allowed per remote neighbor."
+ " Already have: BTS %u -> %s", bts->nr,
+ neighbor_to_str_c(OTC_SELECT, neighbor));
return CMD_WARNING;
}
- return add_local_bts(vty, local_neigh);
}
- /* Allow only one cell ID per remote-BSS neighbor, see OS#3656 */
- exists = neighbor_ident_get(g_neighbor_cells, key);
- if (exists) {
- vty_out(vty, "%% Error: only one Cell Identifier entry is allowed per remote neighbor."
- " Already have: %s -> %s%s", neighbor_ident_key_name(key),
- gsm0808_cell_id_list_name(exists), VTY_NEWLINE);
- return CMD_WARNING;
- }
+ neighbor = talloc_zero(bts, struct neighbor);
+ *neighbor = *n;
+ llist_add_tail(&neighbor->entry, &bts->neighbors);
+ return CMD_SUCCESS;
+}
- /* The cell_id is not known in this BSS, so it must be a remote cell. */
- gsm0808_cell_id_to_list(&cil, cell_id);
- rc = neighbor_ident_add(g_neighbor_cells, key, &cil);
+/* Delete a neighbor from neighborlist. When the parameter *vty is set to NULL all error messages are redirected to the
+ * logtext. */
+int neighbor_ident_del_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n)
+{
+ struct neighbor *neighbor;
- if (rc < 0) {
- const char *reason;
- switch (rc) {
- case -EINVAL:
- reason = ": mismatching type between current and newly added cell identifier";
- break;
- case -ENOSPC:
- reason = ": list is full";
- break;
+ OSMO_ASSERT(bts);
+ OSMO_ASSERT(!vty || (vty->node == BTS_NODE));
+
+ llist_for_each_entry(neighbor, &bts->neighbors, entry) {
+ if (neighbor->type != n->type)
+ continue;
+
+ switch (n->type) {
+ case NEIGHBOR_TYPE_BTS_NR:
+ if (neighbor->bts_nr == n->bts_nr)
+ break;
+ continue;
+
+ case NEIGHBOR_TYPE_CELL_ID:
+ if (gsm0808_cell_ids_match(&neighbor->cell_id.id, &n->cell_id.id, true))
+ break;
+ continue;
default:
- reason = "";
- break;
+ continue;
}
- vty_out(vty, "%% Error adding neighbor-BSS Cell Identifier %s%s%s",
- gsm0808_cell_id_name(cell_id), reason, VTY_NEWLINE);
- return CMD_WARNING;
+ llist_del(&neighbor->entry);
+ talloc_free(neighbor);
+ return CMD_SUCCESS;
}
- vty_out(vty, "%% %s now has %d remote BSS Cell Identifier List %s%s",
- neighbor_ident_key_name(key), rc, rc == 1? "entry" : "entries", VTY_NEWLINE);
- return CMD_SUCCESS;
+ LOGPORVTY(vty, "Error: no such neighbor on BTS %d: %s",
+ bts->nr, neighbor_to_str_c(OTC_SELECT, n));
+ return CMD_WARNING;
}
-static int del_by_key(struct vty *vty, const struct neighbor_ident_key *key)
+static int del_neighbor_by_cell_ab(struct vty *vty, const struct cell_ab *cell_ab)
{
- int removed = 0;
- int rc;
struct gsm_bts *bts = vty->index;
- struct gsm_bts_ref *neigh, *safe;
+ struct neighbor *neighbor, *safe;
+ struct gsm_bts *neighbor_bts;
+ struct cell_ab neighbor_ab;
+ int count = 0;
- if (vty->node != BTS_NODE) {
- vty_out(vty, "%% Error: cannot remove BTS neighbor, not on BTS node%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (!bts) {
- vty_out(vty, "%% Error: cannot remove BTS neighbor, no BTS on this node%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
+ OSMO_ASSERT((vty->node == BTS_NODE) && bts);
+
+ llist_for_each_entry_safe(neighbor, safe, &bts->neighbors, entry) {
+ switch (neighbor->type) {
+ case NEIGHBOR_TYPE_BTS_NR:
+ if (resolve_local_neighbor(&neighbor_bts, bts, neighbor))
+ continue;
+ bts_cell_ab(&neighbor_ab, neighbor_bts);
+ if (!cell_ab_match(&neighbor_ab, cell_ab, false))
+ continue;
+ break;
- /* Is there a local BTS that matches the key? */
- llist_for_each_entry_safe(neigh, safe, &bts->local_neighbors, entry) {
- struct gsm_bts *neigh_bts = neigh->bts;
- if (!neighbor_ident_key_matches_bts(key, neigh->bts))
+ case NEIGHBOR_TYPE_CELL_ID:
+ if (!neighbor->cell_id.ab_present)
+ continue;
+ if (!cell_ab_match(&neighbor->cell_id.ab, cell_ab, false))
+ continue;
+ break;
+ default:
continue;
- rc = gsm_bts_local_neighbor_del(bts, neigh->bts);
- if (rc > 0) {
- vty_out(vty, "%% Removed local neighbor bts %u to bts %u%s",
- bts->nr, neigh_bts->nr, VTY_NEWLINE);
- removed += rc;
}
- }
- if (neighbor_ident_del(g_neighbor_cells, key)) {
- vty_out(vty, "%% Removed remote BSS neighbor %s%s",
- neighbor_ident_key_name(key), VTY_NEWLINE);
- removed ++;
+ llist_del(&neighbor->entry);
+ talloc_free(neighbor);
+ count++;
}
+ if (count)
+ return CMD_SUCCESS;
+
+ vty_out(vty, "%% Cannot remove: no such neighbor on BTS %u: %s%s",
+ bts->nr, cell_ab_to_str_c(OTC_SELECT, cell_ab), VTY_NEWLINE);
+ return CMD_WARNING;
+}
+
+DEFUN(cfg_neighbor_add_bts_nr, cfg_neighbor_add_bts_nr_cmd,
+ NEIGHBOR_ADD_CMD LOCAL_BTS_PARAMS,
+ NEIGHBOR_ADD_DOC LOCAL_BTS_DOC)
+{
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_BTS_NR,
+ .bts_nr = atoi(argv[0]),
+ };
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
+}
- if (!removed) {
- vty_out(vty, "%% Cannot remove, no such neighbor: %s%s",
- neighbor_ident_key_name(key), VTY_NEWLINE);
+DEFUN(cfg_neighbor_add_lac, cfg_neighbor_add_lac_cmd,
+ NEIGHBOR_ADD_CMD LAC_PARAMS,
+ NEIGHBOR_ADD_DOC LAC_DOC)
+{
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ };
+ if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- }
- return CMD_SUCCESS;
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
-struct nil_match_bts_data {
- int bts_nr;
- const struct neighbor_ident_key *found;
-};
+DEFUN(cfg_neighbor_add_lac_ci, cfg_neighbor_add_lac_ci_cmd,
+ NEIGHBOR_ADD_CMD LAC_CI_PARAMS,
+ NEIGHBOR_ADD_DOC LAC_CI_DOC)
+{
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ };
+ if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
+ return CMD_WARNING;
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
+}
-static bool nil_match_bts(const struct neighbor_ident_key *key,
- const struct gsm0808_cell_id_list2 *val,
- void *cb_data)
+DEFUN(cfg_neighbor_add_cgi, cfg_neighbor_add_cgi_cmd,
+ NEIGHBOR_ADD_CMD CGI_PARAMS,
+ NEIGHBOR_ADD_DOC CGI_DOC)
{
- struct nil_match_bts_data *d = cb_data;
- if (key->from_bts == d->bts_nr) {
- d->found = key;
- return false;
- }
- return true;
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ };
+ if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
+ return CMD_WARNING;
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
-bool neighbor_ident_bts_entry_exists(uint8_t from_bts)
+DEFUN(cfg_neighbor_add_cgi_ps, cfg_neighbor_add_cgi_ps_cmd,
+ NEIGHBOR_ADD_CMD CGI_PS_PARAMS,
+ NEIGHBOR_ADD_DOC CGI_PS_DOC)
{
- struct nil_match_bts_data d = {
- .bts_nr = from_bts,
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
};
- neighbor_ident_iter(g_neighbor_cells, nil_match_bts, &d);
- return (bool)d.found;
+ if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
+ return CMD_WARNING;
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
static int neighbor_del_all(struct vty *vty)
{
- int rc;
- int removed = 0;
struct gsm_bts *bts = vty->index;
-
+ struct neighbor *n;
OSMO_ASSERT((vty->node == BTS_NODE) && bts);
- /* Remove all local neighbors and print to VTY for the user to know what changed */
- while (1) {
- struct gsm_bts_ref *neigh = llist_first_entry_or_null(&bts->local_neighbors, struct gsm_bts_ref, entry);
- struct gsm_bts *neigh_bts;
- if (!neigh)
- break;
-
- neigh_bts = neigh->bts;
- OSMO_ASSERT(neigh_bts);
-
- /* It would be more efficient to just llist_del() the gsm_bts_ref directly, but for the sake of
- * safe/sane API use and against code dup, rather invoke the central gsm_bts_local_neighbor_del()
- * function intended for this task. */
- rc = gsm_bts_local_neighbor_del(bts, neigh_bts);
- if (rc > 0) {
- vty_out(vty, "%% Removed local neighbor bts %u to bts %u%s",
- bts->nr, neigh_bts->nr, VTY_NEWLINE);
- removed += rc;
- } else {
- vty_out(vty, "%% Error while removing local neighbor bts %u to bts %u, aborted%s",
- bts->nr, neigh_bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
+ if (llist_empty(&bts->neighbors)) {
+ vty_out(vty, "%% No neighbors configured%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
}
- /* Remove all remote-BSS neighbors */
- while (1) {
- struct neighbor_ident_key k;
- struct nil_match_bts_data d = {
- .bts_nr = bts->nr,
- };
- neighbor_ident_iter(g_neighbor_cells, nil_match_bts, &d);
- if (!d.found)
- break;
- k = *d.found;
- if (neighbor_ident_del(g_neighbor_cells, &k)) {
- vty_out(vty, "%% Removed remote BSS neighbor %s%s",
- neighbor_ident_key_name(&k), VTY_NEWLINE);
- removed++;
- } else {
- vty_out(vty, "%% Error while removing remote BSS neighbor %s, aborted%s",
- neighbor_ident_key_name(&k), VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Remove all local neighbors and print to VTY for the user to know what changed */
+ while ((n = llist_first_entry_or_null(&bts->neighbors, struct neighbor, entry))) {
+ vty_out(vty, "%% Removed neighbor: BTS %u to %s%s",
+ bts->nr, neighbor_to_str_c(OTC_SELECT, n), VTY_NEWLINE);
+ llist_del(&n->entry);
+ talloc_free(n);
}
-
- if (!removed)
- vty_out(vty, "%% No neighbors configured%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(cfg_neighbor_add_lac_arfcn_bsic, cfg_neighbor_add_lac_arfcn_bsic_cmd,
- NEIGHBOR_ADD_CMD LAC_PARAMS " " NEIGHBOR_IDENT_VTY_KEY_PARAMS,
- NEIGHBOR_ADD_DOC LAC_DOC NEIGHBOR_IDENT_VTY_KEY_DOC)
+ NEIGHBOR_ADD_CMD LAC_PARAMS " " CELL_AB_VTY_PARAMS,
+ NEIGHBOR_ADD_DOC LAC_DOC CELL_AB_VTY_DOC)
{
- struct neighbor_ident_key nik;
- struct gsm0808_cell_id *cell_id = neighbor_ident_vty_parse_lac(vty, argv);
- if (!cell_id)
- return CMD_WARNING;
- if (!neighbor_ident_vty_parse_key_params(vty, argv + 1, &nik))
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ .cell_id.ab_present = true,
+ };
+ if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return add_remote_or_local_bts(vty, cell_id, &nik);
+ neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + LAC_ARGC);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_lac_ci_arfcn_bsic, cfg_neighbor_add_lac_ci_arfcn_bsic_cmd,
- NEIGHBOR_ADD_CMD LAC_CI_PARAMS " " NEIGHBOR_IDENT_VTY_KEY_PARAMS,
- NEIGHBOR_ADD_DOC LAC_CI_DOC NEIGHBOR_IDENT_VTY_KEY_DOC)
+ NEIGHBOR_ADD_CMD LAC_CI_PARAMS " " CELL_AB_VTY_PARAMS,
+ NEIGHBOR_ADD_DOC LAC_CI_DOC CELL_AB_VTY_DOC)
{
- struct neighbor_ident_key nik;
- struct gsm0808_cell_id *cell_id = neighbor_ident_vty_parse_lac_ci(vty, argv);
- if (!cell_id)
- return CMD_WARNING;
- if (!neighbor_ident_vty_parse_key_params(vty, argv + 2, &nik))
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ .cell_id.ab_present = true,
+ };
+ if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return add_remote_or_local_bts(vty, cell_id, &nik);
+ neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + LAC_CI_ARGC);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_add_cgi_arfcn_bsic, cfg_neighbor_add_cgi_arfcn_bsic_cmd,
- NEIGHBOR_ADD_CMD CGI_PARAMS " " NEIGHBOR_IDENT_VTY_KEY_PARAMS,
- NEIGHBOR_ADD_DOC CGI_DOC NEIGHBOR_IDENT_VTY_KEY_DOC)
+ NEIGHBOR_ADD_CMD CGI_PARAMS " " CELL_AB_VTY_PARAMS,
+ NEIGHBOR_ADD_DOC CGI_DOC CELL_AB_VTY_DOC)
{
- struct neighbor_ident_key nik;
- struct gsm0808_cell_id *cell_id = neighbor_ident_vty_parse_cgi(vty, argv);
- if (!cell_id)
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ .cell_id.ab_present = true,
+ };
+ if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- if (!neighbor_ident_vty_parse_key_params(vty, argv + 4, &nik))
+ neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + CGI_ARGC);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
+}
+
+DEFUN(cfg_neighbor_add_cgi_ps_arfcn_bsic, cfg_neighbor_add_cgi_ps_arfcn_bsic_cmd,
+ NEIGHBOR_ADD_CMD CGI_PS_PARAMS " " CELL_AB_VTY_PARAMS,
+ NEIGHBOR_ADD_DOC CGI_PS_DOC CELL_AB_VTY_DOC)
+{
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ .cell_id.ab_present = true,
+ };
+ if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
return CMD_WARNING;
- return add_remote_or_local_bts(vty, cell_id, &nik);
+ neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + CGI_PS_ARGC);
+ return neighbor_ident_add_neighbor(vty, vty->index, &n);
}
DEFUN(cfg_neighbor_del_bts_nr, cfg_neighbor_del_bts_nr_cmd,
NEIGHBOR_DEL_CMD LOCAL_BTS_PARAMS,
NEIGHBOR_DEL_DOC LOCAL_BTS_DOC)
{
- return del_local_bts(vty, neighbor_ident_vty_parse_bts_nr(vty, argv));
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_BTS_NR,
+ .bts_nr = atoi(argv[0]),
+ };
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
}
-DEFUN(cfg_neighbor_del_arfcn_bsic, cfg_neighbor_del_arfcn_bsic_cmd,
- NEIGHBOR_DEL_CMD NEIGHBOR_IDENT_VTY_KEY_PARAMS,
- NEIGHBOR_DEL_DOC NEIGHBOR_IDENT_VTY_KEY_DOC)
+DEFUN(cfg_neighbor_del_lac, cfg_neighbor_del_lac_cmd,
+ NEIGHBOR_DEL_CMD LAC_PARAMS,
+ NEIGHBOR_DEL_DOC LAC_DOC)
{
- struct neighbor_ident_key key;
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ };
+ if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
+ return CMD_WARNING;
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
+}
- if (!neighbor_ident_vty_parse_key_params(vty, argv, &key))
+DEFUN(cfg_neighbor_del_lac_ci, cfg_neighbor_del_lac_ci_cmd,
+ NEIGHBOR_DEL_CMD LAC_CI_PARAMS,
+ NEIGHBOR_DEL_DOC LAC_CI_DOC)
+{
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ };
+ if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
return CMD_WARNING;
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
+}
- return del_by_key(vty, &key);
+DEFUN(cfg_neighbor_del_cgi, cfg_neighbor_del_cgi_cmd,
+ NEIGHBOR_DEL_CMD CGI_PARAMS,
+ NEIGHBOR_DEL_DOC CGI_DOC)
+{
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ };
+ if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
+ return CMD_WARNING;
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
+}
+
+DEFUN(cfg_neighbor_del_cgi_ps, cfg_neighbor_del_cgi_ps_cmd,
+ NEIGHBOR_DEL_CMD CGI_PS_PARAMS,
+ NEIGHBOR_DEL_DOC CGI_PS_DOC)
+{
+ struct neighbor n = {
+ .type = NEIGHBOR_TYPE_CELL_ID,
+ };
+ if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
+ return CMD_WARNING;
+ return neighbor_ident_del_neighbor(vty, vty->index, &n);
+}
+
+DEFUN(cfg_neighbor_del_arfcn_bsic, cfg_neighbor_del_arfcn_bsic_cmd,
+ NEIGHBOR_DEL_CMD CELL_AB_VTY_PARAMS,
+ NEIGHBOR_DEL_DOC CELL_AB_VTY_DOC)
+{
+ struct cell_ab ab;
+ neighbor_ident_vty_parse_arfcn_bsic(&ab, argv);
+ return del_neighbor_by_cell_ab(vty, &ab);
}
DEFUN(cfg_neighbor_del_all, cfg_neighbor_del_all_cmd,
@@ -525,107 +487,107 @@ DEFUN(cfg_neighbor_del_all, cfg_neighbor_del_all_cmd,
return neighbor_del_all(vty);
}
-struct write_neighbor_ident_entry_data {
- struct vty *vty;
- const char *indent;
- struct gsm_bts *bts;
-};
+DEFUN(cfg_neighbor_bind, cfg_neighbor_bind_cmd,
+ "neighbor-resolution bind " VTY_IPV46_CMD " [<0-65535>]",
+ NEIGHBOR_DOC "Bind Neighbor Resolution Service (CTRL interface) to given ip and port\n"
+ IP_STR IPV6_STR "Port to bind the service to [defaults to 4248 if not provided]\n")
+{
+ vty_out(vty, "%% Warning: The CTRL interface for Neighbor Address Resolution is now deprecated."
+ "Upgrade osmo-pcu and drop the 'neighbor-resolution bind " VTY_IPV46_CMD " [<0-65535>]' VTY "
+ "option in order to let osmo-pcu use the new resoluton method using the PCUIF over IPA "
+ "multiplex, which will work out of the box without required configuration.%s", VTY_NEWLINE);
+ osmo_talloc_replace_string(bsc_gsmnet, &bsc_gsmnet->neigh_ctrl.addr, argv[0]);
+ if (argc > 1)
+ bsc_gsmnet->neigh_ctrl.port = atoi(argv[1]);
+ else
+ bsc_gsmnet->neigh_ctrl.port = OSMO_CTRL_PORT_BSC_NEIGH;
+ return CMD_SUCCESS;
+}
+
+void neighbor_ident_vty_write_network(struct vty *vty, const char *indent)
+{
+ if (bsc_gsmnet->neigh_ctrl.addr)
+ vty_out(vty, "%sneighbor-resolution bind %s %" PRIu16 "%s", indent, bsc_gsmnet->neigh_ctrl.addr,
+ bsc_gsmnet->neigh_ctrl.port, VTY_NEWLINE);
+}
-static bool write_neighbor_ident_list(const struct neighbor_ident_key *key,
- const struct gsm0808_cell_id_list2 *val,
- void *cb_data)
+static int vty_write_cell_id_u(struct vty *vty, enum CELL_IDENT id_discr, const union gsm0808_cell_id_u *cell_id_u)
{
- struct write_neighbor_ident_entry_data *d = cb_data;
- struct vty *vty = d->vty;
- int i;
-
- if (d->bts) {
- if (d->bts->nr != key->from_bts)
- return true;
- } else if (key->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS)
- return true;
-
-#define NEIGH_BSS_WRITE(fmt, args...) do { \
- vty_out(vty, "%sneighbor " fmt " arfcn %u ", d->indent, ## args, key->arfcn); \
- if (key->bsic == BSIC_ANY) \
- vty_out(vty, "bsic any"); \
- else \
- vty_out(vty, "bsic %u", key->bsic & 0x3f); \
- vty_out(vty, "%s", VTY_NEWLINE); \
- } while(0)
-
- switch (val->id_discr) {
+ const struct osmo_cell_global_id *cgi;
+ const struct osmo_cell_global_id_ps *cgi_ps;
+
+ switch (id_discr) {
case CELL_IDENT_LAC:
- for (i = 0; i < val->id_list_len; i++) {
- NEIGH_BSS_WRITE("lac %u", val->id_list[i].lac);
- }
+ vty_out(vty, "lac %u", cell_id_u->lac);
break;
case CELL_IDENT_LAC_AND_CI:
- for (i = 0; i < val->id_list_len; i++) {
- NEIGH_BSS_WRITE("lac-ci %u %u",
- val->id_list[i].lac_and_ci.lac,
- val->id_list[i].lac_and_ci.ci);
- }
+ vty_out(vty, "lac-ci %u %u", cell_id_u->lac_and_ci.lac, cell_id_u->lac_and_ci.ci);
break;
case CELL_IDENT_WHOLE_GLOBAL:
- for (i = 0; i < val->id_list_len; i++) {
- const struct osmo_cell_global_id *cgi = &val->id_list[i].global;
- NEIGH_BSS_WRITE("cgi %s %s %u %u",
- osmo_mcc_name(cgi->lai.plmn.mcc),
- osmo_mnc_name(cgi->lai.plmn.mnc, cgi->lai.plmn.mnc_3_digits),
- cgi->lai.lac, cgi->cell_identity);
- }
+ cgi = &cell_id_u->global;
+ vty_out(vty, "cgi %s %s %u %u",
+ osmo_mcc_name(cgi->lai.plmn.mcc),
+ osmo_mnc_name(cgi->lai.plmn.mnc, cgi->lai.plmn.mnc_3_digits),
+ cgi->lai.lac, cgi->cell_identity);
+ break;
+ case CELL_IDENT_WHOLE_GLOBAL_PS:
+ cgi_ps = &cell_id_u->global_ps;
+ vty_out(vty, "cgi-ps %s %s %u %u %u",
+ osmo_mcc_name(cgi_ps->rai.lac.plmn.mcc),
+ osmo_mnc_name(cgi_ps->rai.lac.plmn.mnc, cgi_ps->rai.lac.plmn.mnc_3_digits),
+ cgi_ps->rai.lac.lac, cgi_ps->rai.rac,
+ cgi_ps->cell_identity);
break;
default:
- vty_out(vty, "%% Unsupported Cell Identity%s", VTY_NEWLINE);
+ return -1;
}
-#undef NEIGH_BSS_WRITE
-
- return true;
+ return 0;
}
-void neighbor_ident_vty_write_remote_bss(struct vty *vty, const char *indent, struct gsm_bts *bts)
+void neighbor_ident_vty_write_bts(struct vty *vty, const char *indent, struct gsm_bts *bts)
{
- struct write_neighbor_ident_entry_data d = {
- .vty = vty,
- .indent = indent,
- .bts = bts,
- };
+ struct neighbor *n;
- neighbor_ident_iter(g_neighbor_cells, write_neighbor_ident_list, &d);
-}
+ llist_for_each_entry(n, &bts->neighbors, entry) {
+ switch (n->type) {
+ case NEIGHBOR_TYPE_BTS_NR:
+ vty_out(vty, "%sneighbor bts %u%s", indent, n->bts_nr, VTY_NEWLINE);
+ break;
-void neighbor_ident_vty_write_local_neighbors(struct vty *vty, const char *indent, struct gsm_bts *bts)
-{
- struct gsm_bts_ref *neigh;
+ case NEIGHBOR_TYPE_CELL_ID:
+ vty_out(vty, "%sneighbor ", indent);
+ if (vty_write_cell_id_u(vty, n->cell_id.id.id_discr, &n->cell_id.id.id)) {
+ vty_out(vty, "[Unsupported Cell Identity]%s", VTY_NEWLINE);
+ continue;
+ }
+
+ if (n->cell_id.ab_present) {
+ vty_out(vty, " arfcn %u ", n->cell_id.ab.arfcn);
+ if (n->cell_id.ab.bsic == BSIC_ANY)
+ vty_out(vty, "bsic any");
+ else
+ vty_out(vty, "bsic %u", n->cell_id.ab.bsic & 0x3f);
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+ break;
- llist_for_each_entry(neigh, &bts->local_neighbors, entry) {
- vty_out(vty, "%sneighbor bts %u%s", indent, neigh->bts->nr, VTY_NEWLINE);
+ default:
+ /* Ignore anything invalid */
+ break;
+ }
}
}
-void neighbor_ident_vty_write(struct vty *vty, const char *indent, struct gsm_bts *bts)
-{
- neighbor_ident_vty_write_local_neighbors(vty, indent, bts);
- neighbor_ident_vty_write_remote_bss(vty, indent, bts);
-}
-
DEFUN(show_bts_neighbor, show_bts_neighbor_cmd,
- "show bts <0-255> neighbor " NEIGHBOR_IDENT_VTY_KEY_PARAMS,
+ "show bts <0-255> neighbor " CELL_AB_VTY_PARAMS,
SHOW_STR "Display information about a BTS\n" "BTS number\n"
"Query which cell would be the target for this neighbor ARFCN+BSIC\n"
- NEIGHBOR_IDENT_VTY_KEY_DOC)
+ CELL_AB_VTY_DOC)
{
- int found = 0;
- struct neighbor_ident_key key;
- struct gsm_bts_ref *neigh;
- const struct gsm0808_cell_id_list2 *res;
- struct gsm_bts *bts = gsm_bts_num(g_net, atoi(argv[0]));
- struct write_neighbor_ident_entry_data d = {
- .vty = vty,
- .indent = "% ",
- .bts = bts,
- };
+ struct cell_ab ab;
+ struct gsm_bts *local_neighbor = NULL;
+ struct gsm0808_cell_id_list2 remote_neighbors = { 0 };
+ struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, atoi(argv[0]));
if (!bts) {
vty_out(vty, "%% Error: cannot find BTS '%s'%s", argv[0],
@@ -633,43 +595,57 @@ DEFUN(show_bts_neighbor, show_bts_neighbor_cmd,
return CMD_WARNING;
}
- if (!neighbor_ident_bts_parse_key_params(vty, bts, &argv[1], &key))
- return CMD_WARNING;
+ neighbor_ident_vty_parse_arfcn_bsic(&ab, &argv[1]);
- /* Is there a local BTS that matches the key? */
- llist_for_each_entry(neigh, &bts->local_neighbors, entry) {
- if (!neighbor_ident_key_matches_bts(&key, neigh->bts))
- continue;
- vty_out(vty, "%% %s resolves to local BTS %u lac-ci %u %u%s",
- neighbor_ident_key_name(&key), neigh->bts->nr, neigh->bts->location_area_code,
- neigh->bts->cell_identity, VTY_NEWLINE);
- found++;
+ switch (resolve_neighbors(&local_neighbor, &remote_neighbors, bts, &ab, true)) {
+ case 0:
+ break;
+ case -ENOENT:
+ vty_out(vty, "%% No entry for BTS %u -> %s%s", bts->nr, cell_ab_to_str_c(OTC_SELECT, &ab), VTY_NEWLINE);
+ return CMD_WARNING;
+ default:
+ vty_out(vty, "%% Error while resolving neighbors BTS %u -> %s%s", bts->nr,
+ cell_ab_to_str_c(OTC_SELECT, &ab), VTY_NEWLINE);
+ return CMD_WARNING;
}
- res = neighbor_ident_get(g_neighbor_cells, &key);
- if (res) {
- write_neighbor_ident_list(&key, res, &d);
- found++;
+ /* From successful rc == 0, there is exactly either a local_neighbor or a nonempty remote_neighbors list. */
+
+ vty_out(vty, "%% BTS %u -> %s resolves to", bts->nr, cell_ab_to_str_c(OTC_SELECT, &ab));
+ if (local_neighbor) {
+ vty_out(vty, " local BTS %u lac-ci %u %u%s",
+ local_neighbor->nr,
+ local_neighbor->location_area_code,
+ local_neighbor->cell_identity, VTY_NEWLINE);
}
- if (!found)
- vty_out(vty, "%% No entry for %s%s", neighbor_ident_key_name(&key), VTY_NEWLINE);
+ if (remote_neighbors.id_list_len) {
+ vty_out(vty, " remote-BSS neighbors: %s%s",
+ gsm0808_cell_id_list_name_c(OTC_SELECT, &remote_neighbors),
+ VTY_NEWLINE);
+ }
return CMD_SUCCESS;
}
-void neighbor_ident_vty_init(struct gsm_network *net, struct neighbor_ident_list *nil)
+void neighbor_ident_vty_init(void)
{
- g_net = net;
- g_neighbor_cells = nil;
+ install_element(GSMNET_NODE, &cfg_neighbor_bind_cmd);
+
install_element(BTS_NODE, &cfg_neighbor_add_bts_nr_cmd);
install_element(BTS_NODE, &cfg_neighbor_add_lac_cmd);
install_element(BTS_NODE, &cfg_neighbor_add_lac_ci_cmd);
install_element(BTS_NODE, &cfg_neighbor_add_cgi_cmd);
+ install_element(BTS_NODE, &cfg_neighbor_add_cgi_ps_cmd);
install_element(BTS_NODE, &cfg_neighbor_add_lac_arfcn_bsic_cmd);
install_element(BTS_NODE, &cfg_neighbor_add_lac_ci_arfcn_bsic_cmd);
install_element(BTS_NODE, &cfg_neighbor_add_cgi_arfcn_bsic_cmd);
+ install_element(BTS_NODE, &cfg_neighbor_add_cgi_ps_arfcn_bsic_cmd);
install_element(BTS_NODE, &cfg_neighbor_del_bts_nr_cmd);
+ install_element(BTS_NODE, &cfg_neighbor_del_lac_cmd);
+ install_element(BTS_NODE, &cfg_neighbor_del_lac_ci_cmd);
+ install_element(BTS_NODE, &cfg_neighbor_del_cgi_cmd);
+ install_element(BTS_NODE, &cfg_neighbor_del_cgi_ps_cmd);
install_element(BTS_NODE, &cfg_neighbor_del_arfcn_bsic_cmd);
install_element(BTS_NODE, &cfg_neighbor_del_all_cmd);
install_element_ve(&show_bts_neighbor_cmd);
diff --git a/src/osmo-bsc/net_init.c b/src/osmo-bsc/net_init.c
index 6cbb40cdb..8a508dd61 100644
--- a/src/osmo-bsc/net_init.c
+++ b/src/osmo-bsc/net_init.c
@@ -25,41 +25,70 @@
#include <osmocom/bsc/handover_cfg.h>
#include <osmocom/bsc/chan_alloc.h>
#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/bts_setup_ramp.h>
+#include <osmocom/bsc/paging.h>
static struct osmo_tdef gsm_network_T_defs[] = {
- { .T=7, .default_val=10, .desc="inter-BSC/MSC Handover outgoing, BSSMAP HO Required to HO Command timeout" },
- { .T=8, .default_val=10, .desc="inter-BSC/MSC Handover outgoing, BSSMAP HO Command to final Clear timeout" },
- { .T=10, .default_val=6, .desc="RR Assignment" },
- { .T=101, .default_val=10, .desc="inter-BSC/MSC Handover incoming, BSSMAP HO Request to HO Accept" },
- { .T=3101, .default_val=3, .desc="RR Immediate Assignment" },
- { .T=3103, .default_val=5, .desc="Handover" },
- { .T=3105, .default_val=100, .unit=OSMO_TDEF_MS, .desc="Physical Information" },
- { .T=3107, .default_val=5, .desc="(unused)" },
- { .T=3109, .default_val=5, .desc="RSL SACCH deactivation" },
- { .T=3111, .default_val=2, .desc="Wait time before RSL RF Channel Release" },
- { .T=3113, .default_val=7, .desc="Paging"},
- { .T=3115, .default_val=10, .desc="(unused)" },
- { .T=3117, .default_val=10, .desc="(unused)" },
- { .T=3119, .default_val=10, .desc="(unused)" },
- { .T=3122, .default_val=GSM_T3122_DEFAULT, .desc="Wait time after RR Immediate Assignment Reject" },
- { .T=3141, .default_val=10, .desc="(unused)" },
- { .T=3212, .default_val=5, .unit=OSMO_TDEF_CUSTOM,
- .desc="Periodic Location Update timer, sent to MS (1 = 6 minutes)" },
- { .T=-4, .default_val=60, .desc="After Clear Request, wait for MSC to Clear Command (sanity)" },
- { .T=-5, .default_val=5, .desc="Timeout to switch dynamic timeslot PCHAN modes"},
- { .T=-6, .default_val=5, .desc="Timeout for RSL Channel Activate ACK after sending RSL Channel Activate" },
- { .T=-7, .default_val=5, .desc="Timeout for RSL IPA CRCX ACK after sending RSL IPA CRCX" },
- { .T=-8, .default_val=5, .desc="Timeout for RSL IPA MDCX ACK after sending RSL IPA MDCX" },
- { .T=-9, .default_val=5, .desc="Timeout for availability of MGW endpoint" },
- { .T=-10, .default_val=5, .desc="Timeout for fully configured MGW endpoint" },
- { .T=-11, .default_val=5, .desc="Timeout for Perform Location Response from SMLC" },
- { .T=-3111, .default_val=4, .desc="Wait time after lchan was released in error (should be T3111 + 2s)" },
- { .T=-3210, .default_val=20, .desc="After L3 Complete, wait for MSC to confirm" },
+ { .T = 4, .default_val = 5, .desc = "Timeout to receive BSSMAP RESET ACKNOWLEDGE from the MSC" },
+ { .T = 7, .default_val = 10, .desc = "inter-BSC/MSC Handover outgoing, BSSMAP HO Required to HO Command timeout" },
+ { .T = 8, .default_val = 10, .desc = "inter-BSC/MSC Handover outgoing, BSSMAP HO Command to final Clear timeout" },
+ { .T = 10, .default_val = 6, .desc = "RR Assignment" },
+ { .T = 101, .default_val = 10, .desc = "inter-BSC/MSC Handover incoming, BSSMAP HO Request to HO Accept" },
+ { .T = 3101, .default_val = 3, .desc = "RR Immediate Assignment" },
+ { .T = 3103, .default_val = 5, .desc = "Handover" },
+ { .T = 3105, .default_val = GSM_T3105_DEFAULT, .unit = OSMO_TDEF_MS, .desc = "Physical Information" },
+ { .T = 3107, .default_val = 5, .desc = "(unused)" },
+ { .T = 3109, .default_val = 5, .desc = "RSL SACCH deactivation" },
+ { .T = 3111, .default_val = 2, .desc = "Wait time before RSL RF Channel Release" },
+ { .T = 3113, .default_val = 7, .desc = "Paging"},
+ { .T = 3115, .default_val = 10, .desc = "(unused)" },
+ { .T = 3117, .default_val = 10, .desc = "(unused)" },
+ { .T = 3119, .default_val = 10, .desc = "(unused)" },
+ { .T = 3122, .default_val = GSM_T3122_DEFAULT, .desc = "Wait time after RR Immediate Assignment Reject" },
+ { .T = 3141, .default_val = 10, .desc = "(unused)" },
+ { .T = 3212, .default_val = 5, .unit = OSMO_TDEF_CUSTOM,
+ .desc = "Periodic Location Update timer, sent to MS (1 = 6 minutes)" },
+ { .T = -4, .default_val = 60, .desc = "After Clear Request, wait for MSC to Clear Command (sanity)" },
+ { .T = -5, .default_val = 5, .desc = "Timeout to switch dynamic timeslot PCHAN modes"},
+ { .T = -6, .default_val = 5, .desc = "Timeout for RSL Channel Activate ACK after sending RSL Channel Activate" },
+ { .T = -7, .default_val = 5, .desc = "Timeout for RSL IPA CRCX ACK after sending RSL IPA CRCX" },
+ { .T = -8, .default_val = 5, .desc = "Timeout for RSL IPA MDCX ACK after sending RSL IPA MDCX" },
+ { .T = -9, .default_val = 5, .desc = "Timeout for availability of MGW endpoint" },
+ { .T = -10, .default_val = 5, .desc = "Timeout for fully configured MGW endpoint" },
+ { .T = -11, .default_val = 5, .desc = "Timeout for Perform Location Response from SMLC" },
+ { .T = -12, .default_val = 5, .desc = "Timeout for obtaining TA after BSSLAP TA Request" },
+ { .T = -13, .default_val = 5, .desc = "Timeout for RR Channel Mode Modify ACK (BSC <-> MS)" },
+ { .T = -14, .default_val = 5, .desc = "Timeout for RSL Channel Mode Modify ACK (BSC <-> BTS)" },
+ { .T = -16, .default_val = 1000, .unit = OSMO_TDEF_MS,
+ .desc = "Granularity for all_allocated:* rate counters: amount of milliseconds that one counter increment"
+ " represents. See also X17, X18" },
+ { .T = -17, .default_val = 0, .unit = OSMO_TDEF_MS,
+ .desc = "Rounding threshold for all_allocated:* rate counters: round up to the next counter increment"
+ " after this many milliseconds. If set to half of X16 (or 0), employ the usual round() behavior:"
+ " round up after half of a granularity period. If set to 1, behave like ceil(): already"
+ " increment the counter immediately when all channels are allocated. If set >= X16, behave like"
+ " floor(): only increment after a full X16 period of all channels being occupied."
+ " See also X16, X18" },
+ { .T = -18, .default_val = 60000, .unit = OSMO_TDEF_MS,
+ .desc = "Forget-sum period for all_allocated:* rate counters:"
+ " after this amount of idle time, forget internally cumulated time remainders. Zero to always"
+ " keep remainders. See also X16, X17." },
+ { .T = -25, .default_val = 5, .desc = "Timeout for initial user data after an MSC initiated an SCCP connection to the BSS" },
+ { .T = -3105, .default_val = GSM_NY1_DEFAULT, .unit = OSMO_TDEF_CUSTOM,
+ .desc = "Ny1: Maximum number of Physical Information (re)transmissions" },
+ { .T = -3111, .default_val = 4, .desc = "Wait time after lchan was released in error (should be T3111 + 2s)" },
+ { .T = -3113, .default_val = PAGING_THRESHOLD_X3113_DEFAULT_SEC,
+ .desc = "Maximum Paging Request Transmit Delay Threshold: " \
+ "If the estimated transmit delay of the messages in the paging queue surpasses this threshold, then new incoming "
+ "paging requests will if possible replace a request in retransmission state from the queue or otherwise be discarded, "
+ "hence limiting the size of the queue and maximum delay of its scheduled requests. "
+ "X3113 also serves as the upper boundary for dynamic T3113 when estimating the expected maximum delay to get a response" },
+ { .T = -3210, .default_val = 20, .desc = "After L3 Complete, wait for MSC to confirm" },
{}
};
struct osmo_tdef g_mgw_tdefs[] = {
- { .T=-2427, .default_val=5, .desc="timeout for MGCP response from MGW" },
+ { .T = -2427, .default_val = 5, .desc = "timeout for MGCP response from MGW" },
{}
};
@@ -105,5 +134,7 @@ struct gsm_network *gsm_network_init(void *ctx)
net->null_nri_ranges = osmo_nri_ranges_alloc(net);
net->nri_bitlen = OSMO_NRI_BITLEN_DEFAULT;
+ bts_setup_ramp_init_network(net);
+
return net;
}
diff --git a/src/osmo-bsc/nm_bb_transc_fsm.c b/src/osmo-bsc/nm_bb_transc_fsm.c
new file mode 100644
index 000000000..330291879
--- /dev/null
+++ b/src/osmo-bsc/nm_bb_transc_fsm.c
@@ -0,0 +1,430 @@
+/* NM BaseBand Transceiver FSM */
+
+/* (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/>.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
+#include <osmocom/bsc/ipaccess.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/debug.h>
+
+#define X(s) (1 << (s))
+
+#define nm_bb_transc_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+static inline void nm_bb_transc_fsm_becomes_enabled(struct gsm_bts_bb_trx *bb_transc)
+{
+ struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+ nm_obj_fsm_becomes_enabled_disabled(trx->bts, bb_transc, NM_OC_BASEB_TRANSC, true);
+}
+
+static inline void nm_bb_transc_fsm_becomes_disabled(struct gsm_bts_bb_trx *bb_transc)
+{
+ struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+ nm_obj_fsm_becomes_enabled_disabled(trx->bts, bb_transc, NM_OC_BASEB_TRANSC, false);
+}
+
+//////////////////////////
+// FSM STATE ACTIONS
+//////////////////////////
+
+static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+
+ bb_transc->mo.get_attr_sent = false;
+ bb_transc->mo.get_attr_rep_received = false;
+ bb_transc->mo.adm_unlock_sent = false;
+ bb_transc->mo.rsl_connect_sent = false;
+ bb_transc->mo.rsl_connect_ack_received = false;
+ bb_transc->mo.opstart_sent = false;
+}
+
+static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SW_ACT_REP:
+ case NM_EV_SETUP_RAMP_READY:
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /*should not happen... */
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_DEPENDENCY:
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void configure_loop(struct gsm_bts_bb_trx *bb_transc, const struct gsm_nm_state *state, bool allow_opstart)
+{
+ struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+
+ if (bts_setup_ramp_wait(trx->bts))
+ return;
+
+ /* Request TRX-level attributes */
+ if (!bb_transc->mo.get_attr_sent && !bb_transc->mo.get_attr_rep_received) {
+ bb_transc->mo.get_attr_sent = true;
+ /* N. B: we rely on attribute order when parsing response in abis_nm_rx_get_attr_resp() */
+ const uint8_t trx_attr[] = { NM_ATT_MANUF_STATE, NM_ATT_SW_CONFIG, };
+ /* we should not request more attributes than we're ready to handle */
+ OSMO_ASSERT(sizeof(trx_attr) < MAX_BTS_ATTR);
+ abis_nm_get_attr(trx->bts, NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff,
+ trx_attr, sizeof(trx_attr));
+ }
+
+ if (bb_transc->mo.get_attr_rep_received &&
+ state->administrative != NM_STATE_UNLOCKED && !bb_transc->mo.adm_unlock_sent) {
+ bb_transc->mo.adm_unlock_sent = true;
+ /* Note: nanoBTS sometimes fails NACKing the BaseBand
+ Transceiver Unlock command while in Dependency, specially
+ during first attempt after boot. When NACK is received, the
+ OML link is dropped and the whole procedure is restarted. */
+ abis_nm_chg_adm_state(trx->bts, NM_OC_BASEB_TRANSC,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ NM_STATE_UNLOCKED);
+ }
+
+ /* Provision BTS with RSL IP addr & port to connect to: */
+ if (allow_opstart && state->administrative == NM_STATE_UNLOCKED &&
+ !bb_transc->mo.rsl_connect_sent && !bb_transc->mo.rsl_connect_ack_received) {
+ bb_transc->mo.rsl_connect_sent = true;
+ abis_nm_ipaccess_rsl_connect(trx, trx->bts->ip_access.rsl_ip,
+ 3003, trx->rsl_tei_primary);
+ }
+
+ /* OPSTART after receiving RSL CONNECT ACK. We cannot delay until the
+ * RSL/IPA socket is connected to us because nanoBTS only attempts
+ * connection after receiving an OPSTART: */
+ if (allow_opstart && state->administrative == NM_STATE_UNLOCKED &&
+ bb_transc->mo.rsl_connect_ack_received && !bb_transc->mo.opstart_sent) {
+ bb_transc->mo.opstart_sent = true;
+ abis_nm_opstart(trx->bts, NM_OC_BASEB_TRANSC, trx->bts->bts_nr, trx->nr, 0xff);
+ }
+}
+
+static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+ struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+
+ if (trx->bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_OFFLINE);
+ return;
+ }
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, false);
+}
+
+static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+ struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_GET_ATTR_REP:
+ bb_transc->mo.get_attr_rep_received = true;
+ bb_transc->mo.get_attr_sent = false;
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, false);
+ return;
+ case NM_EV_RSL_CONNECT_ACK:
+ bb_transc->mo.rsl_connect_ack_received = true;
+ bb_transc->mo.rsl_connect_sent = false;
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, false);
+ break;
+ case NM_EV_RSL_CONNECT_NACK:
+ ipaccess_drop_oml_deferred(trx->bts);
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_OFFLINE);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ configure_loop(bb_transc, new_state, false);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, false);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, true);
+}
+
+static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+ struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_GET_ATTR_REP:
+ bb_transc->mo.get_attr_rep_received = true;
+ bb_transc->mo.get_attr_sent = false;
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, true);
+ return;
+ case NM_EV_RSL_CONNECT_ACK:
+ bb_transc->mo.rsl_connect_ack_received = true;
+ bb_transc->mo.rsl_connect_sent = false;
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, true);
+ break;
+ case NM_EV_RSL_CONNECT_NACK:
+ ipaccess_drop_oml_deferred(trx->bts);
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ /* There's no point in moving back to Dependency, since it's broken
+ and it acts actually as if it was in Offline state */
+ if (!trx->bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_DEPENDENCY);
+ } else {
+ /* Moreover, in nanoBTS we need to check here for tx
+ Opstart since we may have gone Unlocked state
+ in this event, which means Opstart may be txed here. */
+ configure_loop(bb_transc, new_state, true);
+ }
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ configure_loop(bb_transc, new_state, true);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, true);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+
+ /* Reset state, we don't need it in this state and it will need to be
+ reused as soon as we move back to Disabled */
+ bb_transc->mo.get_attr_sent = false;
+ bb_transc->mo.get_attr_rep_received = false;
+ bb_transc->mo.adm_unlock_sent = false;
+ bb_transc->mo.rsl_connect_ack_received = false;
+ bb_transc->mo.rsl_connect_sent = false;
+ bb_transc->mo.opstart_sent = false;
+
+ nm_bb_transc_fsm_becomes_enabled(bb_transc);
+}
+
+static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED)
+ return;
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_bb_transc_fsm_becomes_disabled(bb_transc);
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_bb_transc_fsm_becomes_disabled(bb_transc);
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_bb_transc_fsm_becomes_disabled(bb_transc);
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+
+ switch (event) {
+ case NM_EV_OPSTART_ACK:
+ case NM_EV_OPSTART_NACK:
+ /* TODO: if on state OFFLINE and rx NACK, try again? */
+ bb_transc->mo.opstart_sent = false;
+ break;
+ case NM_EV_OML_DOWN:
+ if (fi->state != NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED) {
+ if (fi->state == NM_BB_TRANSC_ST_OP_ENABLED)
+ nm_bb_transc_fsm_becomes_disabled(bb_transc);
+ nm_bb_transc_fsm_state_chg(fi, NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED);
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static struct osmo_fsm_state nm_bb_transc_fsm_states[] = {
+ [NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED] = {
+ .in_event_mask =
+ X(NM_EV_SW_ACT_REP) |
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_BB_TRANSC_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_BB_TRANSC_ST_OP_DISABLED_OFFLINE) |
+ X(NM_BB_TRANSC_ST_OP_ENABLED),
+ .name = "DISABLED_NOTINSTALLED",
+ .onenter = st_op_disabled_notinstalled_on_enter,
+ .action = st_op_disabled_notinstalled,
+ },
+ [NM_BB_TRANSC_ST_OP_DISABLED_DEPENDENCY] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_GET_ATTR_REP) |
+ X(NM_EV_RSL_CONNECT_ACK) |
+ X(NM_EV_RSL_CONNECT_NACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_BB_TRANSC_ST_OP_DISABLED_OFFLINE) |
+ X(NM_BB_TRANSC_ST_OP_ENABLED),
+ .name = "DISABLED_DEPENDENCY",
+ .onenter = st_op_disabled_dependency_on_enter,
+ .action = st_op_disabled_dependency,
+ },
+ [NM_BB_TRANSC_ST_OP_DISABLED_OFFLINE] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_GET_ATTR_REP) |
+ X(NM_EV_RSL_CONNECT_ACK) |
+ X(NM_EV_RSL_CONNECT_NACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_BB_TRANSC_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_BB_TRANSC_ST_OP_ENABLED),
+ .name = "DISABLED_OFFLINE",
+ .onenter = st_op_disabled_offline_on_enter,
+ .action = st_op_disabled_offline,
+ },
+ [NM_BB_TRANSC_ST_OP_ENABLED] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP),
+ .out_state_mask =
+ X(NM_BB_TRANSC_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_BB_TRANSC_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_BB_TRANSC_ST_OP_DISABLED_OFFLINE),
+ .name = "ENABLED",
+ .onenter = st_op_enabled_on_enter,
+ .action = st_op_enabled,
+ },
+};
+
+struct osmo_fsm nm_bb_transc_fsm = {
+ .name = "NM_BB_TRANSC_OP",
+ .states = nm_bb_transc_fsm_states,
+ .num_states = ARRAY_SIZE(nm_bb_transc_fsm_states),
+ .allstate_event_mask =
+ X(NM_EV_OPSTART_ACK) |
+ X(NM_EV_OPSTART_NACK) |
+ X(NM_EV_OML_DOWN),
+ .allstate_action = st_op_allstate,
+ .event_names = nm_fsm_event_names,
+ .log_subsys = DNM,
+};
+
+static __attribute__((constructor)) void nm_bb_transc_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&nm_bb_transc_fsm) == 0);
+}
diff --git a/src/osmo-bsc/nm_bts_fsm.c b/src/osmo-bsc/nm_bts_fsm.c
new file mode 100644
index 000000000..92728dce2
--- /dev/null
+++ b/src/osmo-bsc/nm_bts_fsm.c
@@ -0,0 +1,412 @@
+/* NM BTS FSM */
+
+/* (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/>.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/debug.h>
+
+#define X(s) (1 << (s))
+
+#define nm_bts_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+//////////////////////////
+// FSM STATE ACTIONS
+//////////////////////////
+
+static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+
+ bts->mo.get_attr_sent = false;
+ bts->mo.get_attr_rep_received = false;
+ bts->mo.set_attr_sent = false;
+ bts->mo.set_attr_ack_received = false;
+ bts->mo.adm_unlock_sent = false;
+ bts->mo.opstart_sent = false;
+}
+
+static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SW_ACT_REP:
+ case NM_EV_SETUP_RAMP_READY:
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /*should not happen... */
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_DEPENDENCY:
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void configure_loop(struct gsm_bts *bts, const struct gsm_nm_state *state, bool allow_opstart)
+{
+ struct msgb *msgb;
+
+ if (bts_setup_ramp_wait(bts))
+ return;
+
+ /* Request generic BTS-level attributes */
+ if (!bts->mo.get_attr_sent && !bts->mo.get_attr_rep_received) {
+ bts->mo.get_attr_sent = true;
+ /* N. B: we rely on attribute order when parsing response in abis_nm_rx_get_attr_resp() */
+ const uint8_t bts_attr[] = { NM_ATT_MANUF_ID, NM_ATT_SW_CONFIG, };
+ /* we should not request more attributes than we're ready to handle */
+ OSMO_ASSERT(sizeof(bts_attr) < MAX_BTS_ATTR);
+ abis_nm_get_attr(bts, NM_OC_BTS, 0, 0xff, 0xff,
+ bts_attr, sizeof(bts_attr));
+ }
+
+ if (bts->mo.get_attr_rep_received &&
+ !bts->mo.set_attr_sent && !bts->mo.set_attr_ack_received) {
+ bts->mo.set_attr_sent = true;
+ msgb = nanobts_gen_set_bts_attr(bts);
+ abis_nm_set_bts_attr(bts, msgb->data, msgb->len);
+ msgb_free(msgb);
+ }
+
+ if (bts->mo.set_attr_ack_received &&
+ state->administrative != NM_STATE_UNLOCKED && !bts->mo.adm_unlock_sent) {
+ bts->mo.adm_unlock_sent = true;
+ abis_nm_chg_adm_state(bts, NM_OC_BTS,
+ bts->bts_nr, 0xff, 0xff,
+ NM_STATE_UNLOCKED);
+ /* Message containing BTS attributes, including the interference band bounds, was ACKed by the BTS.
+ * Store the sent bounds as the ones being used for logging and comparing intereference levels. */
+ bts->interf_meas_params_used = bts->interf_meas_params_cfg;
+ }
+
+ if (allow_opstart && state->administrative == NM_STATE_UNLOCKED &&
+ bts->mo.set_attr_ack_received) {
+ if (!bts->mo.opstart_sent) {
+ bts->mo.opstart_sent = true;
+ abis_nm_opstart(bts, NM_OC_BTS, bts->bts_nr, 0xff, 0xff);
+ }
+ }
+}
+
+static void rx_get_attr_rep(struct gsm_bts *bts, bool allow_opstart)
+{
+ struct gsm_gprs_nsvc *nsvc;
+
+ bts->mo.get_attr_rep_received = true;
+ bts->mo.get_attr_sent = false;
+
+ /* Announce bts_features are available to related NSVC MOs */
+ for (int i = 0; i < ARRAY_SIZE(bts->site_mgr->gprs.nsvc); i++) {
+ nsvc = gsm_bts_sm_nsvc_num(bts->site_mgr, i);
+ if (nsvc)
+ osmo_fsm_inst_dispatch(nsvc->mo.fi, NM_EV_FEATURE_NEGOTIATED, NULL);
+ }
+
+ /* Move FSM forward */
+ configure_loop(bts, &bts->mo.nm_state, allow_opstart);
+}
+
+static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+
+ /* nanoBTS is broken, doesn't follow TS 12.21. Opstart MUST be sent
+ during Dependency, so we simply move to OFFLINE state here to avoid
+ duplicating code */
+ if (bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_OFFLINE);
+ return;
+ }
+ configure_loop(bts, &bts->mo.nm_state, false);
+}
+
+static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_GET_ATTR_REP:
+ rx_get_attr_rep(bts, false);
+ return;
+ case NM_EV_SET_ATTR_ACK:
+ bts->mo.set_attr_ack_received = true;
+ bts->mo.set_attr_sent = false;
+ configure_loop(bts, &bts->mo.nm_state, false);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_OFFLINE);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ configure_loop(bts, new_state, false);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(bts, &bts->mo.nm_state, false);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+
+ configure_loop(bts, &bts->mo.nm_state, true);
+}
+
+static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_GET_ATTR_REP:
+ rx_get_attr_rep(bts, true);
+ return;
+ case NM_EV_SET_ATTR_ACK:
+ bts->mo.set_attr_ack_received = true;
+ bts->mo.set_attr_sent = false;
+ configure_loop(bts, &bts->mo.nm_state, true);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ /* There's no point in moving back to Dependency, since it's broken
+ and it acts actually as if it was in Offline state */
+ if (!bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_DEPENDENCY);
+ } else {
+ /* Moreover, in nanoBTS we need to check here for tx
+ Opstart since we may have gone Unlocked state
+ in this event, which means Opstart may be txed here. */
+ configure_loop(bts, new_state, true);
+ }
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ configure_loop(bts, new_state, true);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(bts, &bts->mo.nm_state, true);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+
+ /* Reset state, we don't need it in this state and it will need to be
+ reused as soon as we move back to Disabled */
+ bts->mo.opstart_sent = false;
+ bts->mo.adm_unlock_sent = false;
+ bts->mo.get_attr_sent = false;
+ bts->mo.get_attr_rep_received = false;
+ bts->mo.set_attr_sent = false;
+ bts->mo.set_attr_ack_received = false;
+}
+
+static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED)
+ return;
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+
+ switch (event) {
+ case NM_EV_OPSTART_ACK:
+ case NM_EV_OPSTART_NACK:
+ /* TODO: if on state OFFLINE and rx NACK, try again? */
+ bts->mo.opstart_sent = false;
+ break;
+ case NM_EV_OML_DOWN:
+ if (fi->state != NM_BTS_ST_OP_DISABLED_NOTINSTALLED)
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static struct osmo_fsm_state nm_bts_fsm_states[] = {
+ [NM_BTS_ST_OP_DISABLED_NOTINSTALLED] = {
+ .in_event_mask =
+ X(NM_EV_SW_ACT_REP) |
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_BTS_ST_OP_DISABLED_OFFLINE) |
+ X(NM_BTS_ST_OP_ENABLED),
+ .name = "DISABLED_NOTINSTALLED",
+ .onenter = st_op_disabled_notinstalled_on_enter,
+ .action = st_op_disabled_notinstalled,
+ },
+ [NM_BTS_ST_OP_DISABLED_DEPENDENCY] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_GET_ATTR_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_BTS_ST_OP_DISABLED_OFFLINE) |
+ X(NM_BTS_ST_OP_ENABLED),
+ .name = "DISABLED_DEPENDENCY",
+ .onenter = st_op_disabled_dependency_on_enter,
+ .action = st_op_disabled_dependency,
+ },
+ [NM_BTS_ST_OP_DISABLED_OFFLINE] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_GET_ATTR_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_BTS_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_BTS_ST_OP_ENABLED),
+ .name = "DISABLED_OFFLINE",
+ .onenter = st_op_disabled_offline_on_enter,
+ .action = st_op_disabled_offline,
+ },
+ [NM_BTS_ST_OP_ENABLED] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP),
+ .out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_BTS_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_BTS_ST_OP_DISABLED_OFFLINE),
+ .name = "ENABLED",
+ .onenter = st_op_enabled_on_enter,
+ .action = st_op_enabled,
+ },
+};
+
+struct osmo_fsm nm_bts_fsm = {
+ .name = "NM_BTS_OP",
+ .states = nm_bts_fsm_states,
+ .num_states = ARRAY_SIZE(nm_bts_fsm_states),
+ .allstate_event_mask =
+ X(NM_EV_OPSTART_ACK) |
+ X(NM_EV_OPSTART_NACK) |
+ X(NM_EV_OML_DOWN),
+ .allstate_action = st_op_allstate,
+ .event_names = nm_fsm_event_names,
+ .log_subsys = DNM,
+};
+
+static __attribute__((constructor)) void nm_bts_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&nm_bts_fsm) == 0);
+}
diff --git a/src/osmo-bsc/nm_bts_sm_fsm.c b/src/osmo-bsc/nm_bts_sm_fsm.c
new file mode 100644
index 000000000..fb64a69d0
--- /dev/null
+++ b/src/osmo-bsc/nm_bts_sm_fsm.c
@@ -0,0 +1,307 @@
+/* NM BTS Site Manager FSM */
+
+/* (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/>.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/debug.h>
+
+#define X(s) (1 << (s))
+
+#define nm_bts_sm_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+//////////////////////////
+// FSM STATE ACTIONS
+//////////////////////////
+
+static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+ struct gsm_bts *bts = gsm_bts_sm_get_bts(site_mgr);
+
+ site_mgr->peer_has_no_avstate_offline = (bts->type == GSM_BTS_TYPE_NANOBTS);
+ site_mgr->mo.opstart_sent = false;
+}
+
+static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+ struct gsm_bts *bts = gsm_bts_sm_get_bts(site_mgr);
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SW_ACT_REP:
+ case NM_EV_SETUP_RAMP_READY:
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* nanobts always go directly into Reported ENABLED state during
+ startup, but we still need to OPSTART it, otherwise it won't
+ connect on RSL later on */
+ if (bts->type == GSM_BTS_TYPE_NANOBTS) {
+ site_mgr->mo.opstart_sent = true;
+ abis_nm_opstart(bts, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff);
+ } else {
+ LOGPFSML(fi, LOGL_NOTICE, "Received BTS Site Mgr State Report Enabled "
+ "without Opstart. You are probably using a nanoBTS but don't "
+ "have your .cfg with 'type nanobts'. Otherwise, you probably "
+ "are using an old osmo-bts; automatically adjusting OML "
+ "behavior to be backward-compatible.\n");
+ }
+ site_mgr->peer_has_no_avstate_offline = true;
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_DEPENDENCY:
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void configure_loop(struct gsm_bts_sm *site_mgr, const struct gsm_nm_state *_state, bool allow_opstart)
+{
+ struct gsm_bts *bts = gsm_bts_sm_get_bts(site_mgr);
+
+ if (bts_setup_ramp_wait(bts))
+ return;
+
+ if (allow_opstart && !site_mgr->mo.opstart_sent) {
+ site_mgr->mo.opstart_sent = true;
+ abis_nm_opstart(bts, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff);
+ }
+}
+
+
+static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SETUP_RAMP_READY:
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+
+ configure_loop(site_mgr, &site_mgr->mo.nm_state, true);
+}
+
+static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+ struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+
+ switch (event) {
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(site_mgr, &site_mgr->mo.nm_state, true);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED)
+ return;
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+
+ switch (event) {
+ case NM_EV_OPSTART_ACK:
+ case NM_EV_OPSTART_NACK:
+ /* TODO: if on state OFFLINE and rx NACK, try again? */
+ site_mgr->mo.opstart_sent = false;
+ break;
+ case NM_EV_OML_DOWN:
+ if (fi->state != NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED)
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static struct osmo_fsm_state nm_bts_sm_fsm_states[] = {
+ [NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED] = {
+ .in_event_mask =
+ X(NM_EV_SW_ACT_REP) |
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_BTS_SM_ST_OP_DISABLED_OFFLINE) |
+ X(NM_BTS_SM_ST_OP_ENABLED),
+ .name = "DISABLED_NOTINSTALLED",
+ .onenter = st_op_disabled_notinstalled_on_enter,
+ .action = st_op_disabled_notinstalled,
+ },
+ [NM_BTS_SM_ST_OP_DISABLED_DEPENDENCY] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_BTS_SM_ST_OP_DISABLED_OFFLINE) |
+ X(NM_BTS_SM_ST_OP_ENABLED),
+ .name = "DISABLED_DEPENDENCY",
+ .action = st_op_disabled_dependency,
+ },
+ [NM_BTS_SM_ST_OP_DISABLED_OFFLINE] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_BTS_SM_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_BTS_SM_ST_OP_ENABLED),
+ .name = "DISABLED_OFFLINE",
+ .onenter = st_op_disabled_offline_on_enter,
+ .action = st_op_disabled_offline,
+ },
+ [NM_BTS_SM_ST_OP_ENABLED] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP),
+ .out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_BTS_SM_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_BTS_SM_ST_OP_DISABLED_OFFLINE),
+ .name = "ENABLED",
+ .action = st_op_enabled,
+ },
+};
+
+struct osmo_fsm nm_bts_sm_fsm = {
+ .name = "NM_BTS_SM_OP",
+ .states = nm_bts_sm_fsm_states,
+ .num_states = ARRAY_SIZE(nm_bts_sm_fsm_states),
+ .allstate_event_mask =
+ X(NM_EV_OPSTART_ACK) |
+ X(NM_EV_OPSTART_NACK) |
+ X(NM_EV_OML_DOWN),
+ .allstate_action = st_op_allstate,
+ .event_names = nm_fsm_event_names,
+ .log_subsys = DNM,
+};
+
+static __attribute__((constructor)) void nm_bts_sm_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&nm_bts_sm_fsm) == 0);
+}
diff --git a/src/osmo-bsc/nm_channel_fsm.c b/src/osmo-bsc/nm_channel_fsm.c
new file mode 100644
index 000000000..45e455a9a
--- /dev/null
+++ b/src/osmo-bsc/nm_channel_fsm.c
@@ -0,0 +1,363 @@
+/* NM Radio Channel FSM */
+
+/* (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/>.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
+#include <osmocom/bsc/ipaccess.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/debug.h>
+
+#define X(s) (1 << (s))
+
+#define nm_chan_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+//////////////////////////
+// FSM STATE ACTIONS
+//////////////////////////
+
+static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+ ts->mo.set_attr_sent = false;
+ ts->mo.set_attr_ack_received = false;
+ ts->mo.adm_unlock_sent = false;
+ ts->mo.opstart_sent = false;
+}
+
+static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SW_ACT_REP:
+ case NM_EV_SETUP_RAMP_READY:
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /*should not happen... */
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_DEPENDENCY:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void configure_loop(struct gsm_bts_trx_ts *ts, const struct gsm_nm_state *state, bool allow_opstart)
+{
+ enum abis_nm_chan_comb ccomb;
+ struct gsm_bts_trx *trx = ts->trx;
+
+ if (bts_setup_ramp_wait(ts->trx->bts))
+ return;
+
+ if (!ts->mo.set_attr_sent && !ts->mo.set_attr_ack_received) {
+ ts->mo.set_attr_sent = true;
+ ccomb = abis_nm_chcomb4pchan(ts->pchan_from_config);
+ if (abis_nm_set_channel_attr(ts, ccomb) == -EINVAL)
+ ipaccess_drop_oml_deferred(trx->bts);
+ }
+
+ if (state->administrative != NM_STATE_UNLOCKED && !ts->mo.adm_unlock_sent) {
+ ts->mo.adm_unlock_sent = true;
+ abis_nm_chg_adm_state(trx->bts, NM_OC_CHANNEL,
+ trx->bts->bts_nr, trx->nr, ts->nr,
+ NM_STATE_UNLOCKED);
+ }
+
+ if (allow_opstart && state->administrative == NM_STATE_UNLOCKED &&
+ ts->mo.set_attr_ack_received && !ts->mo.opstart_sent) {
+ ts->mo.opstart_sent = true;
+ abis_nm_opstart(trx->bts, NM_OC_CHANNEL, trx->bts->bts_nr, trx->nr, ts->nr);
+ }
+}
+
+static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+ if (ts->trx->bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_OFFLINE);
+ return;
+ }
+ configure_loop(ts, &ts->mo.nm_state, false);
+}
+
+static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SET_ATTR_ACK:
+ ts->mo.set_attr_ack_received = true;
+ ts->mo.set_attr_sent = false;
+ configure_loop(ts, &ts->mo.nm_state, false);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_OFFLINE);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ configure_loop(ts, new_state, false);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(ts, &ts->mo.nm_state, false);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+ configure_loop(ts, &ts->mo.nm_state, true);
+}
+
+static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SET_ATTR_ACK:
+ ts->mo.set_attr_ack_received = true;
+ ts->mo.set_attr_sent = false;
+ configure_loop(ts, &ts->mo.nm_state, true);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ /* There's no point in moving back to Dependency, since it's broken
+ and it acts actually as if it was in Offline state */
+ if (!ts->trx->bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_DEPENDENCY);
+ } else {
+ /* Moreover, in nanoBTS we need to check here for tx
+ Opstart since we may have gone Unlocked state
+ in this event, which means Opstart may be txed here. */
+ configure_loop(ts, new_state, true);
+ }
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ configure_loop(ts, new_state, true);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(ts, &ts->mo.nm_state, true);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+ /* Reset state, we don't need it in this state and it will need to be
+ reused as soon as we move back to Disabled */
+ ts->mo.opstart_sent = false;
+ ts->mo.adm_unlock_sent = false;
+ ts->mo.set_attr_ack_received = false;
+ ts->mo.set_attr_sent = false;
+}
+
+static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED)
+ return;
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+ switch (event) {
+ case NM_EV_OPSTART_ACK:
+ case NM_EV_OPSTART_NACK:
+ /* TODO: if on state OFFLINE and rx NACK, try again? */
+ ts->mo.opstart_sent = false;
+ break;
+ case NM_EV_OML_DOWN:
+ if (fi->state != NM_CHAN_ST_OP_DISABLED_NOTINSTALLED)
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static struct osmo_fsm_state nm_chan_fsm_states[] = {
+ [NM_CHAN_ST_OP_DISABLED_NOTINSTALLED] = {
+ .in_event_mask =
+ X(NM_EV_SW_ACT_REP) |
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
+ X(NM_CHAN_ST_OP_ENABLED),
+ .name = "DISABLED_NOTINSTALLED",
+ .onenter = st_op_disabled_notinstalled_on_enter,
+ .action = st_op_disabled_notinstalled,
+ },
+ [NM_CHAN_ST_OP_DISABLED_DEPENDENCY] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
+ X(NM_CHAN_ST_OP_ENABLED),
+ .name = "DISABLED_DEPENDENCY",
+ .onenter = st_op_disabled_dependency_on_enter,
+ .action = st_op_disabled_dependency,
+ },
+ [NM_CHAN_ST_OP_DISABLED_OFFLINE] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_CHAN_ST_OP_ENABLED),
+ .name = "DISABLED_OFFLINE",
+ .onenter = st_op_disabled_offline_on_enter,
+ .action = st_op_disabled_offline,
+ },
+ [NM_CHAN_ST_OP_ENABLED] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP),
+ .out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_CHAN_ST_OP_DISABLED_OFFLINE),
+ .name = "ENABLED",
+ .onenter = st_op_enabled_on_enter,
+ .action = st_op_enabled,
+ },
+};
+
+struct osmo_fsm nm_chan_fsm = {
+ .name = "NM_CHAN_OP",
+ .states = nm_chan_fsm_states,
+ .num_states = ARRAY_SIZE(nm_chan_fsm_states),
+ .allstate_event_mask =
+ X(NM_EV_OPSTART_ACK) |
+ X(NM_EV_OPSTART_NACK) |
+ X(NM_EV_OML_DOWN),
+ .allstate_action = st_op_allstate,
+ .event_names = nm_fsm_event_names,
+ .log_subsys = DNM,
+};
+
+static __attribute__((constructor)) void nm_chan_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&nm_chan_fsm) == 0);
+}
diff --git a/src/osmo-bsc/nm_common_fsm.c b/src/osmo-bsc/nm_common_fsm.c
new file mode 100644
index 000000000..02e2c5e2a
--- /dev/null
+++ b/src/osmo-bsc/nm_common_fsm.c
@@ -0,0 +1,90 @@
+/* NM FSM, common bits */
+
+/* (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/>.
+ *
+ */
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/signal.h>
+
+const struct value_string nm_fsm_event_names[] = {
+ { NM_EV_SW_ACT_REP, "SW_ACT_REP" },
+ { NM_EV_STATE_CHG_REP, "STATE_CHG_REP" },
+ { NM_EV_GET_ATTR_REP, "GET_ATTR_REP" },
+ { NM_EV_SET_ATTR_ACK, "SET_ATTR_ACK" },
+ { NM_EV_OPSTART_ACK, "OPSTART_ACK" },
+ { NM_EV_OPSTART_NACK, "OPSTART_NACK" },
+ { NM_EV_OML_DOWN, "OML_DOWN" },
+ { NM_EV_FORCE_LOCK, "FORCE_LOCK_CHG" },
+ { NM_EV_FEATURE_NEGOTIATED, "FEATURE_NEGOTIATED" },
+ { NM_EV_RSL_CONNECT_ACK, "RSL_CONNECT_ACK" },
+ { NM_EV_RSL_CONNECT_NACK, "RSL_CONNECT_NACK" },
+ { 0, NULL }
+};
+
+void nm_obj_fsm_becomes_enabled_disabled(struct gsm_bts *bts, void *obj,
+ enum abis_nm_obj_class obj_class, bool running)
+{
+ struct nm_running_chg_signal_data nsd;
+
+ memset(&nsd, 0, sizeof(nsd));
+ nsd.bts = bts;
+ nsd.obj_class = obj_class;
+ nsd.obj = obj;
+ nsd.running = running;
+
+ osmo_signal_dispatch(SS_NM, S_NM_RUNNING_CHG, &nsd);
+}
+
+/* nm_configuring_fsm_inst_dispatch(struct gsm_abis_mo *mo, uint32_t event, void *data) */
+#define nm_configuring_fsm_inst_dispatch(mo, event, data) do { \
+ if ((mo)->nm_state.operational != NM_OPSTATE_ENABLED) \
+ _osmo_fsm_inst_dispatch((mo)->fi, event, data, __FILE__, __LINE__); \
+ } while (0)
+
+/*!
+ * Dispatch an event to all configuring/non-enabled BTS NM fsms
+ *
+ * \param[in] bts a pointer to the BTS instance
+ * \param[in] event the FSM event. See \fn osmo_fsm_inst_dispatch
+ * \param[in] data the private data of the event.
+ */
+void nm_fsm_dispatch_all_configuring(struct gsm_bts *bts, uint32_t event, void *data)
+{
+ struct gsm_bts_trx *trx;
+
+ nm_configuring_fsm_inst_dispatch(&bts->site_mgr->mo, event, data);
+ nm_configuring_fsm_inst_dispatch(&bts->mo, event, data);
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ nm_configuring_fsm_inst_dispatch(&trx->mo, event, data);
+ nm_configuring_fsm_inst_dispatch(&trx->bb_transc.mo, event, data);
+ for (unsigned long i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ nm_configuring_fsm_inst_dispatch(&ts->mo, event, data);
+ }
+ }
+
+ /* GPRS MOs */
+ nm_configuring_fsm_inst_dispatch(&bts->site_mgr->gprs.nse.mo, event, data);
+ for (unsigned long i = 0; i < ARRAY_SIZE(bts->site_mgr->gprs.nsvc); i++)
+ nm_configuring_fsm_inst_dispatch(&bts->site_mgr->gprs.nsvc[i].mo, event, data);
+ nm_configuring_fsm_inst_dispatch(&bts->gprs.cell.mo, event, data);
+}
diff --git a/src/osmo-bsc/nm_gprs_cell_fsm.c b/src/osmo-bsc/nm_gprs_cell_fsm.c
new file mode 100644
index 000000000..3e6165797
--- /dev/null
+++ b/src/osmo-bsc/nm_gprs_cell_fsm.c
@@ -0,0 +1,382 @@
+/* NM GPRS Cell FSM */
+
+/* (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/>.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/debug.h>
+
+#define X(s) (1 << (s))
+
+#define nm_gprs_cell_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+//////////////////////////
+// FSM STATE ACTIONS
+//////////////////////////
+
+static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_cell *cell = (struct gsm_gprs_cell *)fi->priv;
+
+ cell->mo.set_attr_sent = false;
+ cell->mo.set_attr_ack_received = false;
+ cell->mo.adm_unlock_sent = false;
+ cell->mo.opstart_sent = false;
+}
+
+static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SW_ACT_REP:
+ case NM_EV_SETUP_RAMP_READY:
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_DEPENDENCY:
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void configure_loop(struct gsm_gprs_cell *cell, const struct gsm_nm_state *state, bool allow_opstart)
+{
+ struct msgb *msgb;
+ struct gsm_bts *bts = container_of(cell, struct gsm_bts, gprs.cell);
+
+ if (bts->gprs.mode == BTS_GPRS_NONE)
+ return;
+
+ if (bts_setup_ramp_wait(bts))
+ return;
+
+ if (!cell->mo.set_attr_sent && !cell->mo.set_attr_ack_received) {
+ cell->mo.set_attr_sent = true;
+ msgb = nanobts_gen_set_cell_attr(bts);
+ OSMO_ASSERT(msgb);
+ abis_nm_ipaccess_set_attr(bts, NM_OC_GPRS_CELL, bts->bts_nr,
+ 0, 0xff, msgb->data, msgb->len);
+ msgb_free(msgb);
+ }
+
+ if (state->administrative != NM_STATE_UNLOCKED && !cell->mo.adm_unlock_sent) {
+ cell->mo.adm_unlock_sent = true;
+ abis_nm_chg_adm_state(bts, NM_OC_GPRS_CELL,
+ bts->bts_nr, 0, 0xff,
+ NM_STATE_UNLOCKED);
+ }
+
+ if (allow_opstart && state->administrative == NM_STATE_UNLOCKED &&
+ cell->mo.set_attr_ack_received) {
+ if (!cell->mo.opstart_sent) {
+ cell->mo.opstart_sent = true;
+ abis_nm_opstart(bts, NM_OC_GPRS_CELL, bts->bts_nr, 0, 0xff);
+ }
+ }
+}
+
+static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_cell *cell = (struct gsm_gprs_cell *)fi->priv;
+ struct gsm_bts *bts = container_of(cell, struct gsm_bts, gprs.cell);
+
+ /* nanoBTS is broken, doesn't follow TS 12.21. Opstart MUST be sent
+ during Dependency, so we simply move to OFFLINE state here to avoid
+ duplicating code */
+ if (bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_OFFLINE);
+ return;
+ }
+ configure_loop(cell, &cell->mo.nm_state, false);
+}
+
+static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_gprs_cell *cell = (struct gsm_gprs_cell *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SET_ATTR_ACK:
+ cell->mo.set_attr_ack_received = true;
+ cell->mo.set_attr_sent = false;
+ configure_loop(cell, &cell->mo.nm_state, false);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_OFFLINE);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ configure_loop(cell, new_state, false);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(cell, &cell->mo.nm_state, false);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_cell *cell = (struct gsm_gprs_cell *)fi->priv;
+
+ configure_loop(cell, &cell->mo.nm_state, true);
+}
+
+static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_gprs_cell *cell = (struct gsm_gprs_cell *)fi->priv;
+ struct gsm_bts *bts = container_of(cell, struct gsm_bts, gprs.cell);
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SET_ATTR_ACK:
+ cell->mo.set_attr_ack_received = true;
+ cell->mo.set_attr_sent = false;
+ configure_loop(cell, &cell->mo.nm_state, true);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ /* There's no point in moving back to Dependency, since it's broken
+ and it acts actually as if it was in Offline state */
+ if (!bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_DEPENDENCY);
+ } else {
+ /* Moreover, in nanoBTS we need to check here for tx
+ Opstart since we may have gone Unlocked state
+ in this event, which means Opstart may be txed here. */
+ configure_loop(cell, new_state, true);
+ }
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ configure_loop(cell, new_state, true);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(cell, &cell->mo.nm_state, true);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_cell *cell = (struct gsm_gprs_cell *)fi->priv;
+
+ /* Reset state, we don't need it in this state and it will need to be
+ reused as soon as we move back to Disabled */
+ cell->mo.opstart_sent = false;
+ cell->mo.adm_unlock_sent = false;
+ cell->mo.set_attr_ack_received = false;
+ cell->mo.set_attr_sent = false;
+}
+
+static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED)
+ return;
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_gprs_cell *cell = (struct gsm_gprs_cell *)fi->priv;
+ struct gsm_bts *bts = container_of(cell, struct gsm_bts, gprs.cell);
+
+ switch (event) {
+ case NM_EV_OPSTART_ACK:
+ case NM_EV_OPSTART_NACK:
+ /* TODO: if on state OFFLINE and rx NACK, try again? */
+ cell->mo.opstart_sent = false;
+ break;
+ case NM_EV_FORCE_LOCK:
+ cell->mo.force_rf_lock = (bool)(intptr_t)data;
+ abis_nm_chg_adm_state(bts, NM_OC_GPRS_CELL,
+ bts->bts_nr, 0, 0xff,
+ cell->mo.force_rf_lock ? NM_STATE_LOCKED : NM_STATE_UNLOCKED);
+ break;
+ case NM_EV_OML_DOWN:
+ if (fi->state != NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED)
+ nm_gprs_cell_fsm_state_chg(fi, NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static struct osmo_fsm_state nm_gprs_cell_fsm_states[] = {
+ [NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED] = {
+ .in_event_mask =
+ X(NM_EV_SW_ACT_REP) |
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_GPRS_CELL_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_GPRS_CELL_ST_OP_DISABLED_OFFLINE) |
+ X(NM_GPRS_CELL_ST_OP_ENABLED),
+ .name = "DISABLED_NOTINSTALLED",
+ .onenter = st_op_disabled_notinstalled_on_enter,
+ .action = st_op_disabled_notinstalled,
+ },
+ [NM_GPRS_CELL_ST_OP_DISABLED_DEPENDENCY] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_GPRS_CELL_ST_OP_DISABLED_OFFLINE) |
+ X(NM_GPRS_CELL_ST_OP_ENABLED),
+ .name = "DISABLED_DEPENDENCY",
+ .onenter = st_op_disabled_dependency_on_enter,
+ .action = st_op_disabled_dependency,
+ },
+ [NM_GPRS_CELL_ST_OP_DISABLED_OFFLINE] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_GPRS_CELL_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_GPRS_CELL_ST_OP_ENABLED),
+ .name = "DISABLED_OFFLINE",
+ .onenter = st_op_disabled_offline_on_enter,
+ .action = st_op_disabled_offline,
+ },
+ [NM_GPRS_CELL_ST_OP_ENABLED] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP),
+ .out_state_mask =
+ X(NM_GPRS_CELL_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_GPRS_CELL_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_GPRS_CELL_ST_OP_DISABLED_OFFLINE),
+ .name = "ENABLED",
+ .onenter = st_op_enabled_on_enter,
+ .action = st_op_enabled,
+ },
+};
+
+struct osmo_fsm nm_gprs_cell_fsm = {
+ .name = "NM_GPRS_CELL_OP",
+ .states = nm_gprs_cell_fsm_states,
+ .num_states = ARRAY_SIZE(nm_gprs_cell_fsm_states),
+ .allstate_event_mask =
+ X(NM_EV_OPSTART_ACK) |
+ X(NM_EV_OPSTART_NACK) |
+ X(NM_EV_FORCE_LOCK) |
+ X(NM_EV_OML_DOWN),
+ .allstate_action = st_op_allstate,
+ .event_names = nm_fsm_event_names,
+ .log_subsys = DNM,
+};
+
+static __attribute__((constructor)) void nm_gprs_cell_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&nm_gprs_cell_fsm) == 0);
+}
diff --git a/src/osmo-bsc/nm_gprs_nse_fsm.c b/src/osmo-bsc/nm_gprs_nse_fsm.c
new file mode 100644
index 000000000..1403c5fef
--- /dev/null
+++ b/src/osmo-bsc/nm_gprs_nse_fsm.c
@@ -0,0 +1,384 @@
+/* NM GPRS NSE FSM */
+
+/* (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/>.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/bsc/bts_sm.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/debug.h>
+
+#define X(s) (1 << (s))
+
+#define nm_gprs_nse_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+//////////////////////////
+// FSM STATE ACTIONS
+//////////////////////////
+
+static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_nse *nse = (struct gsm_gprs_nse *)fi->priv;
+
+ nse->mo.set_attr_sent = false;
+ nse->mo.set_attr_ack_received = false;
+ nse->mo.adm_unlock_sent = false;
+ nse->mo.opstart_sent = false;
+}
+
+static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SW_ACT_REP:
+ case NM_EV_SETUP_RAMP_READY:
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_DEPENDENCY:
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void configure_loop(struct gsm_gprs_nse *nse, const struct gsm_nm_state *state, bool allow_opstart)
+{
+ struct msgb *msgb;
+ struct gsm_bts_sm *bts_sm = container_of(nse, struct gsm_bts_sm, gprs.nse);
+ struct gsm_bts *bts = gsm_bts_sm_get_bts(bts_sm);
+
+ if (bts_setup_ramp_wait(bts))
+ return;
+
+ if (!nse->mo.set_attr_sent && !nse->mo.set_attr_ack_received) {
+ nse->mo.set_attr_sent = true;
+ msgb = nanobts_gen_set_nse_attr(bts_sm);
+ abis_nm_ipaccess_set_attr(bts, NM_OC_GPRS_NSE, bts->bts_nr,
+ 0xff, 0xff, msgb->data,
+ msgb->len);
+ msgb_free(msgb);
+ }
+
+ /* Attributes must be set before unlocking */
+ if (state->administrative != NM_STATE_UNLOCKED && nse->mo.set_attr_ack_received &&
+ !nse->mo.adm_unlock_sent) {
+ nse->mo.adm_unlock_sent = true;
+ abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE,
+ bts->bts_nr, 0xff, 0xff,
+ NM_STATE_UNLOCKED);
+ }
+
+ if (allow_opstart && state->administrative == NM_STATE_UNLOCKED &&
+ nse->mo.set_attr_ack_received) {
+ if (!nse->mo.opstart_sent) {
+ nse->mo.opstart_sent = true;
+ abis_nm_opstart(bts, NM_OC_GPRS_NSE, bts->bts_nr, 0xff, 0xff);
+ }
+ }
+}
+
+static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_nse *nse = (struct gsm_gprs_nse *)fi->priv;
+ struct gsm_bts_sm *bts_sm = container_of(nse, struct gsm_bts_sm, gprs.nse);
+
+ /* nanoBTS is broken, doesn't follow TS 12.21. Opstart MUST be sent
+ during Dependency, so we simply move to OFFLINE state here to avoid
+ duplicating code */
+ if (bts_sm->peer_has_no_avstate_offline) {
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_OFFLINE);
+ return;
+ }
+ configure_loop(nse, &nse->mo.nm_state, false);
+}
+
+static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_gprs_nse *nse = (struct gsm_gprs_nse *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SET_ATTR_ACK:
+ nse->mo.set_attr_ack_received = true;
+ nse->mo.set_attr_sent = false;
+ configure_loop(nse, &nse->mo.nm_state, false);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_OFFLINE);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ configure_loop(nse, new_state, false);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(nse, &nse->mo.nm_state, false);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_nse *nse = (struct gsm_gprs_nse *)fi->priv;
+
+ configure_loop(nse, &nse->mo.nm_state, true);
+}
+
+static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_gprs_nse *nse = (struct gsm_gprs_nse *)fi->priv;
+ struct gsm_bts_sm *bts_sm = container_of(nse, struct gsm_bts_sm, gprs.nse);
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SET_ATTR_ACK:
+ nse->mo.set_attr_ack_received = true;
+ nse->mo.set_attr_sent = false;
+ configure_loop(nse, &nse->mo.nm_state, true);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ /* There's no point in moving back to Dependency, since it's broken
+ and it acts actually as if it was in Offline state */
+ if (!bts_sm->peer_has_no_avstate_offline) {
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_DEPENDENCY);
+ } else {
+ /* Moreover, in nanoBTS we need to check here for tx
+ Opstart since we may have gone Unlocked state
+ in this event, which means Opstart may be txed here. */
+ configure_loop(nse, new_state, true);
+ }
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ configure_loop(nse, new_state, true);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(nse, &nse->mo.nm_state, true);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_nse *nse = (struct gsm_gprs_nse *)fi->priv;
+
+ /* Reset state, we don't need it in this state and it will need to be
+ reused as soon as we move back to Disabled */
+ nse->mo.opstart_sent = false;
+ nse->mo.adm_unlock_sent = false;
+ nse->mo.set_attr_ack_received = false;
+ nse->mo.set_attr_sent = false;
+}
+
+static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED)
+ return;
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_gprs_nse *nse = (struct gsm_gprs_nse *)fi->priv;
+ struct gsm_bts_sm *bts_sm = container_of(nse, struct gsm_bts_sm, gprs.nse);
+ struct gsm_bts *bts = gsm_bts_sm_get_bts(bts_sm);
+
+ switch (event) {
+ case NM_EV_OPSTART_ACK:
+ case NM_EV_OPSTART_NACK:
+ /* TODO: if on state OFFLINE and rx NACK, try again? */
+ nse->mo.opstart_sent = false;
+ break;
+ case NM_EV_FORCE_LOCK:
+ nse->mo.force_rf_lock = (bool)(intptr_t)data;
+ abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE,
+ bts->bts_nr, 0xff, 0xff,
+ nse->mo.force_rf_lock ? NM_STATE_LOCKED : NM_STATE_UNLOCKED);
+ break;
+ case NM_EV_OML_DOWN:
+ if (fi->state != NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED)
+ nm_gprs_nse_fsm_state_chg(fi, NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static struct osmo_fsm_state nm_gprs_nse_fsm_states[] = {
+ [NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED] = {
+ .in_event_mask =
+ X(NM_EV_SW_ACT_REP) |
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_GPRS_NSE_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_GPRS_NSE_ST_OP_DISABLED_OFFLINE) |
+ X(NM_GPRS_NSE_ST_OP_ENABLED),
+ .name = "DISABLED_NOTINSTALLED",
+ .onenter = st_op_disabled_notinstalled_on_enter,
+ .action = st_op_disabled_notinstalled,
+ },
+ [NM_GPRS_NSE_ST_OP_DISABLED_DEPENDENCY] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_GPRS_NSE_ST_OP_DISABLED_OFFLINE) |
+ X(NM_GPRS_NSE_ST_OP_ENABLED),
+ .name = "DISABLED_DEPENDENCY",
+ .onenter = st_op_disabled_dependency_on_enter,
+ .action = st_op_disabled_dependency,
+ },
+ [NM_GPRS_NSE_ST_OP_DISABLED_OFFLINE] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_GPRS_NSE_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_GPRS_NSE_ST_OP_ENABLED),
+ .name = "DISABLED_OFFLINE",
+ .onenter = st_op_disabled_offline_on_enter,
+ .action = st_op_disabled_offline,
+ },
+ [NM_GPRS_NSE_ST_OP_ENABLED] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP),
+ .out_state_mask =
+ X(NM_GPRS_NSE_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_GPRS_NSE_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_GPRS_NSE_ST_OP_DISABLED_OFFLINE),
+ .name = "ENABLED",
+ .onenter = st_op_enabled_on_enter,
+ .action = st_op_enabled,
+ },
+};
+
+struct osmo_fsm nm_gprs_nse_fsm = {
+ .name = "NM_GPRS_NSE_OP",
+ .states = nm_gprs_nse_fsm_states,
+ .num_states = ARRAY_SIZE(nm_gprs_nse_fsm_states),
+ .allstate_event_mask =
+ X(NM_EV_OPSTART_ACK) |
+ X(NM_EV_OPSTART_NACK) |
+ X(NM_EV_FORCE_LOCK) |
+ X(NM_EV_OML_DOWN),
+ .allstate_action = st_op_allstate,
+ .event_names = nm_fsm_event_names,
+ .log_subsys = DNM,
+};
+
+static __attribute__((constructor)) void nm_gprs_nse_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&nm_gprs_nse_fsm) == 0);
+}
diff --git a/src/osmo-bsc/nm_gprs_nsvc_fsm.c b/src/osmo-bsc/nm_gprs_nsvc_fsm.c
new file mode 100644
index 000000000..518608215
--- /dev/null
+++ b/src/osmo-bsc/nm_gprs_nsvc_fsm.c
@@ -0,0 +1,408 @@
+/* NM GPRS NSVC FSM */
+
+/* (C) 2020 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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 <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/bsc/bts_sm.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/debug.h>
+
+#define X(s) (1 << (s))
+
+#define nm_gprs_nsvc_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+//////////////////////////
+// FSM STATE ACTIONS
+//////////////////////////
+
+static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_nsvc *nsvc = (struct gsm_gprs_nsvc *)fi->priv;
+
+ nsvc->mo.set_attr_sent = false;
+ nsvc->mo.set_attr_sent = false;
+ nsvc->mo.set_attr_ack_received = false;
+ nsvc->mo.adm_unlock_sent = false;
+ nsvc->mo.opstart_sent = false;
+}
+
+static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_FEATURE_NEGOTIATED:
+ case NM_EV_SW_ACT_REP:
+ case NM_EV_SETUP_RAMP_READY:
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_DEPENDENCY:
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static bool has_valid_nsvc(struct gsm_gprs_nsvc *nsvc)
+{
+ switch (nsvc->remote.u.sa.sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ return (nsvc->local_port > 0 && !osmo_sockaddr_is_any(&nsvc->remote));
+ default:
+ return false;
+ }
+}
+
+static void configure_loop(struct gsm_gprs_nsvc *nsvc, const struct gsm_nm_state *state, bool allow_opstart)
+{
+ struct msgb *msgb;
+
+ if (nsvc->bts->gprs.mode == BTS_GPRS_NONE)
+ return;
+
+ if (bts_setup_ramp_wait(nsvc->bts))
+ return;
+
+ /* We need to know BTS features in order to know if we can set IPv6 addresses */
+ if (gsm_bts_features_negotiated(nsvc->bts) && !nsvc->mo.set_attr_sent &&
+ !nsvc->mo.set_attr_ack_received) {
+ if (!osmo_bts_has_feature(&nsvc->bts->features, BTS_FEAT_IPV6_NSVC) &&
+ nsvc->remote.u.sa.sa_family == AF_INET6) {
+ LOGPFSML(nsvc->mo.fi, LOGL_ERROR,
+ "BTS%d does not support IPv6 NSVC but an IPv6 address was configured!\n",
+ nsvc->bts->nr);
+ return;
+ }
+ if (!has_valid_nsvc(nsvc))
+ return;
+
+ nsvc->mo.set_attr_sent = true;
+ msgb = nanobts_gen_set_nsvc_attr(nsvc);
+ OSMO_ASSERT(msgb);
+ abis_nm_ipaccess_set_attr(nsvc->bts, NM_OC_GPRS_NSVC, nsvc->bts->bts_nr,
+ nsvc->id, 0xff, msgb->data, msgb->len);
+ msgb_free(msgb);
+ }
+
+ if (nsvc->mo.set_attr_ack_received && state->administrative != NM_STATE_UNLOCKED &&
+ !nsvc->mo.adm_unlock_sent) {
+ nsvc->mo.adm_unlock_sent = true;
+ abis_nm_chg_adm_state(nsvc->bts, NM_OC_GPRS_NSVC,
+ nsvc->bts->bts_nr, nsvc->id, 0xff,
+ NM_STATE_UNLOCKED);
+ }
+
+ if (allow_opstart && state->administrative == NM_STATE_UNLOCKED &&
+ nsvc->mo.set_attr_ack_received) {
+ if (!nsvc->mo.opstart_sent) {
+ nsvc->mo.opstart_sent = true;
+ abis_nm_opstart(nsvc->bts, NM_OC_GPRS_NSVC,
+ nsvc->bts->bts_nr, nsvc->id, 0xff);
+ }
+ }
+}
+
+static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_nsvc *nsvc = (struct gsm_gprs_nsvc *)fi->priv;
+
+ /* nanoBTS is broken, doesn't follow TS 12.21. Opstart MUST be sent
+ during Dependency, so we simply move to OFFLINE state here to avoid
+ duplicating code */
+ if (nsvc->bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_OFFLINE);
+ return;
+ }
+ configure_loop(nsvc, &nsvc->mo.nm_state, false);
+}
+
+static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_gprs_nsvc *nsvc = (struct gsm_gprs_nsvc *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_FEATURE_NEGOTIATED:
+ configure_loop(nsvc, &nsvc->mo.nm_state, false);
+ return;
+ case NM_EV_SET_ATTR_ACK:
+ nsvc->mo.set_attr_ack_received = true;
+ nsvc->mo.set_attr_sent = false;
+ configure_loop(nsvc, &nsvc->mo.nm_state, false);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_OFFLINE);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ configure_loop(nsvc, new_state, false);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(nsvc, &nsvc->mo.nm_state, false);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_nsvc *nsvc = (struct gsm_gprs_nsvc *)fi->priv;
+
+ configure_loop(nsvc, &nsvc->mo.nm_state, true);
+}
+
+static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_gprs_nsvc *nsvc = (struct gsm_gprs_nsvc *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_FEATURE_NEGOTIATED:
+ configure_loop(nsvc, &nsvc->mo.nm_state, true);
+ return;
+ case NM_EV_SET_ATTR_ACK:
+ nsvc->mo.set_attr_ack_received = true;
+ nsvc->mo.set_attr_sent = false;
+ configure_loop(nsvc, &nsvc->mo.nm_state, true);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ /* There's no point in moving back to Dependency, since it's broken
+ and it acts actually as if it was in Offline state */
+ if (!nsvc->bts->site_mgr->peer_has_no_avstate_offline) {
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_DEPENDENCY);
+ } else {
+ /* Moreover, in nanoBTS we need to check here for tx
+ Opstart since we may have gone Unlocked state
+ in this event, which means Opstart may be txed here. */
+ configure_loop(nsvc, new_state, true);
+ }
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ configure_loop(nsvc, new_state, true);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(nsvc, &nsvc->mo.nm_state, true);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_gprs_nsvc *nsvc = (struct gsm_gprs_nsvc *)fi->priv;
+
+ /* Reset state, we don't need it in this state and it will need to be
+ reused as soon as we move back to Disabled */
+ nsvc->mo.opstart_sent = false;
+ nsvc->mo.adm_unlock_sent = false;
+ nsvc->mo.set_attr_sent = false;
+ nsvc->mo.set_attr_ack_received = false;
+}
+
+static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED)
+ return;
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_gprs_nsvc *nsvc = (struct gsm_gprs_nsvc *)fi->priv;
+
+ switch (event) {
+ case NM_EV_OPSTART_ACK:
+ case NM_EV_OPSTART_NACK:
+ /* TODO: if on state OFFLINE and rx NACK, try again? */
+ nsvc->mo.opstart_sent = false;
+ break;
+ case NM_EV_OML_DOWN:
+ if (fi->state != NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED)
+ nm_gprs_nsvc_fsm_state_chg(fi, NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static struct osmo_fsm_state nm_gprs_nsvc_fsm_states[] = {
+ [NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED] = {
+ .in_event_mask =
+ X(NM_EV_SW_ACT_REP) |
+ X(NM_EV_FEATURE_NEGOTIATED) |
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_GPRS_NSVC_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_GPRS_NSVC_ST_OP_DISABLED_OFFLINE) |
+ X(NM_GPRS_NSVC_ST_OP_ENABLED),
+ .name = "DISABLED_NOTINSTALLED",
+ .onenter = st_op_disabled_notinstalled_on_enter,
+ .action = st_op_disabled_notinstalled,
+ },
+ [NM_GPRS_NSVC_ST_OP_DISABLED_DEPENDENCY] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_FEATURE_NEGOTIATED) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_GPRS_NSVC_ST_OP_DISABLED_OFFLINE) |
+ X(NM_GPRS_NSVC_ST_OP_ENABLED),
+ .name = "DISABLED_DEPENDENCY",
+ .onenter = st_op_disabled_dependency_on_enter,
+ .action = st_op_disabled_dependency,
+ },
+ [NM_GPRS_NSVC_ST_OP_DISABLED_OFFLINE] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_FEATURE_NEGOTIATED) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_GPRS_NSVC_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_GPRS_NSVC_ST_OP_ENABLED),
+ .name = "DISABLED_OFFLINE",
+ .onenter = st_op_disabled_offline_on_enter,
+ .action = st_op_disabled_offline,
+ },
+ [NM_GPRS_NSVC_ST_OP_ENABLED] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP),
+ .out_state_mask =
+ X(NM_GPRS_NSVC_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_GPRS_NSVC_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_GPRS_NSVC_ST_OP_DISABLED_OFFLINE),
+ .name = "ENABLED",
+ .onenter = st_op_enabled_on_enter,
+ .action = st_op_enabled,
+ },
+};
+
+struct osmo_fsm nm_gprs_nsvc_fsm = {
+ .name = "NM_GPRS_NSVC_OP",
+ .states = nm_gprs_nsvc_fsm_states,
+ .num_states = ARRAY_SIZE(nm_gprs_nsvc_fsm_states),
+ .allstate_event_mask =
+ X(NM_EV_OPSTART_ACK) |
+ X(NM_EV_OPSTART_NACK) |
+ X(NM_EV_OML_DOWN),
+ .allstate_action = st_op_allstate,
+ .event_names = nm_fsm_event_names,
+ .log_subsys = DNM,
+};
+
+static __attribute__((constructor)) void nm_gprs_nsvc_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&nm_gprs_nsvc_fsm) == 0);
+}
diff --git a/src/osmo-bsc/nm_rcarrier_fsm.c b/src/osmo-bsc/nm_rcarrier_fsm.c
new file mode 100644
index 000000000..bcf00b80f
--- /dev/null
+++ b/src/osmo-bsc/nm_rcarrier_fsm.c
@@ -0,0 +1,405 @@
+/* NM Radio Carrier FSM */
+
+/* (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/>.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
+#include <osmocom/bsc/nm_common_fsm.h>
+#include <osmocom/bsc/debug.h>
+
+#define X(s) (1 << (s))
+
+#define nm_rcarrier_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+static inline void nm_rcarrier_fsm_becomes_enabled(struct gsm_bts_trx *trx)
+{
+ nm_obj_fsm_becomes_enabled_disabled(trx->bts, trx, NM_OC_RADIO_CARRIER, true);
+}
+
+static inline void nm_rcarrier_fsm_becomes_disabled(struct gsm_bts_trx *trx)
+{
+ nm_obj_fsm_becomes_enabled_disabled(trx->bts, trx, NM_OC_RADIO_CARRIER, false);
+}
+
+//////////////////////////
+// FSM STATE ACTIONS
+//////////////////////////
+
+static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+
+ trx->mo.set_attr_sent = false;
+ trx->mo.set_attr_ack_received = false;
+ trx->mo.adm_unlock_sent = false;
+ trx->mo.opstart_sent = false;
+}
+
+static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SW_ACT_REP:
+ case NM_EV_SETUP_RAMP_READY:
+ break;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /*should not happen... */
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_DEPENDENCY:
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void configure_loop(struct gsm_bts_trx *trx, const struct gsm_nm_state *state, bool allow_opstart)
+{
+ struct msgb *msgb;
+
+ if (bts_setup_ramp_wait(trx->bts))
+ return;
+
+ if (!trx->mo.set_attr_sent && !trx->mo.set_attr_ack_received) {
+ trx->mo.set_attr_sent = true;
+ msgb = nanobts_gen_set_radio_attr(trx->bts, trx);
+ abis_nm_set_radio_attr(trx, msgb->data, msgb->len);
+ msgb_free(msgb);
+ }
+
+ if (!trx->mo.force_rf_lock && state->administrative != NM_STATE_UNLOCKED &&
+ !trx->mo.adm_unlock_sent) {
+ trx->mo.adm_unlock_sent = true;
+ abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ NM_STATE_UNLOCKED);
+ }
+
+ if (allow_opstart && state->administrative == NM_STATE_UNLOCKED &&
+ trx->mo.set_attr_ack_received && !trx->mo.opstart_sent) {
+ trx->mo.opstart_sent = true;
+ abis_nm_opstart(trx->bts, NM_OC_RADIO_CARRIER, trx->bts->bts_nr, trx->nr, 0xff);
+ }
+}
+
+static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+
+ /* In general nanoBTS is broken, doesn't follow TS 12.21. Opstart MUST
+ * be sent during Dependency, so we simply move to OFFLINE state here to
+ * avoid duplicating code. However, RadioCarrier seems to be implemented
+ * correctly and goes to Offline state during startup. If some HW
+ * version is found with the above estated bug, this code needs to be
+ * enabled, similar to what we do in nm_bb_transc_fsm:
+ */
+ /*if (trx->bts->site_mgr.peer_has_no_avstate_offline) {
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_OFFLINE);
+ return;
+ }*/
+ configure_loop(trx, &trx->mo.nm_state, false);
+}
+
+static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SET_ATTR_ACK:
+ trx->mo.set_attr_ack_received = true;
+ trx->mo.set_attr_sent = false;
+ configure_loop(trx, &trx->mo.nm_state, false);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* should not happen... */
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_OFFLINE);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ configure_loop(trx, new_state, false);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(trx, &trx->mo.nm_state, false);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+
+ configure_loop(trx, &trx->mo.nm_state, true);
+}
+
+static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SET_ATTR_ACK:
+ trx->mo.set_attr_ack_received = true;
+ trx->mo.set_attr_sent = false;
+ configure_loop(trx, &trx->mo.nm_state, true);
+ return;
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_ENABLED);
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ configure_loop(trx, new_state, true);
+ return;
+ default:
+ return;
+ }
+ case NM_EV_SETUP_RAMP_READY:
+ configure_loop(trx, &trx->mo.nm_state, true);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+
+ /* Reset state, we don't need it in this state and it will need to be
+ reused as soon as we move back to Disabled */
+ trx->mo.opstart_sent = false;
+ trx->mo.adm_unlock_sent = false;
+ trx->mo.set_attr_ack_received = false;
+ trx->mo.set_attr_sent = false;
+
+ nm_rcarrier_fsm_becomes_enabled(trx);
+}
+
+static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_STATE_CHG_REP:
+ nsd = (struct nm_statechg_signal_data *)data;
+ new_state = &nsd->new_state;
+ /* Op state stays in Enabled, hence either Avail or Admin changed: */
+ if (new_state->operational == NM_OPSTATE_ENABLED) {
+ /* Some sort of availability change we don't care about: */
+ if (nsd->old_state.administrative == new_state->administrative)
+ return;
+ /* HACK: Admin state change without Op state change:
+ * According to TS 52.021 sec 5.3.1, Locking the NM obj should make
+ * it go into Disabled Dependency state, but current and older
+ * versions of osmo-bts (and potentially nanobts?) don't move from
+ * Operative=Enabled state and only change the Adminsitrative one.
+ * Let's account for this behavior here: */
+ switch (new_state->administrative) {
+ case NM_STATE_LOCKED:
+ nm_rcarrier_fsm_becomes_disabled(trx);
+ break;
+ case NM_STATE_UNLOCKED:
+ nm_rcarrier_fsm_becomes_enabled(trx);
+ break;
+ }
+ return;
+ }
+ switch (new_state->availability) { /* operational = DISABLED */
+ case NM_AVSTATE_NOT_INSTALLED:
+ case NM_AVSTATE_POWER_OFF:
+ nm_rcarrier_fsm_becomes_disabled(trx);
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED);
+ return;
+ case NM_AVSTATE_DEPENDENCY:
+ nm_rcarrier_fsm_becomes_disabled(trx);
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_DEPENDENCY);
+ return;
+ case NM_AVSTATE_OFF_LINE:
+ case NM_AVSTATE_OK:
+ nm_rcarrier_fsm_becomes_disabled(trx);
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_OFFLINE);
+ return;
+ default:
+ return;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_op_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+
+ switch (event) {
+ case NM_EV_OPSTART_ACK:
+ case NM_EV_OPSTART_NACK:
+ /* TODO: if on state OFFLINE and rx NACK, try again? */
+ trx->mo.opstart_sent = false;
+ break;
+ case NM_EV_FORCE_LOCK:
+ trx->mo.force_rf_lock = (bool)(intptr_t)data;
+ abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ trx->mo.force_rf_lock ? NM_STATE_LOCKED : NM_STATE_UNLOCKED);
+ break;
+ case NM_EV_OML_DOWN:
+ if (fi->state != NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) {
+ if (fi->state == NM_RCARRIER_ST_OP_ENABLED)
+ nm_rcarrier_fsm_becomes_disabled(trx);
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED);
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static struct osmo_fsm_state nm_rcarrier_fsm_states[] = {
+ [NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED] = {
+ .in_event_mask =
+ X(NM_EV_SW_ACT_REP) |
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_RCARRIER_ST_OP_DISABLED_OFFLINE) |
+ X(NM_RCARRIER_ST_OP_ENABLED),
+ .name = "DISABLED_NOTINSTALLED",
+ .onenter = st_op_disabled_notinstalled_on_enter,
+ .action = st_op_disabled_notinstalled,
+ },
+ [NM_RCARRIER_ST_OP_DISABLED_DEPENDENCY] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_RCARRIER_ST_OP_DISABLED_OFFLINE) |
+ X(NM_RCARRIER_ST_OP_ENABLED),
+ .name = "DISABLED_DEPENDENCY",
+ .onenter = st_op_disabled_dependency_on_enter,
+ .action = st_op_disabled_dependency,
+ },
+ [NM_RCARRIER_ST_OP_DISABLED_OFFLINE] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP) |
+ X(NM_EV_SET_ATTR_ACK) |
+ X(NM_EV_SETUP_RAMP_READY),
+ .out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_RCARRIER_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_RCARRIER_ST_OP_ENABLED),
+ .name = "DISABLED_OFFLINE",
+ .onenter = st_op_disabled_offline_on_enter,
+ .action = st_op_disabled_offline,
+ },
+ [NM_RCARRIER_ST_OP_ENABLED] = {
+ .in_event_mask =
+ X(NM_EV_STATE_CHG_REP),
+ .out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) |
+ X(NM_RCARRIER_ST_OP_DISABLED_DEPENDENCY) |
+ X(NM_RCARRIER_ST_OP_DISABLED_OFFLINE),
+ .name = "ENABLED",
+ .onenter = st_op_enabled_on_enter,
+ .action = st_op_enabled,
+ },
+};
+
+struct osmo_fsm nm_rcarrier_fsm = {
+ .name = "NM_RCARRIER_OP",
+ .states = nm_rcarrier_fsm_states,
+ .num_states = ARRAY_SIZE(nm_rcarrier_fsm_states),
+ .allstate_event_mask =
+ X(NM_EV_OPSTART_ACK) |
+ X(NM_EV_OPSTART_NACK) |
+ X(NM_EV_FORCE_LOCK) |
+ X(NM_EV_OML_DOWN),
+ .allstate_action = st_op_allstate,
+ .event_names = nm_fsm_event_names,
+ .log_subsys = DNM,
+};
+
+static __attribute__((constructor)) void nm_rcarrier_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&nm_rcarrier_fsm) == 0);
+}
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index 8db81acf3..f7696c814 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -34,6 +34,7 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808.h>
@@ -45,6 +46,7 @@
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/bsc/lcs_loc_req.h>
+#include <osmocom/bsc/bssmap_reset.h>
#define IP_V4_ADDR_LEN 4
@@ -61,7 +63,7 @@ static void update_msc_osmux_support(struct bsc_msc_data *msc,
int rc;
bool old_value = msc->remote_supports_osmux;
- rc = tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
+ rc = osmo_bssap_tlv_parse(&tp, msg->l4h + 1, length - 1);
if (rc < 0)
LOGP(DMSC, LOGL_NOTICE, "Failed parsing TLV looking for Osmux support\n");
@@ -96,27 +98,23 @@ static int bssmap_handle_reset(struct bsc_msc_data *msc,
osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance),
&msc->a.msc_addr));
- /* Instruct the bsc to close all open sigtran connections and to
- * close all active channels on the BTS side as well */
- osmo_bsc_sigtran_reset(msc);
-
- /* Drop all ongoing paging requests that this MSC has created on any BTS */
- paging_flush_network(msc->network, msc);
-
update_msc_osmux_support(msc, msg, length);
- /* Inform the MSC that we have received the reset request and
- * that we acted accordingly */
- osmo_bsc_sigtran_tx_reset_ack(msc);
+ if (!msc->a.bssmap_reset) {
+ LOGP(DMSC, LOGL_ERROR, "(msc%d) missing RESET FSM\n", msc->nr);
+ /* Make sure to shut down all open connections, if any */
+ osmo_bsc_sigtran_reset(msc);
+ return -1;
+ }
- return 0;
+ /* Normal case: let the reset FSM orchestrate link down / link up callbacks. */
+ return osmo_fsm_inst_dispatch(msc->a.bssmap_reset->fi, BSSMAP_RESET_EV_RX_RESET, NULL);
}
/* Page a subscriber based on TMSI and LAC via the specified BTS.
* The msc parameter is the MSC which issued the corresponding paging request.
* Log an error if paging failed. */
-static void
-page_subscriber(const struct bsc_paging_params *params, struct gsm_bts *bts, uint32_t lac)
+static void page_subscriber(const struct bsc_paging_params *params, struct gsm_bts *bts, uint32_t lac)
{
int ret;
@@ -133,16 +131,14 @@ page_subscriber(const struct bsc_paging_params *params, struct gsm_bts *bts, uin
"Paging request failed, or repeated paging on LAC %u\n", lac);
}
-static void
-page_all_bts(const struct bsc_paging_params *params)
+static void page_all_bts(const struct bsc_paging_params *params)
{
struct gsm_bts *bts;
llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list)
page_subscriber(params, bts, GSM_LAC_RESERVED_ALL_BTS);
}
-static void
-page_cgi(const struct bsc_paging_params *params)
+static void page_cgi(const struct bsc_paging_params *params)
{
int i;
for (i = 0; i < params->cil.id_list_len; i++) {
@@ -171,8 +167,7 @@ page_cgi(const struct bsc_paging_params *params)
}
}
-static void
-page_lac_and_ci(const struct bsc_paging_params *params)
+static void page_lac_and_ci(const struct bsc_paging_params *params)
{
int i;
@@ -194,8 +189,7 @@ page_lac_and_ci(const struct bsc_paging_params *params)
}
}
-static void
-page_ci(const struct bsc_paging_params *params)
+static void page_ci(const struct bsc_paging_params *params)
{
int i;
@@ -215,8 +209,7 @@ page_ci(const struct bsc_paging_params *params)
}
}
-static void
-page_lai_and_lac(const struct bsc_paging_params *params)
+static void page_lai_and_lac(const struct bsc_paging_params *params)
{
int i;
@@ -243,8 +236,7 @@ page_lai_and_lac(const struct bsc_paging_params *params)
}
}
-static void
-page_lac(const struct bsc_paging_params *params)
+static void page_lac(const struct bsc_paging_params *params)
{
int i;
@@ -278,7 +270,10 @@ static int bssmap_handle_paging(struct bsc_msc_data *msc,
.tmsi = GSM_RESERVED_TMSI,
};
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, payload_length - 1) < 0) {
+ LOGP(DMSC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -1;
+ }
remain = payload_length - 1;
if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) {
@@ -345,7 +340,7 @@ static int bssmap_handle_paging(struct bsc_msc_data *msc,
int bsc_paging_start(struct bsc_paging_params *params)
{
- rate_ctr_inc(&bsc_gsmnet->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->bsc_ctrs, BSC_CTR_PAGING_ATTEMPTED));
if (!params->bsub) {
params->bsub = bsc_subscr_find_or_create_by_imsi(bsc_gsmnet->bsc_subscribers, params->imsi.imsi,
@@ -401,68 +396,52 @@ int bsc_paging_start(struct bsc_paging_params *params)
return 0;
}
-/* select the best cipher permitted by the intersection of both masks */
-static int select_best_cipher(uint8_t msc_mask, uint8_t bsc_mask)
+/* Select the best cipher permitted by the intersection of both masks. Return as the n in A5/n, or -1 if the
+ * intersection is empty. */
+int select_best_cipher(uint8_t msc_mask, uint8_t bsc_mask)
{
+ /* A5/7 ... A5/3: We assume higher is better,
+ * but: A5/1 is better than A5/2, which is better than A5/0 */
+ const uint8_t codec_by_strength[8] = { 7, 6, 5, 4, 3, 1, 2, 0 };
uint8_t intersection = msc_mask & bsc_mask;
int i;
- for (i = 7; i >= 0; i--) {
- if (intersection & (1 << i))
- return i;
+ for (i = 0; i < ARRAY_SIZE(codec_by_strength); i++) {
+ uint8_t codec = codec_by_strength[i];
+ if (intersection & (1 << codec))
+ return codec;
}
return -1;
}
-/*! We received a GSM 08.08 CIPHER MODE from the MSC */
-static int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
- const uint8_t *key, int len, int include_imeisv)
-{
- if (cipher > 0 && key == NULL) {
- LOGP(DRSL, LOGL_ERROR, "%s: Need to have an encryption key.\n",
- bsc_subscr_name(conn->bsub));
- return -1;
- }
-
- if (len > MAX_A5_KEY_LEN) {
- LOGP(DRSL, LOGL_ERROR, "%s: The key is too long: %d\n",
- bsc_subscr_name(conn->bsub), len);
- return -1;
- }
-
- LOGP(DRSL, LOGL_DEBUG, "(subscr %s) Cipher Mode: cipher=%d key=%s include_imeisv=%d\n",
- bsc_subscr_name(conn->bsub), cipher, osmo_hexdump_nospc(key, len), include_imeisv);
-
- conn->lchan->encr.alg_id = RSL_ENC_ALG_A5(cipher);
- if (key) {
- conn->lchan->encr.key_len = len;
- memcpy(conn->lchan->encr.key, key, len);
- }
-
- return gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv);
-}
-
static int bssmap_handle_clear_cmd(struct gsm_subscriber_connection *conn,
struct msgb *msg, unsigned int length)
{
struct tlv_parsed tp;
- struct gscon_clear_cmd_data ccd = {
- .is_csfb = false,
- };
+ enum gsm0808_cause cause_0808;
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, length - 1) < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -1;
+ }
- ccd.cause_0808 = gsm0808_get_cause(&tp);
- if (ccd.cause_0808 < 0) {
+ cause_0808 = gsm0808_get_cause(&tp);
+ if (cause_0808 < 0) {
LOGPFSML(conn->fi, LOGL_ERROR, "Clear Command: Mandatory Cause IE not present.\n");
/* Clear anyway, but without a proper cause. */
- ccd.cause_0808 = GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE;
+ cause_0808 = GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE;
}
- if (TLVP_PRESENT(&tp, GSM0808_IE_CSFB_INDICATION))
- ccd.is_csfb = true;
+ if (TLVP_PRESENT(&tp, GSM0808_IE_CSFB_INDICATION) &&
+ !conn->fast_return.last_eutran_plmn_valid) {
+ LOGPFSML(conn->fi, LOGL_NOTICE,
+ "Clear Command: CSFB Indication present, "
+ "but subscriber has no Last Used E-UTRAN PLMN Id! "
+ "This probably means MSC doesn't support proper return "
+ "to the last used PLMN after CS fallback.\n");
+ }
- osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CLEAR_CMD, &ccd);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CLEAR_CMD, &cause_0808);
return 0;
}
@@ -488,8 +467,9 @@ static int bssmap_handle_cipher_mode(struct gsm_subscriber_connection *conn,
uint16_t enc_key_len;
uint8_t enc_bits_msc;
int chosen_cipher;
+ const struct tlv_p_entry *ie_kc128;
- if (!conn) {
+ if (!conn || !conn->lchan) {
LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
return -1;
}
@@ -502,7 +482,11 @@ static int bssmap_handle_cipher_mode(struct gsm_subscriber_connection *conn,
conn->ciphering_handled = 1;
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, payload_length - 1) < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -1;
+ }
+
if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) {
LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n");
reject_cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
@@ -536,9 +520,6 @@ static int bssmap_handle_cipher_mode(struct gsm_subscriber_connection *conn,
* a5_encryption == 2 --> 0x04 ... */
enc_bits_msc = data[0];
- /* The bit-mask of permitted ciphers from the MSC (sent in ASSIGNMENT COMMAND) is intersected
- * with the vty-configured mask a the BSC. Finally, the best (highest) possible cipher is
- * chosen. */
chosen_cipher = select_best_cipher(enc_bits_msc, bsc_gsmnet->a5_encryption_mask);
if (chosen_cipher < 0) {
LOGP(DMSC, LOGL_ERROR, "Reject: no overlapping A5 ciphers between BSC (0x%02x) "
@@ -547,13 +528,51 @@ static int bssmap_handle_cipher_mode(struct gsm_subscriber_connection *conn,
goto reject;
}
- /* To complete the confusion, gsm0808_cipher_mode again expects the encryption as a number
- * from 0 to 7. */
- if (gsm0808_cipher_mode(conn, chosen_cipher, enc_key, enc_key_len,
- include_imeisv)) {
+ if (chosen_cipher > 0 && !enc_key_len) {
+ LOGP(DRSL, LOGL_ERROR, "%s: Need to have an encryption key.\n",
+ bsc_subscr_name(conn->bsub));
reject_cause = GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC;
goto reject;
}
+
+ if (enc_key_len > MAX_A5_KEY_LEN) {
+ LOGP(DRSL, LOGL_ERROR, "%s: The key is too long: %d\n",
+ bsc_subscr_name(conn->bsub), len);
+ reject_cause = GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC;
+ goto reject;
+ }
+
+ conn->lchan->encr.alg_a5_n = chosen_cipher;
+ if (enc_key_len) {
+ conn->lchan->encr.key_len = enc_key_len;
+ memcpy(conn->lchan->encr.key, enc_key, enc_key_len);
+ }
+ if ((ie_kc128 = TLVP_GET(&tp, GSM0808_IE_KC_128))) {
+ if (ie_kc128->len != sizeof(conn->lchan->encr.kc128)) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Kc128 IE has wrong length: %u (expect %zu)\n",
+ ie_kc128->len, sizeof(conn->lchan->encr.kc128));
+ reject_cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
+ goto reject;
+ }
+ memcpy(conn->lchan->encr.kc128, ie_kc128->val, sizeof(conn->lchan->encr.kc128));
+ conn->lchan->encr.kc128_present = true;
+ }
+
+ if (chosen_cipher == 4 && !conn->lchan->encr.kc128_present) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "A5/4 encryption selected, but no Kc128\n");
+ reject_cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
+ goto reject;
+ }
+
+ LOGP(DRSL, LOGL_DEBUG, "(subscr %s) Cipher Mode: cipher=%d key=%s kc128=%s include_imeisv=%d\n",
+ bsc_subscr_name(conn->bsub), chosen_cipher, osmo_hexdump_nospc(enc_key, enc_key_len),
+ ie_kc128? osmo_hexdump_nospc_c(OTC_SELECT, ie_kc128->val, ie_kc128->len) : "-",
+ include_imeisv);
+
+ if (gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv) < 0) {
+ reject_cause = GSM0808_CAUSE_RADIO_INTERFACE_FAILURE;
+ goto reject;
+ }
return 0;
reject:
@@ -563,7 +582,7 @@ reject:
return -1;
}
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_CIPHER_REJECT]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_CIPHER_REJECT));
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
return -1;
}
@@ -615,16 +634,14 @@ static int bssmap_handle_lcls_connect_ctrl(struct gsm_subscriber_connection *con
struct msgb *resp;
struct tlv_parsed tp;
const uint8_t *config, *control;
- int rc;
OSMO_ASSERT(conn);
- rc = tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
- if (rc < 0) {
- LOGPFSML(conn->fi, LOGL_ERROR, "Error parsing TLVs of LCLS CONNT CTRL: %s\n",
- msgb_hexdump(msg));
- return rc;
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, length - 1) < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -1;
}
+
config = TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONFIG, 1);
control = TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONN_STATUS_CTRL, 1);
@@ -644,20 +661,20 @@ static int bssmap_handle_lcls_connect_ctrl(struct gsm_subscriber_connection *con
LOGPFSM(conn->fi, "Tx LCLS CONNECT CTRL ACK (%s)\n",
gsm0808_lcls_status_name(lcls_get_status(conn)));
resp = gsm0808_create_lcls_conn_ctrl_ack(lcls_get_status(conn));
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_LCLS_CONNECT_CTRL_ACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_LCLS_CONNECT_CTRL_ACK));
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
return 0;
}
/* Select a preferred and an alternative codec rate depending on the available
- * capabilities. This decision does not include the actual channel load yet,
+ * capabilities. This decision does not include the actual lchan availability yet,
* this is also the reason why the result is a preferred and an alternate
* setting. The final decision is made in assignment_fsm.c when the actual
* lchan is requested. The preferred lchan will be requested first. If we
* find an alternate setting here, this one will be tried secondly if our
* primary choice fails. */
-static int select_codecs(struct assignment_request *req, struct gsm0808_channel_type *ct,
+static int select_codecs(struct assignment_request *req, const struct gsm0808_channel_type *ct,
struct gsm_subscriber_connection *conn)
{
int rc, i, nc = 0;
@@ -673,34 +690,34 @@ static int select_codecs(struct assignment_request *req, struct gsm0808_channel_
switch (ct->ch_rate_type) {
case GSM0808_SPEECH_FULL_BM:
- rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+ rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_FR);
- nc += (rc == 0);
+ nc += (rc == 0) ? 1 : 0;
break;
case GSM0808_SPEECH_HALF_LM:
- rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+ rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_HR);
- nc += (rc == 0);
+ nc += (rc == 0) ? 1 : 0;
break;
case GSM0808_SPEECH_PERM:
case GSM0808_SPEECH_PERM_NO_CHANGE:
case GSM0808_SPEECH_FULL_PREF_NO_CHANGE:
case GSM0808_SPEECH_FULL_PREF:
- rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+ rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_FR);
- nc += (rc == 0);
- rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+ nc += (rc == 0) ? 1 : 0;
+ rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_HR);
- nc += (rc == 0);
+ nc += (rc == 0) ? 1 : 0;
break;
case GSM0808_SPEECH_HALF_PREF_NO_CHANGE:
case GSM0808_SPEECH_HALF_PREF:
- rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+ rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_HR);
- nc += (rc == 0);
- rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+ nc += (rc == 0) ? 1 : 0;
+ rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_FR);
- nc += (rc == 0);
+ nc += (rc == 0) ? 1 : 0;
break;
default:
rc = -EINVAL;
@@ -720,8 +737,8 @@ static int select_codecs(struct assignment_request *req, struct gsm0808_channel_
DEBUGP(DMSC, "Found matching audio type (pref=%d): %s %s for channel_type ="
" { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
i,
- req->ch_mode_rate[i].chan_rate == CH_RATE_FULL ? "full rate" : "half rate",
- get_value_string(gsm48_chan_mode_names, req->ch_mode_rate[i].chan_mode),
+ req->ch_mode_rate_list[i].chan_rate == CH_RATE_FULL ? "full rate" : "half rate",
+ get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_list[i].chan_mode),
ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
}
@@ -736,49 +753,231 @@ static int select_sign_chan(struct assignment_request *req, struct gsm0808_chann
switch (ct->ch_rate_type) {
case GSM0808_SIGN_ANY:
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH;
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
break;
case GSM0808_SIGN_SDCCH:
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
break;
case GSM0808_SIGN_SDCCH_FULL_BM:
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH;
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
break;
case GSM0808_SIGN_SDCCH_HALF_LM:
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH;
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
break;
case GSM0808_SIGN_FULL_BM:
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
break;
case GSM0808_SIGN_HALF_LM:
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
break;
case GSM0808_SIGN_FULL_PREF:
case GSM0808_SIGN_FULL_PREF_NO_CHANGE:
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
break;
case GSM0808_SIGN_HALF_PREF:
case GSM0808_SIGN_HALF_PREF_NO_CHANGE:
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
- req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
+ req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
break;
default:
break;
}
for (i = 0; i < nc; i++)
- req->ch_mode_rate[i].chan_mode = GSM48_CMODE_SIGN;
+ req->ch_mode_rate_list[i].chan_mode = GSM48_CMODE_SIGN;
req->n_ch_mode_rate = nc;
return nc > 0 ? 0 : -EINVAL;
}
+static int bssmap_handle_ass_req_tp_cic(struct tlv_parsed *tp, bool aoip, uint16_t *cic, uint8_t *cause)
+{
+ if (TLVP_PRESENT(tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
+ /* CIC is permitted in both AoIP and SCCPlite */
+ *cic = osmo_load16be(TLVP_VAL(tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
+ return 0;
+ }
+
+ if (!aoip) {
+ /* no CIC but SCCPlite: illegal */
+ LOGP(DMSC, LOGL_ERROR, "SCCPlite MSC, but no CIC in ASSIGN REQ?\n");
+ *cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bssmap_handle_ass_req_tp_rtp_addr(struct tlv_parsed *tp, bool aoip, char *msc_rtp_addr,
+ size_t msc_rtp_addr_len, uint16_t *msc_rtp_port, uint8_t *cause)
+{
+ struct sockaddr_storage rtp_addr;
+ int rc;
+ unsigned int rc2;
+
+ if (TLVP_PRESENT(tp, GSM0808_IE_AOIP_TRASP_ADDR)) {
+ if (!aoip) {
+ /* SCCPlite and AoIP transport address: illegal */
+ LOGP(DMSC, LOGL_ERROR, "AoIP Transport address over IPA ?!?\n");
+ *cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ return -1;
+ }
+ /* Decode AoIP transport address element */
+ rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr,
+ TLVP_VAL(tp, GSM0808_IE_AOIP_TRASP_ADDR),
+ TLVP_LEN(tp, GSM0808_IE_AOIP_TRASP_ADDR));
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to decode AoIP transport address.\n");
+ *cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ return -1;
+ }
+
+ rc2 = osmo_sockaddr_to_str_and_uint(msc_rtp_addr, msc_rtp_addr_len, msc_rtp_port,
+ (const struct sockaddr *)&rtp_addr);
+ if (!rc2 || rc >= msc_rtp_addr_len) {
+ LOGP(DMSC, LOGL_ERROR, "Assignment request: RTP address is too long\n");
+ *cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL;
+ return -1;
+ }
+ return 0;
+ }
+
+ if (aoip) {
+ /* no AoIP transport level address but AoIP transport: illegal */
+ LOGP(DMSC, LOGL_ERROR, "AoIP transport address missing in ASSIGN REQ, "
+ "audio would not work; rejecting\n");
+ *cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bssmap_handle_ass_req_tp_osmux(struct gsm_subscriber_connection *conn, struct tlv_parsed *tp,
+ bool *use_osmux, uint8_t *osmux_cid, uint8_t *cause)
+{
+ int rc;
+
+ if (TLVP_PRESENT(tp, GSM0808_IE_OSMO_OSMUX_CID)) {
+ if (conn->sccp.msc->use_osmux == OSMUX_USAGE_OFF) {
+ LOGP(DMSC, LOGL_ERROR, "MSC using Osmux but we have it disabled.\n");
+ *cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ return -1;
+ }
+ *use_osmux = true;
+ rc = gsm0808_dec_osmux_cid(osmux_cid,
+ TLVP_VAL(tp, GSM0808_IE_OSMO_OSMUX_CID),
+ TLVP_LEN(tp, GSM0808_IE_OSMO_OSMUX_CID));
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to decode Osmux CID.\n");
+ *cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ return -1;
+ }
+ return 0;
+ }
+
+ if (conn->sccp.msc->use_osmux == OSMUX_USAGE_ONLY) {
+ LOGP(DMSC, LOGL_ERROR, "MSC not using Osmux but we are forced to use it.\n");
+ *cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ return -1;
+ }
+
+ if (conn->sccp.msc->use_osmux == OSMUX_USAGE_ON)
+ LOGP(DMSC, LOGL_NOTICE, "MSC not using Osmux but we have Osmux enabled.\n");
+
+ return 0;
+}
+
+static int bssmap_handle_ass_req_tp_codec_list(struct gsm_subscriber_connection *conn, struct tlv_parsed *tp, bool aoip,
+ uint8_t *cause)
+{
+ int rc;
+
+ /* Decode speech codec list. First set len = 0. */
+ conn->codec_list = (struct gsm0808_speech_codec_list){};
+ /* Check for speech codec list element */
+ if (TLVP_PRESENT(tp, GSM0808_IE_SPEECH_CODEC_LIST)) {
+ /* Decode Speech Codec list */
+ rc = gsm0808_dec_speech_codec_list(&conn->codec_list,
+ TLVP_VAL(tp, GSM0808_IE_SPEECH_CODEC_LIST),
+ TLVP_LEN(tp, GSM0808_IE_SPEECH_CODEC_LIST));
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to decode speech codec list\n");
+ *cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ return -1;
+ }
+ }
+
+ if (aoip && !conn->codec_list.len) {
+ LOGP(DMSC, LOGL_ERROR, "%s: AoIP speech mode Assignment Request:"
+ " Missing or empty Speech Codec List IE\n", bsc_subscr_name(conn->bsub));
+ *cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bssmap_handle_ass_req_ct_speech(struct gsm_subscriber_connection *conn, struct tlv_parsed *tp,
+ struct gsm0808_channel_type *ct, struct assignment_request *req,
+ uint8_t *cause)
+{
+ bool aoip = gscon_is_aoip(conn);
+ int rc;
+
+ *req = (struct assignment_request){
+ .assign_for = ASSIGN_FOR_BSSMAP_REQ,
+ .aoip = aoip,
+ };
+
+ if (bssmap_handle_ass_req_tp_cic(tp, aoip, &req->msc_assigned_cic, cause) < 0)
+ return -1;
+
+ if (bssmap_handle_ass_req_tp_rtp_addr(tp, aoip, req->msc_rtp_addr, sizeof(req->msc_rtp_addr), &req->msc_rtp_port, cause) < 0)
+ return -1;
+
+ if (bssmap_handle_ass_req_tp_osmux(conn, tp, &req->use_osmux, &req->osmux_cid, cause) < 0)
+ return -1;
+
+ if (bssmap_handle_ass_req_tp_codec_list(conn, tp, aoip, cause) < 0)
+ return -1;
+
+ /* Match codec information from the assignment command against the
+ * local preferences of the BSC and BTS */
+ rc = select_codecs(req, ct, conn);
+ if (rc < 0) {
+ *cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bssmap_handle_ass_req_ct_sign(struct gsm_subscriber_connection *conn, struct gsm0808_channel_type *ct,
+ struct assignment_request *req, uint8_t *cause)
+{
+ int rc;
+
+ *req = (struct assignment_request){
+ .assign_for = ASSIGN_FOR_BSSMAP_REQ,
+ .aoip = gscon_is_aoip(conn),
+ };
+
+ rc = select_sign_chan(req, ct);
+ if (rc < 0) {
+ *cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ return rc;
+ }
+
+ return 0;
+}
+
/*
* Handle the assignment request message.
*
@@ -789,11 +988,6 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn,
{
struct msgb *resp;
struct tlv_parsed tp;
- uint16_t cic = 0;
- bool aoip = false;
- bool use_osmux = false;
- uint8_t osmux_cid = 0;
- struct sockaddr_storage rtp_addr;
struct gsm0808_channel_type ct;
uint8_t cause;
int rc;
@@ -805,9 +999,10 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn,
return -1;
}
- aoip = gscon_is_aoip(conn);
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, length - 1) < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -1;
+ }
/* Check for channel type element, if its missing, immediately reject */
if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) {
@@ -835,124 +1030,12 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn,
cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP;
goto reject;
case GSM0808_CHAN_SPEECH:
- if (TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
- /* CIC is permitted in both AoIP and SCCPlite */
- cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
- } else {
- if (!aoip) {
- /* no CIC but SCCPlite: illegal */
- LOGP(DMSC, LOGL_ERROR, "SCCPlite MSC, but no CIC in ASSIGN REQ?\n");
- cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
- goto reject;
- }
- }
- if (TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) {
- if (!aoip) {
- /* SCCPlite and AoIP transport address: illegal */
- LOGP(DMSC, LOGL_ERROR, "AoIP Transport address over IPA ?!?\n");
- cause = GSM0808_CAUSE_INCORRECT_VALUE;
- goto reject;
- }
- /* Decode AoIP transport address element */
- rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr,
- TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR),
- TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR));
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "Unable to decode AoIP transport address.\n");
- cause = GSM0808_CAUSE_INCORRECT_VALUE;
- goto reject;
- }
- } else if (aoip) {
- /* no AoIP transport level address but AoIP transport: illegal */
- LOGP(DMSC, LOGL_ERROR, "AoIP transport address missing in ASSIGN REQ, "
- "audio would not work; rejecting\n");
- cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
- goto reject;
- }
-
- if (TLVP_PRESENT(&tp, GSM0808_IE_OSMO_OSMUX_CID)) {
- if (conn->sccp.msc->use_osmux == OSMUX_USAGE_OFF) {
- LOGP(DMSC, LOGL_ERROR, "MSC using Osmux but we have it disabled.\n");
- cause = GSM0808_CAUSE_INCORRECT_VALUE;
- goto reject;
- }
- use_osmux = true;
- rc = gsm0808_dec_osmux_cid(&osmux_cid,
- TLVP_VAL(&tp, GSM0808_IE_OSMO_OSMUX_CID),
- TLVP_LEN(&tp, GSM0808_IE_OSMO_OSMUX_CID));
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "Unable to decode Osmux CID.\n");
- cause = GSM0808_CAUSE_INCORRECT_VALUE;
- goto reject;
- }
- } else {
- if (conn->sccp.msc->use_osmux == OSMUX_USAGE_ONLY) {
- LOGP(DMSC, LOGL_ERROR, "MSC not using Osmux but we are forced to use it.\n");
- cause = GSM0808_CAUSE_INCORRECT_VALUE;
- goto reject;
- } else if (conn->sccp.msc->use_osmux == OSMUX_USAGE_ON)
- LOGP(DMSC, LOGL_NOTICE, "MSC not using Osmux but we have Osmux enabled.\n");
- }
-
- /* Decode speech codec list. First set len = 0. */
- conn->codec_list = (struct gsm0808_speech_codec_list){};
- /* Check for speech codec list element */
- if (TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_CODEC_LIST)) {
- /* Decode Speech Codec list */
- rc = gsm0808_dec_speech_codec_list(&conn->codec_list,
- TLVP_VAL(&tp, GSM0808_IE_SPEECH_CODEC_LIST),
- TLVP_LEN(&tp, GSM0808_IE_SPEECH_CODEC_LIST));
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "Unable to decode speech codec list\n");
- cause = GSM0808_CAUSE_INCORRECT_VALUE;
- goto reject;
- }
- }
-
- if (aoip && !conn->codec_list.len) {
- LOGP(DMSC, LOGL_ERROR, "%s: AoIP speech mode Assignment Request:"
- " Missing or empty Speech Codec List IE\n", bsc_subscr_name(conn->bsub));
- cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
- goto reject;
- }
-
- req = (struct assignment_request){
- .aoip = aoip,
- .msc_assigned_cic = cic,
- .use_osmux = use_osmux,
- .osmux_cid = osmux_cid,
- };
-
- /* Match codec information from the assignment command against the
- * local preferences of the BSC and BTS */
- rc = select_codecs(&req, &ct, conn);
- if (rc < 0) {
- cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL;
+ if (bssmap_handle_ass_req_ct_speech(conn, &tp, &ct, &req, &cause) < 0)
goto reject;
- }
-
- if (aoip) {
- unsigned int rc = osmo_sockaddr_to_str_and_uint(req.msc_rtp_addr,
- sizeof(req.msc_rtp_addr),
- &req.msc_rtp_port,
- (const struct sockaddr*)&rtp_addr);
- if (!rc || rc >= sizeof(req.msc_rtp_addr)) {
- LOGP(DMSC, LOGL_ERROR, "Assignment request: RTP address is too long\n");
- cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL;
- goto reject;
- }
- }
break;
case GSM0808_CHAN_SIGN:
- req = (struct assignment_request){
- .aoip = aoip,
- };
-
- rc = select_sign_chan(&req, &ct);
- if (rc < 0) {
- cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ if (bssmap_handle_ass_req_ct_sign(conn, &ct, &req, &cause) < 0)
goto reject;
- }
break;
default:
cause = GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS;
@@ -965,11 +1048,29 @@ reject:
resp = gsm0808_create_assignment_failure(cause, NULL);
OSMO_ASSERT(resp);
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_FAILURE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_FAILURE));
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
return -1;
}
+/* Handle Handover Request message, part of inter-BSC handover:
+ * The MSC opened a new SCCP connection and is asking this BSS to accept an inter-BSC incoming handover.
+ * If we accept, we'll send a Handover Request Acknowledge.
+ * This function is only called when the Handover Request is *not* included in the initial SCCP N-Connect message, but
+ * follows an "empty" N-Connect in a separate DT1 message.
+ */
+static int bssmap_handle_handover_request(struct gsm_subscriber_connection *conn, struct msgb *msg)
+{
+ if (osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_INITIAL_USER_DATA, msg)) {
+ /* A Handover Request message should come in on a newly opened SCCP conn. Apparently the MSC has sent a
+ * Handover Request on an already busy SCCP conn, and naturally we cannot accept another subscriber
+ * here. This is unlikely to ever happen in practice. Respond in the only possible way: */
+ bsc_tx_bssmap_ho_failure(conn);
+ return -EINVAL;
+ }
+ return 0;
+}
+
/* Handle Handover Command message, part of inter-BSC handover:
* This BSS sent a Handover Required message.
* The MSC contacts the remote BSS and receives from it an RR Handover Command; this BSSMAP Handover
@@ -990,7 +1091,10 @@ static int bssmap_handle_handover_cmd(struct gsm_subscriber_connection *conn,
return -EINVAL;
}
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, length - 1) < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -1;
+ }
/* Check for channel type element, if its missing, immediately reject */
if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) {
@@ -1028,7 +1132,10 @@ static int bssmap_handle_confusion(struct gsm_subscriber_connection *conn,
enum gsm0808_cause_class cause_class;
struct gsm0808_diagnostics *diag;
- osmo_bssap_tlv_parse(&tp, msg->l4h + 1, length - 1);
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, length - 1) < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -1;
+ }
/* Check for the Cause and Diagnostic mandatory elements */
if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE) || !TLVP_PRESENT(&tp, GSM0808_IE_DIAGNOSTIC)) {
@@ -1070,9 +1177,11 @@ static int bssmap_handle_common_id(struct gsm_subscriber_connection *conn,
struct msgb *msg, unsigned int length)
{
struct tlv_parsed tp;
- struct osmo_mobile_identity mi_imsi;
- osmo_bssap_tlv_parse(&tp, msg->l4h + 1, length - 1);
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, length - 1) < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -1;
+ }
/* Check for the mandatory elements */
if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) {
@@ -1082,13 +1191,7 @@ static int bssmap_handle_common_id(struct gsm_subscriber_connection *conn,
return -EINVAL;
}
- 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(conn->fi, LOGL_ERROR, "CommonID: could not parse IMSI\n");
- return -EINVAL;
- }
-
- osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_COMMON_ID_IND, &mi_imsi);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_COMMON_ID_IND, &tp);
return 0;
}
@@ -1131,20 +1234,48 @@ static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc,
}
static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn,
- struct msgb *msg, unsigned int length)
+ struct msgb *msg)
{
int ret = 0;
struct rate_ctr *ctrs = conn->sccp.msc->msc_ctrs->ctr;
+ struct bssmap_header *bssmap_header;
+ enum BSS_MAP_MSG_TYPE msg_type;
+ unsigned int length;
- if (length < 1) {
- LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length);
+ if (msgb_l3len(msg) < sizeof(struct bssmap_header)) {
+ LOGPFSML(conn->fi, LOGL_ERROR,
+ "BSSMAP message too short for header (%u bytes)\n",
+ msgb_l3len(msg));
return -1;
}
- LOGP(DMSC, LOGL_INFO, "Rx MSC DT1 BSSMAP %s\n",
- gsm0808_bssmap_name(msg->l4h[0]));
+ bssmap_header = msgb_l3(msg);
+ msg->l4h = &msg->l3h[sizeof(struct bssmap_header)];
+ if (bssmap_header->length > msgb_l4len(msg)) {
+ LOGPFSML(conn->fi, LOGL_ERROR,
+ "Truncated BSSMAP message: header indicates %u bytes, but only %u bytes available\n",
+ bssmap_header->length, msgb_l4len(msg));
+ return -1;
+ }
- switch (msg->l4h[0]) {
+ if (bssmap_header->length < msgb_l4len(msg)) {
+ LOGPFSML(conn->fi, LOGL_INFO,
+ "BSSMAP message with extraneous data: header indicates %u bytes, but %u bytes available;"
+ " trimming\n",
+ bssmap_header->length, msgb_l4len(msg));
+ msgb_trim(msg, (msg->l4h - msg->data) + bssmap_header->length);
+ }
+
+ if (msgb_l4len(msg) < 1) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "BSSMAP message with zero length\n");
+ return -1;
+ }
+
+ msg_type = msg->l4h[0];
+ LOGPFSML(conn->fi, LOGL_INFO, "Rx MSC DT1 BSSMAP %s\n", gsm0808_bssmap_name(msg_type));
+
+ length = msgb_l4len(msg);
+ switch (msg_type) {
case BSS_MAP_MSG_CLEAR_CMD:
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_CLEAR_CMD]);
ret = bssmap_handle_clear_cmd(conn, msg, length);
@@ -1153,14 +1284,18 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn,
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_CIPHER_MODE_CMD]);
ret = bssmap_handle_cipher_mode(conn, msg, length);
break;
- case BSS_MAP_MSG_ASSIGMENT_RQST:
- rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_ASSIGMENT_RQST]);
+ case BSS_MAP_MSG_ASSIGNMENT_RQST:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_ASSIGNMENT_RQST]);
ret = bssmap_handle_assignm_req(conn, msg, length);
break;
case BSS_MAP_MSG_LCLS_CONNECT_CTRL:
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_LCLS_CONNECT_CTRL]);
ret = bssmap_handle_lcls_connect_ctrl(conn, msg, length);
break;
+ case BSS_MAP_MSG_HANDOVER_RQST:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_HANDOVER_RQST]);
+ ret = bssmap_handle_handover_request(conn, msg);
+ break;
case BSS_MAP_MSG_HANDOVER_CMD:
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_HANDOVER_CMD]);
ret = bssmap_handle_handover_cmd(conn, msg, length);
@@ -1188,14 +1323,14 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn,
ret = osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT,
msg);
} else {
- LOGP(DMSC, LOGL_ERROR, "Rx BSSMAP Perform Location Abort without ongoing Location Request\n");
+ LOGPFSML(conn->fi, LOGL_ERROR,
+ "Rx BSSMAP Perform Location Abort without ongoing Location Request\n");
ret = 0;
}
break;
default:
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_UNKNOWN]);
- LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n",
- gsm0808_bssmap_name(msg->l4h[0]));
+ LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg_type));
break;
}
@@ -1251,8 +1386,10 @@ static int dtap_rcvmsg(struct gsm_subscriber_connection *conn,
/* pass it to the filter for extra actions */
bsc_scan_msc_msg(conn, gsm48);
- /* Store link_id in msgb->cb */
- OBSC_LINKID_CB(gsm48) = header->link_id;
+
+ /* convert DLCI to RSL link ID, store in msg->cb */
+ OBSC_LINKID_CB(gsm48) = DLCI2RSL_LINK_ID(header->link_id);
+
dtap_rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MT_DTAP, gsm48);
return dtap_rc;
}
@@ -1287,35 +1424,6 @@ int bsc_handle_udt(struct bsc_msc_data *msc,
return 0;
}
-/* Extract and verify the length information from the BSSMAP header. */
-static unsigned int bssmap_msg_len(struct msgb *msg,
- const struct gsm_subscriber_connection *conn)
-{
- unsigned int expected_len;
- unsigned int calculated_len;
- struct bssmap_header *bssmap_header;
-
- bssmap_header = msgb_l3(msg);
- calculated_len = msgb_l3len(msg) - sizeof(struct bssmap_header);
- expected_len = bssmap_header->length;
-
- /* In case of contradictory length information, decide for the
- * shorter length */
- if (calculated_len > expected_len) {
- LOGPFSML(conn->fi, LOGL_NOTICE,
- "BSSMAP message contains extra data, expected %u bytes, got %u bytes, truncated\n",
- expected_len, calculated_len);
- return expected_len;
- } else if (calculated_len < expected_len) {
- LOGPFSML(conn->fi, LOGL_NOTICE,
- "Short BSSMAP message, expected %u bytes, got %u bytes\n",
- expected_len, calculated_len);
- return calculated_len;
- }
-
- return expected_len;
-}
-
int bsc_handle_dt(struct gsm_subscriber_connection *conn,
struct msgb *msg)
{
@@ -1327,8 +1435,7 @@ int bsc_handle_dt(struct gsm_subscriber_connection *conn,
switch (msg->l3h[0]) {
case BSSAP_MSG_BSS_MANAGEMENT:
- msg->l4h = &msg->l3h[sizeof(struct bssmap_header)];
- bssmap_rcvmsg_dt1(conn, msg, bssmap_msg_len(msg, conn));
+ bssmap_rcvmsg_dt1(conn, msg);
break;
case BSSAP_MSG_DTAP:
dtap_rcvmsg(conn, msg, msgb_l3len(msg));
@@ -1346,6 +1453,7 @@ int bsc_tx_bssmap_ho_required(struct gsm_lchan *lchan, const struct gsm0808_cell
{
int rc;
struct msgb *msg;
+ struct gsm_subscriber_connection *conn = lchan->conn;
struct gsm0808_handover_required params = {
.cause = GSM0808_CAUSE_BETTER_CELL,
.cil = *target_cells,
@@ -1353,12 +1461,21 @@ int bsc_tx_bssmap_ho_required(struct gsm_lchan *lchan, const struct gsm0808_cell
.current_channel_type_1 = gsm0808_current_channel_type_1(lchan->type),
};
+ /* Even if fast_return is now allowed locally, we may still want to
+ * signal the Last EUTRAN PLMN Id to the new cell, since destination
+ * config may differ and allow fast return */
+ if (conn->fast_return.last_eutran_plmn_valid) {
+ params.old_bss_to_new_bss_info_present = true;
+ params.old_bss_to_new_bss_info.last_eutran_plmn_id_present = true;
+ params.old_bss_to_new_bss_info.last_eutran_plmn_id = conn->fast_return.last_eutran_plmn;
+ }
+
switch (lchan->type) {
case GSM_LCHAN_TCH_F:
case GSM_LCHAN_TCH_H:
params.speech_version_used_present = true;
params.speech_version_used = gsm0808_permitted_speech(lchan->type,
- lchan->tch_mode);
+ lchan->current_ch_mode_rate.chan_mode);
if (!params.speech_version_used) {
LOG_HO(lchan->conn, LOGL_ERROR, "Cannot encode Speech Version (Used)"
" for BSSMAP Handover Required message\n");
@@ -1371,14 +1488,14 @@ int bsc_tx_bssmap_ho_required(struct gsm_lchan *lchan, const struct gsm0808_cell
msg = gsm0808_create_handover_required(&params);
if (!msg) {
- LOG_HO(lchan->conn, LOGL_ERROR, "Cannot compose BSSMAP Handover Required message\n");
+ LOG_HO(conn, LOGL_ERROR, "Cannot compose BSSMAP Handover Required message\n");
return -EINVAL;
}
- rate_ctr_inc(&lchan->conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_HANDOVER_REQUIRED]);
- rc = gscon_sigtran_send(lchan->conn, msg);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_HANDOVER_REQUIRED));
+ rc = gscon_sigtran_send(conn, msg);
if (rc) {
- LOG_HO(lchan->conn, LOGL_ERROR, "Cannot send BSSMAP Handover Required message\n");
+ LOG_HO(conn, LOGL_ERROR, "Cannot send BSSMAP Handover Required message\n");
return rc;
}
@@ -1396,14 +1513,17 @@ int bsc_tx_bssmap_ho_request_ack(struct gsm_subscriber_connection *conn, struct
.l3_info = rr_ho_command->data,
.l3_info_len = rr_ho_command->len,
.chosen_channel_present = true,
- .chosen_channel = gsm0808_chosen_channel(new_lchan->type, new_lchan->tch_mode),
- .chosen_encr_alg = new_lchan->encr.alg_id,
- .chosen_speech_version = gsm0808_permitted_speech(new_lchan->type, new_lchan->tch_mode),
+ .chosen_channel = gsm0808_chosen_channel(new_lchan->type, new_lchan->current_ch_mode_rate.chan_mode),
+ .chosen_encr_alg = ALG_A5_NR_TO_BSSAP(new_lchan->encr.alg_a5_n),
+ .chosen_speech_version = gsm0808_permitted_speech(new_lchan->type,
+ new_lchan->current_ch_mode_rate.chan_mode),
};
- if (gscon_is_aoip(conn)) {
+ if (gscon_is_aoip(conn) && new_lchan->activate.info.requires_voice_stream) {
struct osmo_sockaddr_str to_msc_rtp;
const struct mgcp_conn_peer *rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(conn->user_plane.mgw_endpoint_ci_msc);
+ int rc;
+ int perm_spch;
if (!rtp_info) {
LOG_HO(conn, LOGL_ERROR,
"Handover Request Acknowledge: no RTP address known to send as"
@@ -1419,9 +1539,22 @@ int bsc_tx_bssmap_ho_request_ack(struct gsm_subscriber_connection *conn, struct
return -EINVAL;
}
params.aoip_transport_layer = &ss;
+
+ /* speech_codec_chosen */
+ perm_spch = gsm0808_permitted_speech(new_lchan->type, new_lchan->current_ch_mode_rate.chan_mode);
+ params.speech_codec_chosen_present = true;
+ rc = gsm0808_speech_codec_from_chan_type(&params.speech_codec_chosen, perm_spch);
+ if (rc) {
+ LOG_HO(conn, LOGL_ERROR, "Unable to compose Speech Codec (Chosen)\n");
+ return -EINVAL;
+ }
+
+ /* Codec list (BSS Supported) */
+ params.more_items = true;
+ gen_bss_supported_codec_list(&params.codec_list_bss_supported, conn->sccp.msc, new_lchan->ts->trx->bts);
}
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_HANDOVER_RQST_ACKNOWLEDGE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_HANDOVER_RQST_ACKNOWLEDGE));
LOG_HO(conn, LOGL_DEBUG, "Sending BSSMAP Handover Request Acknowledge\n");
msg = gsm0808_create_handover_request_ack2(&params);
msgb_free(rr_ho_command);
@@ -1437,7 +1570,7 @@ int bsc_tx_bssmap_ho_detect(struct gsm_subscriber_connection *conn)
if (!msg)
return -ENOMEM;
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_HANDOVER_DETECT]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_HANDOVER_DETECT));
return osmo_bsc_sigtran_send(conn, msg);
}
@@ -1451,10 +1584,10 @@ enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection
struct gsm0808_handover_complete params = {
.chosen_encr_alg_present = true,
- .chosen_encr_alg = lchan->encr.alg_id,
+ .chosen_encr_alg = ALG_A5_NR_TO_BSSAP(lchan->encr.alg_a5_n),
.chosen_channel_present = true,
- .chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode),
+ .chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode),
.lcls_bss_status_present = (lcls_status != 0xff),
.lcls_bss_status = lcls_status,
@@ -1462,7 +1595,7 @@ enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection
/* speech_codec_chosen */
if (ho->new_lchan->activate.info.requires_voice_stream && gscon_is_aoip(conn)) {
- int perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode);
+ int perm_spch = gsm0808_permitted_speech(lchan->type, lchan->current_ch_mode_rate.chan_mode);
params.speech_codec_chosen_present = true;
rc = gsm0808_speech_codec_from_chan_type(&params.speech_codec_chosen, perm_spch);
if (rc) {
@@ -1477,7 +1610,7 @@ enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection
return HO_RESULT_ERROR;
}
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_HANDOVER_COMPLETE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_HANDOVER_COMPLETE));
rc = osmo_bsc_sigtran_send(conn, msg);
if (rc) {
LOG_HO(conn, LOGL_ERROR, "Cannot send BSSMAP Handover Complete message\n");
@@ -1499,7 +1632,7 @@ void bsc_tx_bssmap_ho_failure(struct gsm_subscriber_connection *conn)
return;
}
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_HANDOVER_FAILURE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_HANDOVER_FAILURE));
rc = osmo_bsc_sigtran_send(conn, msg);
if (rc)
LOG_HO(conn, LOGL_ERROR, "Cannot send BSSMAP Handover Failure message (rc=%d %s)\n",
diff --git a/src/osmo-bsc/osmo_bsc_filter.c b/src/osmo-bsc/osmo_bsc_filter.c
index f664231e8..2b58ccf54 100644
--- a/src/osmo-bsc/osmo_bsc_filter.c
+++ b/src/osmo-bsc/osmo_bsc_filter.c
@@ -92,26 +92,11 @@ static int bsc_patch_mm_info(struct gsm_subscriber_connection *conn,
return 0;
}
-static int has_core_identity(struct bsc_msc_data *msc)
-{
- if (msc->core_plmn.mnc != GSM_MCC_MNC_INVALID)
- return 1;
- if (msc->core_plmn.mcc != GSM_MCC_MNC_INVALID)
- return 1;
- if (msc->core_lac != -1)
- return 1;
- if (msc->core_ci != -1)
- return 1;
- return 0;
-}
-
/**
* Messages coming back from the MSC.
*/
int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
- struct bsc_msc_data *msc;
- struct gsm48_loc_area_id *lai;
struct gsm48_hdr *gh;
uint8_t pdisc;
uint8_t mtype;
@@ -130,21 +115,27 @@ int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg)
return 0;
mtype = gsm48_hdr_msg_type(gh);
- msc = conn->sccp.msc;
-
- if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) {
- struct gsm_bts *bts = conn_get_bts(conn);
- if (bts && has_core_identity(msc)) {
- if (msgb_l3len(msg) >= sizeof(*gh) + sizeof(*lai)) {
- /* overwrite LAI in the message */
- lai = (struct gsm48_loc_area_id *) &gh->data[0];
- gsm48_generate_lai2(lai, bts_lai(bts));
- }
- }
- return 0;
- } else if (mtype == GSM48_MT_MM_INFO) {
+ if (mtype == GSM48_MT_MM_INFO) {
bsc_patch_mm_info(conn, &gh->data[0], length);
}
+ if (conn && conn->lchan) {
+ struct rate_ctr_group *bts_ctrs = conn->lchan->ts->trx->bts->bts_ctrs;
+ switch (mtype) {
+ case GSM48_MT_MM_LOC_UPD_ACCEPT:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_LOCATION_UPDATE_ACCEPT));
+ break;
+ case GSM48_MT_MM_LOC_UPD_REJECT:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_LOCATION_UPDATE_REJECT));
+ break;
+ case GSM48_MT_MM_IMSI_DETACH_IND:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_LOCATION_UPDATE_DETACH));
+ break;
+ default:
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_LOCATION_UPDATE_UNKNOWN));
+ break;
+ }
+ }
+
return 0;
}
diff --git a/src/osmo-bsc/osmo_bsc_lcls.c b/src/osmo-bsc/osmo_bsc_lcls.c
index 4ab04419d..eab0be4d1 100644
--- a/src/osmo-bsc/osmo_bsc_lcls.c
+++ b/src/osmo-bsc/osmo_bsc_lcls.c
@@ -30,6 +30,7 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/osmo_bsc_lcls.h>
#include <osmocom/bsc/lchan_rtp_fsm.h>
+#include <osmocom/bsc/lchan.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
struct value_string lcls_event_names[] = {
@@ -243,13 +244,19 @@ static inline void lcls_rsl(const struct gsm_subscriber_connection *conn, bool e
uint32_t ip = enable ? conn->lcls.other->lchan->abis_ip.bound_ip : lchan->abis_ip.connect_ip;
/* RSL_IE_IPAC_REMOTE_PORT */
uint16_t port = enable ? conn->lcls.other->lchan->abis_ip.bound_port : lchan->abis_ip.connect_port;
+ struct msgb *msg;
if (!conn->lcls.other) {
LOGPFSM(conn->lcls.fi, "%s LCLS: other conn is not available!\n", enable ? "enable" : "disable");
return;
}
- abis_rsl_sendmsg(rsl_make_ipacc_mdcx(lchan, ip, port));
+ msg = rsl_make_ipacc_mdcx(lchan, ip, port);
+ if (!msg) {
+ LOGPFSML(conn->lcls.fi, LOGL_ERROR, "Error encoding IPACC MDCX\n");
+ return;
+ }
+ abis_rsl_sendmsg(msg);
}
static inline bool lcls_check_toggle_allowed(const struct gsm_subscriber_connection *conn, bool enable)
@@ -345,15 +352,14 @@ static bool lcls_enable_possible(const struct gsm_subscriber_connection *conn)
return false;
}
- if (conn->lchan->tch_mode != conn->lcls.other->lchan->tch_mode
+ if (conn->lchan->current_ch_mode_rate.chan_mode != conn->lcls.other->lchan->current_ch_mode_rate.chan_mode
&& conn->sccp.msc->lcls_codec_mismatch_allow == false) {
LOGPFSM(conn->lcls.fi,
"Not enabling LS due to TCH-mode mismatch: %s:%s != %s:%s\n",
gsm_lchan_name(conn->lchan),
- gsm48_chan_mode_name(conn->lchan->tch_mode),
+ gsm48_chan_mode_name(conn->lchan->current_ch_mode_rate.chan_mode),
gsm_lchan_name(conn->lcls.other->lchan),
- gsm48_chan_mode_name(conn->lcls.other->lchan->
- tch_mode));
+ gsm48_chan_mode_name(conn->lcls.other->lchan->current_ch_mode_rate.chan_mode));
return false;
}
@@ -556,7 +562,7 @@ static void lcls_req_lcls_not_supp_fn(struct osmo_fsm_inst *fi, uint32_t event,
case LCLS_EV_UPDATE_CFG_CSC:
if (lcls_handle_cfg_update(conn, data) != 0)
return;
- //FIXME osmo_fsm_inst_state_chg(fi,
+ //FIXME osmo_fsm_inst_state_chg(fi,
return;
case LCLS_EV_APPLY_CFG_CSC:
if (lcls_perform_correlation(conn) != 0) {
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index 03b832ad3..f3a6a02c2 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -57,6 +57,7 @@
#include <osmocom/vty/cpu_sched_vty.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
+#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include <osmocom/abis/abis.h>
#include <osmocom/bsc/abis_om2000.h>
@@ -67,8 +68,10 @@
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
#include <osmocom/mgcp_client/mgcp_client.h>
+#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include <osmocom/sigtran/xua_msg.h>
@@ -84,19 +87,18 @@
#include "../../bscconfig.h"
-struct gsm_network *bsc_gsmnet = 0;
static const char *config_file = "osmo-bsc.cfg";
static const char *rf_ctrl = NULL;
static int daemonize = 0;
-static void print_usage()
+static void print_usage(void)
{
printf("Usage: osmo-bsc\n");
}
-static void print_help()
+static void print_help(void)
{
- printf(" Some useful help...\n");
+ printf("Some useful options:\n");
printf(" -h --help This text.\n");
printf(" -D --daemonize Fork the process into a background daemon.\n");
printf(" -d --debug option --debug=DRLL:DMM:DRR:DRSL:DNM enable debugging.\n");
@@ -104,11 +106,37 @@ static void print_help()
printf(" -T --timestamp Print a timestamp in the debug output.\n");
printf(" -V --version Print the version of OsmoBSC.\n");
printf(" -c --config-file filename The config file to use.\n");
- printf(" -l --local IP The local address of the MGCP.\n");
printf(" -e --log-level number Set a global loglevel.\n");
printf(" -r --rf-ctl NAME A unix domain socket to listen for cmds.\n");
- printf(" -t --testmode A special mode to provoke failures at the MSC.\n");
- printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
+
+ printf("\nVTY reference generation:\n");
+ printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
+ printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
+}
+
+static void handle_long_options(const char *prog_name, const int long_option)
+{
+ static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
+
+ switch (long_option) {
+ case 1:
+ vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
+ if (vty_ref_mode < 0) {
+ fprintf(stderr, "%s: Unknown VTY reference generation "
+ "mode '%s'\n", prog_name, optarg);
+ exit(2);
+ }
+ break;
+ case 2:
+ fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
+ get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
+ get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
+ vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
+ exit(0);
+ default:
+ fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
+ exit(2);
+ }
}
static void handle_options(int argc, char **argv)
@@ -124,15 +152,14 @@ static void handle_options(int argc, char **argv)
{"disable-color", 0, 0, 's'},
{"timestamp", 0, 0, 'T'},
{"version", 0, 0, 'V' },
- {"local", 1, 0, 'l'},
{"log-level", 1, 0, 'e'},
{"rf-ctl", 1, 0, 'r'},
- {"testmode", 0, 0, 't'},
- {"vty-ref-xml", 0, &long_option, 1},
+ {"vty-ref-mode", 1, &long_option, 1},
+ {"vty-ref-xml", 0, &long_option, 2},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, "hd:DsTVc:e:r:t",
+ c = getopt_long(argc, argv, "hd:DsTVc:e:r:",
long_options, &option_index);
if (c == -1)
break;
@@ -143,14 +170,8 @@ static void handle_options(int argc, char **argv)
print_help();
exit(0);
case 0:
- switch (long_option) {
- case 1:
- vty_dump_xml_ref(stdout);
- exit(0);
- default:
- fprintf(stderr, "error parsing cmdline options\n");
- exit(2);
- }
+ handle_long_options(argv[0], long_option);
+ break;
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
@@ -233,14 +254,14 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
}
/* Produce a MA as specified in 10.5.2.21 */
-static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts)
+static void generate_ma_for_ts(struct gsm_bts_trx_ts *ts)
{
/* we have three bitvecs: the per-timeslot ARFCNs, the cell chan ARFCNs
* and the MA */
+ const size_t num_cell_arfcns = ts->trx->bts->si_common.cell_chan_num;
const struct bitvec *cell_chan = &ts->trx->bts->si_common.cell_alloc;
const struct bitvec *ts_arfcn = &ts->hopping.arfcns;
struct bitvec *ma = &ts->hopping.ma;
- unsigned int num_cell_arfcns;
int i;
/* re-set the MA to all-zero */
@@ -248,14 +269,7 @@ static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts)
memset(ma->data, 0, ma->data_len);
if (!ts->hopping.enabled)
- return 0;
-
- /* count the number of ARFCNs in the cell channel allocation */
- num_cell_arfcns = 0;
- for (i = 0; i < 1024; i++) {
- if (bitvec_get_bit_pos(cell_chan, i))
- num_cell_arfcns++;
- }
+ return;
/* pad it to octet-aligned number of bits */
ts->hopping.ma_len = OSMO_BYTES_FOR_BITS(num_cell_arfcns);
@@ -280,13 +294,26 @@ static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts)
else
bitvec_set_bit_pos(ma, ma->cur_bit, 0);
}
+}
- return 0;
+static void generate_ma_for_bts(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+ unsigned int tn;
+
+ OSMO_ASSERT(bts->si_common.cell_chan_num > 0);
+ OSMO_ASSERT(bts->si_common.cell_chan_num <= 64);
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++)
+ generate_ma_for_ts(&trx->ts[tn]);
+ }
}
static void bootstrap_rsl(struct gsm_bts_trx *trx)
{
unsigned int i;
+ int rc;
LOG_TRX(trx, DRSL, LOGL_NOTICE, "bootstrapping RSL "
"on ARFCN %u using MCC-MNC %s LAC=%u CID=%u BSIC=%u\n",
@@ -317,29 +344,73 @@ static void bootstrap_rsl(struct gsm_bts_trx *trx)
rsl_nokia_si_end(trx);
}
+ if (trx->bts->model->power_ctrl_send_def_params != NULL) {
+ rc = trx->bts->model->power_ctrl_send_def_params(trx);
+ if (rc) {
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "Failed to send default "
+ "MS/BS Power control parameters (rc=%d)\n", rc);
+ /* TODO: should we drop RSL connection here? */
+ }
+ }
+
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
struct gsm_bts_trx_ts *ts = &trx->ts[i];
- generate_ma_for_ts(ts);
OSMO_ASSERT(ts->fi);
osmo_fsm_inst_dispatch(ts->fi, TS_EV_RSL_READY, NULL);
}
- /* Start CBCH transmit timer if CBCH is present */
- if (trx->nr == 0 && gsm_bts_get_cbch(trx->bts))
- bts_cbch_timer_schedule(trx->bts);
-
/* Drop all expired channel requests in the list */
abis_rsl_chan_rqd_queue_flush(trx->bts);
}
-static void all_ts_dispatch_event(struct gsm_bts_trx *trx, uint32_t event)
+struct osmo_timer_list update_connection_stats_timer;
+
+/* Periodically call bsc_update_connection_stats() to keep stat items updated.
+ * It would be nicer to trigger this only when OML or RSL state is seen to flip. I tried hard to find all code paths
+ * that should call this and failed to get accurate results; this trivial timer covers all of them. */
+static void update_connection_stats_cb(void *data)
+{
+ bsc_update_connection_stats(bsc_gsmnet);
+ osmo_timer_setup(&update_connection_stats_timer, update_connection_stats_cb, NULL);
+ osmo_timer_schedule(&update_connection_stats_timer, 1, 0);
+}
+
+static void bootstrap_bts(struct gsm_bts *bts)
{
- int ts_i;
- for (ts_i = 0; ts_i < ARRAY_SIZE(trx->ts); ts_i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[ts_i];
- if (ts->fi)
- osmo_fsm_inst_dispatch(ts->fi, event, 0);
+ unsigned int n = 0;
+
+ /* Control Channel Description is set from vty/config */
+
+ /* Determine the value of CCCH_CONF. Is TS0/C0 combined? */
+ if (bts->c0->ts[0].pchan_from_config != GSM_PCHAN_CCCH) {
+ bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
+
+ /* Limit reserved block to 2 on combined channel according to
+ 3GPP TS 44.018 Table 10.5.2.11.1 */
+ if (bts->si_common.chan_desc.bs_ag_blks_res > 2) {
+ LOGP(DNM, LOGL_NOTICE, "CCCH is combined with SDCCHs, "
+ "reducing BS-AG-BLKS-RES value %d -> 2\n",
+ bts->si_common.chan_desc.bs_ag_blks_res);
+ bts->si_common.chan_desc.bs_ag_blks_res = 2;
+ }
+ } else { /* Non-combined TS0/C0 configuration */
+ /* There can be additional CCCHs on even timeslot numbers */
+ n += (bts->c0->ts[2].pchan_from_config == GSM_PCHAN_CCCH);
+ n += (bts->c0->ts[4].pchan_from_config == GSM_PCHAN_CCCH);
+ n += (bts->c0->ts[6].pchan_from_config == GSM_PCHAN_CCCH);
+ bts->si_common.chan_desc.ccch_conf = (n << 1);
}
+
+ bts_setup_ramp_init_bts(bts);
+
+ /* ACC ramping is initialized from vty/config */
+
+ /* Initialize the BTS state */
+ gsm_bts_sm_mo_reset(bts->site_mgr);
+
+ /* Generate Mobile Allocation bit-masks for all timeslots.
+ * This needs to be done here, because it's used for TS configuration. */
+ generate_ma_for_bts(bts);
}
/* Callback function to be called every time we receive a signal from INPUT */
@@ -348,13 +419,7 @@ static int inp_sig_cb(unsigned int subsys, unsigned int signal,
{
struct input_signal_data *isd = signal_data;
struct gsm_bts_trx *trx = isd->trx;
- /* N. B: we rely on attribute order when parsing response in abis_nm_rx_get_attr_resp() */
- const uint8_t bts_attr[] = { NM_ATT_MANUF_ID, NM_ATT_SW_CONFIG, };
- const uint8_t trx_attr[] = { NM_ATT_MANUF_STATE, NM_ATT_SW_CONFIG, };
-
- /* we should not request more attributes than we're ready to handle */
- OSMO_ASSERT(sizeof(bts_attr) < MAX_BTS_ATTR);
- OSMO_ASSERT(sizeof(trx_attr) < MAX_BTS_ATTR);
+ int rc;
if (subsys != SS_L_INPUT)
return -EINVAL;
@@ -364,44 +429,43 @@ static int inp_sig_cb(unsigned int subsys, unsigned int signal,
switch (signal) {
case S_L_INP_TEI_UP:
if (isd->link_type == E1INP_SIGN_OML) {
- /* TODO: this is required for the Nokia BTS, hopping is configured
- during OML, other MA is not set. */
- struct gsm_bts_trx *cur_trx;
- uint8_t ca[20];
- /* has to be called before generate_ma_for_ts to
- set bts->si_common.cell_alloc */
- generate_cell_chan_list(ca, trx->bts);
-
- /* Request generic BTS-level attributes */
- abis_nm_get_attr(trx->bts, NM_OC_BTS, 0xFF, 0xFF, 0xFF, bts_attr, sizeof(bts_attr));
-
- llist_for_each_entry(cur_trx, &trx->bts->trx_list, list) {
- int i;
- /* Request TRX-level attributes */
- abis_nm_get_attr(cur_trx->bts, NM_OC_BASEB_TRANSC, 0, cur_trx->nr, 0xFF,
- trx_attr, sizeof(trx_attr));
- for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++)
- generate_ma_for_ts(&cur_trx->ts[i]);
+ /* Check parameters and apply vty config dependent parameters */
+ rc = gsm_bts_check_cfg(trx->bts);
+ if (rc < 0) {
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) Error in BTS configuration -- cannot bootstrap BTS\n",
+ trx->bts->nr);
+ return rc;
}
+ bootstrap_bts(trx->bts);
}
- if (isd->link_type == E1INP_SIGN_RSL)
+ if (isd->link_type == E1INP_SIGN_RSL) {
+ rc = gsm_bts_check_cfg(trx->bts);
+ if (rc < 0) {
+ LOGP(DNM, LOGL_ERROR, "(bts=%u) Error in BTS configuration -- cannot bootstrap RSL\n",
+ trx->bts->nr);
+ return rc;
+ }
bootstrap_rsl(trx);
+ }
break;
case S_L_INP_TEI_DN:
LOG_TRX(trx, DLMI, LOGL_ERROR, "Lost E1 %s link\n", e1inp_signtype_name(isd->link_type));
if (isd->link_type == E1INP_SIGN_OML) {
- rate_ctr_inc(&trx->bts->bts_ctrs->ctr[BTS_CTR_BTS_OML_FAIL]);
- all_ts_dispatch_event(trx, TS_EV_OML_DOWN);
+ rate_ctr_inc(rate_ctr_group_get_ctr(trx->bts->bts_ctrs, BTS_CTR_BTS_OML_FAIL));
+ /* ip.access BTS models have a single global A-bis/OML link for all
+ * transceivers, so once it's lost we need to notify them all. */
+ if (is_ipaccess_bts(trx->bts))
+ gsm_bts_all_ts_dispatch(trx->bts, TS_EV_OML_DOWN, NULL);
+ else /* Other BTS models (e.g. Ericsson) have per-TRX OML links */
+ gsm_trx_all_ts_dispatch(trx, TS_EV_OML_DOWN, NULL);
} else if (isd->link_type == E1INP_SIGN_RSL) {
- rate_ctr_inc(&trx->bts->bts_ctrs->ctr[BTS_CTR_BTS_RSL_FAIL]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(trx->bts->bts_ctrs, BTS_CTR_BTS_RSL_FAIL));
acc_ramp_abort(&trx->bts->acc_ramp);
- all_ts_dispatch_event(trx, TS_EV_RSL_DOWN);
- if (trx->nr == 0)
- osmo_timer_del(&trx->bts->cbch_timer);
+ gsm_trx_all_ts_dispatch(trx, TS_EV_RSL_DOWN, NULL);
}
- gsm_bts_mo_reset(trx->bts);
+ gsm_bts_sm_mo_reset(trx->bts->site_mgr);
abis_nm_clear_queue(trx->bts);
break;
@@ -412,105 +476,6 @@ static int inp_sig_cb(unsigned int subsys, unsigned int signal,
return 0;
}
-static int bootstrap_bts(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
- unsigned int n = 0;
-
- if (!bts->model)
- return -EFAULT;
-
- if (bts->model->start && !bts->model->started) {
- int ret = bts->model->start(bts->network);
- if (ret < 0)
- return ret;
-
- bts->model->started = true;
- }
-
- /* FIXME: What about secondary TRX of a BTS? What about a BTS that has TRX
- * in different bands? Why is 'band' a parameter of the BTS and not of the TRX? */
- switch (bts->band) {
- case GSM_BAND_1800:
- if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
- LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n");
- return -EINVAL;
- }
- break;
- case GSM_BAND_1900:
- if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) {
- LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n");
- return -EINVAL;
- }
- break;
- case GSM_BAND_900:
- if ((bts->c0->arfcn > 124 && bts->c0->arfcn < 955) ||
- bts->c0->arfcn > 1023) {
- LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 0-124, 955-1023.\n");
- return -EINVAL;
- }
- break;
- case GSM_BAND_850:
- if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) {
- LOGP(DNM, LOGL_ERROR, "GSM850 channel must be between 128-251.\n");
- return -EINVAL;
- }
- break;
- default:
- LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n");
- return -EINVAL;
- }
-
- /* Verify the physical channel mapping */
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (!trx_has_valid_pchan_config(trx)) {
- LOGP(DNM, LOGL_ERROR, "TRX %u has invalid timeslot "
- "configuration\n", trx->nr);
- return -EINVAL;
- }
- }
-
- /* Control Channel Description is set from vty/config */
-
- /* Indicate R99 MSC in SI3 */
- bts->si_common.chan_desc.mscr = 1;
-
- /* Determine the value of CCCH_CONF. Is TS0/C0 combined? */
- if (bts->c0->ts[0].pchan_from_config != GSM_PCHAN_CCCH) {
- bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
-
- /* Limit reserved block to 2 on combined channel according to
- 3GPP TS 44.018 Table 10.5.2.11.1 */
- if (bts->si_common.chan_desc.bs_ag_blks_res > 2) {
- LOGP(DNM, LOGL_NOTICE, "CCCH is combined with SDCCHs, "
- "reducing BS-AG-BLKS-RES value %d -> 2\n",
- bts->si_common.chan_desc.bs_ag_blks_res);
- bts->si_common.chan_desc.bs_ag_blks_res = 2;
- }
- } else { /* Non-combined TS0/C0 configuration */
- /* There can be additional CCCHs on even timeslot numbers */
- n += (bts->c0->ts[2].pchan_from_config == GSM_PCHAN_CCCH);
- n += (bts->c0->ts[4].pchan_from_config == GSM_PCHAN_CCCH);
- n += (bts->c0->ts[6].pchan_from_config == GSM_PCHAN_CCCH);
- bts->si_common.chan_desc.ccch_conf = (n << 1);
- }
-
- bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
-
- bts->si_common.cell_sel_par.acs = 0;
-
- bts->si_common.ncc_permitted = 0xff;
-
- bts->chan_load_samples_idx = 0;
-
- /* ACC ramping is initialized from vty/config */
-
- /* Initialize the BTS state */
- gsm_bts_mo_reset(bts);
-
- return 0;
-}
-
static int bsc_network_configure(const char *config_file)
{
struct gsm_bts *bts;
@@ -523,8 +488,7 @@ static int bsc_network_configure(const char *config_file)
}
/* start telnet after reading config for vty_get_bind_addr() */
- rc = telnet_init_dynif(tall_bsc_ctx, bsc_gsmnet, vty_get_bind_addr(),
- OSMO_VTY_PORT_NITB_BSC);
+ rc = telnet_init_default(tall_bsc_ctx, bsc_gsmnet, OSMO_VTY_PORT_NITB_BSC);
if (rc < 0)
return rc;
@@ -532,11 +496,12 @@ static int bsc_network_configure(const char *config_file)
osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
- rc = bootstrap_bts(bts);
+ rc = gsm_bts_check_cfg(bts);
if (rc < 0) {
- LOGP(DNM, LOGL_FATAL, "Error bootstrapping BTS\n");
+ LOGP(DNM, LOGL_FATAL, "(bts=%u) cannot bootstrap BTS, invalid BTS configuration\n", bts->nr);
return rc;
}
+ bootstrap_bts(bts);
rc = e1_reconfig_bts(bts);
if (rc < 0) {
LOGP(DNM, LOGL_FATAL, "Error enabling E1 input driver\n");
@@ -554,6 +519,11 @@ static int bsc_vty_go_parent(struct vty *vty)
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
+ case MGW_NODE:
+ vty->node = GSMNET_NODE;
+ vty->index = bsc_gsmnet;
+ vty->index_sub = NULL;
+ break;
case BTS_NODE:
vty->node = GSMNET_NODE;
{
@@ -563,6 +533,21 @@ static int bsc_vty_go_parent(struct vty *vty)
vty->index_sub = NULL;
}
break;
+ case POWER_CTRL_NODE:
+ vty->node = BTS_NODE;
+ {
+ const struct gsm_power_ctrl_params *cp = vty->index;
+ struct gsm_bts *bts;
+
+ if (cp->dir == GSM_PWR_CTRL_DIR_UL)
+ bts = container_of(cp, struct gsm_bts, ms_power_ctrl);
+ else
+ bts = container_of(cp, struct gsm_bts, bs_power_ctrl);
+
+ vty->index_sub = &bts->description;
+ vty->index = bts;
+ }
+ break;
case TRX_NODE:
vty->node = BTS_NODE;
{
@@ -578,7 +563,6 @@ static int bsc_vty_go_parent(struct vty *vty)
/* set vty->index correctly ! */
struct gsm_bts_trx_ts *ts = vty->index;
vty->index = ts->trx;
- vty->index_sub = &ts->trx->description;
}
break;
case OML_NODE:
@@ -636,31 +620,60 @@ static struct vty_app_info vty_info = {
.copyright =
"Copyright (C) 2008-2018 Harald Welte, Holger Freyther\r\n"
"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
- "Dieter Spaar, Andreas Eversberg, Sylvain Munaut, Neels Hofmeyr\r\n\r\n"
+ "Dieter Spaar, Andreas Eversberg, Sylvain Munaut, Neels Hofmeyr\r\n"
+ "Copyright (C) 2013-2022 sysmocom - s.f.m.c. GmbH\r\n"
+ "\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n",
.version = PACKAGE_VERSION,
.go_parent_cb = bsc_vty_go_parent,
.is_config_node = bsc_vty_is_config_node,
+ .usr_attr_desc = {
+ [BSC_VTY_ATTR_RESTART_ABIS_OML_LINK] = \
+ "This command applies on A-bis OML link (re)establishment",
+ [BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK] = \
+ "This command applies on A-bis RSL link (re)establishment",
+ [BSC_VTY_ATTR_NEW_LCHAN] = \
+ "This command applies for newly created lchans",
+ [BSC_VTY_ATTR_VENDOR_SPECIFIC] = \
+ "This command/parameter is BTS vendor specific",
+ },
+ .usr_attr_letters = {
+ [BSC_VTY_ATTR_RESTART_ABIS_OML_LINK] = 'o',
+ [BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK] = 'r',
+ [BSC_VTY_ATTR_NEW_LCHAN] = 'l',
+ [BSC_VTY_ATTR_VENDOR_SPECIFIC] = 'v',
+ },
};
extern int bsc_shutdown_net(struct gsm_network *net);
-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 SIGINT:
case SIGTERM:
+ /* If SIGTERM was already sent before, just terminate immediately. */
+ if (osmo_select_shutdown_requested())
+ exit(-1);
bsc_shutdown_net(bsc_gsmnet);
osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
- sleep(3);
- exit(0);
+ osmo_select_shutdown_request();
break;
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(tall_vty_ctx, stderr);
+ talloc_report_full(tall_bsc_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
case SIGUSR1:
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_bsc_ctx, stderr);
@@ -782,6 +795,17 @@ static const struct log_info_cat osmo_bsc_categories[] = {
.description = "Location Services",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
+ [DRESET] = {
+ .name = "DRESET",
+ .description = "RESET/ACK on A and Lb interfaces",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DLOOP] = {
+ .name = "DLOOP",
+ .description = "Control loops",
+ .color = "\033[0;34m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
static int filter_fn(const struct log_context *ctx, struct log_target *tar)
@@ -806,7 +830,41 @@ const struct log_info log_info = {
extern void *tall_paging_ctx;
extern void *tall_fle_ctx;
extern void *tall_tqe_ctx;
-extern void *tall_ctr_ctx;
+
+static int bsc_mgw_setup(void)
+{
+ struct mgcp_client *mgcp_client_single;
+ unsigned int pool_members_initalized;
+
+ /* Initialize MGW pool. This initalizes and connects all MGCP clients that are currently configured in
+ * the pool. Adding additional MGCP clients to the pool is possible but the user has to configure and
+ * (re)connect them manually from the VTY. */
+ pool_members_initalized = mgcp_client_pool_connect(bsc_gsmnet->mgw.mgw_pool);
+ if (pool_members_initalized) {
+ LOGP(DNM, LOGL_NOTICE,
+ "MGW pool with %u pool members configured, (ignoring MGW configuration in VTY node 'msc').\n",
+ pool_members_initalized);
+ return 0;
+ }
+
+ /* Initialize and connect a single MGCP client. This MGCP client will appear as the one and only pool
+ * member if there is no MGW pool configured. */
+ LOGP(DNM, LOGL_NOTICE, "No MGW pool configured, using MGW configuration in VTY node 'msc'\n");
+ mgcp_client_single = mgcp_client_init(bsc_gsmnet, bsc_gsmnet->mgw.conf);
+ if (!mgcp_client_single) {
+ LOGP(DNM, LOGL_ERROR, "MGW (single) client initalization failed\n");
+ return -EINVAL;
+ }
+ if (mgcp_client_connect(mgcp_client_single)) {
+ LOGP(DNM, LOGL_ERROR, "MGW (single) connect failed at (%s:%u)\n",
+ bsc_gsmnet->mgw.conf->remote_addr,
+ bsc_gsmnet->mgw.conf->remote_port);
+ return -EINVAL;
+ }
+ mgcp_client_pool_register_single(bsc_gsmnet->mgw.mgw_pool, mgcp_client_single);
+
+ return 0;
+}
int main(int argc, char **argv)
{
@@ -822,13 +880,13 @@ int main(int argc, char **argv)
tall_paging_ctx = talloc_named_const(tall_bsc_ctx, 0, "paging_request");
tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, "bs11_file_list_entry");
tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 0, "subch_txq_entry");
- tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter");
osmo_init_logging2(tall_bsc_ctx, &log_info);
osmo_stats_init(tall_bsc_ctx);
rate_ctr_init(tall_bsc_ctx);
osmo_fsm_set_dealloc_ctx(OTC_SELECT);
+ osmo_fsm_log_timeouts(true);
/* Allocate global gsm_network struct */
rc = bsc_network_alloc();
@@ -838,6 +896,7 @@ int main(int argc, char **argv)
}
bsc_gsmnet->mgw.conf = talloc_zero(bsc_gsmnet, struct mgcp_client_conf);
+ bsc_gsmnet->mgw.mgw_pool = mgcp_client_pool_alloc(bsc_gsmnet);
mgcp_client_conf_init(bsc_gsmnet->mgw.conf);
bts_init();
@@ -865,11 +924,10 @@ int main(int argc, char **argv)
/* seed the PRNG */
srand(time(NULL));
- ts_fsm_init();
- lchan_fsm_init();
- bsc_subscr_conn_fsm_init();
- assignment_fsm_init();
- handover_fsm_init();
+ lb_init();
+ acc_ramp_global_init();
+ paging_global_init();
+ smscb_global_init();
/* Read the config */
rc = bsc_network_configure(config_file);
@@ -878,11 +936,14 @@ int main(int argc, char **argv)
exit(1);
}
+ if (neighbors_check_cfg()) {
+ fprintf(stderr, "Errors in neighbor configuration, check the DHO log. exiting.\n");
+ exit(1);
+ }
+
/* start control interface after reading config for
* ctrl_vty_get_bind_addr() */
- bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet,
- ctrl_vty_get_bind_addr(),
- OSMO_CTRL_PORT_NITB_BSC);
+ bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet, OSMO_CTRL_PORT_NITB_BSC);
if (!bsc_gsmnet->ctrl) {
fprintf(stderr, "Failed to init the control interface. Exiting.\n");
exit(1);
@@ -894,6 +955,19 @@ int main(int argc, char **argv)
exit(1);
}
+ if (bsc_gsmnet->neigh_ctrl.addr) {
+ bsc_gsmnet->neigh_ctrl.handle = neighbor_controlif_setup(bsc_gsmnet);
+ if (!bsc_gsmnet->neigh_ctrl.handle) {
+ fprintf(stderr, "Failed to bind Neighbor Resolution Service. Exiting.\n");
+ exit(1);
+ }
+ rc = neighbor_ctrl_cmds_install(bsc_gsmnet);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to install Neighbor Resolution Service commands. Exiting.\n");
+ exit(1);
+ }
+ }
+
if (rf_ctrl)
osmo_talloc_replace_string(bsc_gsmnet, &bsc_gsmnet->rf_ctrl_name, rf_ctrl);
@@ -915,6 +989,12 @@ int main(int argc, char **argv)
}
}
+ if (bsc_mgw_setup() != 0)
+ exit(1);
+
+ /* Do not fail on AMR modes warnings, just warn. */
+ check_amr_modes(&bsc_gsmnet->mscs);
+
llist_for_each_entry(msc, &bsc_gsmnet->mscs, entry) {
if (osmo_bsc_msc_init(msc) != 0) {
LOGP(DMSC, LOGL_ERROR, "Failed to start up. Exiting.\n");
@@ -922,15 +1002,6 @@ int main(int argc, char **argv)
}
}
- bsc_gsmnet->mgw.client = mgcp_client_init(bsc_gsmnet, bsc_gsmnet->mgw.conf);
-
- if (mgcp_client_connect(bsc_gsmnet->mgw.client)) {
- LOGP(DNM, LOGL_ERROR, "MGW connect failed at (%s:%u)\n",
- bsc_gsmnet->mgw.conf->remote_addr,
- bsc_gsmnet->mgw.conf->remote_port);
- exit(1);
- }
-
if (osmo_bsc_sigtran_init(&bsc_gsmnet->mscs) != 0) {
LOGP(DNM, LOGL_ERROR, "Failed to initialize sigtran backhaul.\n");
exit(1);
@@ -939,7 +1010,7 @@ int main(int argc, char **argv)
handover_decision_1_init();
hodec2_init(bsc_gsmnet);
bsc_cbc_link_restart();
- lb_init();
+ lb_start_or_stop();
signal(SIGINT, &signal_handler);
signal(SIGTERM, &signal_handler);
@@ -948,6 +1019,9 @@ int main(int argc, char **argv)
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
+ update_connection_stats_cb(NULL);
+ chan_counts_sig_init();
+
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
@@ -956,7 +1030,7 @@ int main(int argc, char **argv)
}
}
- while (1) {
+ while (!osmo_select_shutdown_done()) {
osmo_select_main_ctx(0);
}
diff --git a/src/osmo-bsc/osmo_bsc_msc.c b/src/osmo-bsc/osmo_bsc_msc.c
index 583b6ff23..08a2466e3 100644
--- a/src/osmo-bsc/osmo_bsc_msc.c
+++ b/src/osmo-bsc/osmo_bsc_msc.c
@@ -40,6 +40,7 @@
#include <osmocom/abis/ipa.h>
#include <osmocom/mgcp_client/mgcp_client.h>
+#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
@@ -53,8 +54,9 @@ static const struct rate_ctr_desc msc_ctr_description[] = {
[MSC_CTR_BSSMAP_RX_UDT_UNKNOWN] = {"bssmap:rx:udt:err_unknown", "Number of received BSSMAP unknown UDT messages"},
[MSC_CTR_BSSMAP_RX_DT1_CLEAR_CMD] = {"bssmap:rx:dt1:clear:cmd", "Number of received BSSMAP DT1 CLEAR CMD messages"},
[MSC_CTR_BSSMAP_RX_DT1_CIPHER_MODE_CMD] = {"bssmap:rx:dt1:cipher_mode:cmd", "Number of received BSSMAP DT1 CIPHER MODE CMD messages"},
- [MSC_CTR_BSSMAP_RX_DT1_ASSIGMENT_RQST] = {"bssmap:rx:dt1:assignment:rqst", "Number of received BSSMAP DT1 ASSIGMENT RQST messages"},
+ [MSC_CTR_BSSMAP_RX_DT1_ASSIGNMENT_RQST] = {"bssmap:rx:dt1:assignment:rqst", "Number of received BSSMAP DT1 ASSIGNMENT RQST messages"},
[MSC_CTR_BSSMAP_RX_DT1_LCLS_CONNECT_CTRL] = {"bssmap:rx:dt1:lcls_connect_ctrl:cmd", "Number of received BSSMAP DT1 LCLS CONNECT CTRL messages"},
+ [MSC_CTR_BSSMAP_RX_DT1_HANDOVER_RQST] = {"bssmap:rx:dt1:handover:rqst", "Number of received BSSMAP DT1 HANDOVER RQST messages"},
[MSC_CTR_BSSMAP_RX_DT1_HANDOVER_CMD] = {"bssmap:rx:dt1:handover:cmd", "Number of received BSSMAP DT1 HANDOVER CMD messages"},
[MSC_CTR_BSSMAP_RX_DT1_CLASSMARK_RQST] = {"bssmap:rx:dt1:classmark:rqst", "Number of received BSSMAP DT1 CLASSMARK RQST messages"},
[MSC_CTR_BSSMAP_RX_DT1_CONFUSION] = {"bssmap:rx:dt1:confusion", "Number of received BSSMAP DT1 CONFUSION messages"},
@@ -147,8 +149,8 @@ static const struct rate_ctr_group_desc msc_ctrg_desc = {
};
static const struct osmo_stat_item_desc msc_stat_desc[] = {
- { "msc_links:active", "Number of active MSC links", "", 16, 0 },
- { "msc_links:total", "Number of configured MSC links", "", 16, 0 },
+ [MSC_STAT_MSC_LINKS_ACTIVE] = { "msc_links:active", "Number of active MSC links", "", 16, 0 },
+ [MSC_STAT_MSC_LINKS_TOTAL] = { "msc_links:total", "Number of configured MSC links", "", 16, 0 },
};
static const struct osmo_stat_item_group_desc msc_statg_desc = {
@@ -162,23 +164,32 @@ static const struct osmo_stat_item_group_desc msc_statg_desc = {
int osmo_bsc_msc_init(struct bsc_msc_data *msc)
{
struct gsm_network *net = msc->network;
- uint16_t mgw_port;
+ struct mgcp_client *mgcp_cli;
int rc;
- if (net->mgw.conf->remote_port >= 0)
- mgw_port = net->mgw.conf->remote_port;
- else
- mgw_port = MGCP_CLIENT_REMOTE_PORT_DEFAULT;
+ /* Everything below refers to SCCP-Lite MSC connections only. */
+ if (msc_is_aoip(msc))
+ return 0;
+ /* Note: MGW is preselected here at startup, which means currently
+ * osmo-bsc configured for SCCPLite doesn't support MGW pools with more
+ * than 1 MGW.
+ */
+ mgcp_cli = mgcp_client_pool_get(net->mgw.mgw_pool);
+ OSMO_ASSERT(mgcp_cli);
rc = osmo_sock_init2_ofd(&msc->mgcp_ipa.ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
msc->mgcp_ipa.local_addr, msc->mgcp_ipa.local_port,
- net->mgw.conf->remote_addr, mgw_port,
+ mgcp_client_remote_addr_str(mgcp_cli),
+ mgcp_client_remote_port(mgcp_cli),
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
+ mgcp_client_pool_put(mgcp_cli);
if (rc < 0) {
LOGP(DMSC, LOGL_ERROR, "msc %u: Could not create/connect/bind MGCP proxy socket: %d\n",
msc->nr, rc);
return rc;
}
+ LOGP(DMSC, LOGL_INFO, "msc %u: Socket forwarding IPA-encapsulated MGCP messages towards MGW: %s\n",
+ msc->nr, osmo_sock_get_name2(msc->mgcp_ipa.ofd.fd));
return 0;
}
@@ -196,7 +207,6 @@ struct bsc_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr)
struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr)
{
struct bsc_msc_data *msc_data;
- unsigned int i;
/* check if there is already one */
msc_data = osmo_msc_data_find(net, nr);
@@ -229,8 +239,6 @@ struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr)
.mcc = GSM_MCC_MNC_INVALID,
.mnc = GSM_MCC_MNC_INVALID,
};
- msc_data->core_ci = -1;
- msc_data->core_lac = -1;
msc_data->nr = nr;
msc_data->allow_emerg = 1;
@@ -242,24 +250,16 @@ struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr)
/* Allow the full set of possible codecs by default */
msc_data->audio_length = 5;
- msc_data->audio_support =
- talloc_zero_array(msc_data, struct gsm_audio_support *,
- msc_data->audio_length);
- for (i = 0; i < msc_data->audio_length; i++) {
- msc_data->audio_support[i] =
- talloc_zero(msc_data->audio_support,
- struct gsm_audio_support);
- }
- msc_data->audio_support[0]->ver = 1;
- msc_data->audio_support[0]->hr = 0;
- msc_data->audio_support[1]->ver = 1;
- msc_data->audio_support[1]->hr = 1;
- msc_data->audio_support[2]->ver = 2;
- msc_data->audio_support[2]->hr = 0;
- msc_data->audio_support[3]->ver = 3;
- msc_data->audio_support[3]->hr = 0;
- msc_data->audio_support[4]->ver = 3;
- msc_data->audio_support[4]->hr = 1;
+ msc_data->audio_support[0].ver = 1;
+ msc_data->audio_support[0].hr = 0;
+ msc_data->audio_support[1].ver = 1;
+ msc_data->audio_support[1].hr = 1;
+ msc_data->audio_support[2].ver = 2;
+ msc_data->audio_support[2].hr = 0;
+ msc_data->audio_support[3].ver = 3;
+ msc_data->audio_support[3].hr = 0;
+ msc_data->audio_support[4].ver = 3;
+ msc_data->audio_support[4].hr = 1;
osmo_fd_setup(&msc_data->mgcp_ipa.ofd, -1, OSMO_FD_READ, &bsc_sccplite_mgcp_proxy_cb, msc_data, 0);
msc_data->mgcp_ipa.local_addr = NULL; /* = INADDR(6)_ANY */
@@ -285,8 +285,8 @@ struct osmo_cell_global_id *cgi_for_msc(struct bsc_msc_data *msc, struct gsm_bts
cgi.lai.plmn.mnc = msc->core_plmn.mnc;
cgi.lai.plmn.mnc_3_digits = msc->core_plmn.mnc_3_digits;
}
- cgi.lai.lac = (msc->core_lac != -1) ? msc->core_lac : bts->location_area_code;
- cgi.cell_identity = (msc->core_ci != -1) ? msc->core_ci : bts->cell_identity;
+ cgi.lai.lac = bts->location_area_code;
+ cgi.cell_identity = bts->cell_identity;
return &cgi;
}
diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c
index 7893f2747..26e07d2f2 100644
--- a/src/osmo-bsc/osmo_bsc_sigtran.c
+++ b/src/osmo-bsc/osmo_bsc_sigtran.c
@@ -35,6 +35,7 @@
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/paging.h>
#include <osmocom/mgcp_client/mgcp_common.h>
/* A pointer to a list with all involved MSCs
@@ -80,20 +81,20 @@ static void _gsm0808_extend_announce_osmux(struct msgb *msg)
}
/* Send reset to MSC */
-static void osmo_bsc_sigtran_tx_reset(const struct bsc_msc_data *msc)
+void osmo_bsc_sigtran_tx_reset(const struct bsc_msc_data *msc)
{
struct osmo_ss7_instance *ss7;
struct msgb *msg;
ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
OSMO_ASSERT(ss7);
- LOGP(DMSC, LOGL_NOTICE, "Sending RESET to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
+ LOGP(DRESET, LOGL_INFO, "Sending RESET to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
msg = gsm0808_create_reset();
if (msc_is_aoip(msc) && msc->use_osmux != OSMUX_USAGE_OFF)
_gsm0808_extend_announce_osmux(msg);
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_UDT_RESET]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_BSSMAP_TX_UDT_RESET));
osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr,
&msc->a.msc_addr, msg);
}
@@ -107,13 +108,13 @@ void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc)
ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
OSMO_ASSERT(ss7);
- LOGP(DMSC, LOGL_NOTICE, "Sending RESET ACK to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
+ LOGP(DRESET, LOGL_NOTICE, "Sending RESET ACK to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
msg = gsm0808_create_reset_ack();
if (msc_is_aoip(msc) && msc->use_osmux != OSMUX_USAGE_OFF)
_gsm0808_extend_announce_osmux(msg);
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_UDT_RESET_ACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_BSSMAP_TX_UDT_RESET_ACK));
osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr,
&msc->a.msc_addr, msg);
}
@@ -311,7 +312,8 @@ enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, s
}
/* Open a new connection oriented sigtran connection */
-int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg)
+/* Allow test to overwrite it */
+__attribute__((weak)) int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
struct osmo_ss7_instance *ss7;
struct bsc_msc_data *msc;
@@ -373,26 +375,26 @@ int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *m
if (msg->len >= 3) {
switch (msg->data[0]) {
case BSSAP_MSG_BSS_MANAGEMENT:
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_BSS_MANAGEMENT]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_BSSMAP_TX_BSS_MANAGEMENT));
LOGP(DMSC, LOGL_INFO, "Tx MSC: BSSMAP: %s\n",
gsm0808_bssmap_name(msg->data[2]));
break;
case BSSAP_MSG_DTAP:
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DTAP]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DTAP));
LOGP(DMSC, LOGL_INFO, "Tx MSC: DTAP\n");
break;
default:
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_UNKNOWN]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_BSSMAP_TX_UNKNOWN));
LOGP(DMSC, LOGL_ERROR, "Tx MSC: unknown message type: 0x%x\n",
msg->data[0]);
}
} else {
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_SHORT]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_BSSMAP_TX_SHORT));
LOGP(DMSC, LOGL_ERROR, "Tx MSC: message too short: %u\n", msg->len);
}
if (a_reset_conn_ready(msc) == false) {
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_ERR_CONN_NOT_READY]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_BSSMAP_TX_ERR_CONN_NOT_READY));
LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n");
msgb_free(msg);
return -EINVAL;
@@ -407,19 +409,22 @@ int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *m
rc = osmo_sccp_tx_data_msg(msc->a.sccp_user, conn_id, msg);
if (rc >= 0)
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_SUCCESS]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_BSSMAP_TX_SUCCESS));
else
- rate_ctr_inc(&msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_ERR_SEND]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(msc->msc_ctrs, MSC_CTR_BSSMAP_TX_ERR_SEND));
return rc;
}
/* 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)
{
struct gsm_subscriber_connection *conn, *conn_temp;
OSMO_ASSERT(msc);
+ /* Drop all ongoing paging requests that this MSC has created on any BTS */
+ paging_flush_network(msc->network, msc);
+
/* Close all open connections */
llist_for_each_entry_safe(conn, conn_temp, &bsc_gsmnet->subscr_conns, entry) {
@@ -434,18 +439,6 @@ void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc)
}
}
-/* Callback function: Close all open connections */
-static void osmo_bsc_sigtran_reset_cb(const void *priv)
-{
- struct bsc_msc_data *msc = (struct bsc_msc_data*) priv;
-
- /* Shut down all ongoing traffic */
- osmo_bsc_sigtran_reset(msc);
-
- /* Send reset to MSC */
- osmo_bsc_sigtran_tx_reset(msc);
-}
-
/* Default point-code to be used as local address (BSC) */
#define BSC_DEFAULT_PC "0.23.3"
@@ -619,7 +612,7 @@ int osmo_bsc_sigtran_init(struct llist_head *mscs)
return -EINVAL;
/* Start MSC-Reset procedure */
- a_reset_alloc(msc, msc_name, osmo_bsc_sigtran_reset_cb);
+ a_reset_alloc(msc, msc_name);
}
}
diff --git a/src/osmo-bsc/paging.c b/src/osmo-bsc/paging.c
index 15aca00ea..e6f6fe4ef 100644
--- a/src/osmo-bsc/paging.c
+++ b/src/osmo-bsc/paging.c
@@ -54,21 +54,52 @@
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_stats.h>
void *tall_paging_ctx = NULL;
-#define PAGING_TIMER 0, 500000
+/* How many paging requests to Tx on RSL at max before going back to main loop */
+#define MAX_PAGE_REQ_PER_ITER 10
+
+/* How often to attempt sending new paging requests (initial, not retrans): 250ms */
+static const struct timespec initial_period = {
+ .tv_sec = 0,
+ .tv_nsec = 250 * 1000 * 1000,
+};
+
+/* Minimum period between retransmits of paging req to a subscriber: 500ms */
+static const struct timespec retrans_period = {
+ .tv_sec = 0,
+ .tv_nsec = 500 * 1000 * 1000,
+};
+
+/* If no CCCH Lod Ind is received before this time period, the BTS is considered
+ * to have stopped sending CCCH Load Indication, probaby due to being under Load
+ * Threshold: */
+#define bts_no_ccch_load_ind_timeout_sec(bts) ((bts)->ccch_load_ind_period * 2)
/*
* Kill one paging request update the internal list...
*/
-static void paging_remove_request(struct gsm_bts_paging_state *paging_bts,
- struct gsm_paging_request *to_be_deleted)
+static void paging_remove_request(struct gsm_paging_request *req)
{
- osmo_timer_del(&to_be_deleted->T3113);
- llist_del(&to_be_deleted->entry);
- bsc_subscr_put(to_be_deleted->bsub, BSUB_USE_PAGING_REQUEST);
- talloc_free(to_be_deleted);
+ struct gsm_bts *bts = req->bts;
+ struct gsm_bts_paging_state *bts_pag_st = &bts->paging;
+
+ osmo_timer_del(&req->T3113);
+ llist_del(&req->entry);
+ if (req->attempts == 0) {
+ bts_pag_st->initial_req_list_len--;
+ bts_pag_st->initial_req_pgroup_counts[req->pgroup]--;
+ } else {
+ bts_pag_st->retrans_req_list_len--;
+ }
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_REQ_QUEUE_LENGTH), 1);
+ bsc_subscr_remove_active_paging_request(req->bsub, req);
+ talloc_free(req);
+
+ if (llist_empty(&bts_pag_st->initial_req_list) && llist_empty(&bts_pag_st->retrans_req_list))
+ osmo_timer_del(&bts_pag_st->work_timer);
}
static void page_ms(struct gsm_paging_request *request)
@@ -79,9 +110,9 @@ static void page_ms(struct gsm_paging_request *request)
log_set_context(LOG_CTX_BSC_SUBSCR, request->bsub);
- LOG_BTS(bts, DPAG, LOGL_INFO, "Going to send paging commands: %s"
- " for ch. type %d (attempt %d)\n", bsc_subscr_name(request->bsub),
- request->chan_type, request->attempts);
+ LOG_PAGING_BTS(request, bts, DPAG, LOGL_INFO,
+ "Going to send paging command for ch. type %d (attempt %d)\n",
+ request->chan_type, request->attempts);
if (request->bsub->tmsi == GSM_RESERVED_TMSI) {
mi = (struct osmo_mobile_identity){
@@ -101,24 +132,35 @@ static void page_ms(struct gsm_paging_request *request)
log_set_context(LOG_CTX_BSC_SUBSCR, NULL);
}
+static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts);
+
static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts)
{
- if (llist_empty(&paging_bts->pending_requests))
- return;
-
+ /* paging_handle_pending_requests() will schedule work_timer if work
+ * needs to be partitioned in several iterations. */
if (!osmo_timer_pending(&paging_bts->work_timer))
- osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER);
+ paging_handle_pending_requests(paging_bts);
}
+/* Placeholder to set the value and update the related osmo_stat: */
+static void paging_set_available_slots(struct gsm_bts *bts, uint16_t available_slots)
+{
+ bts->paging.available_slots = available_slots;
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_AVAILABLE_SLOTS), available_slots);
+}
-static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts);
static void paging_give_credit(void *data)
{
- struct gsm_bts_paging_state *paging_bts = data;
-
- LOG_BTS(paging_bts->bts, DPAG, LOGL_NOTICE, "No PCH LOAD IND, adding 20 slots)\n");
- paging_bts->available_slots = 20;
- paging_handle_pending_requests(paging_bts);
+ struct gsm_bts_paging_state *paging_bts_st = data;
+ struct gsm_bts *bts = paging_bts_st->bts;
+ unsigned int load_ind_timeout = bts_no_ccch_load_ind_timeout_sec(bts);
+ uint16_t estimated_slots = paging_estimate_available_slots(bts, load_ind_timeout);
+ LOG_BTS(bts, DPAG, LOGL_INFO,
+ "Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots %u -> %u)\n",
+ paging_bts_st->available_slots, estimated_slots);
+ paging_set_available_slots(bts, estimated_slots);
+ paging_schedule_if_needed(paging_bts_st);
+ osmo_timer_schedule(&bts->paging.credit_timer, load_ind_timeout, 0);
}
/*! count the number of free channels for given RSL channel type required
@@ -171,6 +213,149 @@ count_tch:
return bts->paging.free_chans_need > count;
}
+static void paging_req_timeout_retrans(struct gsm_paging_request *request, const struct timespec *now)
+{
+ struct gsm_bts_paging_state *bts_pag_st = &request->bts->paging;
+ page_ms(request);
+ paging_set_available_slots(request->bts, bts_pag_st->available_slots - 1);
+
+ if (request->attempts == 0) {
+ /* req is removed from initial_req_list and inserted into retrans_req_list, update list lengths: */
+ bts_pag_st->initial_req_list_len--;
+ bts_pag_st->initial_req_pgroup_counts[request->pgroup]--;
+ bts_pag_st->retrans_req_list_len++;
+ }
+ llist_del(&request->entry);
+ llist_add_tail(&request->entry, &bts_pag_st->retrans_req_list);
+
+ request->last_attempt_ts = *now;
+ request->attempts++;
+}
+
+/* Returns number of paged initial requests (up to max_page_req_per_iter).
+ * Returning work_done=false means the work timer has been scheduled internally and the caller should avoid processing
+ * further requests right now.
+ */
+static unsigned int step_page_initial_reqs(struct gsm_bts_paging_state *bts_pag_st, unsigned int max_page_req_per_iter,
+ const struct timespec *now, bool *work_done)
+{
+ struct gsm_paging_request *request, *request2;
+ unsigned int num_paged = 0;
+
+ llist_for_each_entry_safe(request, request2, &bts_pag_st->initial_req_list, entry) {
+ /* We run out of available slots. Wait until next CCCH Load Ind
+ * arrives or credit_timer triggers to keep processing requests.
+ */
+ if (bts_pag_st->available_slots == 0) {
+ LOG_PAGING_BTS(request, request->bts, DPAG, LOGL_INFO,
+ "Paging delayed: waiting for available slots at BTS\n");
+ *work_done = false;
+ return num_paged;
+ }
+
+ if (num_paged == max_page_req_per_iter) {
+ goto sched_next_iter;
+ }
+
+ /* we need to determine the number of free channels */
+ if (bts_pag_st->free_chans_need != -1 &&
+ can_send_pag_req(request->bts, request->chan_type) != 0) {
+ LOG_PAGING_BTS(request, request->bts, DPAG, LOGL_INFO,
+ "Paging delayed: not enough free channels (<%d)\n",
+ bts_pag_st->free_chans_need);
+ goto sched_next_iter;
+ }
+
+ /* handle the paging request now */
+ paging_req_timeout_retrans(request, now);
+ num_paged++;
+ }
+
+ *work_done = true;
+ return num_paged;
+
+sched_next_iter:
+ LOG_BTS(bts_pag_st->bts, DPAG, LOGL_DEBUG, "Scheduling next batch in %lld.%06lds (available_slots=%u)\n",
+ (long long)initial_period.tv_sec, initial_period.tv_nsec / 1000,
+ bts_pag_st->available_slots);
+ osmo_timer_schedule(&bts_pag_st->work_timer, initial_period.tv_sec, initial_period.tv_nsec / 1000);
+ *work_done = false;
+ return num_paged;
+}
+
+static unsigned int step_page_retrans_reqs(struct gsm_bts_paging_state *bts_pag_st, unsigned int max_page_req_per_iter,
+ const struct timespec *now)
+{
+ struct gsm_paging_request *request, *initial_request;
+ unsigned int num_paged = 0;
+ struct timespec retrans_ts;
+
+ /* do while loop: Try send at most first max_page_req_per_iter paging
+ * requests. Since transmitted requests are re-appended at the end of
+ * the list, we check until we find the first req again, in order to
+ * avoid retransmitting repeated requests until next time paging is
+ * scheduled. */
+ initial_request = llist_first_entry_or_null(&bts_pag_st->retrans_req_list,
+ struct gsm_paging_request, entry);
+ if (!initial_request)
+ return num_paged;
+
+ request = initial_request;
+ do {
+ /* We run out of available slots. Wait until next CCCH Load Ind
+ * arrives or credit_timer triggers to keep processing requests.
+ */
+ if (bts_pag_st->available_slots == 0) {
+ LOG_PAGING_BTS(request, request->bts, DPAG, LOGL_INFO,
+ "Paging delayed: waiting for available slots at BTS\n");
+ return num_paged;
+ }
+
+ /* we need to determine the number of free channels */
+ if (bts_pag_st->free_chans_need != -1 &&
+ can_send_pag_req(request->bts, request->chan_type) != 0) {
+ LOG_PAGING_BTS(request, request->bts, DPAG, LOGL_INFO,
+ "Paging delayed: not enough free channels (<%d)\n",
+ bts_pag_st->free_chans_need);
+ goto sched_next_iter;
+ }
+
+ /* Check if time to retransmit has elapsed. Otherwise, wait until its time to retransmit. */
+ timespecadd(&request->last_attempt_ts, &retrans_period, &retrans_ts);
+ if (timespeccmp(now, &retrans_ts, <)) {
+ struct timespec tdiff;
+ timespecsub(&retrans_ts, now, &tdiff);
+ LOG_PAGING_BTS(request, request->bts, DPAG, LOGL_DEBUG,
+ "Paging delayed: retransmission happens in %lld.%06lds\n",
+ (long long)tdiff.tv_sec, tdiff.tv_nsec / 1000);
+ osmo_timer_schedule(&bts_pag_st->work_timer, tdiff.tv_sec, tdiff.tv_nsec / 1000);
+ return num_paged;
+ }
+
+ if (num_paged >= max_page_req_per_iter)
+ goto sched_next_iter;
+
+ /* handle the paging request now */
+ paging_req_timeout_retrans(request, now);
+ num_paged++;
+
+ request = llist_first_entry(&bts_pag_st->retrans_req_list,
+ struct gsm_paging_request, entry);
+ } while (request != initial_request);
+
+ /* Reaching this code paths means all retrans request have been scheduled (and intial_req_list is empty).
+ * Hence, reeschedule ourselves to now + retrans_period. */
+ osmo_timer_schedule(&bts_pag_st->work_timer, retrans_period.tv_sec, retrans_period.tv_nsec / 1000);
+ return num_paged;
+
+sched_next_iter:
+ LOG_BTS(bts_pag_st->bts, DPAG, LOGL_DEBUG, "Scheduling next batch in %lld.%06lds (available_slots=%u)\n",
+ (long long)initial_period.tv_sec, initial_period.tv_nsec / 1000,
+ bts_pag_st->available_slots);
+ osmo_timer_schedule(&bts_pag_st->work_timer, initial_period.tv_sec, initial_period.tv_nsec / 1000);
+ return num_paged;
+}
+
/*
* This is kicked by the periodic PAGING LOAD Indicator
* coming from abis_rsl.c
@@ -180,54 +365,30 @@ count_tch:
*/
static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts)
{
- struct gsm_paging_request *request = NULL;
+ unsigned int num_paged_initial, num_paged_retrans = 0;
+ unsigned int max_page_req_per_iter = MAX_PAGE_REQ_PER_ITER;
+ struct timespec now;
+ bool work_done = false;
/*
* Determine if the pending_requests list is empty and
* return then.
*/
- if (llist_empty(&paging_bts->pending_requests)) {
- /* since the list is empty, no need to reschedule the timer */
- return;
- }
-
- /*
- * In case the BTS does not provide us with load indication and we
- * ran out of slots, call an autofill routine. It might be that the
- * BTS did not like our paging messages and then we have counted down
- * to zero and we do not get any messages.
- */
- if (paging_bts->available_slots == 0) {
- osmo_timer_setup(&paging_bts->credit_timer, paging_give_credit,
- paging_bts);
- osmo_timer_schedule(&paging_bts->credit_timer, 5, 0);
+ if (llist_empty(&paging_bts->initial_req_list) &&
+ llist_empty(&paging_bts->retrans_req_list)) {
+ /* since the lists are empty, no need to reschedule the timer */
return;
}
- request = llist_entry(paging_bts->pending_requests.next,
- struct gsm_paging_request, entry);
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ paging_bts->last_sched_ts = now;
- /* we need to determine the number of free channels */
- if (paging_bts->free_chans_need != -1) {
- if (can_send_pag_req(request->bts, request->chan_type) != 0)
- goto skip_paging;
- }
+ num_paged_initial = step_page_initial_reqs(paging_bts, max_page_req_per_iter, &now, &work_done);
+ if (work_done) /* All work done for initial requests, work on retransmissions now: */
+ num_paged_retrans = step_page_retrans_reqs(paging_bts, max_page_req_per_iter - num_paged_initial, &now);
- /* Skip paging if the bts is down. */
- if (!request->bts->oml_link)
- goto skip_paging;
-
- /* handle the paging request now */
- page_ms(request);
- paging_bts->available_slots--;
- request->attempts++;
-
- /* take the current and add it to the back */
- llist_del(&request->entry);
- llist_add_tail(&request->entry, &paging_bts->pending_requests);
-
-skip_paging:
- osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER);
+ LOG_BTS(paging_bts->bts, DPAG, LOGL_DEBUG, "Paged %u subscribers (%u initial, %u retrans) during last iteration\n",
+ num_paged_initial + num_paged_retrans, num_paged_initial, num_paged_retrans);
}
static void paging_worker(void *data)
@@ -238,36 +399,23 @@ static void paging_worker(void *data)
}
/*! initialize the bts paging state, if it hasn't been initialized yet */
-static void paging_init_if_needed(struct gsm_bts *bts)
+void paging_init(struct gsm_bts *bts)
{
- if (bts->paging.bts)
- return;
-
bts->paging.bts = bts;
-
- /* This should be initialized only once. There is currently no code that sets bts->paging.bts
- * back to NULL, so let's just assert this one instead of graceful handling. */
- OSMO_ASSERT(llist_empty(&bts->paging.pending_requests));
-
- osmo_timer_setup(&bts->paging.work_timer, paging_worker,
- &bts->paging);
-
- /* Large number, until we get a proper message */
- bts->paging.available_slots = 20;
+ bts->paging.free_chans_need = -1;
+ paging_set_available_slots(bts, 0);
+ INIT_LLIST_HEAD(&bts->paging.initial_req_list);
+ INIT_LLIST_HEAD(&bts->paging.retrans_req_list);
+ osmo_timer_setup(&bts->paging.work_timer, paging_worker, &bts->paging);
+ osmo_timer_setup(&bts->paging.credit_timer, paging_give_credit, &bts->paging);
}
-/*! do we have any pending paging requests for given subscriber? */
-static int paging_pending_request(struct gsm_bts_paging_state *bts,
- struct bsc_subscr *bsub)
+/* Called upon the bts struct being freed */
+void paging_destructor(struct gsm_bts *bts)
{
- struct gsm_paging_request *req;
-
- llist_for_each_entry(req, &bts->pending_requests, entry) {
- if (bsub == req->bsub)
- return 1;
- }
-
- return 0;
+ paging_flush_bts(bts, NULL);
+ osmo_timer_del(&bts->paging.credit_timer);
+ osmo_timer_del(&bts->paging.work_timer);
}
/*! Call-back once T3113 (paging timeout) expires for given paging_request */
@@ -277,45 +425,72 @@ static void paging_T3113_expired(void *data)
log_set_context(LOG_CTX_BSC_SUBSCR, req->bsub);
- LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n",
- req, bsc_subscr_name(req->bsub));
+ LOG_PAGING_BTS(req, req->bts, DPAG, LOGL_INFO, "T3113 expired\n");
/* must be destroyed before calling cbfn, to prevent double free */
- rate_ctr_inc(&req->bts->bts_ctrs->ctr[BTS_CTR_PAGING_EXPIRED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(req->bts->bts_ctrs, BTS_CTR_PAGING_EXPIRED));
+
+ /* If last BTS paging times out (active_paging_requests will be
+ * decremented in paging_remove_request below): */
+ if (req->bsub->active_paging_requests_len == 1)
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->bsc_ctrs, BSC_CTR_PAGING_EXPIRED));
/* destroy it now. Do not access req afterwards */
- paging_remove_request(&req->bts->paging, req);
+ paging_remove_request(req);
log_set_context(LOG_CTX_BSC_SUBSCR, NULL);
}
-#define GSM_FRAME_DURATION_us 4615
-#define GSM51_MFRAME_DURATION_us (51 * GSM_FRAME_DURATION_us) /* 235365 us */
-static unsigned int calculate_timer_3113(struct gsm_bts *bts)
+#define GSM51_MFRAME_DURATION_us (51 * GSM_TDMA_FN_DURATION_uS) /* 235365 us */
+static unsigned int paging_estimate_delay_us(struct gsm_bts *bts, unsigned int num_reqs,
+ unsigned int num_reqs_same_pgroup);
+
+static unsigned int calculate_timer_3113(struct gsm_paging_request *req, unsigned int reqs_before,
+ unsigned int reqs_before_same_pgroup, unsigned int max_dynamic_value)
{
- unsigned int to_us, to;
+ unsigned int to_us, estimated_to, to;
+ struct gsm_bts *bts = req->bts;
struct osmo_tdef *d = osmo_tdef_get_entry(bts->network->T_defs, 3113);
+ unsigned int rach_max_trans, rach_tx_integer, bs_pa_mfrms;
/* Note: d should always contain a valid pointer since all timers,
* including 3113 are statically pre-defined in
* struct osmo_tdef gsm_network_T_defs. */
OSMO_ASSERT(d);
- if (!bts->T3113_dynamic)
- return d->val;
-
- /* TODO: take into account load of paging group for req->bsub */
+ if (!bts->T3113_dynamic) {
+ to = d->val;
+ goto ret;
+ }
/* MFRMS defines repeat interval of paging messages for MSs that belong
* to same paging group across multiple 51 frame multiframes.
- * MAXTRANS defines maximum number of RACH retransmissions.
+ * MAXTRANS defines maximum number of RACH retransmissions, spread over
+ * TXINTEGER slots.
*/
- to_us = GSM51_MFRAME_DURATION_us * (bts->si_common.chan_desc.bs_pa_mfrms + 2) *
- bts->si_common.rach_control.max_trans;
+ rach_max_trans = rach_max_trans_raw2val(bts->si_common.rach_control.max_trans);
+ rach_tx_integer = rach_tx_integer_raw2val(bts->si_common.rach_control.tx_integer);
+ bs_pa_mfrms = (bts->si_common.chan_desc.bs_pa_mfrms + 2);
+ to_us = GSM51_MFRAME_DURATION_us * bs_pa_mfrms +
+ GSM_TDMA_FN_DURATION_uS * rach_tx_integer * rach_max_trans;
+
+ /* Now add some extra time based on how many requests need to be transmitted before this one: */
+ to_us += paging_estimate_delay_us(bts, reqs_before, reqs_before_same_pgroup);
/* ceiling in seconds + extra time */
- to = (to_us + 999999) / 1000000 + d->val;
- LOG_BTS(bts, DPAG, LOGL_DEBUG, "Paging request: T3113 expires in %u seconds\n", to);
+ estimated_to = (to_us + 999999) / 1000000 + d->val;
+
+ /* upper bound: see X3113, PAGING_THRESHOLD_X3113_DEFAULT_SEC */
+ if (estimated_to > max_dynamic_value)
+ to = max_dynamic_value;
+ else
+ to = estimated_to;
+
+ LOG_PAGING_BTS(req, bts, DPAG, LOGL_DEBUG,
+ "Paging request: T3113 expires in %u seconds (estimated %u)\n",
+ to, estimated_to);
+ret:
+ osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_T3113), to);
return to;
}
@@ -330,29 +505,83 @@ static int _paging_request(const struct bsc_paging_params *params, struct gsm_bt
struct gsm_bts_paging_state *bts_entry = &bts->paging;
struct gsm_paging_request *req;
unsigned int t3113_timeout_s;
+ unsigned int x3113_s = osmo_tdef_get(bts->network->T_defs, -3113, OSMO_TDEF_S, -1);
+ uint8_t pgroup;
+ unsigned int reqs_before, reqs_before_same_pgroup;
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_PAGING_ATTEMPTED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_PAGING_ATTEMPTED));
- if (paging_pending_request(bts_entry, params->bsub)) {
+ /* Find if we already have one for the given subscriber on this BTS: */
+ if (bsc_subscr_find_req_by_bts(params->bsub, bts)) {
LOG_PAGING_BTS(params, bts, DPAG, LOGL_INFO, "Paging request already pending for this subscriber\n");
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_PAGING_ALREADY]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_PAGING_ALREADY));
return -EEXIST;
}
+ /* Don't try to queue more requests than we can realistically handle within X3113 seconds,
+ * see PAGING_THRESHOLD_X3113_DEFAULT_SEC. */
+ if (paging_pending_requests_nr(bts) > paging_estimate_available_slots(bts, x3113_s)) {
+ struct gsm_paging_request *first_retrans_req;
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_PAGING_OVERLOAD));
+ /* Need to drop a retrans from the queue if possible, in order to make space for the new initial req. */
+ if (bts_entry->retrans_req_list_len == 0) {
+ /* There are no retrans to be replaced by this initial request, discard it. */
+ return -ENOSPC;
+ }
+ first_retrans_req = llist_first_entry(&bts_entry->retrans_req_list, struct gsm_paging_request, entry);
+ paging_remove_request(first_retrans_req);
+ }
+
+ pgroup = gsm0502_calc_paging_group(&bts->si_common.chan_desc, str_to_imsi(params->bsub->imsi));
+ reqs_before = bts_entry->initial_req_list_len;
+ reqs_before_same_pgroup = bts_entry->initial_req_pgroup_counts[pgroup];
+
LOG_PAGING_BTS(params, bts, DPAG, LOGL_DEBUG, "Start paging\n");
req = talloc_zero(tall_paging_ctx, struct gsm_paging_request);
OSMO_ASSERT(req);
req->reason = params->reason;
req->bsub = params->bsub;
- bsc_subscr_get(req->bsub, BSUB_USE_PAGING_REQUEST);
req->bts = bts;
req->chan_type = params->chan_needed;
+ req->pgroup = pgroup;
req->msc = params->msc;
osmo_timer_setup(&req->T3113, paging_T3113_expired, req);
- t3113_timeout_s = calculate_timer_3113(bts);
+ bsc_subscr_add_active_paging_request(req->bsub, req);
+
+ bts_entry->initial_req_list_len++;
+ bts_entry->initial_req_pgroup_counts[req->pgroup]++;
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_REQ_QUEUE_LENGTH), 1);
+ llist_add_tail(&req->entry, &bts_entry->initial_req_list);
+
+ t3113_timeout_s = calculate_timer_3113(req, reqs_before, reqs_before_same_pgroup, x3113_s);
osmo_timer_schedule(&req->T3113, t3113_timeout_s, 0);
- llist_add_tail(&req->entry, &bts_entry->pending_requests);
- paging_schedule_if_needed(bts_entry);
+
+ /* Trigger scheduler if needed: */
+ if (!osmo_timer_pending(&bts_entry->work_timer)) {
+ paging_handle_pending_requests(bts_entry);
+ } else if (bts_entry->initial_req_list_len == 1) {
+ /* Worker timer is armed -> there was already one req before
+ * bts_entry->initial_req_list_len == 1 -> There were no initial requests
+ * in the list, aka the timer is waiting for retransmition,
+ * which is a longer period.
+ * Let's recaculate the time to adapt it to initial_period: */
+ struct timespec now, elapsed, tdiff;
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecsub(&now, &bts_entry->last_sched_ts, &elapsed);
+ if (timespeccmp(&elapsed, &initial_period, <)) {
+ timespecsub(&initial_period, &elapsed, &tdiff);
+ } else {
+ tdiff = (struct timespec){.tv_sec = 0, .tv_nsec = 0 };
+ }
+ LOG_PAGING_BTS(req, req->bts, DPAG, LOGL_DEBUG,
+ "New req arrived: re-scheduling next batch in %lld.%06lds\n",
+ (long long)tdiff.tv_sec, tdiff.tv_nsec / 1000);
+ /* Avoid scheduling timer for short periods, run cb directly: */
+ if (tdiff.tv_sec == 0 && tdiff.tv_nsec < 5000)
+ paging_worker(bts_entry);
+ else
+ osmo_timer_schedule(&bts_entry->work_timer, tdiff.tv_sec, tdiff.tv_nsec / 1000);
+ } /* else: worker is already ongoing submitting initial requests, nothing do be done */
return 0;
}
@@ -371,9 +600,6 @@ int paging_request_bts(const struct bsc_paging_params *params, struct gsm_bts *b
if (!trx_is_usable(bts->c0))
return 0;
- /* maybe it is the first time we use it */
- paging_init_if_needed(bts);
-
/* Trigger paging, pass any error to the caller */
rc = _paging_request(params, bts);
if (rc < 0)
@@ -381,128 +607,100 @@ int paging_request_bts(const struct bsc_paging_params *params, struct gsm_bts *b
return 1;
}
-/*! Stop paging a given subscriber on a given BTS.
- * \param[out] returns the MSC that paged the subscriber, if any.
- * \param[out] returns the reason for a pending paging, if any.
- * \param[in] bts BTS which has received a paging response.
- * \param[in] bsub subscriber.
- * \returns number of pending pagings.
- */
-static int paging_request_stop_bts(struct bsc_msc_data **msc_p, enum bsc_paging_reason *reason_p,
- struct gsm_bts *bts, struct bsc_subscr *bsub)
-{
- struct gsm_bts_paging_state *bts_entry = &bts->paging;
- struct gsm_paging_request *req, *req2;
-
- *msc_p = NULL;
- *reason_p = BSC_PAGING_NONE;
-
- paging_init_if_needed(bts);
-
- llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests,
- entry) {
- if (req->bsub != bsub)
- continue;
- *msc_p = req->msc;
- *reason_p = req->reason;
- LOG_BTS(bts, DPAG, LOGL_DEBUG, "Stop paging %s\n", bsc_subscr_name(bsub));
- paging_remove_request(&bts->paging, req);
- return 1;
- }
-
- return 0;
-}
-
/*! Stop paging on all cells and return the MSC that paged (if any) and all pending paging reasons.
* \param[out] returns the MSC that paged the subscriber, if there was a pending request.
* \param[out] returns the ORed bitmask of all reasons of pending pagings.
* \param[in] bts BTS which has received a paging response
* \param[in] bsub subscriber
- * \returns number of pending pagings.
*/
-int paging_request_stop(struct bsc_msc_data **msc_p, enum bsc_paging_reason *reasons_p,
+void paging_request_stop(struct bsc_msc_data **msc_p, enum bsc_paging_reason *reasons_p,
struct gsm_bts *bts, struct bsc_subscr *bsub)
{
- struct gsm_bts *bts_i;
- struct bsc_msc_data *paged_from_msc;
- int count;
- enum bsc_paging_reason reasons;
+ struct bsc_msc_data *paged_from_msc = NULL;
+ enum bsc_paging_reason reasons = BSC_PAGING_NONE;
OSMO_ASSERT(bts);
-
- count = paging_request_stop_bts(&paged_from_msc, &reasons, bts, bsub);
- if (paged_from_msc) {
- count++;
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_PAGING_RESPONDED]);
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_RESPONDED]);
+ struct gsm_paging_request *req = bsc_subscr_find_req_by_bts(bsub, bts);
+
+ /* Avoid accessing bsub after reaching 0 active_paging_request_len,
+ * since it could be freed during put(): */
+ unsigned remaining = bsub->active_paging_requests_len;
+
+ if (req) {
+ paged_from_msc = req->msc;
+ reasons = req->reason;
+ LOG_PAGING_BTS(req, bts, DPAG, LOGL_DEBUG, "Stop paging\n");
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_PAGING_RESPONDED));
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->network->bsc_ctrs, BSC_CTR_PAGING_RESPONDED));
+ paging_remove_request(req);
+ remaining--;
}
- llist_for_each_entry(bts_i, &bsc_gsmnet->bts_list, list) {
- struct bsc_msc_data *paged_from_msc2;
- enum bsc_paging_reason reason2;
- count += paging_request_stop_bts(&paged_from_msc2, &reason2, bts_i, bsub);
- if (paged_from_msc2) {
- reasons |= reason2;
- if (!paged_from_msc) {
- /* If this happened, it would be a bit weird: it means there was no Paging Request
- * pending on the BTS that sent the Paging Reponse, but there *is* a Paging Request
- * pending on a different BTS. But why not return an MSC when we found one. */
- paged_from_msc = paged_from_msc2;
- }
+ while (remaining > 0) {
+ struct gsm_paging_request *req;
+ req = llist_first_entry(&bsub->active_paging_requests,
+ struct gsm_paging_request, bsub_entry);
+ LOG_PAGING_BTS(req, req->bts, DPAG, LOGL_DEBUG, "Stop paging\n");
+ reasons |= req->reason;
+ if (!paged_from_msc) {
+ /* If this happened, it would be a bit weird: it means there was no Paging Request
+ * pending on the BTS that sent the Paging Response, but there *is* a Paging Request
+ * pending on a different BTS. But why not return an MSC when we found one. */
+ paged_from_msc = req->msc;
}
+ paging_remove_request(req);
+ remaining--;
}
*msc_p = paged_from_msc;
*reasons_p = reasons;
-
- return count;
}
/* Remove all paging requests, for specific reasons only. */
-int paging_request_cancel(struct bsc_subscr *bsub, enum bsc_paging_reason reasons)
+void paging_request_cancel(struct bsc_subscr *bsub, enum bsc_paging_reason reasons)
{
- struct gsm_bts *bts;
- int count = 0;
-
- llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
- struct gsm_paging_request *req, *req2;
+ struct gsm_paging_request *req, *req2;
+ OSMO_ASSERT(bsub);
- paging_init_if_needed(bts);
+ /* Avoid accessing bsub after reaching 0 active_paging_request_len,
+ * since it could be freed during put(): */
+ unsigned remaining = bsub->active_paging_requests_len;
- llist_for_each_entry_safe(req, req2, &bts->paging.pending_requests, entry) {
- if (req->bsub != bsub)
- continue;
- if (!(req->reason & reasons))
- continue;
- LOG_BTS(bts, DPAG, LOGL_DEBUG, "Cancel paging %s\n", bsc_subscr_name(bsub));
- paging_remove_request(&bts->paging, req);
- count++;
+ llist_for_each_entry_safe(req, req2, &bsub->active_paging_requests, bsub_entry) {
+ if (!(req->reason & reasons))
+ continue;
+ LOG_PAGING_BTS(req, req->bts, DPAG, LOGL_DEBUG, "Cancel paging reasons=0x%x\n",
+ reasons);
+ if (req->reason & ~reasons) {
+ /* Other reasons are active, simply drop the reasons from func arg: */
+ req->reason &= ~reasons;
+ continue;
}
+ /* No reason to keep the paging, remove it: */
+ paging_remove_request(req);
+ remaining--;
+ if (remaining == 0)
+ break;
}
- return count;
}
/*! Update the BTS paging buffer slots on given BTS */
void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots)
{
- paging_init_if_needed(bts);
-
- osmo_timer_del(&bts->paging.credit_timer);
- bts->paging.available_slots = free_slots;
- paging_schedule_if_needed(&bts->paging);
+ LOG_BTS(bts, DPAG, LOGL_DEBUG, "Rx CCCH Load Indication from BTS (available_slots %u -> %u)\n",
+ bts->paging.available_slots, free_slots);
+ paging_set_available_slots(bts, free_slots);
+ /* Re-arm credit_timer if needed */
+ if (trx_is_usable(bts->c0)) {
+ paging_schedule_if_needed(&bts->paging);
+ osmo_timer_schedule(&bts->paging.credit_timer,
+ bts_no_ccch_load_ind_timeout_sec(bts), 0);
+ }
}
/*! Count the number of pending paging requests on given BTS */
-unsigned int paging_pending_requests_nr(struct gsm_bts *bts)
+unsigned int paging_pending_requests_nr(const struct gsm_bts *bts)
{
- unsigned int requests = 0;
- struct gsm_paging_request *req;
-
- paging_init_if_needed(bts);
-
- llist_for_each_entry(req, &bts->paging.pending_requests, entry)
- ++requests;
-
- return requests;
+ return bts->paging.initial_req_list_len + bts->paging.retrans_req_list_len;
}
/*! Flush all paging requests at a given BTS for a given MSC (or NULL if all MSC should be flushed). */
@@ -510,19 +708,22 @@ void paging_flush_bts(struct gsm_bts *bts, struct bsc_msc_data *msc)
{
struct gsm_paging_request *req, *req2;
int num_cancelled = 0;
+ int i;
- paging_init_if_needed(bts);
+ struct llist_head *lists[] = { &bts->paging.initial_req_list, &bts->paging.retrans_req_list };
- llist_for_each_entry_safe(req, req2, &bts->paging.pending_requests, entry) {
- if (msc && req->msc != msc)
- continue;
- /* now give up the data structure */
- LOG_BTS(bts, DPAG, LOGL_DEBUG, "Stop paging %s (flush)\n", bsc_subscr_name(req->bsub));
- paging_remove_request(&bts->paging, req);
- num_cancelled++;
+ for (i = 0; i < ARRAY_SIZE(lists); i++) {
+ llist_for_each_entry_safe(req, req2, lists[i], entry) {
+ if (msc && req->msc != msc)
+ continue;
+ /* now give up the data structure */
+ LOG_PAGING_BTS(req, bts, DPAG, LOGL_DEBUG, "Stop paging (flush)\n");
+ paging_remove_request(req);
+ num_cancelled++;
+ }
}
- rate_ctr_add(&bts->bts_ctrs->ctr[BTS_CTR_PAGING_MSC_FLUSH], num_cancelled);
+ rate_ctr_add(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_PAGING_MSC_FLUSH), num_cancelled);
}
/*! Flush all paging requests issued by \a msc on any BTS in \a net */
@@ -533,3 +734,115 @@ void paging_flush_network(struct gsm_network *net, struct bsc_msc_data *msc)
llist_for_each_entry(bts, &net->bts_list, list)
paging_flush_bts(bts, msc);
}
+
+/* Shim to avoid problems when compiling against libosmocore <= 1.7.0, since
+ * gsm0502_get_n_pag_blocks() was not declared const despite being readonly. Once
+ * osmo-bsc depends on libosmocore > 1.7.0, this shim can be dropped. */
+static inline unsigned int _gsm0502_get_n_pag_blocks(const struct gsm48_control_channel_descr *chan_desc)
+{
+ return gsm0502_get_n_pag_blocks((struct gsm48_control_channel_descr *)chan_desc);
+}
+
+/*! Estimate available_slots credit over a time period, used when below CCCH Load Indication Threshold */
+uint16_t paging_estimate_available_slots(const struct gsm_bts *bts, unsigned int time_span_s)
+{
+ unsigned int n_pag_blocks = _gsm0502_get_n_pag_blocks(&bts->si_common.chan_desc);
+ uint16_t available_slots = n_pag_blocks * time_span_s * 1000000 / GSM51_MFRAME_DURATION_us;
+ LOG_BTS(bts, DPAG, LOGL_DEBUG, "Estimated %u paging available_slots over %u seconds\n",
+ available_slots, time_span_s);
+ return available_slots;
+}
+
+/*! Conservative estimate of time needed by BTS to schedule a number of paging
+ * requests (num_reqs), based on current load at the BSC queue (doesn't take into
+ * account BTs own buffer) */
+static unsigned int paging_estimate_delay_us(struct gsm_bts *bts, unsigned int num_reqs,
+ unsigned int num_reqs_same_pgroup)
+{
+ unsigned int n_pag_blocks, n_mframes, time_us = 0;
+
+ n_pag_blocks = _gsm0502_get_n_pag_blocks(&bts->si_common.chan_desc);
+
+ /* First of all, we need to extend the timeout in relation to the amount
+ * of paging requests in the BSC queue. In here we don't care about the
+ * paging group, because they are mixed in the same queue. If we don't
+ * take this into account, it could happen that if lots of requests are
+ * received at the BSC (from MSC) around the same time, they could time
+ * out in the BSC queue before arriving at the BTS. We already account of
+ * same-paging-group ones further below, so don't take them into account
+ * here: */
+ unsigned int num_reqs_other_groups = num_reqs - num_reqs_same_pgroup;
+ time_us += ((num_reqs_other_groups * GSM51_MFRAME_DURATION_us) + (n_pag_blocks - 1)) / n_pag_blocks;
+
+ /* Now we extend the timeout based on the amount of requests of the same
+ * paging group before the present one: */
+ n_mframes = (num_reqs_same_pgroup + (n_pag_blocks - 1)) / n_pag_blocks;
+ time_us += n_mframes * GSM51_MFRAME_DURATION_us;
+ /* the multiframes are not consecutive for a paging group, let's add the spacing between: */
+ if (n_mframes > 1) {
+ unsigned int bs_pa_mfrms = (bts->si_common.chan_desc.bs_pa_mfrms + 2);
+ time_us += (n_mframes - 1) * bs_pa_mfrms * GSM51_MFRAME_DURATION_us;
+ }
+ return time_us;
+}
+
+/* Callback function to be called every time we receive a signal from NM */
+static int nm_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct nm_running_chg_signal_data *nsd;
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+ unsigned int load_ind_timeout;
+ uint16_t estimated_slots;
+
+ if (signal != S_NM_RUNNING_CHG)
+ return 0;
+
+ nsd = signal_data;
+ bts = nsd->bts;
+
+ switch (nsd->obj_class) {
+ case NM_OC_RADIO_CARRIER:
+ trx = (struct gsm_bts_trx *)nsd->obj;
+ break;
+ case NM_OC_BASEB_TRANSC:
+ trx = gsm_bts_bb_trx_get_trx((struct gsm_bts_bb_trx *)nsd->obj);
+ break;
+ default:
+ return 0;
+ }
+
+ /* We only care about state changes of C0. */
+ if (trx != trx->bts->c0)
+ return 0;
+
+ if (nsd->running) {
+ if (trx_is_usable(trx)) {
+ LOG_BTS(bts, DPAG, LOGL_INFO, "C0 becomes available for paging\n");
+ /* Fill in initial credit */
+ load_ind_timeout = bts_no_ccch_load_ind_timeout_sec(bts);
+ estimated_slots = paging_estimate_available_slots(bts, load_ind_timeout);
+ paging_set_available_slots(bts, estimated_slots);
+ /* Start scheduling credit_timer */
+ osmo_timer_schedule(&bts->paging.credit_timer,
+ bts_no_ccch_load_ind_timeout_sec(bts), 0);
+ /* work_timer will be started when new paging requests arrive. */
+ }
+ } else {
+ /* If credit timer was not pending it means C0 was already unavailable before (rcarrier||bbtransc) */
+ if (osmo_timer_pending(&bts->paging.credit_timer)) {
+ LOG_BTS(bts, DPAG, LOGL_INFO, "C0 becomes unavailable for paging\n");
+ /* Note: flushing will osmo_timer_del(&bts->paging.work_timer) when queue becomes empty */
+ paging_flush_bts(bts, NULL);
+ osmo_timer_del(&bts->paging.credit_timer);
+ }
+ }
+ return 0;
+}
+
+/* To be called once at startup of the process: */
+void paging_global_init(void)
+{
+ osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
+}
diff --git a/src/osmo-bsc/pcu_sock.c b/src/osmo-bsc/pcu_sock.c
index a1a1cfc18..a0c9827dd 100644
--- a/src/osmo-bsc/pcu_sock.c
+++ b/src/osmo-bsc/pcu_sock.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -37,6 +33,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/bsc/abis_nm.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/pcu_if.h>
@@ -46,10 +43,9 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bts_sm.h>
static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg);
-uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
-int pcu_direct = 1;
static const char *sapi_string[] = {
[PCU_IF_SAPI_RACH] = "RACH",
@@ -58,8 +54,8 @@ static const char *sapi_string[] = {
[PCU_IF_SAPI_BCCH] = "BCCH",
[PCU_IF_SAPI_PDTCH] = "PDTCH",
[PCU_IF_SAPI_PRACH] = "PRACH",
- [PCU_IF_SAPI_PTCCH] = "PTCCH",
- [PCU_IF_SAPI_AGCH_DT] = "AGCH_DT",
+ [PCU_IF_SAPI_PTCCH] = "PTCCH",
+ [PCU_IF_SAPI_PCH_DT] = "PCH_DT",
};
/* Check if BTS has a PCU connection */
@@ -96,6 +92,33 @@ struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
return msg;
}
+/* Check if the timeslot can be utilized as PDCH now
+ * (PDCH is currently active on BTS) */
+static bool ts_now_usable_as_pdch(const struct gsm_bts_trx_ts *ts)
+{
+ switch (ts->pchan_is) {
+ case GSM_PCHAN_PDCH:
+ /* NOTE: We currently only support Ericsson RBS as a BSC
+ * co-located BTS. This BTS only supports dynamic channels. */
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Check if it is possible to use the TS as PDCH (not now, but maybe later) */
+static bool ts_usable_as_pdch(const struct gsm_bts_trx_ts *ts)
+{
+ switch (ts->pchan_from_config) {
+ case GSM_PCHAN_TCH_F_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
+ case GSM_PCHAN_PDCH:
+ return true;
+ default:
+ return false;
+ }
+}
+
/* Fill the frequency hopping parameter */
static void info_ind_fill_fhp(struct gsm_pcu_if_info_trx_ts *ts_info,
const struct gsm_bts_trx_ts *ts)
@@ -108,6 +131,48 @@ static void info_ind_fill_fhp(struct gsm_pcu_if_info_trx_ts *ts_info,
ts_info->ma_bit_len = ts->hopping.ma_len * 8 - ts->hopping.ma.cur_bit;
}
+/* Fill the TRX parameter */
+static void info_ind_fill_trx(struct gsm_pcu_if_info_trx *trx_info, const struct gsm_bts_trx *trx)
+{
+ unsigned int tn;
+ const struct gsm_bts_trx_ts *ts;
+
+ trx_info->hlayer1 = 0x2342;
+ trx_info->pdch_mask = 0;
+ trx_info->arfcn = trx->arfcn;
+
+ if (trx->mo.nm_state.operational != NM_OPSTATE_ENABLED ||
+ trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) {
+ LOG_TRX(trx, DPCU, LOGL_INFO, "unavailable for PCU (op=%s adm=%s)\n",
+ abis_nm_opstate_name(trx->mo.nm_state.operational),
+ abis_nm_admin_name(trx->mo.nm_state.administrative));
+ return;
+ }
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ ts = &trx->ts[tn];
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (!ts_now_usable_as_pdch(ts))
+ continue;
+
+ trx_info->pdch_mask |= (1 << tn);
+ trx_info->ts[tn].tsc =
+ (ts->tsc >= 0) ? ts->tsc : trx->bts->bsic & 7;
+
+ if (ts->hopping.enabled)
+ info_ind_fill_fhp(&trx_info->ts[tn], ts);
+
+ LOG_TRX(trx, DPCU, LOGL_INFO, "PDCH on ts=%u is available (tsc=%u ", ts->nr,
+ trx_info->ts[tn].tsc);
+ if (ts->hopping.enabled)
+ LOGPC(DPCU, LOGL_INFO, "hopping=yes hsn=%u maio=%u ma_bit_len=%u)\n",
+ ts->hopping.hsn, ts->hopping.maio, trx_info->ts[tn].ma_bit_len);
+ else
+ LOGPC(DPCU, LOGL_INFO, "hopping=no arfcn=%u)\n", trx->arfcn);
+ }
+}
+
/* Send BTS properties to the PCU */
static int pcu_tx_info_ind(struct gsm_bts *bts)
{
@@ -115,15 +180,14 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_info_ind *info_ind;
struct gprs_rlc_cfg *rlcc;
- struct gsm_bts_gprs_nsvc *nsvc;
+ struct gsm_bts_sm *bts_sm;
+ struct gsm_gprs_nsvc *nsvc;
struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
- int i, tn;
+ int i;
- OSMO_ASSERT(bts);
- OSMO_ASSERT(bts->network);
+ bts_sm = bts->site_mgr;
- LOGP(DPCU, LOGL_INFO, "Sending info for BTS %d\n",bts->nr);
+ LOGP(DPCU, LOGL_INFO, "Sending info for BTS %d\n", bts->nr);
rlcc = &bts->gprs.cell.rlc_cfg;
@@ -135,9 +199,8 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
info_ind = &pcu_prim->u.info_ind;
info_ind->version = PCU_IF_VERSION;
info_ind->flags |= PCU_IF_FLAG_ACTIVE;
-
- if (pcu_direct)
- info_ind->flags |= PCU_IF_FLAG_SYSMO;
+ info_ind->flags |= PCU_IF_FLAG_SYSMO;
+ info_ind->flags |= PCU_IF_FLAG_DT;
/* RAI */
info_ind->mcc = bts->network->plmn.mcc;
@@ -147,11 +210,12 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
info_ind->rac = bts->gprs.rac;
/* NSE */
- info_ind->nsei = bts->gprs.nse.nsei;
- memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7);
+ info_ind->nsei = bts_sm->gprs.nse.nsei;
+ memcpy(info_ind->nse_timer, bts_sm->gprs.nse.timer, 7);
memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11);
/* cell attributes */
+ info_ind->bsic = bts->bsic;
info_ind->cell_id = bts->cell_identity;
info_ind->repeat_time = rlcc->paging.repeat_time;
info_ind->repeat_count = rlcc->paging.repeat_count;
@@ -193,16 +257,16 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
if (rlcc->cs_mask & (1 << GPRS_MCS9))
info_ind->flags |= PCU_IF_FLAG_MCS9;
}
-#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs"
+ /* TODO: isn't dl_tbf_ext wrong?: * 10 and no ntohs */
info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT];
-#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs"
+ /* TODO: isn't ul_tbf_ext wrong?: * 10 and no ntohs */
info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT];
info_ind->initial_cs = rlcc->initial_cs;
info_ind->initial_mcs = rlcc->initial_mcs;
/* NSVC */
for (i = 0; i < ARRAY_SIZE(info_ind->nsvci); i++) {
- nsvc = &bts->gprs.nsvc[i];
+ nsvc = &bts->site_mgr->gprs.nsvc[i];
info_ind->nsvci[i] = nsvc->nsvci;
info_ind->local_port[i] = nsvc->local_port;
@@ -210,14 +274,14 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
case AF_INET:
info_ind->address_type[i] = PCU_IF_ADDR_TYPE_IPV4;
info_ind->remote_ip[i].v4 = nsvc->remote.u.sin.sin_addr;
- info_ind->remote_port[i] = nsvc->remote.u.sin.sin_port;
+ info_ind->remote_port[i] = ntohs(nsvc->remote.u.sin.sin_port);
break;
case AF_INET6:
info_ind->address_type[i] = PCU_IF_ADDR_TYPE_IPV6;
memcpy(&info_ind->remote_ip[i].v6,
&nsvc->remote.u.sin6.sin6_addr,
sizeof(struct in6_addr));
- info_ind->remote_port[i] = nsvc->remote.u.sin6.sin6_port;
+ info_ind->remote_port[i] = ntohs(nsvc->remote.u.sin6.sin6_port);
break;
default:
info_ind->address_type[i] = PCU_IF_ADDR_TYPE_UNSPEC;
@@ -229,39 +293,114 @@ static int pcu_tx_info_ind(struct gsm_bts *bts)
trx = gsm_bts_trx_num(bts, i);
if (!trx)
continue;
- info_ind->trx[i].hlayer1 = 0x2342;
- info_ind->trx[i].pdch_mask = 0;
- info_ind->trx[i].arfcn = trx->arfcn;
- for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
- ts = &trx->ts[tn];
- if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED ||
- ts->pchan_is != GSM_PCHAN_PDCH)
- continue;
+ if (trx->nr >= ARRAY_SIZE(info_ind->trx)) {
+ LOG_TRX(trx, DPCU, LOGL_NOTICE, "PCU interface (version %u) "
+ "cannot handle more than %zu transceivers => skipped\n",
+ PCU_IF_VERSION, ARRAY_SIZE(info_ind->trx));
+ break;
+ }
+ info_ind_fill_trx(&info_ind->trx[trx->nr], trx);
+ }
+
+ return pcu_sock_send(bts, msg);
+}
- info_ind->trx[i].pdch_mask |= (1 << tn);
- info_ind->trx[i].ts[tn].tsc =
- (ts->tsc >= 0) ? ts->tsc : bts->bsic & 7;
+static int pcu_tx_e1_ccu_ind(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
- if (ts->hopping.enabled)
- info_ind_fill_fhp(&info_ind->trx[i].ts[tn], ts);
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct gsm_pcu_if_e1_ccu_ind *e1_ccu_ind;
+ int i;
- LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: PDCH is available "
- "(tsc=%u ", trx->nr, ts->nr, info_ind->trx[i].ts[tn].tsc);
- if (ts->hopping.enabled)
- LOGPC(DPCU, LOGL_INFO, "hopping=yes hsn=%u maio=%u ma_bit_len=%u)\n",
- ts->hopping.hsn, ts->hopping.maio, trx->ts[tn].hopping.ma.data_len - trx->ts[tn].hopping.ma.cur_bit);
- else
- LOGPC(DPCU, LOGL_INFO, "hopping=no arfcn=%u)\n", trx->arfcn);
+ if (trx->nr >= PCU_IF_NUM_TRX) {
+ LOG_TRX(trx, DPCU, LOGL_NOTICE, "PCU interface (version %u) "
+ "cannot handle more than %u transceivers => skipped\n",
+ PCU_IF_VERSION, PCU_IF_NUM_TRX);
+ continue;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct gsm_pcu_if *pcu_prim;
+ struct gsm_bts_trx_ts *ts;
+ struct msgb *msg;
+ int rc;
+
+ ts = &trx->ts[i];
+
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (!ts_usable_as_pdch(ts))
+ continue;
+
+ msg = pcu_msgb_alloc(PCU_IF_MSG_E1_CCU_IND, bts->nr);
+ if (!msg)
+ return -ENOMEM;
+ pcu_prim = (struct gsm_pcu_if *)msg->data;
+ e1_ccu_ind = &pcu_prim->u.e1_ccu_ind;
+ e1_ccu_ind->ts_nr = ts->nr;
+ e1_ccu_ind->trx_nr = trx->nr;
+ e1_ccu_ind->e1_nr = ts->e1_link.e1_nr;
+ e1_ccu_ind->e1_ts = ts->e1_link.e1_ts;
+ e1_ccu_ind->e1_ts_ss = ts->e1_link.e1_ts_ss;
+
+ LOG_TRX(trx, DPCU, LOGL_INFO, "Sending E1 CCU info for TS %d\n", e1_ccu_ind->ts_nr);
+ rc = pcu_sock_send(bts, msg);
+ if (rc < 0)
+ return -EINVAL;
}
}
- return pcu_sock_send(bts, msg);
+ return 0;
}
-void pcu_info_update(struct gsm_bts *bts)
+/* Allow test to overwrite it */
+__attribute__((weak)) void pcu_info_update(struct gsm_bts *bts)
{
- if (pcu_connected(bts))
+ if (pcu_connected(bts)) {
+ /* In cases where the CCU is connected via an E1 line, we transmit the connection parameters for the
+ * PDCH before we announce the other BTS related parameters. At the moment Ericsson RBS is the only
+ * E1 BTS we support. */
+ if (is_ericsson_bts(bts))
+ pcu_tx_e1_ccu_ind(bts);
+
pcu_tx_info_ind(bts);
+ }
+}
+
+static int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t sapi, uint32_t fn,
+ uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len,
+ int8_t rssi, uint16_t ber10k, int16_t bto, int16_t lqual)
+{
+ struct msgb *msg;
+ struct gsm_pcu_if *pcu_prim;
+ struct gsm_pcu_if_data *data_ind;
+ struct gsm_bts *bts = ts->trx->bts;
+
+ LOGP(DPCU, LOGL_DEBUG, "Sending data indication: sapi=%s arfcn=%d block=%d data=%s\n",
+ sapi_string[sapi], arfcn, block_nr, osmo_hexdump(data, len));
+
+ msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_IND, bts->nr);
+ if (!msg)
+ return -ENOMEM;
+ pcu_prim = (struct gsm_pcu_if *) msg->data;
+ data_ind = &pcu_prim->u.data_ind;
+
+ data_ind->sapi = sapi;
+ data_ind->rssi = rssi;
+ data_ind->fn = fn;
+ data_ind->arfcn = arfcn;
+ data_ind->trx_nr = ts->trx->nr;
+ data_ind->ts_nr = ts->nr;
+ data_ind->block_nr = block_nr;
+ data_ind->ber10k = ber10k;
+ data_ind->ta_offs_qbits = bto;
+ data_ind->lqual_cb = lqual;
+ if (len)
+ memcpy(data_ind->data, data, len);
+ data_ind->len = len;
+
+ return pcu_sock_send(bts, msg);
}
/* Forward rach indication to PCU */
@@ -368,11 +507,31 @@ static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group,
return rc;
}
+/* Helper function for pcu_rx_data_req() to extract paging group info (3 byte) */
+static uint8_t extract_paging_group(struct gsm_bts *bts, uint8_t *data)
+{
+ char imsi_digit_buf[4];
+ uint8_t pag_grp;
+
+ /* the first three bytes are the last three digits of the IMSI, which we need to compute the paging group */
+ imsi_digit_buf[0] = data[0];
+ imsi_digit_buf[1] = data[1];
+ imsi_digit_buf[2] = data[2];
+ imsi_digit_buf[3] = '\0';
+
+ pag_grp = gsm0502_calc_paging_group(&bts->si_common.chan_desc,
+ str_to_imsi(imsi_digit_buf));
+
+ LOGP(DPCU, LOGL_DEBUG, "Calculating paging group: imsi_digit_buf=%s ==> pag_grp=0x%02x\n",
+ imsi_digit_buf, pag_grp);
+
+ return pag_grp;
+}
+
static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
struct gsm_pcu_if_data *data_req)
{
struct msgb *msg;
- char imsi_digit_buf[4];
uint32_t tlli = -1;
uint8_t pag_grp;
int rc = 0;
@@ -384,15 +543,8 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
switch (data_req->sapi) {
case PCU_IF_SAPI_PCH:
- /* the first three bytes are the last three digits of
- * the IMSI, which we need to compute the paging group */
- imsi_digit_buf[0] = data_req->data[0];
- imsi_digit_buf[1] = data_req->data[1];
- imsi_digit_buf[2] = data_req->data[2];
- imsi_digit_buf[3] = '\0';
- LOGP(DPCU, LOGL_DEBUG, "SAPI PCH imsi %s\n", imsi_digit_buf);
- pag_grp = gsm0502_calc_paging_group(&bts->si_common.chan_desc,
- str_to_imsi(imsi_digit_buf));
+ /* Extract 3 byte paging group */
+ pag_grp = extract_paging_group(bts, data_req->data);
pcu_rx_rr_paging(bts, pag_grp, data_req->data+3);
break;
case PCU_IF_SAPI_AGCH:
@@ -409,28 +561,42 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
rc = -EIO;
}
break;
- case PCU_IF_SAPI_AGCH_DT:
- /* DT = direct tlli. A tlli is prefixed */
+ case PCU_IF_SAPI_PCH_DT:
+ /* DT = direct TLLI. A tlli is prefixed so that the BSC/BTS can confirm the sending of the downlink
+ * IMMEDIATE ASSIGNMENT towards the PCU using this TLLI as a reference. */
- if (data_req->len < 5) {
- LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
- "invalid/small length %d\n", data_req->len);
+ if (data_req->len < 8) {
+ LOGP(DPCU, LOGL_ERROR, "Received PCU data request with invalid/small length %d\n",
+ data_req->len);
break;
}
+
+ /* Extract 4 byte TLLI */
memcpy(&tlli, data_req->data, 4);
- msg = msgb_alloc(data_req->len - 4, "pcu_agch");
+ /* Extract 3 byte paging group */
+ pag_grp = extract_paging_group(bts, data_req->data + 4);
+
+ LOGP(DPCU, LOGL_DEBUG, "PCU Sends immediate assignment via PCH (tlli=0x%08x, pag_grp=0x%02x)\n",
+ tlli, pag_grp);
+ msg = msgb_alloc(data_req->len - 7, "pcu_pch");
if (!msg) {
rc = -ENOMEM;
break;
}
- msg->l3h = msgb_put(msg, data_req->len - 4);
- memcpy(msg->l3h, data_req->data + 4, data_req->len - 4);
-
- if (bts->type == GSM_BTS_TYPE_RBS2000)
- rc = rsl_ericsson_imm_assign_cmd(bts, tlli, msg->len, msg->data);
- else
- rc = rsl_imm_assign_cmd(bts, msg->len, msg->data);
+ msg->l3h = msgb_put(msg, data_req->len - 7);
+ memcpy(msg->l3h, data_req->data + 7, data_req->len - 7);
+
+ /* NOTE: Sending an IMMEDIATE ASSIGNMENT via PCH became necessary with GPRS in order to be able to
+ * assign downlink TBFs directly through the paging channel. However, this method never became part
+ * of the RSL specs. This means that each BTS vendor has to come up with a proprietary method. At
+ * the moment we only support Ericsson RBS here. */
+ if (bts->type == GSM_BTS_TYPE_RBS2000) {
+ rc = rsl_ericsson_imm_assign_cmd(bts, tlli, msg->len, msg->data, pag_grp);
+ } else {
+ LOGP(DPCU, LOGL_ERROR, "BTS model does not support sending immediate assignment via PCH!\n");
+ rc = -ENOTSUP;
+ }
if (rc) {
msgb_free(msg);
@@ -446,8 +612,103 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
return rc;
}
+static int pcu_tx_si(const struct gsm_bts *bts, enum osmo_sysinfo_type si_type, bool enable)
+{
+ /* the SI is per-BTS so it doesn't matter which TRX we use */
+ struct gsm_bts_trx *trx = bts->c0;
+
+ uint8_t si_buf[GSM_MACBLOCK_LEN];
+ uint8_t len;
+ int rc;
+
+ if (enable) {
+ memcpy(si_buf, GSM_BTS_SI(bts, si_type), GSM_MACBLOCK_LEN);
+ len = GSM_MACBLOCK_LEN;
+ LOGP(DPCU, LOGL_DEBUG, "Updating SI%s to PCU: %s\n",
+ get_value_string(osmo_sitype_strs, si_type),
+ osmo_hexdump_nospc(si_buf, GSM_MACBLOCK_LEN));
+ } else {
+ si_buf[0] = si_type;
+ len = 1;
+
+ /* Note: SI13 is the only system information type that is revked
+ * by just sending a completely empty message. This is due to
+ * historical reasons */
+ if (si_type != SYSINFO_TYPE_13)
+ len = 0;
+
+ LOGP(DPCU, LOGL_DEBUG, "Revoking SI%s from PCU\n",
+ get_value_string(osmo_sitype_strs, si_buf[0]));
+ }
+
+ /* The low-level data like FN, ARFCN etc will be ignored but we have to
+ * set lqual high enough to bypass the check at lower levels */
+ rc = pcu_tx_data_ind(&trx->ts[0], PCU_IF_SAPI_BCCH, 0, 0, 0, si_buf, len,
+ 0, 0, 0, INT16_MAX);
+ if (rc < 0)
+ LOGP(DPCU, LOGL_NOTICE, "Failed to send SI%s to PCU: rc=%d\n",
+ get_value_string(osmo_sitype_strs, si_type), rc);
+
+ return rc;
+}
+
+static int pcu_tx_si_all(struct gsm_bts *bts)
+{
+ const enum osmo_sysinfo_type si_types[] = { SYSINFO_TYPE_1, SYSINFO_TYPE_2, SYSINFO_TYPE_3, SYSINFO_TYPE_13 };
+ unsigned int i;
+ int rc = 0;
+
+ for (i = 0; i < ARRAY_SIZE(si_types); i++) {
+ if (GSM_BTS_HAS_SI(bts, si_types[i])) {
+ rc = pcu_tx_si(bts, si_types[i], true);
+ if (rc < 0)
+ return rc;
+ } else {
+ LOGP(DPCU, LOGL_INFO,
+ "SI%s is not available on PCU connection\n",
+ get_value_string(osmo_sitype_strs, si_types[i]));
+ }
+ }
+
+ return 0;
+}
+
+static int pcu_rx_txt_ind(struct gsm_bts *bts,
+ const struct gsm_pcu_if_txt_ind *txt)
+{
+ int rc;
+
+ switch (txt->type) {
+ case PCU_VERSION:
+ LOGP(DPCU, LOGL_INFO, "OsmoPCU version %s connected\n",
+ txt->text);
+ rc = pcu_tx_si_all(bts);
+ if (rc < 0)
+ return -EINVAL;
+ break;
+ case PCU_OML_ALERT:
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "PCU external alarm: %s\n", txt->text);
+ break;
+ default:
+ LOGP(DPCU, LOGL_ERROR, "Unknown TXT_IND type %u received\n",
+ txt->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define CHECK_IF_MSG_SIZE(prim_len, prim_msg) \
+ do { \
+ size_t _len = PCUIF_HDR_SIZE + sizeof(prim_msg); \
+ if (prim_len < _len) { \
+ LOGP(DPCU, LOGL_ERROR, "Received %zu bytes on PCU Socket, but primitive %s " \
+ "size is %zu, discarding\n", prim_len, #prim_msg, _len); \
+ return -EINVAL; \
+ } \
+ } while (0)
static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
- struct gsm_pcu_if *pcu_prim)
+ struct gsm_pcu_if *pcu_prim, size_t prim_len)
{
int rc = 0;
struct gsm_bts *bts;
@@ -458,8 +719,13 @@ static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
switch (msg_type) {
case PCU_IF_MSG_DATA_REQ:
case PCU_IF_MSG_PAG_REQ:
+ CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.data_req);
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
break;
+ case PCU_IF_MSG_TXT_IND:
+ CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.txt_ind);
+ rc = pcu_rx_txt_ind(bts, &pcu_prim->u.txt_ind);
+ break;
default:
LOGP(DPCU, LOGL_ERROR, "Received unknown PCU msg type %d\n",
msg_type);
@@ -495,7 +761,7 @@ static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg)
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
- conn_bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(conn_bfd);
return 0;
}
@@ -518,7 +784,7 @@ static void pcu_sock_close(struct pcu_sock_state *state)
osmo_fd_unregister(bfd);
/* re-enable the generation of ACCEPT for new connections */
- state->listen_bfd.when |= OSMO_FD_READ;
+ osmo_fd_read_enable(&state->listen_bfd);
#if 0
/* remove si13, ... */
@@ -554,7 +820,7 @@ static int pcu_sock_read(struct osmo_fd *bfd)
struct msgb *msg;
int rc;
- msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
+ msg = msgb_alloc(sizeof(*pcu_prim) + 1000, "pcu_sock_rx");
if (!msg)
return -ENOMEM;
@@ -565,12 +831,21 @@ static int pcu_sock_read(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
- if (errno == EAGAIN)
+ if (errno == EAGAIN) {
+ msgb_free(msg);
return 0;
+ }
goto close;
}
- rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim);
+ if (rc < PCUIF_HDR_SIZE) {
+ LOGP(DPCU, LOGL_ERROR, "Received %d bytes on PCU Socket, but primitive hdr size "
+ "is %zu, discarding\n", rc, PCUIF_HDR_SIZE);
+ msgb_free(msg);
+ return 0;
+ }
+
+ rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim, rc);
/* as we always synchronously process the message in pcu_rx() and
* its callbacks, we can free the message here. */
@@ -597,7 +872,7 @@ static int pcu_sock_write(struct osmo_fd *bfd)
msg = llist_entry(state->upqueue.next, struct msgb, list);
pcu_prim = (struct gsm_pcu_if *)msg->data;
- bfd->when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(bfd);
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
@@ -612,7 +887,7 @@ static int pcu_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
- bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(bfd);
break;
}
goto close;
@@ -667,15 +942,12 @@ static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have "
"another active connection ?!?\n");
/* We already have one PCU connected, this is all we support */
- state->listen_bfd.when &= ~OSMO_FD_READ;
+ osmo_fd_read_disable(&state->listen_bfd);
close(rc);
return 0;
}
- conn_bfd->fd = rc;
- conn_bfd->when = OSMO_FD_READ;
- conn_bfd->cb = pcu_sock_cb;
- conn_bfd->data = state;
+ osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, pcu_sock_cb, state, 0);
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DPCU, LOGL_ERROR, "Failed to register new connection "
@@ -707,18 +979,15 @@ int pcu_sock_init(const char *path, struct gsm_bts *bts)
bfd = &state->listen_bfd;
- bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path,
- OSMO_SOCK_F_BIND);
- if (bfd->fd < 0) {
+ rc = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND);
+ if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
strerror(errno));
talloc_free(state);
return -1;
}
- bfd->when = OSMO_FD_READ;
- bfd->cb = pcu_sock_accept;
- bfd->data = state;
+ osmo_fd_setup(bfd, rc, OSMO_FD_READ, pcu_sock_accept, state, 0);
rc = osmo_fd_register(bfd);
if (rc < 0) {
diff --git a/src/osmo-bsc/penalty_timers.c b/src/osmo-bsc/penalty_timers.c
index 02cf2468a..124a36255 100644
--- a/src/osmo-bsc/penalty_timers.c
+++ b/src/osmo-bsc/penalty_timers.c
@@ -28,34 +28,22 @@
#include <osmocom/bsc/penalty_timers.h>
#include <osmocom/bsc/gsm_data.h>
-struct penalty_timers {
- struct llist_head timers;
-};
-
-struct penalty_timer {
- struct llist_head entry;
- const void *for_object;
- unsigned int timeout;
-};
-
static unsigned int time_now(void)
{
- time_t now;
- time(&now);
- /* FIXME: use monotonic clock */
- return (unsigned int)now;
-}
-
-struct penalty_timers *penalty_timers_init(void *ctx)
-{
- struct penalty_timers *pt = talloc_zero(ctx, struct penalty_timers);
- if (!pt)
- return NULL;
- INIT_LLIST_HEAD(&pt->timers);
- return pt;
+ struct timespec tp;
+ if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp))
+ return 0;
+ return (unsigned int)tp.tv_sec;
}
-void penalty_timers_add(struct penalty_timers *pt, const void *for_object, int timeout)
+/* Add a penalty timer for a target cell ID.
+ * \param ctx talloc context to allocate new struct penalty_timer from.
+ * \param penalty_timers llist head to add penalty timer to.
+ * \param for_target_cell Which handover target to penalize.
+ * \param timeout Penalty time in seconds.
+ */
+void penalty_timers_add(void *ctx, struct llist_head *penalty_timers,
+ const struct gsm0808_cell_id *for_target_cell, int timeout)
{
struct penalty_timer *timer;
unsigned int now;
@@ -67,9 +55,9 @@ void penalty_timers_add(struct penalty_timers *pt, const void *for_object, int t
then = now + timeout;
- /* timer already running for that BTS? */
- llist_for_each_entry(timer, &pt->timers, entry) {
- if (timer->for_object != for_object)
+ /* timer already running for that target cell? */
+ llist_for_each_entry(timer, penalty_timers, entry) {
+ if (!gsm0808_cell_ids_match(&timer->for_target_cell, for_target_cell, true))
continue;
/* raise, if running timer will timeout earlier or has timed
* out already, otherwise keep later timeout */
@@ -79,24 +67,49 @@ void penalty_timers_add(struct penalty_timers *pt, const void *for_object, int t
}
/* add new timer */
- timer = talloc_zero(pt, struct penalty_timer);
+ timer = talloc_zero(ctx, struct penalty_timer);
if (!timer)
return;
- timer->for_object = for_object;
+ timer->for_target_cell = *for_target_cell;
timer->timeout = then;
- llist_add_tail(&timer->entry, &pt->timers);
+ llist_add_tail(&timer->entry, penalty_timers);
}
-unsigned int penalty_timers_remaining(struct penalty_timers *pt, const void *for_object)
+/* Add a penalty timer for each target cell ID in the given list.
+ * \param ctx talloc context to allocate new struct penalty_timer from.
+ * \param penalty_timers llist head to add penalty timer to.
+ * \param for_target_cells Which handover targets to penalize.
+ * \param timeout Penalty time in seconds.
+ */
+void penalty_timers_add_list(void *ctx, struct llist_head *penalty_timers,
+ const struct gsm0808_cell_id_list2 *for_target_cells, int timeout)
+{
+ int i;
+ for (i = 0; i < for_target_cells->id_list_len; i++) {
+ struct gsm0808_cell_id add = {
+ .id_discr = for_target_cells->id_discr,
+ .id = for_target_cells->id_list[i],
+ };
+ penalty_timers_add(ctx, penalty_timers, &add, timeout);
+ }
+}
+
+/* Return the amount of penalty time in seconds remaining for a target cell.
+ * \param penalty_timers llist head to look up penalty time in.
+ * \param for_target_cell Which handover target to query.
+ * \returns seconds remaining until all penalty time has expired.
+ */
+unsigned int penalty_timers_remaining(struct llist_head *penalty_timers,
+ const struct gsm0808_cell_id *for_target_cell)
{
struct penalty_timer *timer;
unsigned int now = time_now();
unsigned int max_remaining = 0;
- llist_for_each_entry(timer, &pt->timers, entry) {
+ llist_for_each_entry(timer, penalty_timers, entry) {
unsigned int remaining;
- if (timer->for_object != for_object)
+ if (!gsm0808_cell_ids_match(&timer->for_target_cell, for_target_cell, true))
continue;
if (now >= timer->timeout)
continue;
@@ -107,23 +120,39 @@ unsigned int penalty_timers_remaining(struct penalty_timers *pt, const void *for
return max_remaining;
}
-void penalty_timers_clear(struct penalty_timers *pt, const void *for_object)
+/* Return the largest amount of penalty time in seconds remaining for any one of the given target cells.
+ * Call penalty_timers_remaining() for each entry of for_target_cells and return the largest value encountered.
+ * \param penalty_timers llist head to look up penalty time in.
+ * \param for_target_cells Which handover targets to query.
+ * \returns seconds remaining until all penalty time has expired.
+ */
+unsigned int penalty_timers_remaining_list(struct llist_head *penalty_timers,
+ const struct gsm0808_cell_id_list2 *for_target_cells)
+{
+ int i;
+ unsigned int max_remaining = 0;
+ for (i = 0; i < for_target_cells->id_list_len; i++) {
+ unsigned int remaining;
+ struct gsm0808_cell_id query = {
+ .id_discr = for_target_cells->id_discr,
+ .id = for_target_cells->id_list[i],
+ };
+ remaining = penalty_timers_remaining(penalty_timers, &query);
+ max_remaining = OSMO_MAX(max_remaining, remaining);
+ }
+ return max_remaining;
+}
+
+/* Clear penalty timers for one target cell, or completely clear the entire list.
+ * \param penalty_timers llist head to add penalty timer to.
+ * \param for_target_cell Which handover target to clear timers for, or NULL to clear all timers. */
+void penalty_timers_clear(struct llist_head *penalty_timers, const struct gsm0808_cell_id *for_target_cell)
{
struct penalty_timer *timer, *timer2;
- llist_for_each_entry_safe(timer, timer2, &pt->timers, entry) {
- if (for_object && timer->for_object != for_object)
+ llist_for_each_entry_safe(timer, timer2, penalty_timers, entry) {
+ if (for_target_cell && !gsm0808_cell_ids_match(&timer->for_target_cell, for_target_cell, true))
continue;
llist_del(&timer->entry);
talloc_free(timer);
}
}
-
-void penalty_timers_free(struct penalty_timers **pt_p)
-{
- struct penalty_timers *pt = *pt_p;
- if (!pt)
- return;
- penalty_timers_clear(pt, NULL);
- talloc_free(pt);
- *pt_p = NULL;
-}
diff --git a/src/osmo-bsc/power_control.c b/src/osmo-bsc/power_control.c
new file mode 100644
index 000000000..e26b0d6f2
--- /dev/null
+++ b/src/osmo-bsc/power_control.c
@@ -0,0 +1,476 @@
+/* MS Power Control Loop L1 */
+
+/* (C) 2014 by Holger Hans Peter Freyther
+ * (C) 2020-2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bsc_subscriber.h>
+#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/meas_rep.h>
+#include <osmocom/bsc/power_control.h>
+
+/* We don't want to deal with floating point, so we scale up */
+#define EWMA_SCALE_FACTOR 100
+/* EWMA_SCALE_FACTOR/2 = +50: Round to nearest value when downscaling, otherwise floor() is applied. */
+#define EWMA_ROUND_FACTOR (EWMA_SCALE_FACTOR / 2)
+
+/* Base Low-Pass Single-Pole IIR Filter (EWMA) formula:
+ *
+ * Avg[n] = a * Val[n] + (1 - a) * Avg[n - 1]
+ *
+ * where parameter 'a' determines how much weight of the latest measurement value
+ * 'Val[n]' carries vs the weight of the accumulated average 'Avg[n - 1]'. The
+ * value of 'a' is usually a float in range 0 .. 1, so:
+ *
+ * - value 0.5 gives equal weight to both 'Val[n]' and 'Avg[n - 1]';
+ * - value 1.0 means no filtering at all (pass through);
+ * - value 0.0 makes no sense.
+ *
+ * Further optimization:
+ *
+ * Avg[n] = a * Val[n] + Avg[n - 1] - a * Avg[n - 1]
+ * ^^^^^^ ^^^^^^^^^^
+ *
+ * a) this can be implemented in C using '+=' operator:
+ *
+ * Avg += a * Val - a * Avg
+ * Avg += a * (Val - Avg)
+ *
+ * b) everything is scaled up by 100 to avoid floating point stuff:
+ *
+ * Avg100 += A * (Val - Avg)
+ *
+ * where 'Avg100' is 'Avg * 100' and 'A' is 'a * 100'.
+ *
+ * For more details, see:
+ *
+ * https://en.wikipedia.org/wiki/Moving_average
+ * https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
+ * https://tomroelandts.com/articles/low-pass-single-pole-iir-filter
+ */
+static int do_pf_ewma(const struct gsm_power_ctrl_meas_params *mp,
+ struct gsm_power_ctrl_meas_proc_state *mps,
+ const int Val)
+{
+ const uint8_t A = mp->ewma.alpha;
+ int *Avg100 = &mps->ewma.Avg100;
+
+ /* We don't have 'Avg[n - 1]' if this is the first run */
+ if (mps->meas_num++ == 0) {
+ *Avg100 = Val * EWMA_SCALE_FACTOR;
+ return Val;
+ }
+
+ *Avg100 += A * (Val - (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR);
+ return (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR;
+}
+
+/* Calculate target RxLev value from lower/upper thresholds */
+#define CALC_TARGET(mp) \
+ ((mp).lower_thresh + (mp).upper_thresh) / 2
+
+static int do_avg_algo(const struct gsm_power_ctrl_meas_params *mp,
+ struct gsm_power_ctrl_meas_proc_state *mps,
+ const int val)
+{
+ int val_avg;
+ switch (mp->algo) {
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:
+ val_avg = do_pf_ewma(mp, mps, val);
+ break;
+ /* TODO: implement other pre-processing methods */
+ case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE:
+ default:
+ /* No filtering (pass through) */
+ val_avg = val;
+ }
+ return val_avg;
+}
+/* Calculate a 'delta' value (for the given MS/BS power control parameters)
+ * to be applied to the current Tx power level to approach the target level. */
+static int calc_delta_rxlev(const struct gsm_power_ctrl_params *params, const uint8_t rxlev)
+{
+ int delta;
+
+ /* Check if RxLev is within the threshold window */
+ if (rxlev >= params->rxlev_meas.lower_thresh &&
+ rxlev <= params->rxlev_meas.upper_thresh)
+ return 0;
+
+ /* How many dBs measured power should be increased (+) or decreased (-)
+ * to reach expected power. */
+ delta = CALC_TARGET(params->rxlev_meas) - rxlev;
+
+ /* Don't ever change more than PWR_{LOWER,RAISE}_MAX_DBM during one loop
+ * iteration, i.e. reduce the speed at which the MS transmit power can
+ * change. A higher value means a lower level (and vice versa) */
+ if (delta > params->inc_step_size_db)
+ delta = params->inc_step_size_db;
+ else if (delta < -params->red_step_size_db)
+ delta = -params->red_step_size_db;
+
+ return delta;
+}
+
+/* Shall we skip current block based on configured interval? */
+static bool ctrl_interval_skip_block(const struct gsm_power_ctrl_params *params,
+ struct lchan_power_ctrl_state *state)
+{
+ /* Power control interval: how many blocks do we skip? */
+ if (state->skip_block_num-- > 0)
+ return true;
+
+ /* Can we be sure if ONE Report is always going to correspond
+ * to ONE SACCH block at the BTS? - If not this is as approximation
+ * but it should not hurt. */
+
+ /* Reset the number of SACCH blocks to be skipped:
+ * ctrl_interval=0 => 0 blocks to skip,
+ * ctrl_interval=1 => 1 blocks to skip,
+ * ctrl_interval=2 => 3 blocks to skip,
+ * so basically ctrl_interval * 2 - 1. */
+ state->skip_block_num = params->ctrl_interval * 2 - 1;
+ return false;
+}
+
+int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan, const struct gsm_meas_rep *mr)
+{
+ struct lchan_power_ctrl_state *state = &lchan->ms_power_ctrl;
+ struct gsm_bts_trx *trx = lchan->ts->trx;
+ struct gsm_bts *bts = trx->bts;
+ enum gsm_band band = bts->band;
+ const struct gsm_power_ctrl_params *params = &bts->ms_power_ctrl;
+ int8_t new_power_lvl; /* TS 05.05 power level */
+ int8_t ms_dbm, new_dbm, current_dbm, bsc_max_dbm;
+ uint8_t rxlev_avg;
+ uint8_t ms_power_lvl = ms_pwr_ctl_lvl(band, mr->ms_l1.pwr);
+ int8_t ul_rssi_dbm;
+ bool ignore;
+
+ if (params == NULL)
+ return 0;
+ /* Not doing the power loop here if we are not handling it */
+ if (params->mode != GSM_PWR_CTRL_MODE_DYN_BSC)
+ return 0;
+
+ /* Shall we skip current block based on configured interval? */
+ if (ctrl_interval_skip_block(params, state))
+ return 0;
+
+ /* If DTx is active on Uplink,
+ * use the '-SUB', otherwise '-FULL': */
+ if (mr->flags & MEAS_REP_F_UL_DTX)
+ ul_rssi_dbm = rxlev2dbm(mr->ul.sub.rx_lev);
+ else
+ ul_rssi_dbm = rxlev2dbm(mr->ul.full.rx_lev);
+
+ ms_dbm = ms_pwr_dbm(band, ms_power_lvl);
+ if (ms_dbm < 0) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE,
+ "Failed to calculate dBm for power ctl level %" PRIu8 " on band %s\n",
+ ms_power_lvl, gsm_band_name(band));
+ return 0;
+ }
+
+ bsc_max_dbm = bts->ms_max_power;
+ rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, dbm2rxlev(ul_rssi_dbm));
+ new_dbm = ms_dbm + calc_delta_rxlev(params, rxlev_avg);
+
+ /* Make sure new_dbm is never negative. ms_pwr_ctl_lvl() can later on
+ cope with any unsigned dbm value, regardless of band minimal value. */
+ if (new_dbm < 0)
+ new_dbm = 0;
+ /* Don't ask for smaller ms power level than the one set by ms max power for this BTS */
+ if (new_dbm > bsc_max_dbm)
+ new_dbm = bsc_max_dbm;
+
+ new_power_lvl = ms_pwr_ctl_lvl(band, new_dbm);
+ if (new_power_lvl < 0) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE,
+ "Failed to retrieve power level for %" PRId8 " dBm on band %d\n",
+ new_dbm, band);
+ return 0;
+ }
+
+ current_dbm = ms_pwr_dbm(band, lchan->ms_power);
+
+ /* In this Power Control Loop, we infer a new good MS Power Level based
+ * on the previous MS Power Level announced by the MS (not the previous
+ * one we requested!) together with the related computed measurements.
+ * Hence, and since we allow for several good MS Power Levels falling into our
+ * thresholds, we could finally converge into an oscillation loop where
+ * the MS bounces between 2 different correct MS Power levels all the
+ * time, due to the fact that we "accept" and "request back" whatever
+ * good MS Power Level we received from the MS, but at that time the MS
+ * will be transmitting using the previous MS Power Level we
+ * requested, which we will later "accept" and "request back" on next loop
+ * iteration. As a result MS effectively bounces between those 2 MS
+ * Power Levels.
+ * In order to fix this permanent oscillation, if current MS_PWR used/announced
+ * by MS is good ("ms_dbm == new_dbm", hence within thresholds and no change
+ * required) but has higher Tx power than the one we last requested, we ignore
+ * it and keep requesting for one with lower Tx power. This way we converge to
+ * the lowest good Tx power avoiding oscillating over values within thresholds.
+ */
+ ignore = (ms_dbm == new_dbm && ms_dbm > current_dbm);
+
+ if (lchan->ms_power == new_power_lvl || ignore) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping MS power at control level %d (%d dBm): "
+ "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm\n",
+ new_power_lvl, ms_dbm, ms_power_lvl, bsc_max_dbm, ul_rssi_dbm, rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh));
+ return 0;
+ }
+
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s MS power control level %d (%d dBm) => %d (%d dBm): "
+ "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm\n",
+ (new_dbm > current_dbm) ? "Raising" : "Lowering",
+ lchan->ms_power, current_dbm, new_power_lvl, new_dbm, ms_power_lvl,
+ bsc_max_dbm, ul_rssi_dbm, rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh));
+
+ lchan_update_ms_power_ctrl_level(lchan, new_dbm);
+
+ return 1;
+
+}
+
+/* Default MS/BS Power Control parameters (see 3GPP TS 45.008, table A.1) */
+const struct gsm_power_ctrl_params power_ctrl_params_def = {
+ /* Static Power Control is the safe default */
+ .mode = GSM_PWR_CTRL_MODE_STATIC,
+
+ /* BS Power reduction value / maximum (in dB) */
+ .bs_power_val_db = 0, /* no attenuation in static mode */
+ .bs_power_max_db = 12, /* up to 12 dB in dynamic mode */
+
+ /* Power increasing/reducing step size */
+ .inc_step_size_db = 4, /* 2, 4, or 6 dB */
+ .red_step_size_db = 2, /* 2 or 4 dB */
+
+ /* RxLev measurement parameters */
+ .rxlev_meas = {
+ .enabled = true,
+ /* Thresholds for RxLev (see 3GPP TS 45.008, A.3.2.1) */
+ .lower_thresh = 32, /* L_RXLEV_XX_P (-78 dBm) */
+ .upper_thresh = 38, /* U_RXLEV_XX_P (-72 dBm) */
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_RXLEV_XX_P */
+ .lower_cmp_p = 10, /* P1 as in 3GPP TS 45.008, A.3.2.1 (case a) */
+ .lower_cmp_n = 12, /* N1 as in 3GPP TS 45.008, A.3.2.1 (case a) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_RXLEV_XX_P */
+ .upper_cmp_p = 19, /* P2 as in 3GPP TS 45.008, A.3.2.1 (case b) */
+ .upper_cmp_n = 20, /* N2 as in 3GPP TS 45.008, A.3.2.1 (case b) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+
+ /* RxQual measurement parameters */
+ .rxqual_meas = {
+ .enabled = true,
+ /* Thresholds for RxQual (see 3GPP TS 45.008, A.3.2.1) */
+ .lower_thresh = 3, /* L_RXQUAL_XX_P (0.8% <= BER < 1.6%) */
+ .upper_thresh = 0, /* U_RXQUAL_XX_P (BER < 0.2%) */
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_RXQUAL_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_RXQUAL_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+
+ /* C/I measurement parameters.
+ * Target C/I retrieved from "GSM/EDGE: Evolution and Performance" Table 10.3.
+ * Set lower and upper so that (lower + upper) / 2 is equal or slightly
+ * above the target.
+ */
+ .ci_fr_meas = { /* FR: Target C/I = 15 dB, Soft blocking threshold = 10 dB */
+ .enabled = false,
+ .lower_thresh = 13,
+ .upper_thresh = 17,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_FR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_FR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_hr_meas = { /* HR: Target C/I = 18 dB, Soft blocking threshold = 13 dB */
+ .enabled = false,
+ .lower_thresh = 16,
+ .upper_thresh = 21,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_HR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_HR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_amr_fr_meas = { /* AMR-FR: Target C/I = 9 dB, Soft blocking threshold = 4 dB */
+ .enabled = false,
+ .lower_thresh = 7,
+ .upper_thresh = 11,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_AMR_FR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_AMR_FR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_amr_hr_meas = { /* AMR-HR: Target C/I = 15 dB, Soft blocking threshold = 10 dB */
+ .enabled = false,
+ .lower_thresh = 13,
+ .upper_thresh = 17,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_AMR_HR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_AMR_HR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_sdcch_meas = { /* SDCCH: Target C/I = 14 dB, Soft blocking threshold = 9 dB */
+ .enabled = false,
+ .lower_thresh = 12,
+ .upper_thresh = 16,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_SDCCH_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_SDCCH_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_gprs_meas = { /* GPRS: Target C/I = 20 dB, Soft blocking threshold = 15 dB */
+ .enabled = false,
+ .lower_thresh = 18,
+ .upper_thresh = 24,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_GPRS_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_GPRS_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+};
+
+void power_ctrl_params_def_reset(struct gsm_power_ctrl_params *params,
+ enum gsm_power_ctrl_dir dir)
+{
+ *params = power_ctrl_params_def;
+ params->dir = dir;
+
+ /* Trigger loop every N-th SACCH block. See 3GPP TS 45.008 section 4.7.1. */
+ if (dir == GSM_PWR_CTRL_DIR_UL)
+ params->ctrl_interval = 2; /* N=4 (1.92s) */
+ else
+ params->ctrl_interval = 1; /* N=2 (0.960) */
+}
diff --git a/src/osmo-bsc/rest_octets.c b/src/osmo-bsc/rest_octets.c
deleted file mode 100644
index 7307cb888..000000000
--- a/src/osmo-bsc/rest_octets.c
+++ /dev/null
@@ -1,899 +0,0 @@
-/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface,
- * rest octet handling according to
- * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
-
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- *
- * 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 <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <stdbool.h>
-
-#include <osmocom/bsc/debug.h>
-#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/core/bitvec.h>
-#include <osmocom/gsm/bitvec_gsm.h>
-#include <osmocom/bsc/rest_octets.h>
-#include <osmocom/bsc/arfcn_range_encode.h>
-#include <osmocom/bsc/system_information.h>
-#include <osmocom/bsc/bts.h>
-
-/* generate SI1 rest octets */
-int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 1;
-
- if (nch_pos) {
- bitvec_set_bit(&bv, H);
- bitvec_set_uint(&bv, *nch_pos, 5);
- } else
- bitvec_set_bit(&bv, L);
-
- if (is1800_net)
- bitvec_set_bit(&bv, L);
- else
- bitvec_set_bit(&bv, H);
-
- bitvec_spare_padding(&bv, 6);
- return bv.data_len;
-}
-
-/* Append Repeated E-UTRAN Neighbour Cell to bitvec: see 3GPP TS 44.018 Table 10.5.2.33b.1 */
-static inline bool append_eutran_neib_cell(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget)
-{
- const struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- unsigned i, skip = 0;
- size_t offset = bts->e_offset;
- int16_t rem = budget - 6; /* account for mandatory stop bit and THRESH_E-UTRAN_high */
- uint8_t earfcn_budget;
-
- if (budget <= 6)
- return false;
-
- OSMO_ASSERT(budget <= SI2Q_MAX_LEN);
-
- /* first we have to properly adjust budget requirements */
- if (e->prio_valid) /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/
- rem -= 4;
- else
- rem--;
-
- if (e->thresh_lo_valid) /* THRESH_E-UTRAN_low: */
- rem -= 6;
- else
- rem--;
-
- if (e->qrxlm_valid) /* E-UTRAN_QRXLEVMIN: */
- rem -= 6;
- else
- rem--;
-
- if (rem < 0)
- return false;
-
- /* now we can proceed with actually adding EARFCNs within adjusted budget limit */
- for (i = 0; i < e->length; i++) {
- if (e->arfcn[i] != OSMO_EARFCN_INVALID) {
- if (skip < offset) {
- skip++; /* ignore EARFCNs added on previous calls */
- } else {
- earfcn_budget = 17; /* compute budget per-EARFCN */
- if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i])
- earfcn_budget++;
- else
- earfcn_budget += 4;
-
- if (rem - earfcn_budget < 0)
- break;
- else {
- bts->e_offset++;
- rem -= earfcn_budget;
-
- if (rem < 0)
- return false;
-
- bitvec_set_bit(bv, 1); /* EARFCN: */
- bitvec_set_uint(bv, e->arfcn[i], 16);
-
- if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i])
- bitvec_set_bit(bv, 0);
- else { /* Measurement Bandwidth: 9.1.54 */
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, e->meas_bw[i], 3);
- }
- }
- }
- }
- }
-
- /* stop bit - end of EARFCN + Measurement Bandwidth sequence */
- bitvec_set_bit(bv, 0);
-
- /* Note: we don't support different EARFCN arrays each with different priority, threshold etc. */
-
- if (e->prio_valid) {
- /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, e->prio, 3);
- } else
- bitvec_set_bit(bv, 0);
-
- /* THRESH_E-UTRAN_high */
- bitvec_set_uint(bv, e->thresh_hi, 5);
-
- if (e->thresh_lo_valid) {
- /* THRESH_E-UTRAN_low: */
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, e->thresh_lo, 5);
- } else
- bitvec_set_bit(bv, 0);
-
- if (e->qrxlm_valid) {
- /* E-UTRAN_QRXLEVMIN: */
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, e->qrxlm, 5);
- } else
- bitvec_set_bit(bv, 0);
-
- return true;
-}
-
-static inline void append_earfcn(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget)
-{
- bool appended;
- unsigned int old = bv->cur_bit; /* save current position to make rollback possible */
- int rem = ((int)budget) - 40;
- if (rem <= 0)
- return;
-
- OSMO_ASSERT(budget <= SI2Q_MAX_LEN);
-
- /* Additions in Rel-5: */
- bitvec_set_bit(bv, H);
- /* No 3G Additional Measurement Param. Descr. */
- bitvec_set_bit(bv, 0);
- /* No 3G ADDITIONAL MEASUREMENT Param. Descr. 2 */
- bitvec_set_bit(bv, 0);
- /* Additions in Rel-6: */
- bitvec_set_bit(bv, H);
- /* 3G_CCN_ACTIVE */
- bitvec_set_bit(bv, 0);
- /* Additions in Rel-7: */
- bitvec_set_bit(bv, H);
- /* No 700_REPORTING_OFFSET */
- bitvec_set_bit(bv, 0);
- /* No 810_REPORTING_OFFSET */
- bitvec_set_bit(bv, 0);
- /* Additions in Rel-8: */
- bitvec_set_bit(bv, H);
-
- /* Priority and E-UTRAN Parameters Description */
- bitvec_set_bit(bv, 1);
-
- /* budget: 10 bits used above */
-
- /* Serving Cell Priority Parameters Descr. is Present,
- * see also: 3GPP TS 44.018, Table 10.5.2.33b.1 */
- bitvec_set_bit(bv, 1);
-
- /* GERAN_PRIORITY */
- bitvec_set_uint(bv, 0, 3);
-
- /* THRESH_Priority_Search */
- bitvec_set_uint(bv, 0, 4);
-
- /* THRESH_GSM_low */
- bitvec_set_uint(bv, 0, 4);
-
- /* H_PRIO */
- bitvec_set_uint(bv, 0, 2);
-
- /* T_Reselection */
- bitvec_set_uint(bv, 0, 2);
-
- /* budget: 26 bits used above */
-
- /* No 3G Priority Parameters Description */
- bitvec_set_bit(bv, 0);
- /* E-UTRAN Parameters Description */
- bitvec_set_bit(bv, 1);
-
- /* E-UTRAN_CCN_ACTIVE */
- bitvec_set_bit(bv, 0);
- /* E-UTRAN_Start: 9.1.54 */
- bitvec_set_bit(bv, 1);
- /* E-UTRAN_Stop: 9.1.54 */
- bitvec_set_bit(bv, 1);
-
- /* No E-UTRAN Measurement Parameters Descr. */
- bitvec_set_bit(bv, 0);
- /* No GPRS E-UTRAN Measurement Param. Descr. */
- bitvec_set_bit(bv, 0);
-
- /* Note: each of next 3 "repeated" structures might be repeated any
- (0, 1, 2...) times - we only support 1 and 0 */
-
- /* Repeated E-UTRAN Neighbour Cells */
- bitvec_set_bit(bv, 1);
-
- /* budget: 34 bits used above */
-
- appended = append_eutran_neib_cell(bv, bts, rem);
- if (!appended) { /* appending is impossible within current budget: rollback */
- bv->cur_bit = old;
- return;
- }
-
- /* budget: further 6 bits used below, totalling 40 bits */
-
- /* stop bit - end of Repeated E-UTRAN Neighbour Cells sequence: */
- bitvec_set_bit(bv, 0);
-
- /* Note: following 2 repeated structs are not supported ATM */
- /* stop bit - end of Repeated E-UTRAN Not Allowed Cells sequence: */
- bitvec_set_bit(bv, 0);
- /* stop bit - end of Repeated E-UTRAN PCID to TA mapping sequence: */
- bitvec_set_bit(bv, 0);
-
- /* Priority and E-UTRAN Parameters Description ends here */
- /* No 3G CSG Description */
- bitvec_set_bit(bv, 0);
- /* No E-UTRAN CSG Description */
- bitvec_set_bit(bv, 0);
- /* No Additions in Rel-9: */
- bitvec_set_bit(bv, L);
-}
-
-static inline int f0_helper(int *sc, size_t length, uint8_t *chan_list)
-{
- int w[RANGE_ENC_MAX_ARFCNS] = { 0 };
-
- return range_encode(ARFCN_RANGE_1024, sc, length, w, 0, chan_list);
-}
-
-/* Estimate how many bits it'll take to append single FDD UARFCN */
-static inline int append_utran_fdd_length(uint16_t u, const int *sc, size_t sc_len, size_t length)
-{
- uint8_t chan_list[16] = { 0 };
- int tmp[sc_len], f0;
-
- memcpy(tmp, sc, sizeof(tmp));
-
- f0 = f0_helper(tmp, length, chan_list);
- if (f0 < 0)
- return f0;
-
- return 21 + range1024_p(length);
-}
-
-/* Append single FDD UARFCN */
-static inline int append_utran_fdd(struct bitvec *bv, uint16_t u, int *sc, size_t length)
-{
- uint8_t chan_list[16] = { 0 };
- int f0 = f0_helper(sc, length, chan_list);
-
- if (f0 < 0)
- return f0;
-
- /* Repeated UTRAN FDD Neighbour Cells */
- bitvec_set_bit(bv, 1);
-
- /* FDD-ARFCN */
- bitvec_set_bit(bv, 0);
- bitvec_set_uint(bv, u, 14);
-
- /* FDD_Indic0: parameter value '0000000000' is a member of the set? */
- bitvec_set_bit(bv, f0);
- /* NR_OF_FDD_CELLS */
- bitvec_set_uint(bv, length, 5);
-
- f0 = bv->cur_bit;
- bitvec_add_range1024(bv, (struct gsm48_range_1024 *)chan_list);
- bv->cur_bit = f0 + range1024_p(length);
-
- return 21 + range1024_p(length);
-}
-
-static inline int try_adding_uarfcn(struct bitvec *bv, struct gsm_bts *bts, uint16_t uarfcn,
- uint8_t num_sc, uint8_t start_pos, uint8_t budget)
-{
- int i, k, rc, a[bts->si_common.uarfcn_length];
-
- if (budget < 23)
- return -ENOMEM;
-
- /* copy corresponding Scrambling Codes: range encoder make in-place modifications */
- for (i = start_pos, k = 0; i < num_sc; a[k++] = bts->si_common.data.scramble_list[i++]);
-
- /* estimate bit length requirements */
- rc = append_utran_fdd_length(uarfcn, a, bts->si_common.uarfcn_length, k);
- if (rc < 0)
- return rc; /* range encoder failure */
-
- if (budget - rc <= 0)
- return -ENOMEM; /* we have ran out of budget in current SI2q */
-
- /* compute next offset */
- bts->u_offset += k;
-
- return budget - append_utran_fdd(bv, uarfcn, a, k);
-}
-
-/* Append multiple FDD UARFCNs */
-static inline void append_uarfcns(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget)
-{
- const uint16_t *u = bts->si_common.data.uarfcn_list;
- int i, rem = budget - 7, st = bts->u_offset; /* account for constant bits right away */
- uint16_t cu = u[bts->u_offset]; /* caller ensures that length is positive */
-
- OSMO_ASSERT(budget <= SI2Q_MAX_LEN);
-
- if (budget <= 7)
- return;
-
- /* 3G Neighbour Cell Description */
- bitvec_set_bit(bv, 1);
- /* No Index_Start_3G */
- bitvec_set_bit(bv, 0);
- /* No Absolute_Index_Start_EMR */
- bitvec_set_bit(bv, 0);
-
- /* UTRAN FDD Description */
- bitvec_set_bit(bv, 1);
- /* No Bandwidth_FDD */
- bitvec_set_bit(bv, 0);
-
- for (i = bts->u_offset; i <= bts->si_common.uarfcn_length; i++)
- if (u[i] != cu) { /* we've reached new UARFCN */
- rem = try_adding_uarfcn(bv, bts, cu, i, st, rem);
- if (rem < 0)
- break;
-
- if (i < bts->si_common.uarfcn_length) {
- cu = u[i];
- st = i;
- } else
- break;
- }
-
- /* stop bit - end of Repeated UTRAN FDD Neighbour Cells */
- bitvec_set_bit(bv, 0);
-
- /* UTRAN TDD Description */
- bitvec_set_bit(bv, 0);
-}
-
-/* generate SI2quater rest octets: 3GPP TS 44.018 § 10.5.2.33b */
-int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts)
-{
- int rc;
- struct bitvec bv;
-
- if (bts->si2q_count < bts->si2q_index)
- return -EINVAL;
-
- bv.data = data;
- bv.data_len = 20;
- bitvec_zero(&bv);
-
- /* BA_IND: Set to '0' as that's what we use for SI2xxx type,
- * whereas '1' is used for SI5xxx type messages. The point here
- * is to be able to correlate whether a given MS measurement
- * report was using the neighbor cells advertised in SI2 or in
- * SI5, as those two could very well be different */
- bitvec_set_bit(&bv, 0);
- /* 3G_BA_IND */
- bitvec_set_bit(&bv, 1);
- /* MP_CHANGE_MARK */
- bitvec_set_bit(&bv, 0);
-
- /* SI2quater_INDEX */
- bitvec_set_uint(&bv, bts->si2q_index, 4);
- /* SI2quater_COUNT */
- bitvec_set_uint(&bv, bts->si2q_count, 4);
-
- /* No Measurement_Parameters Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_Real Time Difference Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_BSIC Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_REPORT PRIORITY Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_MEASUREMENT_Parameters Description */
- bitvec_set_bit(&bv, 0);
- /* No NC Measurement Parameters */
- bitvec_set_bit(&bv, 0);
- /* No extension (length) */
- bitvec_set_bit(&bv, 0);
-
- rc = SI2Q_MAX_LEN - (bv.cur_bit + 3);
- if (rc > 0 && bts->si_common.uarfcn_length - bts->u_offset > 0)
- append_uarfcns(&bv, bts, rc);
- else /* No 3G Neighbour Cell Description */
- bitvec_set_bit(&bv, 0);
-
- /* No 3G Measurement Parameters Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_3G_MEASUREMENT Parameters Descr. */
- bitvec_set_bit(&bv, 0);
-
- rc = SI2Q_MAX_LEN - bv.cur_bit;
- if (rc > 0 && si2q_earfcn_count(&bts->si_common.si2quater_neigh_list) - bts->e_offset > 0)
- append_earfcn(&bv, bts, rc);
- else /* No Additions in Rel-5: */
- bitvec_set_bit(&bv, L);
-
- bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
- return bv.data_len;
-}
-
-/* Append selection parameters to bitvec */
-static void append_selection_params(struct bitvec *bv,
- const struct gsm48_si_selection_params *sp)
-{
- if (sp->present) {
- bitvec_set_bit(bv, H);
- bitvec_set_bit(bv, sp->cbq);
- bitvec_set_uint(bv, sp->cell_resel_off, 6);
- bitvec_set_uint(bv, sp->temp_offs, 3);
- bitvec_set_uint(bv, sp->penalty_time, 5);
- } else
- bitvec_set_bit(bv, L);
-}
-
-/* Append power offset to bitvec */
-static void append_power_offset(struct bitvec *bv,
- const struct gsm48_si_power_offset *po)
-{
- if (po->present) {
- bitvec_set_bit(bv, H);
- bitvec_set_uint(bv, po->power_offset, 2);
- } else
- bitvec_set_bit(bv, L);
-}
-
-/* Append GPRS indicator to bitvec */
-static void append_gprs_ind(struct bitvec *bv,
- const struct gsm48_si3_gprs_ind *gi)
-{
- if (gi->present) {
- bitvec_set_bit(bv, H);
- bitvec_set_uint(bv, gi->ra_colour, 3);
- /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */
- bitvec_set_bit(bv, gi->si13_position);
- } else
- bitvec_set_bit(bv, L);
-}
-
-/* Generate SI2ter Rest Octests 3GPP TS 44.018 Table 10.5.2.33a.1 */
-int rest_octets_si2ter(uint8_t *data)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 4;
-
- /* No SI2ter_MP_CHANGE_MARK */
- bitvec_set_bit(&bv, L);
-
- bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
-
- return bv.data_len;
-}
-
-/* Generate SI2bis Rest Octests 3GPP TS 44.018 Table 10.5.2.33.1 */
-int rest_octets_si2bis(uint8_t *data)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 1;
-
- bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
-
- return bv.data_len;
-}
-
-/* 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)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 4;
-
- /* Optional Selection Parameters */
- append_selection_params(&bv, &si3->selection_params);
-
- /* Optional Power Offset */
- append_power_offset(&bv, &si3->power_offset);
-
- /* Do we have a SI2ter on the BCCH? */
- if (si3->si2ter_indicator)
- bitvec_set_bit(&bv, H);
- else
- bitvec_set_bit(&bv, L);
-
- /* Early Classmark Sending Control */
- if (si3->early_cm_ctrl)
- bitvec_set_bit(&bv, H);
- else
- bitvec_set_bit(&bv, L);
-
- /* Do we have a SI Type 9 on the BCCH? */
- if (si3->scheduling.present) {
- bitvec_set_bit(&bv, H);
- bitvec_set_uint(&bv, si3->scheduling.where, 3);
- } else
- bitvec_set_bit(&bv, L);
-
- /* GPRS Indicator */
- append_gprs_ind(&bv, &si3->gprs_ind);
-
- /* 3G Early Classmark Sending Restriction. If H, then controlled by
- * early_cm_ctrl above */
- if (si3->early_cm_restrict_3g)
- bitvec_set_bit(&bv, L);
- else
- bitvec_set_bit(&bv, H);
-
- if (si3->si2quater_indicator) {
- bitvec_set_bit(&bv, H); /* indicator struct present */
- bitvec_set_uint(&bv, 0, 1); /* message is sent on BCCH Norm */
- }
-
- bitvec_spare_padding(&bv, (bv.data_len*8)-1);
- return bv.data_len;
-}
-
-static int append_lsa_params(struct bitvec *bv,
- const struct gsm48_lsa_params *lsa_params)
-{
- /* FIXME */
- return -1;
-}
-
-/* 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)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = len;
-
- /* SI4 Rest Octets O */
- append_selection_params(&bv, &si4->selection_params);
- append_power_offset(&bv, &si4->power_offset);
- append_gprs_ind(&bv, &si4->gprs_ind);
-
- if (0 /* FIXME */) {
- /* H and SI4 Rest Octets S */
- bitvec_set_bit(&bv, H);
-
- /* LSA Parameters */
- if (si4->lsa_params.present) {
- bitvec_set_bit(&bv, H);
- append_lsa_params(&bv, &si4->lsa_params);
- } else
- bitvec_set_bit(&bv, L);
-
- /* Cell Identity */
- if (1) {
- bitvec_set_bit(&bv, H);
- bitvec_set_uint(&bv, si4->cell_id, 16);
- } else
- bitvec_set_bit(&bv, L);
-
- /* LSA ID Information */
- if (0) {
- bitvec_set_bit(&bv, H);
- /* FIXME */
- } else
- bitvec_set_bit(&bv, L);
- } else {
- /* L and break indicator */
- bitvec_set_bit(&bv, L);
- bitvec_set_bit(&bv, si4->break_ind ? H : L);
- }
-
- return bv.data_len;
-}
-
-
-/* GSM 04.18 ETSI TS 101 503 V8.27.0 (2006-05)
-
-<SI6 rest octets> ::=
-{L | H <PCH and NCH info>}
-{L | H <VBS/VGCS options : bit(2)>}
-{ < DTM_support : bit == L > I < DTM_support : bit == H >
-< RAC : bit (8) >
-< MAX_LAPDm : bit (3) > }
-< Band indicator >
-{ L | H < GPRS_MS_TXPWR_MAX_CCH : bit (5) > }
-<implicit spare >;
-*/
-int rest_octets_si6(uint8_t *data, bool is1800_net)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 1;
-
- /* no PCH/NCH info */
- bitvec_set_bit(&bv, L);
- /* no VBS/VGCS options */
- bitvec_set_bit(&bv, L);
- /* no DTM_support */
- bitvec_set_bit(&bv, L);
- /* band indicator */
- if (is1800_net)
- bitvec_set_bit(&bv, L);
- else
- bitvec_set_bit(&bv, H);
- /* no GPRS_MS_TXPWR_MAX_CCH */
- bitvec_set_bit(&bv, L);
-
- bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
- return bv.data_len;
-}
-
-/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a:
- < GPRS Mobile Allocation IE > ::=
- < HSN : bit (6) >
- { 0 | 1 < RFL number list : < RFL number list struct > > }
- { 0 < MA_LENGTH : bit (6) >
- < MA_BITMAP: bit (val(MA_LENGTH) + 1) >
- | 1 { 0 | 1 <ARFCN index list : < ARFCN index list struct > > } } ;
-
- < RFL number list struct > :: =
- < RFL_NUMBER : bit (4) >
- { 0 | 1 < RFL number list struct > } ;
- < ARFCN index list struct > ::=
- < ARFCN_INDEX : bit(6) >
- { 0 | 1 < ARFCN index list struct > } ;
- */
-static int append_gprs_mobile_alloc(struct bitvec *bv)
-{
- /* Hopping Sequence Number */
- bitvec_set_uint(bv, 0, 6);
-
- if (0) {
- /* We want to use a RFL number list */
- bitvec_set_bit(bv, 1);
- /* FIXME: RFL number list */
- } else
- bitvec_set_bit(bv, 0);
-
- if (0) {
- /* We want to use a MA_BITMAP */
- bitvec_set_bit(bv, 0);
- /* FIXME: MA_LENGTH, MA_BITMAP, ... */
- } else {
- bitvec_set_bit(bv, 1);
- if (0) {
- /* We want to provide an ARFCN index list */
- bitvec_set_bit(bv, 1);
- /* FIXME */
- } else
- bitvec_set_bit(bv, 0);
- }
- return 0;
-}
-
-static int encode_t3192(unsigned int t3192)
-{
- /* See also 3GPP TS 44.060
- Table 12.24.2: GPRS Cell Options information element details */
- if (t3192 == 0)
- return 3;
- else if (t3192 <= 80)
- return 4;
- else if (t3192 <= 120)
- return 5;
- else if (t3192 <= 160)
- return 6;
- else if (t3192 <= 200)
- return 7;
- else if (t3192 <= 500)
- return 0;
- else if (t3192 <= 1000)
- return 1;
- else if (t3192 <= 1500)
- return 2;
- else
- return -EINVAL;
-}
-
-static int encode_drx_timer(unsigned int drx)
-{
- if (drx == 0)
- return 0;
- else if (drx == 1)
- return 1;
- else if (drx == 2)
- return 2;
- else if (drx <= 4)
- return 3;
- else if (drx <= 8)
- return 4;
- else if (drx <= 16)
- return 5;
- else if (drx <= 32)
- return 6;
- else if (drx <= 64)
- return 7;
- else
- return -EINVAL;
-}
-
-/* GPRS Cell Options as per TS 04.60 Chapter 12.24
- < GPRS Cell Options IE > ::=
- < NMO : bit(2) >
- < T3168 : bit(3) >
- < T3192 : bit(3) >
- < DRX_TIMER_MAX: bit(3) >
- < ACCESS_BURST_TYPE: bit >
- < CONTROL_ACK_TYPE : bit >
- < BS_CV_MAX: bit(4) >
- { 0 | 1 < PAN_DEC : bit(3) >
- < PAN_INC : bit(3) >
- < PAN_MAX : bit(3) >
- { 0 | 1 < Extension Length : bit(6) >
- < bit (val(Extension Length) + 1
- & { < Extension Information > ! { bit ** = <no string> } } ;
- < Extension Information > ::=
- { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit >
- < BEP_PERIOD : bit(4) > }
- < PFC_FEATURE_MODE : bit >
- < DTM_SUPPORT : bit >
- <BSS_PAGING_COORDINATION: bit >
- <spare bit > ** ;
- */
-static int append_gprs_cell_opt(struct bitvec *bv,
- const struct gprs_cell_options *gco)
-{
- int t3192, drx_timer_max;
-
- t3192 = encode_t3192(gco->t3192);
- if (t3192 < 0)
- return t3192;
-
- drx_timer_max = encode_drx_timer(gco->drx_timer_max);
- if (drx_timer_max < 0)
- return drx_timer_max;
-
- bitvec_set_uint(bv, gco->nmo, 2);
-
- /* See also 3GPP TS 44.060
- Table 12.24.2: GPRS Cell Options information element details */
- bitvec_set_uint(bv, gco->t3168 / 500 - 1, 3);
-
- bitvec_set_uint(bv, t3192, 3);
- bitvec_set_uint(bv, drx_timer_max, 3);
- /* ACCESS_BURST_TYPE: Hard-code 8bit */
- bitvec_set_bit(bv, 0);
- /* CONTROL_ACK_TYPE: */
- bitvec_set_bit(bv, gco->ctrl_ack_type_use_block);
- bitvec_set_uint(bv, gco->bs_cv_max, 4);
-
- if (0) {
- /* hard-code no PAN_{DEC,INC,MAX} */
- bitvec_set_bit(bv, 0);
- } else {
- /* copied from ip.access BSC protocol trace */
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, 1, 3); /* DEC */
- bitvec_set_uint(bv, 1, 3); /* INC */
- bitvec_set_uint(bv, 15, 3); /* MAX */
- }
-
- if (!gco->ext_info_present) {
- /* no extension information */
- bitvec_set_bit(bv, 0);
- } else {
- /* extension information */
- bitvec_set_bit(bv, 1);
- if (!gco->ext_info.egprs_supported) {
- /* 6bit length of extension */
- bitvec_set_uint(bv, (1 + 3)-1, 6);
- /* EGPRS supported in the cell */
- bitvec_set_bit(bv, 0);
- } else {
- /* 6bit length of extension */
- bitvec_set_uint(bv, (1 + 5 + 3)-1, 6);
- /* EGPRS supported in the cell */
- bitvec_set_bit(bv, 1);
-
- /* 1bit EGPRS PACKET CHANNEL REQUEST (inverted logic) */
- bitvec_set_bit(bv, !gco->ext_info.use_egprs_p_ch_req);
-
- /* 4bit BEP PERIOD */
- bitvec_set_uint(bv, gco->ext_info.bep_period, 4);
- }
- bitvec_set_bit(bv, gco->ext_info.pfc_supported);
- bitvec_set_bit(bv, gco->ext_info.dtm_supported);
- bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination);
- }
-
- return 0;
-}
-
-static void append_gprs_pwr_ctrl_pars(struct bitvec *bv,
- const struct gprs_power_ctrl_pars *pcp)
-{
- bitvec_set_uint(bv, pcp->alpha, 4);
- bitvec_set_uint(bv, pcp->t_avg_w, 5);
- bitvec_set_uint(bv, pcp->t_avg_t, 5);
- bitvec_set_uint(bv, pcp->pc_meas_chan, 1);
- bitvec_set_uint(bv, pcp->n_avg_i, 4);
-}
-
-/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */
-int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 20;
-
- if (0) {
- /* No rest octets */
- bitvec_set_bit(&bv, L);
- } else {
- bitvec_set_bit(&bv, H);
- bitvec_set_uint(&bv, si13->bcch_change_mark, 3);
- bitvec_set_uint(&bv, si13->si_change_field, 4);
- if (1) {
- bitvec_set_bit(&bv, 0);
- } else {
- bitvec_set_bit(&bv, 1);
- bitvec_set_uint(&bv, si13->bcch_change_mark, 2);
- append_gprs_mobile_alloc(&bv);
- }
- /* PBCCH not present in cell:
- it shall never be indicated according to 3GPP TS 44.018 Table 10.5.2.37b.1 */
- bitvec_set_bit(&bv, 0);
- bitvec_set_uint(&bv, si13->rac, 8);
- bitvec_set_bit(&bv, si13->spgc_ccch_sup);
- bitvec_set_uint(&bv, si13->prio_acc_thr, 3);
- bitvec_set_uint(&bv, si13->net_ctrl_ord, 2);
- append_gprs_cell_opt(&bv, &si13->cell_opts);
- append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars);
-
- /* 3GPP TS 44.018 Release 6 / 10.5.2.37b */
- bitvec_set_bit(&bv, H); /* added Release 99 */
- /* claim our SGSN is compatible with Release 99, as EDGE and EGPRS
- * was only added in this Release */
- bitvec_set_bit(&bv, 1);
- }
- bitvec_spare_padding(&bv, (bv.data_len*8)-1);
- return bv.data_len;
-}
diff --git a/src/osmo-bsc/smscb.c b/src/osmo-bsc/smscb.c
index b34780b6d..8e1c6348e 100644
--- a/src/osmo-bsc/smscb.c
+++ b/src/osmo-bsc/smscb.c
@@ -22,10 +22,12 @@
#include <limits.h>
#include <osmocom/core/stats.h>
+#include <osmocom/core/utils.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/byteswap.h>
+#include <osmocom/core/signal.h>
#include <osmocom/gsm/cbsp.h>
#include <osmocom/gsm/protocol/gsm_23_041.h>
@@ -37,11 +39,11 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/smscb.h>
-#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/signal.h>
/*********************************************************************************
* Helper Functions
@@ -58,8 +60,6 @@ static void llist_replace_head(struct llist_head *new, struct llist_head *old)
INIT_LLIST_HEAD(old);
}
-#define ETWS_PRIM_NOTIF_SIZE 56
-
/* Build a ETWS Primary Notification message as per TS 23.041 9.4.1.3 */
static int gen_etws_primary_notification(uint8_t *out, uint16_t serial_nr, uint16_t msg_id,
uint16_t warn_type, const uint8_t *sec_info)
@@ -71,13 +71,24 @@ static int gen_etws_primary_notification(uint8_t *out, uint16_t serial_nr, uint1
osmo_store16be(serial_nr, out);
etws->msg_id = osmo_htons(msg_id);
etws->warning_type = osmo_htons(warn_type);
-
- if (sec_info)
- memcpy(etws->data, sec_info, ETWS_PRIM_NOTIF_SIZE - sizeof(*etws));
+ memcpy(etws->data, sec_info, ETWS_PRIM_NOTIF_SIZE - sizeof(*etws));
return ETWS_PRIM_NOTIF_SIZE;
}
+static void bts_cbch_init_state(struct bts_smscb_chan_state *cstate, struct gsm_bts *bts)
+{
+ cstate->bts = bts;
+ INIT_LLIST_HEAD(&cstate->messages);
+}
+
+void bts_cbch_init(struct gsm_bts *bts)
+{
+ bts_cbch_init_state(&bts->cbch_basic, bts);
+ bts_cbch_init_state(&bts->cbch_extended, bts);
+ osmo_timer_setup(&bts->cbch_timer, &bts_cbch_timer_cb, bts);
+}
+
/*! Obtain SMSCB Channel State for given BTS (basic or extended CBCH) */
struct bts_smscb_chan_state *bts_get_smscb_chan(struct gsm_bts *bts, bool extended)
{
@@ -110,6 +121,8 @@ static void __bts_smscb_add(struct bts_smscb_chan_state *cstate, struct bts_smsc
return;
}
}
+ /* we didn't find any messages with longer period than us, insert us at tail */
+ llist_add_tail(&new->list, &cstate->messages);
}
/* stringify a SMSCB for logging */
@@ -242,6 +255,12 @@ static void append_bcast_compl(struct response_state *r_state, struct gsm_bts *b
llist_add_tail(&cent->list, &r_state->num_completed.list);
}
+static bool etws_msg_id_matches(uint16_t a, uint16_t b)
+{
+ /* ETWS messages are identified by the twelve most significant bits of the Message ID */
+ return (a & 0xFFF0) == (b & 0xFFF0);
+}
+
/*! Iterate over all BTSs, find matching ones, execute command on BTS, add result
* to succeeded/failed lists.
* \param[in] net GSM network in which we operate
@@ -366,7 +385,7 @@ static struct bts_smscb_message *bts_smscb_msg_from_wrepl(struct gsm_bts *bts,
smscb->input.dcs = wrepl->u.cbs.dcs;
smscb->num_pages = llist_count(&wrepl->u.cbs.msg_content);
if (smscb->num_pages > ARRAY_SIZE(smscb->page)) {
- LOG_BTS(bts, DCBS, LOGL_ERROR, "SMSCB with too many pages (%u > %lu)\n",
+ LOG_BTS(bts, DCBS, LOGL_ERROR, "SMSCB with too many pages (%u > %zu)\n",
smscb->num_pages, ARRAY_SIZE(smscb->page));
talloc_free(smscb);
return NULL;
@@ -382,6 +401,9 @@ static struct bts_smscb_message *bts_smscb_msg_from_wrepl(struct gsm_bts *bts,
page = &smscb->page[i++];
msg_param = (struct gsm23041_msg_param_gsm *) &page->data[0];
+ /* ensure we don't overflow in the memcpy below */
+ osmo_static_assert(sizeof(*page) > sizeof(*msg_param) + sizeof(cont->data), smscb_space);
+
/* build 6 byte header according to TS 23.041 9.4.1.2 */
osmo_store16be(wrepl->new_serial_nr, &msg_param->serial_nr);
osmo_store16be(wrepl->msg_id, &msg_param->message_id);
@@ -391,7 +413,9 @@ static struct bts_smscb_message *bts_smscb_msg_from_wrepl(struct gsm_bts *bts,
OSMO_ASSERT(cont->user_len <= ARRAY_SIZE(cont->data));
OSMO_ASSERT(cont->user_len <= ARRAY_SIZE(page->data) - sizeof(*msg_param));
- memcpy(&msg_param->content, cont->data, cont->user_len);
+ /* we must not use cont->user_len as length here, as it would truncate any
+ * possible 7-bit padding at the end. Always copy the whole page */
+ memcpy(&msg_param->content, cont->data, sizeof(cont->data));
bytes_used = sizeof(*msg_param) + cont->user_len;
/* compute number of valid blocks in page */
page->num_blocks = bytes_used / 22;
@@ -452,6 +476,44 @@ int cbsp_tx_restart(struct bsc_cbc_link *cbc, bool is_emerg)
return cbsp_tx_decoded(cbc, cbsp);
}
+/* transmit a CBSP RESTART-INDICATION message stating a cell is operative again */
+int cbsp_tx_restart_bts(struct bsc_cbc_link *cbc, bool is_emerg, struct gsm_bts *bts)
+{
+ struct osmo_cbsp_decoded *cbsp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_RESTART);
+ struct osmo_cbsp_cell_ent cell_ent;
+ struct osmo_cell_global_id *cgi;
+
+ if (is_emerg)
+ cbsp->u.restart.bcast_msg_type = 0x01;
+ cbsp->u.restart.recovery_ind = 0x00; /* message data available */
+ cbsp->u.restart.cell_list.id_discr = CELL_IDENT_WHOLE_GLOBAL;
+
+ cgi = bts_get_cgi(bts);
+ cell_ent.cell_id.global = *cgi;
+ llist_add(&cell_ent.list, &cbsp->u.restart.cell_list.list);
+
+ return cbsp_tx_decoded(cbc, cbsp);
+}
+
+/* transmit a CBSP FAILURE-INDICATION message stating all message data was lost for one cell */
+int cbsp_tx_failure_bts(struct bsc_cbc_link *cbc, bool is_emerg, struct gsm_bts *bts)
+{
+ struct osmo_cbsp_decoded *cbsp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_FAILURE);
+ struct osmo_cbsp_fail_ent fail_ent;
+ struct osmo_cell_global_id *cgi;
+
+ if (is_emerg)
+ cbsp->u.failure.bcast_msg_type = 0x01;
+
+ cgi = bts_get_cgi(bts);
+ fail_ent.id_discr = CELL_IDENT_WHOLE_GLOBAL;
+ fail_ent.cell_id.global = *cgi;
+ fail_ent.cause = OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_OPERATIONAL;
+ llist_add(&fail_ent.list, &cbsp->u.failure.fail_list);
+
+ return cbsp_tx_decoded(cbc, cbsp);
+}
+
/* transmit a CBSP KEEPALIVE COMPLETE to the CBC */
static int tx_cbsp_keepalive_compl(struct bsc_cbc_link *cbc)
{
@@ -463,25 +525,34 @@ static int tx_cbsp_keepalive_compl(struct bsc_cbc_link *cbc)
* Per-BTS Processing of CBSP from CBC, called via cbsp_per_bts()
*********************************************************************************/
+static void etws_pn_stop(struct gsm_bts *bts, bool timeout)
+{
+ if (osmo_bts_has_feature(&bts->features, BTS_FEAT_ETWS_PN)) {
+ LOG_BTS(bts, DCBS, LOGL_NOTICE, "ETWS PN broadcast via PCH disabled (cause=%s)\n",
+ timeout ? "timeout" : "request");
+ rsl_etws_pn_command(bts, RSL_CHAN_PCH_AGCH, NULL, 0);
+ }
+ bts->etws.active = false;
+ if (!timeout)
+ osmo_timer_del(&bts->etws.timer);
+}
+
/* timer call-back once ETWS warning period has expired */
static void etws_pn_cb(void *data)
{
struct gsm_bts *bts = (struct gsm_bts *)data;
- LOG_BTS(bts, DCBS, LOGL_NOTICE, "ETWS PN Timeout; disabling broadcast via PCH\n");
- rsl_etws_pn_command(bts, RSL_CHAN_PCH_AGCH, NULL, 0);
+ etws_pn_stop(bts, true);
}
-static void etws_primary_to_bts(struct gsm_bts *bts, const struct osmo_cbsp_write_replace *wrepl)
+
+/* the actual "execution" part: Send ETWS to all active lchan in the BTS and via PCH */
+static void bts_send_etws(struct gsm_bts *bts)
{
- uint8_t etws_primary[ETWS_PRIM_NOTIF_SIZE];
+ struct bts_etws_state *bes = &bts->etws;
struct gsm_bts_trx *trx;
unsigned int count = 0;
int i, j;
- gen_etws_primary_notification(etws_primary, wrepl->new_serial_nr, wrepl->msg_id,
- wrepl->u.emergency.warning_type,
- wrepl->u.emergency.warning_sec_info);
-
/* iterate over all lchan in each TS in each TRX of this BTS */
llist_for_each_entry(trx, &bts->trx_list, list) {
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
@@ -490,8 +561,8 @@ static void etws_primary_to_bts(struct gsm_bts *bts, const struct osmo_cbsp_writ
struct gsm_lchan *lchan = &ts->lchan[j];
if (!lchan_may_receive_data(lchan))
continue;
- gsm48_send_rr_app_info(lchan, 0x1, 0x0, etws_primary,
- sizeof(etws_primary));
+ gsm48_send_rr_app_info(lchan, 0x1, 0x0, bes->primary,
+ sizeof(bes->primary));
count++;
}
}
@@ -502,18 +573,64 @@ static void etws_primary_to_bts(struct gsm_bts *bts, const struct osmo_cbsp_writ
/* Notify BTS of primary ETWS notification via vendor-specific Abis message */
if (osmo_bts_has_feature(&bts->features, BTS_FEAT_ETWS_PN)) {
- rsl_etws_pn_command(bts, RSL_CHAN_PCH_AGCH, etws_primary, sizeof(etws_primary));
+ rsl_etws_pn_command(bts, RSL_CHAN_PCH_AGCH, bes->primary, sizeof(bes->primary));
LOG_BTS(bts, DCBS, LOGL_NOTICE, "Sent ETWS Primary Notification via common channel\n");
- if (wrepl->u.emergency.warning_period != 0xffffffff) {
- osmo_timer_setup(&bts->etws_timer, etws_pn_cb, bts);
- osmo_timer_schedule(&bts->etws_timer, wrepl->u.emergency.warning_period, 0);
- } else
- LOG_BTS(bts, DCBS, LOGL_NOTICE, "Unlimited ETWS PN broadcast, this breaks "
- "normal network operation due to PCH blockage\n");
} else
LOG_BTS(bts, DCBS, LOGL_ERROR, "BTS doesn't support RSL command for ETWS PN\n");
}
+static int etws_primary_to_bts(struct gsm_bts *bts, const struct osmo_cbsp_write_replace *wrepl)
+{
+ struct bts_etws_state *bes = &bts->etws;
+
+ if (bes->active) {
+ /* we were already broadcasting emergency before receiving this WRITE-REPLACE */
+
+ /* If only the New Serial Number IE, and not the Old Serial Number IE, is included in the
+ * WRITE-REPLACE message, then the BSC shall interpret the message as a write request, i.e. a
+ * broadcast request of a new emergency message without replacing an ongoing emergency message
+ * broadcast. */
+ if (!wrepl->old_serial_nr) {
+ /* If a write request is received for a cell where an emergency message broadcast is
+ * currently ongoing, the write request is considered as failed */
+ LOG_BTS(bts, DCBS, LOGL_NOTICE, "Rx CBSP WRITE rejected due to ongoing emergency "
+ "while no Old Serial Nr IE present in CBSP WRITE\n");
+ return -CBSP_CAUSE_BSC_CAPACITY_EXCEEDED;
+ }
+
+ if (!etws_msg_id_matches(*wrepl->old_serial_nr, bes->input.serial_nr)) {
+ LOG_BTS(bts, DCBS, LOGL_NOTICE, "Rx CBSP WRITE-REPLACE old_serial 0x%04x doesn't match "
+ "current serial 0x%04x. Is the CBC confused?\n",
+ *wrepl->old_serial_nr, bes->input.serial_nr);
+ /* we allow the WRITE-REPLACE to continue, TS 48.049 doesn't specify how to
+ * handle situations like this */
+ }
+ }
+
+ /* copy over all the data to per-BTS private state */
+ bes->input.msg_id = wrepl->msg_id;
+ bes->input.serial_nr = wrepl->new_serial_nr;
+ bes->input.warn_type = wrepl->u.emergency.warning_type;
+ memcpy(bes->input.sec_info, wrepl->u.emergency.warning_sec_info, sizeof(bes->input.sec_info));
+
+ /* generate the encoded ETWS PN */
+ gen_etws_primary_notification(bes->primary, bes->input.serial_nr, bes->input.msg_id,
+ bes->input.warn_type, bes->input.sec_info);
+
+ bes->active = true;
+
+ bts_send_etws(bts);
+
+ /* start the expiration timer, if any */
+ if (wrepl->u.emergency.warning_period != 0xffffffff) {
+ osmo_timer_schedule(&bts->etws.timer, wrepl->u.emergency.warning_period, 0);
+ } else
+ LOG_BTS(bts, DCBS, LOGL_NOTICE, "Unlimited ETWS PN broadcast, this breaks "
+ "normal network operation due to PCH blockage\n");
+
+ return 0;
+}
+
/*! Try to execute a write-replace operation; roll-back if it fails.
* \param[in] chan_state BTS CBCH channel state
* \param[in] extended_cbch Basic (false) or Extended (true) CBCH
@@ -583,8 +700,7 @@ static int bts_rx_write_replace(struct gsm_bts *bts, const struct osmo_cbsp_deco
int rc;
if (!wrepl->is_cbs) {
- etws_primary_to_bts(bts, wrepl);
- return 0;
+ return etws_primary_to_bts(bts, wrepl);
}
/* check if cell has a CBCH at all */
@@ -630,21 +746,53 @@ static int bts_rx_kill(struct gsm_bts *bts, const struct osmo_cbsp_decoded *dec,
struct response_state *r_state, void *priv)
{
const struct osmo_cbsp_kill *kill = &dec->u.kill;
- struct bts_smscb_chan_state *chan_state;
- struct bts_smscb_message *smscb;
- bool extended = false;
- if (kill->channel_ind && *kill->channel_ind == 0x01)
- extended = true;
- chan_state = bts_get_smscb_chan(bts, extended);
+ if (kill->channel_ind) {
+ /* KILL for CBS message */
+ struct bts_smscb_chan_state *chan_state;
+ struct bts_smscb_message *smscb;
+ bool extended = false;
- /* Find message by msg_id + old_serial_nr */
- smscb = bts_find_smscb(chan_state, kill->msg_id, kill->old_serial_nr);
- if (!smscb)
- return -CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED;
+ if (*kill->channel_ind == 0x01)
+ extended = true;
+
+ chan_state = bts_get_smscb_chan(bts, extended);
+
+ /* Find message by msg_id + old_serial_nr */
+ smscb = bts_find_smscb(chan_state, kill->msg_id, kill->old_serial_nr);
+ if (!smscb)
+ return -CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED;
+
+ append_bcast_compl(r_state, chan_state->bts, smscb);
+
+ /* Remove it */
+ bts_smscb_del(smscb, chan_state, "KILL");
+ } else {
+ /* KILL for Emergency */
+ struct bts_etws_state *bes = &bts->etws;
+
+ if (!bes->active) {
+ LOG_BTS(bts, DCBS, LOGL_NOTICE, "Rx CBSP KILL (Emerg) but no emergency "
+ "broadcast is currently active in this cell\n");
+ return -CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED;
+ }
+
+ if (kill->msg_id != bes->input.msg_id) {
+ LOG_BTS(bts, DCBS, LOGL_NOTICE, "Rx CBSP KILL (Emerg) for msg_id 0x%04x, but "
+ "current emergency msg_id is 0x%04x\n", kill->msg_id, bes->input.msg_id);
+ return -CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED;
+ }
+
+ if (!etws_msg_id_matches(kill->old_serial_nr, bes->input.serial_nr)) {
+ LOG_BTS(bts, DCBS, LOGL_NOTICE, "Rx CBSP KILL (Emerg) for old_serial_nr 0x%04x, but "
+ "current emergency serial_nr is 0x%04x\n",
+ kill->old_serial_nr, bes->input.serial_nr);
+ return -CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED;
+ }
- /* Remove it */
- bts_smscb_del(smscb, chan_state, "KILL");
+ /* stop broadcasting the PN in this BTS */
+ etws_pn_stop(bts, false);
+ }
return 0;
}
@@ -666,13 +814,36 @@ static int bts_rx_reset(struct gsm_bts *bts, const struct osmo_cbsp_decoded *dec
llist_for_each_entry_safe(smscb, smscb2, &chan_state->messages, list)
bts_smscb_del(smscb, chan_state, "RESET");
- osmo_timer_del(&bts->etws_timer);
+ osmo_timer_del(&bts->etws.timer);
/* Make sure that broadcast is disabled */
rsl_etws_pn_command(bts, RSL_CHAN_PCH_AGCH, NULL, 0);
return 0;
}
+static int bts_rx_status_query(struct gsm_bts *bts, const struct osmo_cbsp_decoded *dec,
+ struct response_state *r_state, void *priv)
+{
+ const struct osmo_cbsp_msg_status_query *query = &dec->u.msg_status_query;
+ struct bts_smscb_chan_state *chan_state;
+ struct bts_smscb_message *smscb;
+ bool extended = false;
+
+ if (query->channel_ind == 0x01)
+ extended = true;
+ chan_state = bts_get_smscb_chan(bts, extended);
+
+ /* Find message by msg_id + old_serial_nr */
+ smscb = bts_find_smscb(chan_state, query->msg_id, query->old_serial_nr);
+ if (!smscb)
+ return -CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED;
+
+ append_bcast_compl(r_state, chan_state->bts, smscb);
+
+ return 0;
+}
+
+
/*********************************************************************************
* Receive of CBSP from CBC
*********************************************************************************/
@@ -758,11 +929,16 @@ static int cbsp_rx_kill(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded
fail->channel_ind = kill->channel_ind;
llist_replace_head(&fail->fail_list, &r_state->fail);
- fail->cell_list.id_discr = r_state->success.id_discr;
- llist_replace_head(&fail->cell_list.list, &r_state->success.list);
-
- fail->num_compl_list.id_discr = r_state->num_completed.id_discr;
- llist_replace_head(&fail->num_compl_list.list, &r_state->num_completed.list);
+ /* if the KILL relates to CBS, the "Channel Indicator" IE is present */
+ if (kill->channel_ind) {
+ /* only if it was CBS */
+ fail->num_compl_list.id_discr = r_state->num_completed.id_discr;
+ llist_replace_head(&fail->num_compl_list.list, &r_state->num_completed.list);
+ } else {
+ /* only if it was emergency */
+ fail->cell_list.id_discr = r_state->success.id_discr;
+ llist_replace_head(&fail->cell_list.list, &r_state->success.list);
+ }
} else {
resp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_KILL_COMPL);
struct osmo_cbsp_kill_complete *compl = &resp->u.kill_compl;
@@ -770,11 +946,16 @@ static int cbsp_rx_kill(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded
compl->old_serial_nr = kill->old_serial_nr;
compl->channel_ind = kill->channel_ind;
- compl->cell_list.id_discr = r_state->success.id_discr;
- llist_replace_head(&compl->cell_list.list, &r_state->success.list);
-
- compl->num_compl_list.id_discr = r_state->num_completed.id_discr;
- llist_replace_head(&compl->num_compl_list.list, &r_state->num_completed.list);
+ /* if the KILL relates to CBS, the "Channel Indicator" IE is present */
+ if (kill->channel_ind) {
+ /* only if it was CBS */
+ compl->num_compl_list.id_discr = r_state->num_completed.id_discr;
+ llist_replace_head(&compl->num_compl_list.list, &r_state->num_completed.list);
+ } else {
+ /* only if it was emergency */
+ compl->cell_list.id_discr = r_state->success.id_discr;
+ llist_replace_head(&compl->cell_list.list, &r_state->success.list);
+ }
}
cbsp_tx_decoded(cbc, resp);
@@ -816,6 +997,48 @@ static int cbsp_rx_reset(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decode
return rc;
}
+static int cbsp_rx_status_query(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded *dec)
+{
+ const struct osmo_cbsp_msg_status_query *query = &dec->u.msg_status_query;
+ struct gsm_network *net = cbc->net;
+ struct response_state *r_state = talloc_zero(cbc, struct response_state);
+ struct osmo_cbsp_decoded *resp;
+ int rc;
+
+ LOGP(DCBS, LOGL_DEBUG, "CBSP Rx MESSAGE STATUS QUERY\n");
+
+ rc = cbsp_per_bts(net, r_state, &dec->u.msg_status_query.cell_list, bts_rx_status_query, dec, NULL);
+ if (rc < 0) {
+ resp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_MSG_STATUS_QUERY_FAIL);
+ struct osmo_cbsp_msg_status_query_failure *fail = &resp->u.msg_status_query_fail;
+ fail->msg_id = query->msg_id;
+ fail->old_serial_nr = query->old_serial_nr;
+ fail->channel_ind = query->channel_ind;
+ llist_replace_head(&fail->fail_list, &r_state->fail);
+
+ fail->num_compl_list.id_discr = r_state->num_completed.id_discr;
+ llist_replace_head(&fail->num_compl_list.list, &r_state->num_completed.list);
+ } else {
+ resp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_MSG_STATUS_QUERY_COMPL);
+ struct osmo_cbsp_msg_status_query_complete *compl = &resp->u.msg_status_query_compl;
+ compl->msg_id = query->msg_id;
+ compl->old_serial_nr = query->old_serial_nr;
+ compl->channel_ind = query->channel_ind;
+
+ if (dec->u.msg_status_query.cell_list.id_discr == CELL_IDENT_BSS) {
+ /* replace the list of individual cell identities with CELL_IDENT_BSS */
+ compl->num_compl_list.id_discr = CELL_IDENT_BSS;
+ /* no need to free num_completed_list entries, hierarchical talloc works */
+ } else {
+ compl->num_compl_list.id_discr = r_state->num_completed.id_discr;
+ llist_replace_head(&compl->num_compl_list.list, &r_state->num_completed.list);
+ }
+ }
+ cbsp_tx_decoded(cbc, resp);
+ talloc_free(r_state);
+ return rc;
+}
+
/*! process an incoming, already decoded CBSP message from the CBC.
* \param[in] cbc link to the CBC
@@ -838,8 +1061,10 @@ int cbsp_rx_decoded(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded *de
case CBSP_MSGT_RESET: /* stop broadcasting of all messages */
rc = cbsp_rx_reset(cbc, dec);
break;
- case CBSP_MSGT_LOAD_QUERY:
case CBSP_MSGT_MSG_STATUS_QUERY:
+ rc = cbsp_rx_status_query(cbc, dec);
+ break;
+ case CBSP_MSGT_LOAD_QUERY:
case CBSP_MSGT_SET_DRX:
LOGP(DCBS, LOGL_ERROR, "Received Unimplemented CBSP Message Type %s",
get_value_string(cbsp_msg_type_names, dec->msg_type));
@@ -853,54 +1078,76 @@ int cbsp_rx_decoded(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded *de
return rc;
}
-/*********************************************************************************
- * VTY Interface (Introspection)
- *********************************************************************************/
-
-static void vty_dump_smscb_chan_state(struct vty *vty, const struct bts_smscb_chan_state *cs)
+/* initialize the ETWS state of a BTS */
+void bts_etws_init(struct gsm_bts *bts)
{
- const struct bts_smscb_message *sm;
-
- vty_out(vty, "%s CBCH:%s", cs == &cs->bts->cbch_basic ? "BASIC" : "EXTENDED", VTY_NEWLINE);
+ bts->etws.active = false;
+ osmo_timer_setup(&bts->etws.timer, etws_pn_cb, bts);
+}
- vty_out(vty, " MsgId | SerNo | Pg | Category | Perd | #Tx | #Req | DCS%s", VTY_NEWLINE);
- vty_out(vty, "-------|-------|----|---------------|------|------|------|----%s", VTY_NEWLINE);
- llist_for_each_entry(sm, &cs->messages, list) {
- vty_out(vty, " %04x | %04x | %2u | %13s | %4u | %4u | %4u | %02x%s",
- sm->input.msg_id, sm->input.serial_nr, sm->num_pages,
- get_value_string(cbsp_category_names, sm->input.category),
- sm->input.rep_period, sm->bcast_count, sm->input.num_bcast_req,
- sm->input.dcs, VTY_NEWLINE);
- }
- vty_out(vty, "%s", VTY_NEWLINE);
+/* BSC is bootstrapping a BTS; install any currently active ETWS PN */
+static void bts_etws_bootstrap(struct gsm_bts *bts)
+{
+ if (bts->etws.active)
+ bts_send_etws(bts);
}
-DEFUN(bts_show_cbs, bts_show_cbs_cmd,
- "show bts <0-255> smscb [(basic|extended)]",
- SHOW_STR "Display information about a BTS\n" "BTS number\n"
- "SMS Cell Broadcast State\n"
- "Show only information related to CBCH BASIC\n"
- "Show only information related to CBCH EXTENDED\n")
+/* Callback function to be called every time we receive a signal from NM */
+static int nm_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
{
- struct gsm_network *net = gsmnet_from_vty(vty);
- int bts_nr = atoi(argv[0]);
+ struct nm_running_chg_signal_data *nsd;
struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+
+ if (signal != S_NM_RUNNING_CHG)
+ return 0;
+
+ nsd = signal_data;
+ bts = nsd->bts;
- if (bts_nr >= net->num_bts) {
- vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE);
- return CMD_WARNING;
+ switch (nsd->obj_class) {
+ case NM_OC_RADIO_CARRIER:
+ trx = (struct gsm_bts_trx *)nsd->obj;
+ break;
+ case NM_OC_BASEB_TRANSC:
+ trx = gsm_bts_bb_trx_get_trx((struct gsm_bts_bb_trx *)nsd->obj);
+ break;
+ default:
+ return 0;
}
- bts = gsm_bts_num(net, bts_nr);
- if (argc < 2 || !strcmp(argv[1], "basic"))
- vty_dump_smscb_chan_state(vty, &bts->cbch_basic);
- if (argc < 2 || !strcmp(argv[1], "extended"))
- vty_dump_smscb_chan_state(vty, &bts->cbch_extended);
+ struct gsm_lchan *cbch = gsm_bts_get_cbch(bts);
+ if (!cbch)
+ return 0;
+ /* We only care about state changes of TRX holding the CBCH. */
+ if (trx != cbch->ts->trx)
+ return 0;
- return CMD_SUCCESS;
+ if (nsd->running) {
+ if (trx_is_usable(trx)) {
+ LOG_BTS(bts, DCBS, LOGL_INFO, "BTS becomes available for CBCH\n");
+ /* Start CBCH transmit timer if CBCH is present */
+ bts_cbch_timer_schedule(trx->bts);
+ /* Start ETWS/PWS Primary Notification, if active */
+ bts_etws_bootstrap(trx->bts);
+ cbsp_tx_restart_bts(bts->network->cbc, true, bts);
+ cbsp_tx_restart_bts(bts->network->cbc, false, bts);
+ }
+ } else {
+ if (osmo_timer_pending(&bts->cbch_timer)) {
+ /* If timer is ongoing it means CBCH was available */
+ LOG_BTS(bts, DCBS, LOGL_INFO, "BTS becomes unavailable for CBCH\n");
+ osmo_timer_del(&bts->cbch_timer);
+ cbsp_tx_failure_bts(bts->network->cbc, true, bts);
+ cbsp_tx_failure_bts(bts->network->cbc, false, bts);
+ } /* else: CBCH was already unavailable before */
+ }
+ return 0;
}
-void smscb_vty_init(void)
+/* To be called once at startup of the process: */
+void smscb_global_init(void)
{
- install_element_ve(&bts_show_cbs_cmd);
+ osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
}
diff --git a/src/osmo-bsc/smscb_vty.c b/src/osmo-bsc/smscb_vty.c
new file mode 100644
index 000000000..b13d2db07
--- /dev/null
+++ b/src/osmo-bsc/smscb_vty.c
@@ -0,0 +1,421 @@
+/* CBSP (Cell Broadcast Service Protocol) Handling for OsmoBSC */
+/*
+ * (C) 2019 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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/gsm_data.h>
+#include <osmocom/bsc/vty.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/smscb.h>
+#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bts.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/gsm/cbsp.h>
+
+/*********************************************************************************
+ * cbc
+ *********************************************************************************/
+static struct bsc_cbc_link *vty_cbc_data(struct vty *vty)
+{
+ return bsc_gsmnet->cbc;
+}
+
+DEFUN(cfg_cbc, cfg_cbc_cmd,
+ "cbc", "Configure CBSP Link to Cell Broadcast Centre\n")
+{
+ vty->node = CBC_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_mode, cfg_cbc_mode_cmd,
+ "mode (server|client|disabled)",
+ "Set OsmoBSC as CBSP server or client\n"
+ "CBSP Server: listen for inbound TCP connections from a remote Cell Broadcast Centre\n"
+ "CBSP Client: establish outbound TCP connection to a remote Cell Broadcast Centre\n"
+ "Disable CBSP link\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ cbc->mode = get_string_value(bsc_cbc_link_mode_names, argv[0]);
+ OSMO_ASSERT(cbc->mode >= 0);
+
+ /* Immediately restart/stop CBSP only when coming from a telnet session. The settings from the config file take
+ * effect in osmo_bsc_main.c's invocation of bsc_cbc_link_restart(). */
+ if (vty->type != VTY_FILE)
+ bsc_cbc_link_restart();
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_server, cfg_cbc_server_cmd,
+ "server", "Configure OsmoBSC's CBSP server role\n")
+{
+ vty->node = CBC_SERVER_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_server_local_ip, cfg_cbc_server_local_ip_cmd,
+ "local-ip " VTY_IPV46_CMD,
+ "Set IP Address to listen on for inbound CBSP from a Cell Broadcast Centre\n"
+ "IPv4 address\n" "IPv6 address\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ osmo_sockaddr_str_from_str(&cbc->server.local_addr, argv[0], cbc->server.local_addr.port);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_server_local_port, cfg_cbc_server_local_port_cmd,
+ "local-port <1-65535>",
+ "Set TCP port to listen on for inbound CBSP from a Cell Broadcast Centre\n"
+ "CBSP port number (Default: " OSMO_STRINGIFY_VAL(CBSP_TCP_PORT) ")\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ cbc->server.local_addr.port = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_client, cfg_cbc_client_cmd,
+ "client", "Configure OsmoBSC's CBSP client role\n")
+{
+ vty->node = CBC_CLIENT_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_client_remote_ip, cfg_cbc_client_remote_ip_cmd,
+ "remote-ip " VTY_IPV46_CMD,
+ "Set IP Address of the Cell Broadcast Centre, to establish CBSP link to\n"
+ "IPv4 address\n" "IPv6 address\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ osmo_sockaddr_str_from_str(&cbc->client.remote_addr, argv[0], cbc->client.remote_addr.port);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_client_remote_port, cfg_cbc_client_remote_port_cmd,
+ "remote-port <1-65535>",
+ "Set TCP port of the Cell Broadcast Centre, to establish CBSP link to\n"
+ "CBSP port number (Default: " OSMO_STRINGIFY_VAL(CBSP_TCP_PORT) ")\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ cbc->client.remote_addr.port = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_client_local_ip, cfg_cbc_client_local_ip_cmd,
+ "local-ip " VTY_IPV46_CMD,
+ "Set local bind address for the outbound CBSP link to the Cell Broadcast Centre\n"
+ "IPv4 address\n" "IPv6 address\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ osmo_sockaddr_str_from_str(&cbc->client.local_addr, argv[0], cbc->client.local_addr.port);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_client_local_port, cfg_cbc_client_local_port_cmd,
+ "local-port <1-65535>",
+ "Set local bind port for the outbound CBSP link to the Cell Broadcast Centre\n"
+ "port number\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ cbc->client.local_addr.port = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_client_no_local_ip, cfg_cbc_client_no_local_ip_cmd,
+ "no local-ip",
+ NO_STR "Remove local IP address bind config for the CBSP client mode\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ cbc->client.local_addr = (struct osmo_sockaddr_str){ .port = cbc->client.local_addr.port };
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cbc_client_no_local_port, cfg_cbc_client_no_local_port_cmd,
+ "no local-port",
+ NO_STR "Remove local TCP port bind config for the CBSP client mode\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ cbc->client.local_addr.port = 0;
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node cbc_node = {
+ CBC_NODE,
+ "%s(config-cbc)# ",
+ 1,
+};
+
+static struct cmd_node cbc_server_node = {
+ CBC_SERVER_NODE,
+ "%s(config-cbc-server)# ",
+ 1,
+};
+
+static struct cmd_node cbc_client_node = {
+ CBC_CLIENT_NODE,
+ "%s(config-cbc-client)# ",
+ 1,
+};
+
+static int config_write_cbc(struct vty *vty)
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+
+ bool default_server_local;
+ bool default_client_remote;
+ bool default_client_local;
+
+ default_server_local = !osmo_sockaddr_str_cmp(&cbc->server.local_addr,
+ &bsc_cbc_default_server_local_addr);
+ default_client_remote = !osmo_sockaddr_str_is_set(&cbc->client.remote_addr);
+ default_client_local = !osmo_sockaddr_str_is_set(&cbc->client.local_addr);
+
+ /* If all reflects default values, skip the 'cbc' section */
+ if (cbc->mode == BSC_CBC_LINK_MODE_DISABLED
+ && default_server_local
+ && default_client_remote && default_client_local)
+ return 0;
+
+ vty_out(vty, "cbc%s", VTY_NEWLINE);
+ vty_out(vty, " mode %s%s", bsc_cbc_link_mode_name(cbc->mode), VTY_NEWLINE);
+
+ if (!default_server_local) {
+ vty_out(vty, " server%s", VTY_NEWLINE);
+
+ if (strcmp(cbc->server.local_addr.ip, bsc_cbc_default_server_local_addr.ip))
+ vty_out(vty, " local-ip %s%s", cbc->server.local_addr.ip, VTY_NEWLINE);
+ if (cbc->server.local_addr.port != bsc_cbc_default_server_local_addr.port)
+ vty_out(vty, " local-port %u%s", cbc->server.local_addr.port, VTY_NEWLINE);
+ }
+
+ if (!(default_client_remote && default_client_local)) {
+ vty_out(vty, " client%s", VTY_NEWLINE);
+
+ if (osmo_sockaddr_str_is_set(&cbc->client.remote_addr)) {
+ vty_out(vty, " remote-ip %s%s", cbc->client.remote_addr.ip, VTY_NEWLINE);
+ if (cbc->client.remote_addr.port != CBSP_TCP_PORT)
+ vty_out(vty, " remote-port %u%s", cbc->client.remote_addr.port, VTY_NEWLINE);
+ }
+
+ if (cbc->client.local_addr.ip[0])
+ vty_out(vty, " local-ip %s%s", cbc->client.local_addr.ip, VTY_NEWLINE);
+ if (cbc->client.local_addr.port)
+ vty_out(vty, " local-port %u%s", cbc->client.local_addr.port, VTY_NEWLINE);
+ }
+
+ return 0;
+}
+
+DEFUN(show_cbc, show_cbc_cmd,
+ "show cbc",
+ SHOW_STR "Display state of CBC / CBSP\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+
+ switch (cbc->mode) {
+ case BSC_CBC_LINK_MODE_DISABLED:
+ vty_out(vty, "CBSP link is disabled%s", VTY_NEWLINE);
+ break;
+
+ case BSC_CBC_LINK_MODE_SERVER:
+ vty_out(vty, "OsmoBSC is configured as CBSP Server on " OSMO_SOCKADDR_STR_FMT "%s",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->server.local_addr), VTY_NEWLINE);
+ vty_out(vty, "CBSP Server Connection: %s%s",
+ cbc->server.sock_name ? cbc->server.sock_name : "Disconnected", VTY_NEWLINE);
+ break;
+
+ case BSC_CBC_LINK_MODE_CLIENT:
+ vty_out(vty, "OsmoBSC is configured as CBSP Client to remote CBC at " OSMO_SOCKADDR_STR_FMT "%s",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->client.remote_addr), VTY_NEWLINE);
+ vty_out(vty, "CBSP Client Connection: %s%s",
+ cbc->client.sock_name ? cbc->client.sock_name : "Disconnected", VTY_NEWLINE);
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+/* --- Deprecated 'cbc' commands for backwards compat --- */
+
+DEFUN_DEPRECATED(cfg_cbc_remote_ip, cfg_cbc_remote_ip_cmd,
+ "remote-ip A.B.C.D",
+ "IP Address of the Cell Broadcast Centre\n"
+ "IP Address of the Cell Broadcast Centre\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ vty_out(vty, "%% cbc/remote-ip config is deprecated, instead use cbc/client/remote-ip and cbc/ mode%s",
+ VTY_NEWLINE);
+ osmo_sockaddr_str_from_str(&cbc->client.remote_addr, argv[0], cbc->client.remote_addr.port);
+ cbc->mode = BSC_CBC_LINK_MODE_CLIENT;
+ if (vty->type != VTY_FILE)
+ bsc_cbc_link_restart();
+ return CMD_SUCCESS;
+}
+DEFUN_DEPRECATED(cfg_cbc_no_remote_ip, cfg_cbc_no_remote_ip_cmd,
+ "no remote-ip",
+ NO_STR "Remove IP address of CBC; disables outbound CBSP connections\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ vty_out(vty, "%% cbc/remote-ip config is deprecated, instead use cbc/client/remote-ip and cbc/mode%s",
+ VTY_NEWLINE);
+ if (cbc->mode == BSC_CBC_LINK_MODE_CLIENT) {
+ cbc->mode = BSC_CBC_LINK_MODE_DISABLED;
+ if (vty->type != VTY_FILE)
+ bsc_cbc_link_restart();
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_cbc_remote_port, cfg_cbc_remote_port_cmd,
+ "remote-port <1-65535>",
+ "TCP Port number of the Cell Broadcast Centre (Default: 48049)\n"
+ "TCP Port number of the Cell Broadcast Centre (Default: 48049)\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ vty_out(vty, "%% cbc/remote-port config is deprecated, instead use cbc/client/remote-port%s",
+ VTY_NEWLINE);
+ cbc->client.remote_addr.port = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_cbc_listen_port, cfg_cbc_listen_port_cmd,
+ "listen-port <1-65535>",
+ "Local TCP port at which BSC listens for incoming CBSP connections from CBC\n"
+ "Local TCP port at which BSC listens for incoming CBSP connections from CBC\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ vty_out(vty, "%% cbc/listen-port config is deprecated, instead use cbc/server/local-port and cbc/mode%s",
+ VTY_NEWLINE);
+ cbc->mode = BSC_CBC_LINK_MODE_SERVER;
+ cbc->server.local_addr.port = atoi(argv[0]);
+ if (vty->type != VTY_FILE)
+ bsc_cbc_link_restart();
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_cbc_no_listen_port, cfg_cbc_no_listen_port_cmd,
+ "no listen-port",
+ NO_STR "Remove CBSP Listen Port; disables inbound CBSP connections\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ vty_out(vty, "%% cbc/listen-port config is deprecated, instead use cbc/server/local-port and cbc/mode%s",
+ VTY_NEWLINE);
+ if (cbc->mode == BSC_CBC_LINK_MODE_SERVER) {
+ cbc->mode = BSC_CBC_LINK_MODE_DISABLED;
+ if (vty->type != VTY_FILE)
+ bsc_cbc_link_restart();
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_cbc_listen_ip, cfg_cbc_listen_ip_cmd,
+ "listen-ip A.B.C.D",
+ "Local IP Address where BSC listens for incoming CBC connections (Default: 127.0.0.1)\n"
+ "Local IP Address where BSC listens for incoming CBC connections\n")
+{
+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);
+ vty_out(vty, "%% cbc/listen-ip config is deprecated, instead use cbc/server/local-ip%s",
+ VTY_NEWLINE);
+ osmo_sockaddr_str_from_str(&cbc->server.local_addr, argv[0], cbc->server.local_addr.port);
+ return CMD_SUCCESS;
+}
+
+void cbc_vty_init(void)
+{
+ install_element_ve(&show_cbc_cmd);
+
+ install_element(CONFIG_NODE, &cfg_cbc_cmd);
+ install_node(&cbc_node, config_write_cbc);
+ install_element(CBC_NODE, &cfg_cbc_mode_cmd);
+
+ install_element(CBC_NODE, &cfg_cbc_server_cmd);
+ install_node(&cbc_server_node, NULL);
+ install_element(CBC_SERVER_NODE, &cfg_cbc_server_local_ip_cmd);
+ install_element(CBC_SERVER_NODE, &cfg_cbc_server_local_port_cmd);
+
+ install_element(CBC_NODE, &cfg_cbc_client_cmd);
+ install_node(&cbc_client_node, NULL);
+ install_element(CBC_CLIENT_NODE, &cfg_cbc_client_remote_ip_cmd);
+ install_element(CBC_CLIENT_NODE, &cfg_cbc_client_remote_port_cmd);
+ install_element(CBC_CLIENT_NODE, &cfg_cbc_client_local_ip_cmd);
+ install_element(CBC_CLIENT_NODE, &cfg_cbc_client_local_port_cmd);
+ install_element(CBC_CLIENT_NODE, &cfg_cbc_client_no_local_ip_cmd);
+ install_element(CBC_CLIENT_NODE, &cfg_cbc_client_no_local_port_cmd);
+
+ /* Deprecated, for backwards compat */
+ install_element(CBC_NODE, &cfg_cbc_remote_ip_cmd);
+ install_element(CBC_NODE, &cfg_cbc_no_remote_ip_cmd);
+ install_element(CBC_NODE, &cfg_cbc_remote_port_cmd);
+ install_element(CBC_NODE, &cfg_cbc_listen_port_cmd);
+ install_element(CBC_NODE, &cfg_cbc_no_listen_port_cmd);
+ install_element(CBC_NODE, &cfg_cbc_listen_ip_cmd);
+}
+
+
+/*********************************************************************************
+ * smscb
+ *********************************************************************************/
+static void vty_dump_smscb_chan_state(struct vty *vty, const struct bts_smscb_chan_state *cs)
+{
+ const struct bts_smscb_message *sm;
+
+ vty_out(vty, "%s CBCH:%s", cs == &cs->bts->cbch_basic ? "BASIC" : "EXTENDED", VTY_NEWLINE);
+
+ vty_out(vty, " MsgId | SerNo | Pg | Category | Perd | #Tx | #Req | DCS%s", VTY_NEWLINE);
+ vty_out(vty, "-------|-------|----|---------------|------|------|------|----%s", VTY_NEWLINE);
+ llist_for_each_entry(sm, &cs->messages, list) {
+ vty_out(vty, " %04x | %04x | %2u | %13s | %4u | %4u | %4u | %02x%s",
+ sm->input.msg_id, sm->input.serial_nr, sm->num_pages,
+ get_value_string(cbsp_category_names, sm->input.category),
+ sm->input.rep_period, sm->bcast_count, sm->input.num_bcast_req,
+ sm->input.dcs, VTY_NEWLINE);
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+}
+
+DEFUN(bts_show_cbs, bts_show_cbs_cmd,
+ "show bts <0-255> smscb [(basic|extended)]",
+ SHOW_STR "Display information about a BTS\n" "BTS number\n"
+ "SMS Cell Broadcast State\n"
+ "Show only information related to CBCH BASIC\n"
+ "Show only information related to CBCH EXTENDED\n")
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ int bts_nr = atoi(argv[0]);
+ struct gsm_bts *bts;
+
+ if (bts_nr >= net->num_bts) {
+ vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts = gsm_bts_num(net, bts_nr);
+
+ if (argc < 2 || !strcmp(argv[1], "basic"))
+ vty_dump_smscb_chan_state(vty, &bts->cbch_basic);
+ if (argc < 2 || !strcmp(argv[1], "extended"))
+ vty_dump_smscb_chan_state(vty, &bts->cbch_extended);
+
+ return CMD_SUCCESS;
+}
+
+void smscb_vty_init(void)
+{
+ install_element_ve(&bts_show_cbs_cmd);
+}
diff --git a/src/osmo-bsc/system_information.c b/src/osmo-bsc/system_information.c
index b9699899f..cfc9a0450 100644
--- a/src/osmo-bsc/system_information.c
+++ b/src/osmo-bsc/system_information.c
@@ -31,13 +31,13 @@
#include <osmocom/core/utils.h>
#include <osmocom/gsm/sysinfo.h>
#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/gsm48_rest_octets.h>
#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gsm48_arfcn_range_encode.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/abis_rsl.h>
-#include <osmocom/bsc/rest_octets.h>
-#include <osmocom/bsc/arfcn_range_encode.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/acc.h>
#include <osmocom/bsc/neighbor_ident.h>
@@ -52,7 +52,7 @@ struct gsm0808_cell_id_list2;
* array. DCS1800 and PCS1900 can not be used at the same time so conserve
* memory and do the below.
*/
-static int band_compatible(const struct gsm_bts *bts, int arfcn)
+int band_compatible(const struct gsm_bts *bts, int arfcn)
{
enum gsm_band band;
@@ -169,7 +169,12 @@ static int make_si2quaters(struct gsm_bts *bts, bool counting)
si2q->header.system_information = GSM48_MT_RR_SYSINFO_2quater;
}
- rc = rest_octets_si2quater(si2q->rest_octets, bts);
+ rc = osmo_gsm48_rest_octets_si2quater_encode(si2q->rest_octets, bts->si2q_index,
+ bts->si2q_count, bts->si_common.data.uarfcn_list,
+ &bts->u_offset, bts->si_common.uarfcn_length,
+ bts->si_common.data.scramble_list,
+ &bts->si_common.si2quater_neigh_list,
+ &bts->e_offset);
if (rc < 0)
return rc;
@@ -426,7 +431,7 @@ static inline int enc_freq_lst_var_bitmap(uint8_t *chan_list,
return 0;
}
-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)
{
/*
@@ -435,22 +440,22 @@ int range_encode(enum gsm48_range r, int *arfcns, int arfcns_used, int *w,
*/
int rc, f0_included;
- range_enc_filter_arfcns(arfcns, arfcns_used, f0, &f0_included);
+ osmo_gsm48_range_enc_filter_arfcns(arfcns, arfcns_used, f0, &f0_included);
- rc = range_enc_arfcns(r, arfcns, arfcns_used, w, 0);
+ rc = osmo_gsm48_range_enc_arfcns(r, arfcns, arfcns_used, w, 0);
if (rc < 0)
return rc;
/* Select the range and the amount of bits needed */
switch (r) {
- case ARFCN_RANGE_128:
- return range_enc_range128(chan_list, f0, w);
- case ARFCN_RANGE_256:
- return range_enc_range256(chan_list, f0, w);
- case ARFCN_RANGE_512:
- return range_enc_range512(chan_list, f0, w);
- case ARFCN_RANGE_1024:
- return range_enc_range1024(chan_list, f0, f0_included, w);
+ case OSMO_GSM48_ARFCN_RANGE_128:
+ return osmo_gsm48_range_enc_128(chan_list, f0, w);
+ case OSMO_GSM48_ARFCN_RANGE_256:
+ return osmo_gsm48_range_enc_256(chan_list, f0, w);
+ case OSMO_GSM48_ARFCN_RANGE_512:
+ return osmo_gsm48_range_enc_512(chan_list, f0, w);
+ case OSMO_GSM48_ARFCN_RANGE_1024:
+ return osmo_gsm48_range_enc_1024(chan_list, f0, f0_included, w);
default:
return -ERANGE;
};
@@ -463,8 +468,8 @@ static inline int enc_freq_lst_range(uint8_t *chan_list,
const struct bitvec *bv, const struct gsm_bts *bts,
bool bis, bool ter, bool pgsm)
{
- int arfcns[RANGE_ENC_MAX_ARFCNS];
- int w[RANGE_ENC_MAX_ARFCNS];
+ int arfcns[OSMO_GSM48_RANGE_ENC_MAX_ARFCNS];
+ int w[OSMO_GSM48_RANGE_ENC_MAX_ARFCNS];
int arfcns_used = 0;
int i, range, f0;
@@ -483,8 +488,8 @@ static inline int enc_freq_lst_range(uint8_t *chan_list,
/*
* Check if the given list of ARFCNs can be encoded.
*/
- range = range_enc_determine_range(arfcns, arfcns_used, &f0);
- if (range == ARFCN_RANGE_INVALID)
+ range = osmo_gsm48_range_enc_determine_range(arfcns, arfcns_used, &f0);
+ if (range == OSMO_GSM48_ARFCN_RANGE_INVALID)
return -2;
memset(w, 0, sizeof(w));
@@ -499,20 +504,29 @@ static int bitvec2freq_list(uint8_t *chan_list, const struct bitvec *bv,
bool pgsm = false;
memset(chan_list, 0, 16);
- if (bts->band == GSM_BAND_900
- && bts->c0->arfcn >= 1 && bts->c0->arfcn <= 124)
+ /* According to 3GPP TS 44.018, section 10.5.2.1b.2, only ARFCN values
+ * in range 1..124 can be encoded using the 'bit map 0' format. */
+ if (bts->band == GSM_BAND_900)
pgsm = true;
+ /* Check presence of E-GSM ARFCN 0 */
+ if (pgsm && bitvec_get_bit_pos(bv, 0) == ONE)
+ pgsm = false;
+ /* Check presence of E-GSM ARFCNs 975..1023 */
+ for (i = 975; pgsm && i <= 1023; i++) {
+ if (bitvec_get_bit_pos(bv, i) == ONE)
+ pgsm = false;
+ }
+
/* P-GSM-only handsets only support 'bit map 0 format' */
if (!bis && !ter && pgsm) {
chan_list[0] = 0;
- for (i = 0; i < bv->data_len*8; i++) {
- if (i >= 1 && i <= 124
- && bitvec_get_bit_pos(bv, i)) {
- rc = freq_list_bm0_set_arfcn(chan_list, i);
- if (rc < 0)
- return rc;
- }
+ for (i = 1; i <= 124; i++) {
+ if (!bitvec_get_bit_pos(bv, i))
+ continue;
+ rc = freq_list_bm0_set_arfcn(chan_list, i);
+ if (rc < 0)
+ return rc;
}
return 0;
}
@@ -548,7 +562,7 @@ static int bitvec2freq_list(uint8_t *chan_list, const struct bitvec *bv,
max = i;
}
- if (max == -1) {
+ if (arfcns == 0) {
/* Empty set, use 'bit map 0 format' */
chan_list[0] = 0;
return 0;
@@ -569,51 +583,63 @@ static int bitvec2freq_list(uint8_t *chan_list, const struct bitvec *bv,
return -EINVAL;
}
-/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
-int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts)
+/* (Re)generate Cell Allocation (basically a bit-vector of all cell channels) */
+int generate_cell_chan_alloc(struct gsm_bts *bts)
{
- struct gsm_bts_trx *trx;
- struct bitvec *bv = &bts->si_common.cell_alloc;
-
- /* Zero-initialize the bit-vector */
- memset(bv->data, 0, bv->data_len);
+ const struct gsm_bts_trx *trx;
+ unsigned int chan, chan_num;
+ unsigned int tn;
+
+ /* Temporary Cell Allocation bit-vector */
+ uint8_t ca[1024 / 8] = { 0 };
+ struct bitvec bv = {
+ .data_len = sizeof(ca),
+ .data = &ca[0],
+ };
- /* first we generate a bitvec of all TRX ARFCN's in our BTS */
+ /* Calculate a bit-mask of all assigned ARFCNs */
llist_for_each_entry(trx, &bts->trx_list, list) {
- unsigned int i, j;
/* Always add the TRX's ARFCN */
- bitvec_set_bit_pos(bv, trx->arfcn, 1);
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ bitvec_set_bit_pos(&bv, trx->arfcn, 1);
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
/* Add any ARFCNs present in hopping channels */
- for (j = 0; j < 1024; j++) {
- if (bitvec_get_bit_pos(&ts->hopping.arfcns, j))
- bitvec_set_bit_pos(bv, j, 1);
+ for (chan = 0; chan < sizeof(ca) * 8; chan++) {
+ if (bitvec_get_bit_pos(&ts->hopping.arfcns, chan) == ONE)
+ bitvec_set_bit_pos(&bv, chan, ONE);
}
}
}
- /* then we generate a GSM 04.08 frequency list from the bitvec */
- return bitvec2freq_list(chan_list, bv, bts, false, false);
-}
+ /* Calculate the overall number of assigned ARFCNs */
+ for (chan_num = 0, chan = 0; chan < sizeof(ca) * 8; chan++) {
+ if (bitvec_get_bit_pos(&bv, chan) == ONE)
+ chan_num++;
+ }
-struct generate_bcch_chan_list__ni_iter_data {
- struct gsm_bts *bts;
- struct bitvec *bv;
-};
+ /* The Mobile Allocation IE may contain up to 64 bits, so here we
+ * cannot allow more than 64 channels in Cell Allocation. */
+ if (chan_num > 64) {
+ LOGP(DRR, LOGL_ERROR, "Failed to (re)generate Cell Allocation: "
+ "number of channels (%u) exceeds the maximum number of "
+ "ARFCNs in Mobile Allocation (64)\n", chan_num);
+ return -E2BIG;
+ }
-static bool generate_bcch_chan_list__ni_iter_cb(const struct neighbor_ident_key *key,
- const struct gsm0808_cell_id_list2 *val,
- void *cb_data)
-{
- struct generate_bcch_chan_list__ni_iter_data *data = cb_data;
+ /* Commit the new Channel Allocation */
+ memcpy(&bts->si_common.data.cell_alloc[0], ca, sizeof(ca));
+ bts->si_common.cell_chan_num = chan_num;
- if (key->from_bts != NEIGHBOR_IDENT_KEY_ANY_BTS
- && key->from_bts != data->bts->nr)
- return true;
+ return 0;
+}
+
+/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
+int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts)
+{
+ const struct bitvec *bv = &bts->si_common.cell_alloc;
- bitvec_set_bit_pos(data->bv, key->arfcn, 1);
- return true;
+ /* generate a Frequency List from the Cell Allocation */
+ return bitvec2freq_list(chan_list, bv, bts, false, false);
}
/*! generate a cell channel list as per Section 10.5.2.22 of 04.08
@@ -641,7 +667,7 @@ static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts,
/* Zero-initialize the bit-vector */
memset(bv->data, 0, bv->data_len);
- if (llist_empty(&bts->local_neighbors)) {
+ if (llist_empty(&bts->neighbors)) {
/* There are no explicit neighbors, assume all BTS are. */
llist_for_each_entry(cur_bts, &bts->network->bts_list, list) {
if (cur_bts == bts)
@@ -650,21 +676,23 @@ static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts,
}
} else {
/* Only add explicit neighbor cells */
- struct gsm_bts_ref *neigh;
- llist_for_each_entry(neigh, &bts->local_neighbors, entry) {
- bitvec_set_bit_pos(bv, neigh->bts->c0->arfcn, 1);
+ struct neighbor *n;
+ llist_for_each_entry(n, &bts->neighbors, entry) {
+ if (n->type == NEIGHBOR_TYPE_CELL_ID && n->cell_id.ab_present) {
+ bitvec_set_bit_pos(bv, n->cell_id.ab.arfcn, 1);
+ } else {
+ struct gsm_bts *neigh_bts;
+ if (resolve_local_neighbor(&neigh_bts, bts, n)) {
+ LOGP(DHO, LOGL_ERROR,
+ "Neither local nor remote neighbor: BTS %u -> %s\n",
+ bts->nr, neighbor_to_str_c(OTC_SELECT, n));
+ continue;
+ }
+ if (neigh_bts->c0)
+ bitvec_set_bit_pos(bv, neigh_bts->c0->arfcn, 1);
+ }
}
}
-
- /* Also add neighboring BSS cells' ARFCNs */
- {
- struct generate_bcch_chan_list__ni_iter_data data = {
- .bv = bv,
- .bts = bts,
- };
- neighbor_ident_iter(bts->network->neighbor_bss_cells,
- generate_bcch_chan_list__ni_iter_cb, &data);
- }
}
/* then we generate a GSM 04.08 frequency list from the bitvec */
@@ -690,7 +718,7 @@ static int list_arfcn(uint8_t *chan_list, uint8_t mask, char *text)
struct gsm_sysinfo_freq freq[1024];
memset(freq, 0, sizeof(freq));
- gsm48_decode_freq_list(freq, chan_list, 16, 0xce, 1);
+ gsm48_decode_freq_list(freq, chan_list, 16, mask, 1);
for (i = 0; i < 1024; i++) {
if (freq[i].mask) {
if (!n)
@@ -729,7 +757,7 @@ static int generate_si1(enum osmo_sysinfo_type t, struct gsm_bts *bts)
* SI1 Rest Octets (10.5.2.32), contains NCH position and band
* indicator but that is not in the 04.08.
*/
- rc = rest_octets_si1(si1->rest_octets, NULL, is_dcs_net(bts));
+ rc = osmo_gsm48_rest_octets_si1_encode(si1->rest_octets, NULL, is_dcs_net(bts));
return sizeof(*si1) + rc;
}
@@ -759,6 +787,20 @@ static int generate_si2(enum osmo_sysinfo_type t, struct gsm_bts *bts)
return sizeof(*si2);
}
+/* Generate SI2bis Rest Octests 3GPP TS 44.018 Table 10.5.2.33.1 */
+static int rest_octets_si2bis(uint8_t *data)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 1;
+
+ bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
+
+ return bv.data_len;
+}
+
static int generate_si2bis(enum osmo_sysinfo_type t, struct gsm_bts *bts)
{
int rc;
@@ -796,6 +838,24 @@ static int generate_si2bis(enum osmo_sysinfo_type t, struct gsm_bts *bts)
return sizeof(*si2b) + rc;
}
+
+/* Generate SI2ter Rest Octests 3GPP TS 44.018 Table 10.5.2.33a.1 */
+static int rest_octets_si2ter(uint8_t *data)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 4;
+
+ /* No SI2ter_MP_CHANGE_MARK */
+ bitvec_set_bit(&bv, L);
+
+ bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
+
+ return bv.data_len;
+}
+
static int generate_si2ter(enum osmo_sysinfo_type t, struct gsm_bts *bts)
{
int rc;
@@ -865,7 +925,7 @@ static int generate_si2quater(enum osmo_sysinfo_type t, struct gsm_bts *bts)
return sizeof(*si2q) + rc;
}
-static struct gsm48_si_ro_info si_info = {
+static struct osmo_gsm48_si_ro_info si_info = {
.selection_params = {
.present = 0,
},
@@ -903,6 +963,10 @@ static int generate_si3(enum osmo_sysinfo_type t, struct gsm_bts *bts)
si3->header.skip_indicator = 0;
si3->header.system_information = GSM48_MT_RR_SYSINFO_3;
+ /* The value in bts->si_common.chan_desc may get out of sync with the actual value
+ * in net->T_defs (e.g. after changing it via the VTY), so we need to sync it here. */
+ bts->si_common.chan_desc.t3212 = osmo_tdef_get(bts->network->T_defs, 3212, OSMO_TDEF_CUSTOM, 0);
+
si3->cell_identity = htons(bts->cell_identity);
gsm48_generate_lai2(&si3->lai, bts_lai(bts));
si3->control_channel_desc = bts->si_common.chan_desc;
@@ -934,7 +998,7 @@ static int generate_si3(enum osmo_sysinfo_type t, struct gsm_bts *bts)
CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME
Power Offset, 2ter Indicator, Early Classmark Sending,
Scheduling if and WHERE, GPRS Indicator, SI13 position */
- rc = rest_octets_si3(si3->rest_octets, &si_info);
+ rc = osmo_gsm48_rest_octets_si3_encode(si3->rest_octets, &si_info);
return sizeof(*si3) + rc;
}
@@ -966,8 +1030,11 @@ static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts)
const struct gsm_bts_trx_ts *ts = cbch_lchan->ts;
struct gsm48_chan_desc cd;
- /* 10.5.2.5 (TV) CBCH Channel Description IE */
- gsm48_lchan2chan_desc_as_configured(&cd, cbch_lchan);
+ /* 10.5.2.5 (TV) CBCH Channel Description IE.
+ * CBCH is never in VAMOS mode, so just pass allow_osmo_cbits == false. */
+ if (gsm48_lchan_and_pchan2chan_desc(&cd, cbch_lchan, cbch_lchan->ts->pchan_from_config,
+ gsm_ts_tsc(cbch_lchan->ts), false))
+ return -EINVAL;
tail = tv_fixed_put(tail, GSM48_IE_CBCH_CHAN_DESC,
sizeof(cd), (uint8_t *) &cd);
l2_plen += 1 + sizeof(cd);
@@ -989,7 +1056,7 @@ static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts)
/* SI4 Rest Octets (10.5.2.35), containing
Optional Power offset, GPRS Indicator,
Cell Identity, LSA ID, Selection Parameter */
- rc = rest_octets_si4(tail, &si_info, (uint8_t *)GSM_BTS_SI(bts, t) + GSM_MACBLOCK_LEN - tail);
+ rc = osmo_gsm48_rest_octets_si4_encode(tail, &si_info, (uint8_t *)GSM_BTS_SI(bts, t) + GSM_MACBLOCK_LEN - tail);
return l2_plen + 1 + rc;
}
@@ -1003,14 +1070,9 @@ static int generate_si5(enum osmo_sysinfo_type t, struct gsm_bts *bts)
memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
/* ip.access nanoBTS needs l2_plen!! */
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
+ if (is_ipaccess_bts(bts)) {
*output++ = GSM48_LEN2PLEN(l2_plen);
l2_plen++;
- break;
- default:
- break;
}
si5 = (struct gsm48_system_information_type_5 *) output;
@@ -1039,14 +1101,9 @@ static int generate_si5bis(enum osmo_sysinfo_type t, struct gsm_bts *bts)
memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
/* ip.access nanoBTS needs l2_plen!! */
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
+ if (is_ipaccess_bts(bts)) {
*output++ = GSM48_LEN2PLEN(l2_plen);
l2_plen++;
- break;
- default:
- break;
}
si5b = (struct gsm48_system_information_type_5bis *) output;
@@ -1083,14 +1140,9 @@ static int generate_si5ter(enum osmo_sysinfo_type t, struct gsm_bts *bts)
memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
/* ip.access nanoBTS needs l2_plen!! */
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
+ if (is_ipaccess_bts(bts)) {
*output++ = GSM48_LEN2PLEN(l2_plen);
l2_plen++;
- break;
- default:
- break;
}
si5t = (struct gsm48_system_information_type_5ter *) output;
@@ -1114,21 +1166,18 @@ static int generate_si5ter(enum osmo_sysinfo_type t, struct gsm_bts *bts)
static int generate_si6(enum osmo_sysinfo_type t, struct gsm_bts *bts)
{
struct gsm48_system_information_type_6 *si6;
+ struct osmo_gsm48_si6_ro_info si6_ro_info;
uint8_t *output = GSM_BTS_SI(bts, t);
int l2_plen = 11;
int rc;
memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+ memset(&si6_ro_info, 0, sizeof(si6_ro_info));
/* ip.access nanoBTS needs l2_plen!! */
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
+ if (is_ipaccess_bts(bts)) {
*output++ = GSM48_LEN2PLEN(l2_plen);
l2_plen++;
- break;
- default:
- break;
}
si6 = (struct gsm48_system_information_type_6 *) output;
@@ -1145,48 +1194,17 @@ static int generate_si6(enum osmo_sysinfo_type t, struct gsm_bts *bts)
gsm48_set_dtx(&si6->cell_options, bts->dtxu, bts->dtxu, false);
/* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */
- rc = rest_octets_si6(si6->rest_octets, is_dcs_net(bts));
+ si6_ro_info.band_indicator_1900 = !is_dcs_net(bts);
+ rc = osmo_gsm48_rest_octets_si6_encode(si6->rest_octets, &si6_ro_info);
return l2_plen + rc;
}
-static struct gsm48_si13_info si13_default = {
- .cell_opts = {
- .nmo = GPRS_NMO_II,
- .t3168 = 2000,
- .t3192 = 1500,
- .drx_timer_max = 3,
- .bs_cv_max = 15,
- .ctrl_ack_type_use_block = true,
- .ext_info_present = 0,
- .ext_info = {
- .egprs_supported = 0, /* overridden in gsm_generate_si() */
- .use_egprs_p_ch_req = 0, /* overridden in generate_si13() */
- .bep_period = 5,
- .pfc_supported = 0,
- .dtm_supported = 0,
- .bss_paging_coordination = 0,
- },
- },
- .pwr_ctrl_pars = {
- .alpha = 0, /* a = 0.0 */
- .t_avg_w = 16,
- .t_avg_t = 16,
- .pc_meas_chan = 0, /* downling measured on CCCH */
- .n_avg_i = 8,
- },
- .bcch_change_mark = 1,
- .si_change_field = 0,
- .rac = 0, /* needs to be patched */
- .spgc_ccch_sup = 0,
- .net_ctrl_ord = 0,
- .prio_acc_thr = 6,
-};
-
static int generate_si13(enum osmo_sysinfo_type t, struct gsm_bts *bts)
{
struct gsm48_system_information_type_13 *si13 =
(struct gsm48_system_information_type_13 *) GSM_BTS_SI(bts, t);
+ struct osmo_gsm48_si13_info si13_info;
int ret;
memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
@@ -1195,27 +1213,66 @@ static int generate_si13(enum osmo_sysinfo_type t, struct gsm_bts *bts)
si13->header.skip_indicator = 0;
si13->header.system_information = GSM48_MT_RR_SYSINFO_13;
- si13_default.rac = bts->gprs.rac;
- si13_default.net_ctrl_ord = bts->gprs.net_ctrl_ord;
-
- si13_default.cell_opts.ctrl_ack_type_use_block =
- bts->gprs.ctrl_ack_type_use_block;
-
- /* Information about the other SIs */
- si13_default.bcch_change_mark = bts->bcch_change_mark;
+ si13_info = (struct osmo_gsm48_si13_info){
+ .cell_opts = {
+ .nmo = GPRS_NMO_II,
+ .t3168 = 2000,
+ .t3192 = 1500,
+ .drx_timer_max = 3,
+ .bs_cv_max = 15,
+ .ctrl_ack_type_use_block = bts->gprs.ctrl_ack_type_use_block,
+ .ext_info_present = true,
+ .ext_info = {
+ .egprs_supported = 0, /* overridden below */
+ .use_egprs_p_ch_req = 0, /* overridden below */
+ .bep_period = 5,
+ .pfc_supported = 0,
+ .dtm_supported = 0,
+ .bss_paging_coordination = 0, /* overridden below */
+ .ccn_active = false, /* overridden below */
+ },
+ },
+ .pwr_ctrl_pars = {
+ .alpha = bts->gprs.pwr_ctrl.alpha, /* a = 0.0 */
+ .t_avg_w = 16,
+ .t_avg_t = 16,
+ .pc_meas_chan = 0, /* downling measured on CCCH */
+ .n_avg_i = 8,
+ },
+ .bcch_change_mark = bts->bcch_change_mark, /* Information about the other SIs */
+ .si_change_field = 0,
+ .rac = bts->gprs.rac,
+ .spgc_ccch_sup = 0,
+ .net_ctrl_ord = bts->gprs.net_ctrl_ord,
+ .prio_acc_thr = 6,
+ };
- /* Whether EGPRS capable MSs shall use EGPRS PACKET CHANNEL REQUEST */
- if (bts->gprs.egprs_pkt_chan_request)
- si13_default.cell_opts.ext_info.use_egprs_p_ch_req = 1;
- else
- si13_default.cell_opts.ext_info.use_egprs_p_ch_req = 0;
+ switch (bts->gprs.mode) {
+ case BTS_GPRS_EGPRS:
+ si13_info.cell_opts.ext_info.egprs_supported = 1;
+ /* Whether EGPRS capable MSs shall use EGPRS PACKET CHANNEL REQUEST */
+ if (bts->gprs.egprs_pkt_chan_request)
+ si13_info.cell_opts.ext_info.use_egprs_p_ch_req = 1;
+ else
+ si13_info.cell_opts.ext_info.use_egprs_p_ch_req = 0;
+ break;
+ case BTS_GPRS_GPRS:
+ case BTS_GPRS_NONE:
+ si13_info.cell_opts.ext_info.egprs_supported = 0;
+ si13_info.cell_opts.ext_info.use_egprs_p_ch_req = 0;
+ break;
+ }
if (osmo_bts_has_feature(&bts->features, BTS_FEAT_PAGING_COORDINATION))
- si13_default.cell_opts.ext_info.bss_paging_coordination = 1;
+ si13_info.cell_opts.ext_info.bss_paging_coordination = 1;
else
- si13_default.cell_opts.ext_info.bss_paging_coordination = 0;
+ si13_info.cell_opts.ext_info.bss_paging_coordination = 0;
+
+ si13_info.cell_opts.ext_info.ccn_active = bts->gprs.ccn.forced_vty ?
+ bts->gprs.ccn.active :
+ osmo_bts_has_feature(&bts->features, BTS_FEAT_CCN);
- ret = rest_octets_si13(si13->rest_octets, &si13_default);
+ ret = osmo_gsm48_rest_octets_si13_encode(si13->rest_octets, &si13_info);
if (ret < 0)
return ret;
@@ -1249,9 +1306,6 @@ int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type)
switch (bts->gprs.mode) {
case BTS_GPRS_EGPRS:
- si13_default.cell_opts.ext_info_present = 1;
- si13_default.cell_opts.ext_info.egprs_supported = 1;
- /* fallthrough */
case BTS_GPRS_GPRS:
si_info.gprs_ind.present = 1;
break;
@@ -1262,7 +1316,7 @@ int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type)
memcpy(&si_info.selection_params,
&bts->si_common.cell_ro_sel_par,
- sizeof(struct gsm48_si_selection_params));
+ sizeof(struct osmo_gsm48_si_selection_params));
gen_si = gen_si_fn[si_type];
if (!gen_si) {
diff --git a/src/osmo-bsc/timeslot_fsm.c b/src/osmo-bsc/timeslot_fsm.c
index 106e6a1fa..c9902b6b3 100644
--- a/src/osmo-bsc/timeslot_fsm.c
+++ b/src/osmo-bsc/timeslot_fsm.c
@@ -29,6 +29,7 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/pcu_if.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/lchan.h>
static struct osmo_fsm ts_fsm;
@@ -48,11 +49,11 @@ struct gsm_bts_trx_ts *ts_fi_ts(struct osmo_fsm_inst *fi)
static void ts_fsm_update_id(struct gsm_bts_trx_ts *ts)
{
- osmo_fsm_inst_update_id_f(ts->fi, "%u-%u-%u-%s", ts->trx->bts->nr, ts->trx->nr, ts->nr,
- gsm_pchan_id(ts->pchan_on_init));
+ osmo_fsm_inst_update_id_f_sanitize(ts->fi, '_', "%u-%u-%u-%s", ts->trx->bts->nr, ts->trx->nr, ts->nr,
+ gsm_pchan_name(ts->pchan_on_init));
}
-void ts_fsm_init()
+static __attribute__((constructor)) void ts_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&ts_fsm) == 0);
}
@@ -66,6 +67,14 @@ void ts_fsm_alloc(struct gsm_bts_trx_ts *ts)
ts_fsm_update_id(ts);
}
+void ts_fsm_free(struct gsm_bts_trx_ts *ts)
+{
+ if (ts->fi) {
+ osmo_fsm_inst_free(ts->fi);
+ ts->fi = NULL;
+ }
+}
+
enum lchan_sanity {
LCHAN_IS_INSANE = -1,
LCHAN_IS_READY_TO_GO,
@@ -85,7 +94,9 @@ static enum lchan_sanity is_lchan_sane(struct gsm_bts_trx_ts *ts, struct gsm_lch
return LCHAN_IS_READY_TO_GO;
switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
+ if (lchan->type == GSM_LCHAN_SDCCH)
+ return LCHAN_NEEDS_PCHAN_CHANGE;
if (lchan->type == GSM_LCHAN_TCH_H)
return LCHAN_NEEDS_PCHAN_CHANGE;
/* fall thru */
@@ -112,7 +123,7 @@ static int ts_count_active_lchans(struct gsm_bts_trx_ts *ts)
struct gsm_lchan *lchan;
int count = 0;
- ts_for_each_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
if (lchan->fi->state == LCHAN_ST_UNUSED)
continue;
count++;
@@ -125,7 +136,7 @@ static void ts_lchans_dispatch(struct gsm_bts_trx_ts *ts, int lchan_state, uint3
{
struct gsm_lchan *lchan;
- ts_for_each_potential_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
if (lchan_state >= 0
&& !lchan_state_is(lchan, lchan_state))
continue;
@@ -137,7 +148,7 @@ static void ts_terminate_lchan_fsms(struct gsm_bts_trx_ts *ts)
{
struct gsm_lchan *lchan;
- ts_for_each_potential_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
osmo_fsm_inst_term(lchan->fi, OSMO_FSM_TERM_REQUEST, NULL);
}
}
@@ -146,7 +157,7 @@ static int ts_lchans_waiting(struct gsm_bts_trx_ts *ts)
{
struct gsm_lchan *lchan;
int count = 0;
- ts_for_each_potential_lchan(lchan, ts)
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible)
if (lchan->fi->state == LCHAN_ST_WAIT_TS_READY)
count++;
return count;
@@ -187,17 +198,60 @@ static void ts_fsm_err_ready_to_go_in_pdch(struct osmo_fsm_inst *fi, struct gsm_
osmo_fsm_inst_state_name(fi), gsm_lchan_name(lchan));
}
+void ts_set_pchan_is(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan_is)
+{
+ int i;
+ struct gsm_lchan *lchan;
+ uint8_t max_lchans_possible_vamos;
+
+ ts->pchan_is = pchan_is;
+ ts->max_primary_lchans = pchan_subslots(ts->pchan_is);
+ max_lchans_possible_vamos = pchan_subslots_vamos(ts->pchan_is);
+ LOG_TS(ts, LOGL_DEBUG, "pchan_is=%s max_primary_lchans=%d max_lchans_possible=%d (%u VAMOS)\n",
+ gsm_pchan_name(ts->pchan_is), ts->max_primary_lchans, ts->max_lchans_possible,
+ max_lchans_possible_vamos);
+ switch (ts->pchan_is) {
+ case GSM_PCHAN_TCH_F:
+ case GSM_PCHAN_TCH_H:
+ for (i = 0; i < ts->max_lchans_possible; i++) {
+ lchan = &ts->lchan[i];
+ if (i >= ts->max_primary_lchans &&
+ (i - ts->max_primary_lchans) < (int)max_lchans_possible_vamos)
+ lchan->vamos.is_secondary = true;
+ else
+ lchan->vamos.is_secondary = false;
+ lchan_fsm_update_id(lchan);
+ }
+ break;
+ default:
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
+ lchan->vamos.is_secondary = false;
+ lchan_fsm_update_id(lchan);
+ }
+ break;
+ }
+ chan_counts_ts_update(ts);
+}
+
static void ts_setup_lchans(struct gsm_bts_trx_ts *ts)
{
int i, max_lchans;
+ int max_lchans_vamos;
ts->pchan_on_init = ts->pchan_from_config;
ts_fsm_update_id(ts);
max_lchans = pchan_subslots(ts->pchan_on_init);
- LOG_TS(ts, LOGL_DEBUG, "max lchans: %d\n", max_lchans);
-
- for (i = 0; i < max_lchans; i++) {
+ if (osmo_bts_has_feature(&ts->trx->bts->features, BTS_FEAT_VAMOS))
+ max_lchans_vamos = pchan_subslots_vamos(ts->pchan_on_init);
+ else
+ max_lchans_vamos = 0;
+ LOG_TS(ts, LOGL_DEBUG, "max lchans: %d + %d VAMOS secondaries\n", max_lchans, max_lchans_vamos);
+ ts->max_lchans_possible = max_lchans + max_lchans_vamos;
+ ts->max_primary_lchans = 0;
+
+ OSMO_ASSERT(ts->max_lchans_possible <= TS_MAX_LCHAN);
+ for (i = 0; i < ts->max_lchans_possible; i++) {
/* If we receive more than one Channel OPSTART ACK, don't fail on the second init. */
if (ts->lchan[i].fi)
continue;
@@ -205,18 +259,16 @@ static void ts_setup_lchans(struct gsm_bts_trx_ts *ts)
}
switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- ts->pchan_is = GSM_PCHAN_NONE;
+ case GSM_PCHAN_OSMO_DYN:
+ ts_set_pchan_is(ts, GSM_PCHAN_NONE);
break;
case GSM_PCHAN_TCH_F_PDCH:
- ts->pchan_is = GSM_PCHAN_TCH_F;
+ ts_set_pchan_is(ts, GSM_PCHAN_TCH_F);
break;
default:
- ts->pchan_is = ts->pchan_on_init;
+ ts_set_pchan_is(ts, ts->pchan_on_init);
break;
}
-
- LOG_TS(ts, LOGL_DEBUG, "lchans initialized: %d\n", max_lchans);
}
static void ts_fsm_not_initialized(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -265,11 +317,19 @@ static void ts_fsm_not_initialized(struct osmo_fsm_inst *fi, uint32_t event, voi
osmo_fsm_inst_state_chg(fi, TS_ST_UNUSED, 0, 0);
}
+static void ts_fsm_not_initialized_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx_ts *ts = ts_fi_ts(fi);
+ chan_counts_ts_clear(ts);
+}
+
static void ts_fsm_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_trx_ts *ts = ts_fi_ts(fi);
struct gsm_bts *bts = ts->trx->bts;
+ chan_counts_ts_update(ts);
+
/* We are entering the unused state. There must by definition not be any lchans waiting to be
* activated. */
if (ts_lchans_waiting(ts)) {
@@ -280,7 +340,7 @@ static void ts_fsm_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
}
switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
case GSM_PCHAN_TCH_F_PDCH:
if (bts->gprs.mode == BTS_GPRS_NONE) {
LOG_TS(ts, LOGL_DEBUG, "GPRS mode is 'none': not activating PDCH.\n");
@@ -366,6 +426,7 @@ static void ts_fsm_wait_pdch_act_onenter(struct osmo_fsm_inst *fi, uint32_t prev
static void ts_fsm_wait_pdch_act(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts_trx_ts *ts = ts_fi_ts(fi);
+ struct rate_ctr_group *bts_ctrs = ts->trx->bts->bts_ctrs;
switch (event) {
case TS_EV_PDCH_ACT_ACK:
@@ -374,9 +435,9 @@ static void ts_fsm_wait_pdch_act(struct osmo_fsm_inst *fi, uint32_t event, void
case TS_EV_PDCH_ACT_NACK:
if (ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH)
- rate_ctr_inc(&ts->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_RSL_IPA_NACK));
else
- rate_ctr_inc(&ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_ACT_NACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts_ctrs, BTS_CTR_CHAN_ACT_NACK));
ts->pdch_act_allowed = false;
ts_fsm_error(fi, TS_ST_UNUSED, "Received PDCH activation NACK");
return;
@@ -416,10 +477,10 @@ static void ts_fsm_pdch_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
/* Set pchan = PDCH status, but double check. */
switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
case GSM_PCHAN_TCH_F_PDCH:
case GSM_PCHAN_PDCH:
- ts->pchan_is = GSM_PCHAN_PDCH;
+ ts_set_pchan_is(ts, GSM_PCHAN_PDCH);
break;
default:
ts_fsm_error(fi, TS_ST_BORKEN, "pchan %s is incapable of activating PDCH",
@@ -491,11 +552,11 @@ static void ts_fsm_wait_pdch_deact(struct osmo_fsm_inst *fi, uint32_t event, voi
case TS_EV_PDCH_DEACT_ACK:
/* Remove pchan = PDCH status, but double check. */
switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- ts->pchan_is = GSM_PCHAN_NONE;
+ case GSM_PCHAN_OSMO_DYN:
+ ts_set_pchan_is(ts, GSM_PCHAN_NONE);
break;
case GSM_PCHAN_TCH_F_PDCH:
- ts->pchan_is = GSM_PCHAN_TCH_F;
+ ts_set_pchan_is(ts, GSM_PCHAN_TCH_F);
break;
default:
ts_fsm_error(fi, TS_ST_BORKEN, "pchan %s is incapable of deactivating PDCH",
@@ -511,7 +572,7 @@ static void ts_fsm_wait_pdch_deact(struct osmo_fsm_inst *fi, uint32_t event, voi
case TS_EV_PDCH_DEACT_NACK:
if (ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH)
- rate_ctr_inc(&ts->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ts->trx->bts->bts_ctrs, BTS_CTR_RSL_IPA_NACK));
/* For Osmocom style dyn TS, there actually is no NACK, since there is no RF Channel
* Release NACK message in RSL. */
ts_fsm_error(fi, TS_ST_BORKEN, "Received PDCH deactivation NACK");
@@ -556,7 +617,7 @@ static void ts_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
ts->pdch_act_allowed = true;
/* For static TS, check validity. For dyn TS, figure out which PCHAN this should become. */
- ts_for_each_potential_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
if (lchan_state_is(lchan, LCHAN_ST_UNUSED))
continue;
@@ -573,7 +634,7 @@ static void ts_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
if (!ok && lchan_state_is(lchan, LCHAN_ST_WAIT_TS_READY)) {
LOG_TS(ts, LOGL_ERROR, "lchan activation of %s is not permitted for %s (%s)\n",
- gsm_lchant_name(lchan->type), gsm_pchan_name(ts->pchan_on_init),
+ gsm_chan_t_name(lchan->type), gsm_pchan_name(ts->pchan_on_init),
gsm_lchan_name(lchan));
lchan_dispatch(lchan, LCHAN_EV_TS_ERROR);
}
@@ -585,7 +646,7 @@ static void ts_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
activating_type = lchan->type;
else if (activating_type != lchan->type) {
LOG_TS(ts, LOGL_ERROR, "lchan type %s mismatches %s (%s)\n",
- gsm_lchant_name(lchan->type), gsm_lchant_name(activating_type),
+ gsm_chan_t_name(lchan->type), gsm_chan_t_name(activating_type),
gsm_lchan_name(lchan));
lchan_dispatch(lchan, LCHAN_EV_TS_ERROR);
}
@@ -604,7 +665,7 @@ static void ts_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
break;
default:
- LOG_TS(ts, LOGL_ERROR, "cannot use timeslot as %s\n", gsm_lchant_name(activating_type));
+ LOG_TS(ts, LOGL_ERROR, "cannot use timeslot as %s\n", gsm_chan_t_name(activating_type));
ts_lchans_dispatch(ts, LCHAN_ST_WAIT_TS_READY, LCHAN_EV_TS_ERROR);
break;
}
@@ -614,10 +675,12 @@ static void ts_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
return;
}
+ chan_counts_ts_update(ts);
+
/* Make sure dyn TS pchan_is is updated. For TCH/F_PDCH, there are only PDCH or TCH/F modes, but
- * for Osmocom style TCH/F_TCH/H_PDCH the pchan_is == NONE until an lchan is activated. */
- if (ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH)
- ts->pchan_is = gsm_pchan_by_lchan_type(activating_type);
+ * for Osmocom style TCH/F_TCH/H_SDCCH8_PDCH the pchan_is == NONE until an lchan is activated. */
+ if (ts->pchan_on_init == GSM_PCHAN_OSMO_DYN)
+ ts_set_pchan_is(ts, gsm_pchan_by_lchan_type(activating_type));
ts_lchans_dispatch(ts, LCHAN_ST_WAIT_TS_READY, LCHAN_EV_TS_READY);
}
@@ -659,6 +722,7 @@ static void ts_fsm_in_use(struct osmo_fsm_inst *fi, uint32_t event, void *data)
static void ts_fsm_borken_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_trx_ts *ts = ts_fi_ts(fi);
+ struct gsm_bts *bts = ts->trx->bts;
enum bts_counter_id ctr;
switch (prev_state) {
case TS_ST_NOT_INITIALIZED:
@@ -685,8 +749,8 @@ static void ts_fsm_borken_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
default:
ctr = BTS_CTR_TS_BORKEN_FROM_UNKNOWN;
}
- rate_ctr_inc(&ts->trx->bts->bts_ctrs->ctr[ctr]);
- osmo_stat_item_inc(ts->trx->bts->bts_statg->items[BTS_STAT_TS_BORKEN], 1);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, ctr));
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_TS_BORKEN), 1);
}
static void ts_fsm_borken(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -710,8 +774,8 @@ static void ts_fsm_borken(struct osmo_fsm_inst *fi, uint32_t event, void *data)
struct gsm_bts *bts = ts->trx->bts;
/* Late PDCH activation ACK/NACK is not a crime.
* Just process them as normal. */
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK]);
- osmo_stat_item_dec(bts->bts_statg->items[BTS_STAT_TS_BORKEN], 1);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK));
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_TS_BORKEN), 1);
ts_fsm_wait_pdch_act(fi, event, data);
return;
}
@@ -723,8 +787,8 @@ static void ts_fsm_borken(struct osmo_fsm_inst *fi, uint32_t event, void *data)
struct gsm_bts *bts = ts->trx->bts;
/* Late PDCH deactivation ACK/NACK is also not a crime.
* Just process them as normal. */
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK]);
- osmo_stat_item_dec(bts->bts_statg->items[BTS_STAT_TS_BORKEN], 1);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK));
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_TS_BORKEN), 1);
ts_fsm_wait_pdch_deact(fi, event, data);
return;
}
@@ -762,7 +826,8 @@ static void ts_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data
osmo_fsm_inst_state_chg(fi, TS_ST_NOT_INITIALIZED, 0, 0);
OSMO_ASSERT(fi->state == TS_ST_NOT_INITIALIZED);
ts_terminate_lchan_fsms(ts);
- ts->pchan_is = ts->pchan_on_init = GSM_PCHAN_NONE;
+ ts->pchan_on_init = GSM_PCHAN_NONE;
+ ts_set_pchan_is(ts, GSM_PCHAN_NONE);
ts_fsm_update_id(ts);
break;
@@ -771,7 +836,7 @@ static void ts_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data
if (fi->state != TS_ST_NOT_INITIALIZED)
osmo_fsm_inst_state_chg(fi, TS_ST_NOT_INITIALIZED, 0, 0);
OSMO_ASSERT(fi->state == TS_ST_NOT_INITIALIZED);
- ts->pchan_is = GSM_PCHAN_NONE;
+ ts_set_pchan_is(ts, GSM_PCHAN_NONE);
ts_lchans_dispatch(ts, -1, LCHAN_EV_TS_ERROR);
break;
@@ -783,9 +848,10 @@ static void ts_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data
static void ts_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct gsm_bts_trx_ts *ts = ts_fi_ts(fi);
+ struct gsm_bts *bts = ts->trx->bts;
if (ts->fi->state == TS_ST_BORKEN) {
- rate_ctr_inc(&ts->trx->bts->bts_ctrs->ctr[BTS_CTR_TS_BORKEN_EV_TEARDOWN]);
- osmo_stat_item_dec(ts->trx->bts->bts_statg->items[BTS_STAT_TS_BORKEN], 1);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_TS_BORKEN_EV_TEARDOWN));
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_TS_BORKEN), 1);
}
}
@@ -794,6 +860,7 @@ static void ts_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
static const struct osmo_fsm_state ts_fsm_states[] = {
[TS_ST_NOT_INITIALIZED] = {
.name = "NOT_INITIALIZED",
+ .onenter = ts_fsm_not_initialized_onenter,
.action = ts_fsm_not_initialized,
.in_event_mask = 0
| S(TS_EV_OML_READY)
@@ -865,7 +932,6 @@ static const struct osmo_fsm_state ts_fsm_states[] = {
.out_state_mask = 0
| S(TS_ST_IN_USE)
| S(TS_ST_UNUSED)
- | S(TS_ST_BORKEN)
| S(TS_ST_NOT_INITIALIZED)
| S(TS_ST_BORKEN)
,
@@ -940,7 +1006,7 @@ static struct osmo_fsm ts_fsm = {
bool ts_is_lchan_waiting_for_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config *target_pchan)
{
struct gsm_lchan *lchan;
- ts_for_each_potential_lchan(lchan, ts) {
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
if (lchan->fi->state == LCHAN_ST_WAIT_TS_READY) {
if (target_pchan)
*target_pchan = gsm_pchan_by_lchan_type(lchan->type);
@@ -981,7 +1047,7 @@ bool ts_is_pchan_switching(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config
* waiting for PDCH DEACT (N)ACK */
if (target_pchan) {
switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
if (target_pchan)
*target_pchan = GSM_PCHAN_NONE;
break;
@@ -1004,7 +1070,7 @@ bool ts_is_pchan_switching(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config
/* Does the timeslot's *current* state allow use as this PCHAN kind? If the ts is in switchover, return
* true if the switchover's target PCHAN matches, i.e. an lchan for this pchan kind could be requested
* and will be served after the switch. (Do not check whether any lchans are actually available.) */
-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)
{
enum gsm_phys_chan_config target_pchan;
@@ -1022,5 +1088,11 @@ bool ts_usable_as_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config as_
if (ts_is_lchan_waiting_for_pchan(ts, &target_pchan))
return target_pchan == as_pchan;
- return ts_is_capable_of_pchan(ts, as_pchan);
+ if (!ts_is_capable_of_pchan(ts, as_pchan))
+ return false;
+
+ if (!allow_pchan_switch && ts->pchan_is != as_pchan)
+ return false;
+
+ return true;
}
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
index fd03cb384..18debe7d5 100644
--- a/src/utils/Makefile.am
+++ b/src/utils/Makefile.am
@@ -9,6 +9,8 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(SQLITE3_CFLAGS) \
$(NULL)
@@ -47,14 +49,7 @@ bs11_config_SOURCES = \
$(NULL)
bs11_config_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/bts_siemens_bs11.o \
- $(top_builddir)/src/osmo-bsc/e1_config.o \
- $(top_builddir)/src/osmo-bsc/gsm_data.o \
- $(top_builddir)/src/osmo-bsc/net_init.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
@@ -120,11 +115,7 @@ meas_json_SOURCES = \
$(NULL)
meas_json_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 \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
diff --git a/src/utils/bs11_config.c b/src/utils/bs11_config.c
index c279179d5..e79507642 100644
--- a/src/utils/bs11_config.c
+++ b/src/utils/bs11_config.c
@@ -974,24 +974,3 @@ int main(int argc, char **argv)
exit(0);
}
-
-/* Stub */
-int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- return 0;
-}
-
-/* Stub */
-int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- return 0;
-}
-
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int abis_rsl_rcvmsg(struct msgb *msg) { return 0; }
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/src/utils/meas_db.c b/src/utils/meas_db.c
index 7233dcda6..2f81524ae 100644
--- a/src/utils/meas_db.c
+++ b/src/utils/meas_db.c
@@ -29,10 +29,11 @@
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/bsc/meas_rep.h>
+#include <osmocom/bsc/meas_feed.h>
#include "meas_db.h"
-#define INS_MR "INSERT INTO meas_rep (time, imsi, name, scenario, nr, bs_power, ms_timing_offset, fpc, ms_l1_pwr, ms_l1_ta) VALUES (?,?,?,?,?,?,?,?,?,?)"
+#define INS_MR "INSERT INTO meas_rep (time, bts_nr, trx_nr, ts_nr, ss_nr, lchan_type, pchan_type, imsi, name, scenario, nr, bs_power, ms_timing_offset, fpc, ms_l1_pwr, ms_l1_ta) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
#define INS_UD "INSERT INTO meas_rep_unidir (meas_id, rx_lev_full, rx_lev_sub, rx_qual_full, rx_qual_sub, dtx, uplink) VALUES (?,?,?,?,?,?,?)"
#define UPD_MR "UPDATE meas_rep SET ul_unidir=?, dl_unidir=? WHERE id=?"
@@ -59,8 +60,6 @@ struct meas_db_state {
static int _insert_ud(struct meas_db_state *st, unsigned long meas_id, int dtx,
int uplink, const struct gsm_meas_rep_unidir *ud)
{
- unsigned long rowid;
-
SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 1, meas_id));
SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 2,
rxlev2dbm(ud->full.rx_lev)));
@@ -81,53 +80,57 @@ err_io:
}
/* insert a measurement report into the database */
-int meas_db_insert(struct meas_db_state *st, const char *imsi,
- const char *name, unsigned long timestamp,
- const char *scenario,
- const struct gsm_meas_rep *mr)
+int meas_db_insert(struct meas_db_state *st, unsigned long timestamp,
+ const struct meas_feed_meas *mfm)
{
- int rc;
sqlite3_int64 rowid, ul_rowid, dl_rowid;
SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 1, timestamp));
- if (imsi)
- SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 2,
- imsi, -1, SQLITE_STATIC));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 2, mfm->bts_nr));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 3, mfm->trx_nr));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 4, mfm->ts_nr));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 5, mfm->ss_nr));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 6, mfm->lchan_type));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 7, mfm->pchan_type));
+
+ if (mfm->imsi[0] != '\0')
+ SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 8,
+ mfm->imsi, -1, SQLITE_STATIC));
else
- SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 2));
+ SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 8));
- if (name)
- SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 3,
- name, -1, SQLITE_STATIC));
+ if (mfm->name[0] != '\0')
+ SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 9,
+ mfm->name, -1, SQLITE_STATIC));
else
- SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 3));
+ SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 9));
- if (scenario)
- SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 4,
- scenario, -1, SQLITE_STATIC));
+ if (mfm->scenario[0] != '\0')
+ SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 10,
+ mfm->scenario, -1, SQLITE_STATIC));
else
- SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 4));
+ SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 10));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 5, mr->nr));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 6, mr->bs_power));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 11, mfm->mr.nr));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 12, mfm->mr.bs_power_db / 2));
- if (mr->flags & MEAS_REP_F_MS_TO)
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 7, mr->ms_timing_offset));
+ if (mfm->mr.flags & MEAS_REP_F_MS_TO)
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 13, mfm->mr.ms_timing_offset));
else
- SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 7));
+ SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 13));
- if (mr->flags & MEAS_REP_F_FPC)
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 1));
+ if (mfm->mr.flags & MEAS_REP_F_FPC)
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 14, 1));
else
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 0));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 14, 0));
- if (mr->flags & MEAS_REP_F_MS_L1) {
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 9,
- mr->ms_l1.pwr));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 10,
- mr->ms_l1.ta));
+ if (mfm->mr.flags & MEAS_REP_F_MS_L1) {
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 15,
+ mfm->mr.ms_l1.pwr));
+ SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 16,
+ mfm->mr.ms_l1.ta));
}
SCK_DONE(st->db, sqlite3_step(st->stmt_ins_mr));
@@ -136,14 +139,14 @@ int meas_db_insert(struct meas_db_state *st, const char *imsi,
rowid = sqlite3_last_insert_rowid(st->db);
/* insert uplink measurement */
- ul_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_UL_DTX,
- 1, &mr->ul);
+ ul_rowid = _insert_ud(st, rowid, mfm->mr.flags & MEAS_REP_F_UL_DTX,
+ 1, &mfm->mr.ul);
SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 1, ul_rowid));
/* insert downlink measurement, if present */
- if (mr->flags & MEAS_REP_F_DL_VALID) {
- dl_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_DL_DTX,
- 0, &mr->dl);
+ if (mfm->mr.flags & MEAS_REP_F_DL_VALID) {
+ dl_rowid = _insert_ud(st, rowid, mfm->mr.flags & MEAS_REP_F_DL_DTX,
+ 0, &mfm->mr.dl);
SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 2, dl_rowid));
} else
SCK_OK(st->db, sqlite3_bind_null(st->stmt_upd_mr, 2));
@@ -184,6 +187,12 @@ static const char *create_stmts[] = {
"CREATE TABLE IF NOT EXISTS meas_rep ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"time TIMESTAMP,"
+ "bts_nr INTEGER,"
+ "trx_nr INTEGER,"
+ "ts_nr INTEGER,"
+ "ss_nr INTEGER,"
+ "lchan_type INTEGER,"
+ "pchan_type INTEGER,"
"imsi TEXT,"
"name TEXT,"
"scenario TEXT,"
@@ -210,6 +219,12 @@ static const char *create_stmts[] = {
"SELECT "
"meas_rep.id, "
"datetime(time,'unixepoch') AS timestamp, "
+ "bts_nr,"
+ "trx_nr,"
+ "ts_nr,"
+ "ss_nr,"
+ "lchan_type,"
+ "pchan_type,"
"imsi, "
"name, "
"scenario, "
@@ -241,6 +256,12 @@ static const char *create_stmts[] = {
"SELECT "
"id,"
"timestamp,"
+ "bts_nr,"
+ "trx_nr,"
+ "ts_nr,"
+ "ss_nr,"
+ "lchan_type,"
+ "pchan_type,"
"imsi,"
"name,"
"scenario,"
@@ -257,7 +278,7 @@ static const char *create_stmts[] = {
static int check_create_tbl(struct meas_db_state *st)
{
- int i, rc;
+ int i;
for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
SCK_OK(st->db, sqlite3_exec(st->db, create_stmts[i],
diff --git a/src/utils/meas_db.h b/src/utils/meas_db.h
index 889e9022f..8f8a8c679 100644
--- a/src/utils/meas_db.h
+++ b/src/utils/meas_db.h
@@ -9,9 +9,7 @@ void meas_db_close(struct meas_db_state *st);
int meas_db_begin(struct meas_db_state *st);
int meas_db_commit(struct meas_db_state *st);
-int meas_db_insert(struct meas_db_state *st, const char *imsi,
- const char *name, unsigned long timestamp,
- const char *scenario,
- const struct gsm_meas_rep *mr);
+int meas_db_insert(struct meas_db_state *st, unsigned long timestamp,
+ const struct meas_feed_meas *mfm);
#endif
diff --git a/src/utils/meas_json.c b/src/utils/meas_json.c
index b44a3008c..953b114f3 100644
--- a/src/utils/meas_json.c
+++ b/src/utils/meas_json.c
@@ -62,7 +62,7 @@ static void print_meas_rep_json(struct gsm_meas_rep *mr)
printf(", \"UL_MEAS\":{");
print_meas_rep_uni_json(&mr->ul);
printf("}");
- printf(", \"BS_POWER\":%d", mr->bs_power);
+ printf(", \"BS_POWER\":%d", mr->bs_power_db / 2);
if (mr->flags & MEAS_REP_F_MS_TO)
printf(", \"MS_TO\":%d", mr->ms_timing_offset);
@@ -99,7 +99,7 @@ static void print_chan_info_json(struct meas_feed_meas *mfm)
{
printf("\"lchan_type\":\"%s\", \"pchan_type\":\"%s\", "
"\"bts_nr\":%d, \"trx_nr\":%d, \"ts_nr\":%d, \"ss_nr\":%d",
- gsm_lchant_name(mfm->lchan_type), gsm_pchan_name(mfm->pchan_type),
+ gsm_chan_t_name(mfm->lchan_type), gsm_pchan_name(mfm->pchan_type),
mfm->bts_nr, mfm->trx_nr, mfm->ts_nr, mfm->ss_nr);
}
@@ -201,12 +201,3 @@ int main(int argc, char **argv)
exit(0);
}
-
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int abis_rsl_rcvmsg(struct msgb *msg) { return 0; }
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/src/utils/meas_pcap2db.c b/src/utils/meas_pcap2db.c
index db00fae49..eb69d506c 100644
--- a/src/utils/meas_pcap2db.c
+++ b/src/utils/meas_pcap2db.c
@@ -47,21 +47,13 @@ static struct meas_db_state *db;
static void handle_mfm(const struct pcap_pkthdr *h,
const struct meas_feed_meas *mfm)
{
- const char *scenario;
-
- if (strlen(mfm->scenario))
- scenario = mfm->scenario;
- else
- scenario = NULL;
-
- meas_db_insert(db, mfm->imsi, mfm->name, h->ts.tv_sec,
- scenario, &mfm->mr);
+ meas_db_insert(db, h->ts.tv_sec, mfm);
}
static void pcap_cb(u_char *user, const struct pcap_pkthdr *h,
const u_char *bytes)
{
- const char *cur = bytes;
+ const u_char *cur = bytes;
const struct iphdr *ip;
const struct udphdr *udp;
const struct meas_feed_meas *mfm;
diff --git a/src/utils/meas_udp2db.c b/src/utils/meas_udp2db.c
index 07023c323..f40f13702 100644
--- a/src/utils/meas_udp2db.c
+++ b/src/utils/meas_udp2db.c
@@ -47,7 +47,6 @@ static int handle_msg(struct msgb *msg)
{
struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg);
struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg);
- const char *scenario;
time_t now = time(NULL);
if (mfh->version != MEAS_FEED_VERSION)
@@ -56,13 +55,7 @@ static int handle_msg(struct msgb *msg)
if (mfh->msg_type != MEAS_FEED_MEAS)
return -EINVAL;
- if (strlen(mfm->scenario))
- scenario = mfm->scenario;
- else
- scenario = NULL;
-
- meas_db_insert(db, mfm->imsi, mfm->name, now,
- scenario, &mfm->mr);
+ meas_db_insert(db, now, mfm);
return 0;
}
diff --git a/src/utils/meas_vis.c b/src/utils/meas_vis.c
index 73ccfc4aa..c3ee2a5e2 100644
--- a/src/utils/meas_vis.c
+++ b/src/utils/meas_vis.c
@@ -193,7 +193,7 @@ void write_uni(struct ms_state *ms, struct ms_state_uni *msu,
if (dir == DIR_UL) {
pwr = ms->mr.ms_l1.pwr;
} else {
- pwr = ms->mr.bs_power;
+ pwr = ms->mr.bs_power_db / 2;
}
color = A_REVERSE | COLOR_PAIR(lev_col) | ' ';
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c061baaf1..8ee8bec51 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,12 +1,13 @@
SUBDIRS = \
+ abis \
acc \
bsc \
codec_pref \
gsm0408 \
- abis \
- subscr \
- nanobts_omlattr \
handover \
+ nanobts_omlattr \
+ paging \
+ subscr \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
@@ -33,13 +34,10 @@ EXTRA_DIST = \
$(TESTSUITE) \
vty_test_runner.py \
ctrl_test_runner.py \
- cbc.vty \
- gprs_bvci_default.vty \
- handover_cfg.vty \
- neighbor_ident.vty \
- nri_cfg.vty \
- osmo-bsc.vty \
- timer.vty \
+ $(srcdir)/*.vty \
+ ctrl/osmo-bsc-neigh-test.cfg \
+ ctrl/osmo-bsc-apply-config-file.cfg \
+ ctrl/osmo-bsc-apply-config-file-invalid.cfg \
$(NULL)
TESTSUITE = $(srcdir)/testsuite
@@ -55,7 +53,6 @@ python-tests: $(BUILT_SOURCES)
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
$(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
$(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v
- rm -f $(top_builddir)/sms.db $(top_builddir)/gsn_restart $(top_builddir)/gtphub_restart_count
else
python-tests: $(BUILT_SOURCES)
echo "Not running python-based tests (determined at configure-time)"
diff --git a/tests/abis/Makefile.am b/tests/abis/Makefile.am
index a245b51c8..d17eff825 100644
--- a/tests/abis/Makefile.am
+++ b/tests/abis/Makefile.am
@@ -7,8 +7,10 @@ AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -16,7 +18,7 @@ EXTRA_DIST = \
abis_test.ok \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
abis_test \
$(NULL)
@@ -25,12 +27,7 @@ abis_test_SOURCES = \
$(NULL)
abis_test_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/net_init.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOGSM_LIBS) \
diff --git a/tests/abis/abis_test.c b/tests/abis/abis_test.c
index 9d26eddcd..a48154bac 100644
--- a/tests/abis/abis_test.c
+++ b/tests/abis/abis_test.c
@@ -183,16 +183,3 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { return true; }
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/tests/acc/Makefile.am b/tests/acc/Makefile.am
index 4726ddc72..a169c5ca9 100644
--- a/tests/acc/Makefile.am
+++ b/tests/acc/Makefile.am
@@ -7,8 +7,10 @@ AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -16,7 +18,7 @@ EXTRA_DIST = \
acc_test.ok \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
acc_test \
$(NULL)
@@ -25,12 +27,7 @@ acc_test_SOURCES = \
$(NULL)
acc_test_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/net_init.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOGSM_LIBS) \
diff --git a/tests/acc/acc_test.c b/tests/acc/acc_test.c
index f463a2e64..887e89104 100644
--- a/tests/acc/acc_test.c
+++ b/tests/acc/acc_test.c
@@ -33,9 +33,7 @@
static void clock_debug(char* str)
{
- struct timespec ts;
struct timeval tv;
- osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
osmo_gettimeofday(&tv, NULL);
fprintf(stderr, "sys={%lu.%06lu}: %s\n",
tv.tv_sec, tv.tv_usec, str);
@@ -44,7 +42,8 @@ static void clock_debug(char* str)
#define bts_init(net) _bts_init(net, __func__)
static inline struct gsm_bts *_bts_init(struct gsm_network *net, const char *msg)
{
- struct gsm_bts *bts = gsm_bts_alloc(net, 0);
+ struct gsm_bts_sm *bts_sm = gsm_bts_sm_alloc(net, 0);
+ struct gsm_bts *bts = bts_sm->bts[0];
if (!bts) {
fprintf(stderr, "BTS allocation failure in %s()\n", msg);
exit(1);
@@ -59,14 +58,10 @@ static inline struct gsm_bts *_bts_init(struct gsm_network *net, const char *msg
#define bts_del(bts) _bts_del(bts, __func__)
static inline void _bts_del(struct gsm_bts *bts, const char *msg)
{
- osmo_stat_item_group_free(bts->bts_statg);
- rate_ctr_group_free(bts->bts_ctrs);
- if (osmo_timer_pending(&bts->acc_mgr.rotate_timer))
- osmo_timer_del(&bts->acc_mgr.rotate_timer);
- if (osmo_timer_pending(&bts->acc_ramp.step_timer))
- osmo_timer_del(&bts->acc_ramp.step_timer);
+ osmo_timer_del(&bts->acc_mgr.rotate_timer);
+ osmo_timer_del(&bts->acc_ramp.step_timer);
/* no need to llist_del(&bts->list), we never registered the bts there. */
- talloc_free(bts);
+ talloc_free(bts->site_mgr);
fprintf(stderr, "BTS deallocated OK in %s()\n", msg);
}
@@ -491,12 +486,15 @@ int main(int argc, char **argv)
osmo_gettimeofday_override = true;
osmo_gettimeofday_override_time = (struct timeval) {0, 0};
- tall_bsc_ctx = talloc_named_const(NULL, 0, "gsm0408_test");
+ tall_bsc_ctx = talloc_named_const(NULL, 0, "acc_test");
osmo_init_logging2(tall_bsc_ctx, &log_info);
log_set_log_level(osmo_stderr_target, LOGL_INFO);
- log_set_print_category_hex(osmo_stderr_target, false);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 0);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_use_color(osmo_stderr_target, 0);
+ log_parse_category_mask(osmo_stderr_target, "DRSL,1:");
+ osmo_fsm_log_addr(false);
net = gsm_network_init(tall_bsc_ctx);
if (!net) {
@@ -521,8 +519,8 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
-/* Whenever ACC code changes the set of barred ACCs, gsm_bts_set_system_infos()
- * is called which ends up calling pcu_info_update */
+/* stub: Whenever ACC code changes the set of barred ACCs, gsm_bts_set_system_infos()
+ * is called which ends up calling pcu_info_update. */
void pcu_info_update(struct gsm_bts *bts) {
struct gsm48_rach_control rach_control = {0};
@@ -542,15 +540,15 @@ void pcu_info_update(struct gsm_bts *bts) {
);
}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { return true; }
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
+/* stub: Whenever ACC code changes the set of barred ACCs, gsm_bts_set_system_infos()
+ * is called which ends up calling rsl_bcch_info. We need to return success to
+ * have pcu_info_update() called. */
int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
+
+/* stub: Whenever ACC code changes the set of barred ACCs, gsm_bts_set_system_infos()
+ * is called which ends up calling rsl_sacch_filling. We need to return success to
+ * have pcu_info_update() called. */
+int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type,
+ const uint8_t *data, int len)
+{ return 0; }
diff --git a/tests/acch_overpower.vty b/tests/acch_overpower.vty
new file mode 100644
index 000000000..70090a438
--- /dev/null
+++ b/tests/acch_overpower.vty
@@ -0,0 +1,88 @@
+OsmoBSC> enable
+
+OsmoBSC# ### Default configuration (overpower disabled)
+OsmoBSC# show running-config
+... !overpower
+
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)# list with-flags
+...
+ . ..l overpower (dl-acch|dl-sacch|dl-facch) <1-4>
+ . ..l no overpower dl-acch
+ . ..l overpower rxqual (0|1|2|3|4|5|6|7)
+ . ..l overpower chan-mode (speech-amr|any)
+...
+
+OsmoBSC(config-net-bts)# overpower?
+ overpower Temporary ACCH overpower
+OsmoBSC(config-net-bts)# overpower ?
+ dl-acch Enable overpower for both SACCH and FACCH
+ dl-sacch Enable overpower for SACCH only
+ dl-facch Enable overpower for FACCH only
+ rxqual Set RxQual (BER) threshold (default 4)
+ chan-mode Allow temporary overpower for specific Channel mode(s)
+
+OsmoBSC(config-net-bts)# overpower dl-acch ?
+ <1-4> Overpower value in dB
+OsmoBSC(config-net-bts)# overpower dl-acch 2
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-acch 2
+ overpower rxqual 4
+ overpower chan-mode speech-amr
+...
+OsmoBSC(config-net-bts)# overpower dl-sacch 3
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-sacch 3
+ overpower rxqual 4
+ overpower chan-mode speech-amr
+...
+OsmoBSC(config-net-bts)# overpower dl-facch 4
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-facch 4
+ overpower rxqual 4
+ overpower chan-mode speech-amr
+...
+
+OsmoBSC(config-net-bts)# overpower rxqual ?
+ 0 BER >= 0% (always on)
+ 1 BER >= 0.2%
+ 2 BER >= 0.4%
+ 3 BER >= 0.8%
+ 4 BER >= 1.6% (default)
+ 5 BER >= 3.2%
+ 6 BER >= 6.4%
+ 7 BER >= 12.8%
+OsmoBSC(config-net-bts)# overpower rxqual 0
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-facch 4
+ overpower rxqual 0
+ overpower chan-mode speech-amr
+...
+OsmoBSC(config-net-bts)# overpower rxqual 7
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-facch 4
+ overpower rxqual 7
+ overpower chan-mode speech-amr
+...
+
+OsmoBSC(config-net-bts)# overpower chan-mode ?
+ speech-amr Speech channels using AMR codec (default)
+ any Any kind of channel mode
+OsmoBSC(config-net-bts)# overpower chan-mode any
+OsmoBSC(config-net-bts)# show running-config
+...
+ overpower dl-facch 4
+ overpower rxqual 7
+ overpower chan-mode any
+...
+
+OsmoBSC(config-net-bts)# no overpower dl-acch
+OsmoBSC(config-net-bts)# show running-config
+... !overpower
diff --git a/tests/bsc/Makefile.am b/tests/bsc/Makefile.am
index 468c0321e..6b5c3e864 100644
--- a/tests/bsc/Makefile.am
+++ b/tests/bsc/Makefile.am
@@ -8,11 +8,11 @@ AM_CFLAGS = \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOLEGACYMGCP_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(LIBOSMOMGCPCLIENT_CFLAGS) \
- $(LIBOSMOSIGTRAN_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -24,7 +24,7 @@ EXTRA_DIST = \
bsc_test.ok \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
bsc_test \
$(NULL)
@@ -33,23 +33,17 @@ bsc_test_SOURCES = \
$(NULL)
bsc_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/abis_nm.o \
- $(top_builddir)/src/osmo-bsc/acc.o \
- $(top_builddir)/src/osmo-bsc/arfcn_range_encode.o \
- $(top_builddir)/src/osmo-bsc/osmo_bsc_filter.o \
- $(top_builddir)/src/osmo-bsc/bsc_subscriber.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/handover_cfg.o \
- $(top_builddir)/src/osmo-bsc/handover_logic.o \
- $(top_builddir)/src/osmo-bsc/neighbor_ident.o \
- $(top_builddir)/src/osmo-bsc/net_init.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
+ $(LIBOSMOCTRL_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOLEGACYMGCP_LIBS) \
$(LIBRARY_GSM) \
-lrt \
$(NULL)
+
+.PHONY: update_exp
+update_exp:
+ $(builddir)/bsc_test > $(srcdir)/bsc_test.ok
diff --git a/tests/bsc/bsc_test.c b/tests/bsc/bsc_test.c
index 0ed504bcd..62757b4c4 100644
--- a/tests/bsc/bsc_test.c
+++ b/tests/bsc/bsc_test.c
@@ -29,6 +29,12 @@
#include <osmocom/bsc/osmo_bsc.h>
#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bss.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/assignment_fsm.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/gsm/gad.h>
#include <osmocom/core/application.h>
@@ -125,6 +131,7 @@ static void test_scan(void)
struct gsm_network *net = gsm_network_init(ctx);
struct gsm_subscriber_connection *conn = talloc_zero(net, struct gsm_subscriber_connection);
+ bsc_gsmnet = net;
conn->network = net;
/* start testing with proper messages */
@@ -176,6 +183,62 @@ out:
}
talloc_free(net);
+ bsc_gsmnet = NULL;
+}
+
+static void test_fsm_ids_with_pchan_names(void)
+{
+ struct gsm_network *net;
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+ struct gsm_lchan *lchan;
+ enum gsm_phys_chan_config pchan;
+ struct gsm_subscriber_connection *conn;
+
+ rate_ctr_init(ctx);
+ tall_bsc_ctx = ctx;
+ bsc_network_alloc();
+ net = bsc_gsmnet;
+
+ /* Have a BTS so that we have trx, timeslots, lchans that have FSMs to check the id of */
+ bts = bsc_bts_alloc_register(net, GSM_BTS_TYPE_UNKNOWN, HARDCODED_BSIC);
+ trx = gsm_bts_trx_alloc(bts);
+
+ printf("\nTesting FSM ids that contain pchan names\n");
+ ts = &trx->ts[0];
+ lchan = &ts->lchan[0];
+
+ conn = bsc_subscr_con_allocate(net);
+ conn->lchan = lchan;
+ conn->assignment.new_lchan = lchan;
+ conn->sccp.conn_id = 123;
+ conn->bsub = bsc_subscr_find_or_create_by_tmsi(net->bsc_subscribers, 0x423, "test");
+ gscon_update_id(conn);
+
+ /* dirty dirty hack, to just point at some fi so we can update the id */
+ conn->assignment.fi = trx->ts[1].fi;
+
+ for (pchan = 0; pchan < _GSM_PCHAN_MAX; pchan++) {
+ ts->pchan_from_config = pchan;
+ /* trigger ID update in ts and lchan */
+ osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_READY, NULL);
+
+ if (lchan->fi)
+ assignment_fsm_update_id(conn);
+
+ printf("pchan=%s:\n ts->fi->id = %s\n lchan->fi->id = %s\n assignment.fi->id = %s\n",
+ gsm_pchan_name(pchan),
+ ts->fi->id,
+ lchan->fi ? lchan->fi->id : "null",
+ lchan->fi ? conn->assignment.fi->id : "null");
+
+ osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_DOWN, NULL);
+ }
+
+ talloc_free(net);
+ bsc_gsmnet = NULL;
+ printf("\n");
}
static const struct log_info_cat log_categories[] = {
@@ -214,35 +277,9 @@ int main(int argc, char **argv)
osmo_init_logging2(ctx, &log_info);
test_scan();
+ test_fsm_ids_with_pchan_names();
printf("Testing execution completed.\n");
talloc_free(ctx);
return 0;
}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, uint8_t dlci, enum gsm0808_cause cause) {}
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) {}
-int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel)
-{ return 0; }
-void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) {}
-void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause) {}
-void bsc_assign_fail(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause) {}
-void bsc_cm_update(struct gsm_subscriber_connection *conn,
- const uint8_t *cm2, uint8_t cm2_len,
- const uint8_t *cm3, uint8_t cm3_len) {}
-void gscon_submit_rsl_dtap(struct gsm_subscriber_connection *conn,
- struct msgb *msg, int link_id, int allow_sacch) {}
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-void lchan_activate(struct gsm_lchan *lchan, void *info) {}
-bool neighbor_ident_bts_entry_exists(uint8_t from_bts) { return false; }
-const char *handover_status(struct gsm_subscriber_connection *conn) { return "x"; }
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/tests/bsc/bsc_test.ok b/tests/bsc/bsc_test.ok
index 0564bf0cd..172ee0d8b 100644
--- a/tests/bsc/bsc_test.ok
+++ b/tests/bsc/bsc_test.ok
@@ -1,4 +1,55 @@
Testing BTS<->MSC message scan.
Going to test item: 0
Going to test item: 1
+
+Testing FSM ids that contain pchan names
+pchan=NONE:
+ ts->fi->id = 0-1-0-NONE
+ lchan->fi->id = null
+ assignment.fi->id = null
+pchan=CCCH:
+ ts->fi->id = 0-1-0-CCCH
+ lchan->fi->id = null
+ assignment.fi->id = null
+pchan=CCCH+SDCCH4:
+ ts->fi->id = 0-1-0-CCCH_SDCCH4
+ lchan->fi->id = 0-1-0-CCCH_SDCCH4-0
+ assignment.fi->id = msc4294967295-conn123_subscr-TMSI-0x00000423_0-1-0-CCCH_SDCCH4-0
+pchan=TCH/F:
+ ts->fi->id = 0-1-0-TCH_F
+ lchan->fi->id = 0-1-0-TCH_F-0
+ assignment.fi->id = msc4294967295-conn123_subscr-TMSI-0x00000423_0-1-0-TCH_F-0
+pchan=TCH/H:
+ ts->fi->id = 0-1-0-TCH_H
+ lchan->fi->id = 0-1-0-TCH_H-0
+ assignment.fi->id = msc4294967295-conn123_subscr-TMSI-0x00000423_0-1-0-TCH_H-0
+pchan=SDCCH8:
+ ts->fi->id = 0-1-0-SDCCH8
+ lchan->fi->id = 0-1-0-SDCCH8-0
+ assignment.fi->id = msc4294967295-conn123_subscr-TMSI-0x00000423_0-1-0-SDCCH8-0
+pchan=PDCH:
+ ts->fi->id = 0-1-0-PDCH
+ lchan->fi->id = null
+ assignment.fi->id = null
+pchan=DYNAMIC/IPACCESS:
+ ts->fi->id = 0-1-0-DYNAMIC_IPACCESS
+ lchan->fi->id = 0-1-0-DYNAMIC_IPACCESS-0
+ assignment.fi->id = msc4294967295-conn123_subscr-TMSI-0x00000423_0-1-0-DYNAMIC_IPACCESSasTCH_F-0
+pchan=UNKNOWN:
+ ts->fi->id = 0-1-0-UNKNOWN
+ lchan->fi->id = null
+ assignment.fi->id = null
+pchan=CCCH+SDCCH4+CBCH:
+ ts->fi->id = 0-1-0-CCCH_SDCCH4_CBCH
+ lchan->fi->id = 0-1-0-CCCH_SDCCH4_CBCH-0
+ assignment.fi->id = msc4294967295-conn123_subscr-TMSI-0x00000423_0-1-0-CCCH_SDCCH4_CBCH-0
+pchan=SDCCH8+CBCH:
+ ts->fi->id = 0-1-0-SDCCH8_CBCH
+ lchan->fi->id = 0-1-0-SDCCH8_CBCH-0
+ assignment.fi->id = msc4294967295-conn123_subscr-TMSI-0x00000423_0-1-0-SDCCH8_CBCH-0
+pchan=DYNAMIC/OSMOCOM:
+ ts->fi->id = 0-1-0-DYNAMIC_OSMOCOM
+ lchan->fi->id = 0-1-0-DYNAMIC_OSMOCOM-0
+ assignment.fi->id = msc4294967295-conn123_subscr-TMSI-0x00000423_0-1-0-DYNAMIC_OSMOCOMasNONE-0
+
Testing execution completed.
diff --git a/tests/bts_features.vty b/tests/bts_features.vty
new file mode 100644
index 000000000..3768a4d52
--- /dev/null
+++ b/tests/bts_features.vty
@@ -0,0 +1,27 @@
+OsmoBSC> ### see doc/bts-features.txt
+
+OsmoBSC> enable
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+
+OsmoBSC(config-net)# ### osmo-bts: all feature checks pass before it is connected (features_get_reported is true)
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)# gprs mode egprs
+OsmoBSC(config-net-bts)# trx 0
+OsmoBSC(config-net-bts-trx)# timeslot 2
+OsmoBSC(config-net-bts-trx-ts)# hopping enabled 1
+OsmoBSC(config-net-bts-trx-ts)# exit
+OsmoBSC(config-net-bts-trx)# exit
+OsmoBSC(config-net-bts)# exit
+
+OsmoBSC(config-net)# ### bs11: checks against hardcoded features (features_get_reported is false)
+OsmoBSC(config-net)# bts 1
+OsmoBSC(config-net-bts)# type bs11
+OsmoBSC(config-net-bts)# gprs mode egprs
+% This BTS type does not support egprs
+OsmoBSC(config-net-bts)# trx 0
+OsmoBSC(config-net-bts-trx)# timeslot 2
+OsmoBSC(config-net-bts-trx-ts)# hopping enabled 1
+OsmoBSC(config-net-bts-trx-ts)# exit
+OsmoBSC(config-net-bts-trx)# exit
+OsmoBSC(config-net-bts)# exit
diff --git a/tests/codec_pref/Makefile.am b/tests/codec_pref/Makefile.am
index e000252da..ec25da5b8 100644
--- a/tests/codec_pref/Makefile.am
+++ b/tests/codec_pref/Makefile.am
@@ -8,6 +8,7 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(NULL)
@@ -18,7 +19,7 @@ EXTRA_DIST = \
codec_pref_test.ok \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
codec_pref_test \
$(NULL)
@@ -27,7 +28,7 @@ codec_pref_test_SOURCES = \
$(NULL)
codec_pref_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/codec_pref.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
-lrt \
diff --git a/tests/codec_pref/codec_pref_test.c b/tests/codec_pref/codec_pref_test.c
index 5f4c831d4..920352c54 100644
--- a/tests/codec_pref/codec_pref_test.c
+++ b/tests/codec_pref/codec_pref_test.c
@@ -30,27 +30,8 @@
void *ctx = NULL;
-#define MSC_AUDIO_SUPPORT_MAX 5
#define N_CONFIG_VARIANTS 9
-/* Make sure that there is some memory to put our test configuration. */
-static void init_msc_config(struct bsc_msc_data *msc)
-{
- unsigned int i;
-
- msc->audio_support = talloc_zero_array(ctx, struct gsm_audio_support *, MSC_AUDIO_SUPPORT_MAX);
- msc->audio_length = MSC_AUDIO_SUPPORT_MAX;
- for (i = 0; i < MSC_AUDIO_SUPPORT_MAX; i++) {
- msc->audio_support[i] = talloc_zero(msc->audio_support, struct gsm_audio_support);
- }
-}
-
-/* Free memory that we have used for the test configuration. */
-static void free_msc_config(struct bsc_msc_data *msc)
-{
- talloc_free(msc->audio_support);
-}
-
/* The speech codec list is sent by the MS and lists the voice codec settings
* that the MS is able to support. The BSC must select one of this codecs
* depending on what the MSC is able to support. The following function
@@ -215,74 +196,74 @@ static void make_msc_config(struct bsc_msc_data *msc, uint8_t config_no)
switch (config_no) {
case 0:
/* FR1 only */
- msc->audio_support[0]->ver = 1;
- msc->audio_support[0]->hr = 0;
+ msc->audio_support[0].ver = 1;
+ msc->audio_support[0].hr = 0;
msc->audio_length = 1;
break;
case 1:
/* HR1 only */
- msc->audio_support[0]->ver = 1;
- msc->audio_support[0]->hr = 1;
+ msc->audio_support[0].ver = 1;
+ msc->audio_support[0].hr = 1;
msc->audio_length = 1;
break;
case 2:
/* FR2 only */
- msc->audio_support[0]->ver = 2;
- msc->audio_support[0]->hr = 0;
+ msc->audio_support[0].ver = 2;
+ msc->audio_support[0].hr = 0;
msc->audio_length = 1;
break;
case 3:
/* FR3 only */
- msc->audio_support[0]->ver = 3;
- msc->audio_support[0]->hr = 0;
+ msc->audio_support[0].ver = 3;
+ msc->audio_support[0].hr = 0;
msc->audio_length = 1;
break;
case 4:
/* HR3 only */
- msc->audio_support[0]->ver = 3;
- msc->audio_support[0]->hr = 1;
+ msc->audio_support[0].ver = 3;
+ msc->audio_support[0].hr = 1;
msc->audio_length = 1;
break;
case 5:
/* FR1 and HR1 */
- msc->audio_support[0]->ver = 1;
- msc->audio_support[0]->hr = 0;
- msc->audio_support[1]->ver = 1;
- msc->audio_support[1]->hr = 1;
+ msc->audio_support[0].ver = 1;
+ msc->audio_support[0].hr = 0;
+ msc->audio_support[1].ver = 1;
+ msc->audio_support[1].hr = 1;
msc->audio_length = 2;
break;
case 6:
/* FR1, FR2 and HR1 */
- msc->audio_support[0]->ver = 1;
- msc->audio_support[0]->hr = 0;
- msc->audio_support[1]->ver = 2;
- msc->audio_support[1]->hr = 0;
- msc->audio_support[2]->ver = 1;
- msc->audio_support[2]->hr = 1;
+ msc->audio_support[0].ver = 1;
+ msc->audio_support[0].hr = 0;
+ msc->audio_support[1].ver = 2;
+ msc->audio_support[1].hr = 0;
+ msc->audio_support[2].ver = 1;
+ msc->audio_support[2].hr = 1;
msc->audio_length = 3;
break;
case 7:
/* FR1, FR3 and HR3 */
- msc->audio_support[0]->ver = 1;
- msc->audio_support[0]->hr = 0;
- msc->audio_support[1]->ver = 3;
- msc->audio_support[1]->hr = 0;
- msc->audio_support[2]->ver = 3;
- msc->audio_support[2]->hr = 1;
+ msc->audio_support[0].ver = 1;
+ msc->audio_support[0].hr = 0;
+ msc->audio_support[1].ver = 3;
+ msc->audio_support[1].hr = 0;
+ msc->audio_support[2].ver = 3;
+ msc->audio_support[2].hr = 1;
msc->audio_length = 3;
break;
case 8:
/* FR1, FR2, FR3, HR1 and HR3 */
- msc->audio_support[0]->ver = 1;
- msc->audio_support[0]->hr = 0;
- msc->audio_support[1]->ver = 2;
- msc->audio_support[1]->hr = 0;
- msc->audio_support[2]->ver = 3;
- msc->audio_support[2]->hr = 0;
- msc->audio_support[3]->ver = 1;
- msc->audio_support[3]->hr = 1;
- msc->audio_support[4]->ver = 3;
- msc->audio_support[4]->hr = 1;
+ msc->audio_support[0].ver = 1;
+ msc->audio_support[0].hr = 0;
+ msc->audio_support[1].ver = 2;
+ msc->audio_support[1].hr = 0;
+ msc->audio_support[2].ver = 3;
+ msc->audio_support[2].hr = 0;
+ msc->audio_support[3].ver = 1;
+ msc->audio_support[3].hr = 1;
+ msc->audio_support[4].ver = 3;
+ msc->audio_support[4].hr = 1;
msc->audio_length = 5;
break;
}
@@ -394,10 +375,10 @@ static int test_match_codec_pref(const struct gsm0808_channel_type *ct, const st
printf(" * BSS: audio support settings (%u items):\n", msc->audio_length);
for (i = 0; i < msc->audio_length; i++)
- if (msc->audio_support[i]->hr)
- printf(" audio_support[%u]=HR%u\n", i, msc->audio_support[i]->ver);
+ if (msc->audio_support[i].hr)
+ printf(" audio_support[%u]=HR%u\n", i, msc->audio_support[i].ver);
else
- printf(" audio_support[%u]=FR%u\n", i, msc->audio_support[i]->ver);
+ printf(" audio_support[%u]=FR%u\n", i, msc->audio_support[i].ver);
printf(" * BTS: audio support settings:\n");
printf(" (GSM-FR implicitly supported)\n");
@@ -426,8 +407,6 @@ static void test_one_to_one(void)
printf("============== test_one_to_one ==============\n\n");
- init_msc_config(&msc_local);
-
for (i = 0; i < N_CONFIG_VARIANTS; i++) {
make_msc_config(&msc_local, i);
make_scl_config(&scl_ms, i);
@@ -436,8 +415,6 @@ static void test_one_to_one(void)
rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local);
OSMO_ASSERT(rc == 0);
}
-
- free_msc_config(&msc_local);
}
/* Network supports all combinations, MS varies */
@@ -452,8 +429,6 @@ static void test_ms(void)
printf("============== test_ms ==============\n\n");
- init_msc_config(&msc_local);
-
make_msc_config(&msc_local, 8);
make_ct_config(&ct_msc, 8);
make_bts_config(&bts_local, 8);
@@ -462,8 +437,6 @@ static void test_ms(void)
rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local);
OSMO_ASSERT(rc == 0);
}
-
- free_msc_config(&msc_local);
}
/* BSS and MS support all combinations, MSC varies */
@@ -478,8 +451,6 @@ static void test_ct(void)
printf("============== test_ct ==============\n\n");
- init_msc_config(&msc_local);
-
make_msc_config(&msc_local, 8);
make_scl_config(&scl_ms, 8);
make_bts_config(&bts_local, 8);
@@ -488,8 +459,6 @@ static void test_ct(void)
rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local);
OSMO_ASSERT(rc == 0);
}
-
- free_msc_config(&msc_local);
}
/* MSC and MS support all combinations, BSS varies */
@@ -504,8 +473,6 @@ static void test_msc(void)
printf("============== test_msc ==============\n\n");
- init_msc_config(&msc_local);
-
make_ct_config(&ct_msc, 8);
make_scl_config(&scl_ms, 8);
make_bts_config(&bts_local, 8);
@@ -514,8 +481,6 @@ static void test_msc(void)
rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local);
OSMO_ASSERT(rc == 0);
}
-
- free_msc_config(&msc_local);
}
/* Some mixed configurations that are supposed to work */
@@ -529,8 +494,6 @@ static void test_selected_working(void)
printf("============== test_selected_working ==============\n\n");
- init_msc_config(&msc_local);
-
make_scl_config(&scl_ms, 6);
make_ct_config(&ct_msc, 5);
make_msc_config(&msc_local, 7);
@@ -572,8 +535,6 @@ static void test_selected_working(void)
make_bts_config(&bts_local, 1);
rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local);
OSMO_ASSERT(rc == 0);
-
- free_msc_config(&msc_local);
}
/* Some mixed configurations that can not work */
@@ -587,8 +548,6 @@ static void test_selected_non_working(void)
printf("============== test_selected_non_working ==============\n\n");
- init_msc_config(&msc_local);
-
make_scl_config(&scl_ms, 1);
make_ct_config(&ct_msc, 5);
make_msc_config(&msc_local, 7);
@@ -630,8 +589,6 @@ static void test_selected_non_working(void)
make_bts_config(&bts_local, 7);
rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local);
OSMO_ASSERT(rc == -1);
-
- free_msc_config(&msc_local);
}
/* Try execute bss_supp_codec_list(), display input and output parameters */
@@ -644,10 +601,10 @@ static void test_gen_bss_supported_codec_list(const struct bsc_msc_data *msc, st
printf(" * BSS: audio support settings (%u items):\n", msc->audio_length);
for (i = 0; i < msc->audio_length; i++)
- if (msc->audio_support[i]->hr)
- printf(" audio_support[%u]=HR%u\n", i, msc->audio_support[i]->ver);
+ if (msc->audio_support[i].hr)
+ printf(" audio_support[%u]=HR%u\n", i, msc->audio_support[i].ver);
else
- printf(" audio_support[%u]=FR%u\n", i, msc->audio_support[i]->ver);
+ printf(" audio_support[%u]=FR%u\n", i, msc->audio_support[i].ver);
printf(" * BTS: audio support settings:\n");
printf(" (GSM-FR implicitly supported)\n");
@@ -660,7 +617,7 @@ static void test_gen_bss_supported_codec_list(const struct bsc_msc_data *msc, st
printf(" * result: speech codec list (%u items):\n", scl.len);
for (i = 0; i < scl.len; i++) {
printf(" codec[%u]->type=%s", i, gsm0808_speech_codec_type_name(scl.codec[i].type));
- if (msc->audio_support[i]->ver == 3)
+ if (msc->audio_support[i].ver == 3)
printf(" S15-S0=%04x", scl.codec[i].cfg);
printf("\n");
}
@@ -676,8 +633,6 @@ static void test_gen_bss_supported_codec_list_cfgs(void)
uint8_t k;
printf("============== test_gen_bss_supp_codec_list_cfgs ==============\n\n");
- init_msc_config(&msc_local);
-
for (i = 0; i < N_CONFIG_VARIANTS; i++) {
for (k = 0; k < N_CONFIG_VARIANTS; k++) {
make_msc_config(&msc_local, i);
@@ -686,8 +641,6 @@ static void test_gen_bss_supported_codec_list_cfgs(void)
test_gen_bss_supported_codec_list(&msc_local, &bts_local);
}
}
-
- free_msc_config(&msc_local);
}
static const struct log_info_cat log_categories[] = {
diff --git a/tests/ctrl/osmo-bsc-apply-config-file-invalid.cfg b/tests/ctrl/osmo-bsc-apply-config-file-invalid.cfg
new file mode 100644
index 000000000..b819f7dc2
--- /dev/null
+++ b/tests/ctrl/osmo-bsc-apply-config-file-invalid.cfg
@@ -0,0 +1,2 @@
+network
+ btssss-invalid-cmd
diff --git a/tests/ctrl/osmo-bsc-apply-config-file.cfg b/tests/ctrl/osmo-bsc-apply-config-file.cfg
new file mode 100644
index 000000000..5f274658d
--- /dev/null
+++ b/tests/ctrl/osmo-bsc-apply-config-file.cfg
@@ -0,0 +1,55 @@
+network
+ bts 1
+ type osmo-bts
+ band DCS1800
+ cell_identity 123
+ location_area_code 0x0001
+ base_station_id_code 55
+ ms max power 15
+ cell reselection hysteresis 4
+ rxlev access min 0
+ radio-link-timeout 32
+ channel allocator mode set-all ascending
+ rach tx integer 9
+ rach max transmission 7
+ channel-description attach 1
+ channel-description bs-pa-mfrms 5
+ channel-description bs-ag-blks-res 1
+ early-classmark-sending forbidden
+ ipa unit-id 55 0
+ oml ipa stream-id 255 line 0
+ codec-support fr
+ gprs mode gprs
+ gprs routing area 6
+ neighbor bts 0
+ trx 0
+ rf_locked 0
+ arfcn 880
+ nominal power 23
+ ! to use full TRX power, set max_power_red 0
+ max_power_red 20
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
diff --git a/tests/ctrl/osmo-bsc-neigh-test.cfg b/tests/ctrl/osmo-bsc-neigh-test.cfg
new file mode 100644
index 000000000..80acd6454
--- /dev/null
+++ b/tests/ctrl/osmo-bsc-neigh-test.cfg
@@ -0,0 +1,202 @@
+e1_input
+ e1_line 0 driver ipa
+network
+ network country code 1
+ mobile network code 1
+ encryption a5 0
+ neci 1
+ paging any use tch 0
+ handover 0
+ handover algorithm 1
+ handover1 window rxlev averaging 10
+ handover1 window rxqual averaging 1
+ handover1 window rxlev neighbor averaging 10
+ handover1 power budget interval 6
+ handover1 power budget hysteresis 3
+ handover1 maximum distance 9999
+ periodic location update 30
+ neighbor-resolution bind 127.0.0.1
+ mgw 0
+ remote-ip 127.0.0.1
+ remote-port 2427
+ local-port 2727
+ bts 0
+ type osmo-bts
+ band DCS1800
+ cell_identity 6969
+ location_area_code 0x0001
+ base_station_id_code 63
+ ms max power 15
+ cell reselection hysteresis 4
+ rxlev access min 0
+ radio-link-timeout 32
+ channel allocator mode set-all ascending
+ rach tx integer 9
+ rach max transmission 7
+ channel-description attach 1
+ channel-description bs-pa-mfrms 5
+ channel-description bs-ag-blks-res 1
+ early-classmark-sending forbidden
+ ipa unit-id 6969 0
+ oml ipa stream-id 255 line 0
+ codec-support fr
+ gprs mode gprs
+ gprs routing area 5
+ ! local neigh, refers to bts 1:
+ neighbor cgi-ps 1 1 1 6 123
+ ! remote neigh:
+ neighbor cgi-ps 23 42 423 2 5 arfcn 23 bsic 32
+ trx 0
+ rf_locked 0
+ arfcn 871
+ nominal power 23
+ ! to use full TRX power, set max_power_red 0
+ max_power_red 20
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ bts 1
+ type osmo-bts
+ band DCS1800
+ cell_identity 123
+ location_area_code 0x0001
+ base_station_id_code 55
+ ms max power 15
+ cell reselection hysteresis 4
+ rxlev access min 0
+ radio-link-timeout 32
+ channel allocator mode set-all ascending
+ rach tx integer 9
+ rach max transmission 7
+ channel-description attach 1
+ channel-description bs-pa-mfrms 5
+ channel-description bs-ag-blks-res 1
+ early-classmark-sending forbidden
+ ipa unit-id 55 0
+ oml ipa stream-id 255 line 0
+ codec-support fr
+ gprs mode gprs
+ gprs routing area 6
+ neighbor bts 0
+ neighbor bts 1
+ neighbor bts 2
+ neighbor bts 2
+ trx 0
+ rf_locked 0
+ arfcn 880
+ nominal power 23
+ ! to use full TRX power, set max_power_red 0
+ max_power_red 20
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ bts 2
+ type osmo-bts
+ band DCS1800
+ cell_identity 123
+ location_area_code 0x0001
+ base_station_id_code 55
+ ms max power 15
+ cell reselection hysteresis 4
+ rxlev access min 0
+ radio-link-timeout 32
+ channel allocator mode set-all ascending
+ rach tx integer 9
+ rach max transmission 7
+ channel-description attach 1
+ channel-description bs-pa-mfrms 5
+ channel-description bs-ag-blks-res 1
+ early-classmark-sending forbidden
+ ipa unit-id 55 0
+ oml ipa stream-id 255 line 0
+ codec-support fr
+ gprs mode gprs
+ gprs routing area 6
+ trx 0
+ rf_locked 0
+ arfcn 880
+ nominal power 23
+ ! to use full TRX power, set max_power_red 0
+ max_power_red 20
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+msc 0
+ type normal
+ allow-emergency allow
+ amr-config 12_2k forbidden
+ amr-config 10_2k forbidden
+ amr-config 7_95k forbidden
+ amr-config 7_40k forbidden
+ amr-config 6_70k forbidden
+ amr-config 5_90k allowed
+ amr-config 5_15k forbidden
+ amr-config 4_75k forbidden
+bsc
+ mid-call-timeout 0
diff --git a/tests/ctrl_test_runner.py b/tests/ctrl_test_runner.py
index 2ccbfe8d0..e88999282 100755
--- a/tests/ctrl_test_runner.py
+++ b/tests/ctrl_test_runner.py
@@ -55,7 +55,6 @@ class TestCtrlBase(unittest.TestCase):
except OSError:
print("Current directory: %s" % os.getcwd(), file=sys.stderr)
print("Consider setting -b", file=sys.stderr)
- time.sleep(2)
appstring = self.ctrl_app()[2]
appport = self.ctrl_app()[0]
@@ -234,8 +233,8 @@ class TestCtrlBSC(TestCtrlBase):
self.assertEqual(r['mtype'], 'GET_REPLY')
self.assertEqual(r['value'],
'CCCH+SDCCH4,0,0 TCH/F,0,0 TCH/H,0,0 SDCCH8,0,0'
- + ' TCH/F_PDCH,0,0 CCCH+SDCCH4+CBCH,0,0'
- + ' SDCCH8+CBCH,0,0 TCH/F_TCH/H_PDCH,0,0')
+ + ' DYNAMIC/IPACCESS,0,0 CCCH+SDCCH4+CBCH,0,0'
+ + ' SDCCH8+CBCH,0,0 DYNAMIC/OSMOCOM,0,0')
def testBtsOmlConnectionState(self):
"""Check OML state. It will not be connected"""
@@ -292,7 +291,7 @@ class TestCtrlBSC(TestCtrlBase):
r = self.do_get('bts.0.rf_state')
self.assertEqual(r['mtype'], 'GET_REPLY')
self.assertEqual(r['var'], 'bts.0.rf_state')
- self.assertEqual(r['value'], 'inoperational,unlocked,on')
+ self.assertEqual(r['value'], 'inoperational,locked,on')
r = self.do_set('rf_locked', '1')
self.assertEqual(r['mtype'], 'SET_REPLY')
@@ -321,7 +320,7 @@ class TestCtrlBSC(TestCtrlBase):
r = self.do_get('bts.0.rf_state')
self.assertEqual(r['mtype'], 'GET_REPLY')
self.assertEqual(r['var'], 'bts.0.rf_state')
- self.assertEqual(r['value'], 'inoperational,unlocked,on')
+ self.assertEqual(r['value'], 'inoperational,locked,on')
r = self.do_get('rf_locked')
self.assertEqual(r['mtype'], 'GET_REPLY')
@@ -489,11 +488,315 @@ class TestCtrlBSC(TestCtrlBase):
self.assertEqual(r['var'], 'mcc')
self.assertEqual(r['value'], '002')
-def add_bsc_test(suite, workdir):
+
+ def testApplyConfigFile(self):
+
+ vty_file = os.path.join(confpath, 'tests/ctrl/osmo-bsc-apply-config-file.cfg')
+ vty_file_invalid = os.path.join(confpath, 'tests/ctrl/osmo-bsc-apply-config-file-invalid.cfg')
+
+ # Test some invalid input
+ r = self.do_set('apply-config-file', 'wrong-file-name-nonexistent')
+ self.assertEqual(r['mtype'], 'ERROR')
+
+ # Test some existing file with invalid content
+ r = self.do_set('apply-config-file', vty_file_invalid)
+ self.assertEqual(r['mtype'], 'ERROR')
+
+ #bts1 shouldn't exist yet, let's check:
+ r = self.do_get('bts.1.location-area-code')
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Error while resolving object')
+
+ r = self.do_set('apply-config-file', vty_file)
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'apply-config-file')
+ self.assertEqual(r['value'], 'OK')
+
+ # BTS1 should exist now:
+ r = self.do_get('bts.1.location-area-code')
+ self.assertEqual(r['mtype'], 'GET_REPLY')
+ self.assertEqual(r['var'], 'bts.1.location-area-code')
+ self.assertEqual(r['value'], '1')
+
+ # Set it again
+ r = self.do_set('apply-config-file', vty_file)
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'apply-config-file')
+ self.assertEqual(r['value'], 'OK')
+
+ def testNeighborList(self):
+ # Enter manual neighbor-list mode
+ r = self.do_set('bts.0.neighbor-list.mode', 'manual')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-list.mode')
+ self.assertEqual(r['value'], 'OK')
+
+ # Add an ARFCN
+ r = self.do_set('bts.0.neighbor-list.add', '123')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-list.add')
+ self.assertEqual(r['value'], 'OK')
+
+ # Delete the ARFCN again
+ r = self.do_set('bts.0.neighbor-list.del', '123')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-list.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # Go back to automatic neighbor-list mode
+ r = self.do_set('bts.0.neighbor-list.mode', 'automatic')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-list.mode')
+ self.assertEqual(r['value'], 'OK')
+
+ # This must not work as we are in automatic neighbor-list mode
+ r = self.do_set('bts.0.neighbor-list.add', '123')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Neighbor list not in manual mode')
+
+ # Try an invalid neighbor-list mode
+ r = self.do_set('bts.0.neighbor-list.mode', 'qwertzuiop')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Invalid mode')
+
+class TestCtrlBSCNeighbor(TestCtrlBase):
+
+ def tearDown(self):
+ TestCtrlBase.tearDown(self)
+ os.unlink("tmp_dummy_sock")
+
+ def ctrl_command(self):
+ return ["./src/osmo-bsc/osmo-bsc", "-r", "tmp_dummy_sock", "-c",
+ "tests/ctrl/osmo-bsc-neigh-test.cfg"]
+
+ def ctrl_app(self):
+ return (4248, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc")
+
+ def testCtrlNeighborResolutionLocalBtsNr(self):
+ r = self.do_get('neighbor_resolve_cgi_ps_from_lac_ci.1.123.871.63')
+ self.assertEqual(r['mtype'], 'GET_REPLY')
+ self.assertEqual(r['var'], 'neighbor_resolve_cgi_ps_from_lac_ci.1.123.871.63')
+ self.assertEqual(r['value'], '001-01-1-5-6969')
+
+ def testCtrlNeighborResolutionLocalWithoutArfcnBsic(self):
+ r = self.do_get('neighbor_resolve_cgi_ps_from_lac_ci.1.6969.880.55')
+ self.assertEqual(r['mtype'], 'GET_REPLY')
+ self.assertEqual(r['var'], 'neighbor_resolve_cgi_ps_from_lac_ci.1.6969.880.55')
+ self.assertEqual(r['value'], '001-01-1-6-123')
+
+ def testCtrlNeighborResolutionWrongSyntax(self):
+ r = self.do_get('neighbor_resolve_cgi_ps_from_lac_ci')
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'The format is <src_lac>,<src_cell_id>,<dst_arfcn>,<dst_bsic>')
+
+ def testCtrlNeighborResolutionRemote(self):
+ r = self.do_get('neighbor_resolve_cgi_ps_from_lac_ci.1.6969.23.32')
+ self.assertEqual(r['mtype'], 'GET_REPLY')
+ self.assertEqual(r['var'], 'neighbor_resolve_cgi_ps_from_lac_ci.1.6969.23.32')
+ self.assertEqual(r['value'], '023-42-423-2-5')
+
+
+class TestCtrlBSCNeighborCell(TestCtrlBase):
+
+ def tearDown(self):
+ TestCtrlBase.tearDown(self)
+ os.unlink("tmp_dummy_sock")
+
+ def ctrl_command(self):
+ return ["./src/osmo-bsc/osmo-bsc", "-r", "tmp_dummy_sock", "-c",
+ "tests/ctrl/osmo-bsc-neigh-test.cfg"]
+
+ def ctrl_app(self):
+ return (4249, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc")
+
+ def testCtrlListBTS(self):
+ # Get BTS local neighbors (configured via 'neighbor cgi-ps ...')
+ r = self.do_get('bts.0.neighbor-bts.list')
+ self.assertEqual(r['mtype'], 'GET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-bts.list')
+ self.assertEqual(r['value'], '1')
+
+ # Get BTS locally configured neighbors (when none configured)
+ r = self.do_get('bts.2.neighbor-bts.list')
+ self.assertEqual(r['mtype'], 'GET_REPLY')
+ self.assertEqual(r['var'], 'bts.2.neighbor-bts.list')
+ self.assertEqual(r['value'], None)
+
+ # Get BTS locally configured neighbors
+ r = self.do_get('bts.1.neighbor-bts.list')
+ self.assertEqual(r['mtype'], 'GET_REPLY')
+ self.assertEqual(r['var'], 'bts.1.neighbor-bts.list')
+ self.assertEqual(r['value'], '0,2')
+
+ def testCtrlAddDelBTS(self):
+ r = self.do_set('bts.0.neighbor-bts.add', '1')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-bts.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-bts.del', '1')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-bts.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlAddDelLAC(self):
+ # without ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-lac.add', '100')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-lac.del', '100')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # with ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-lac.add', '100-123-4')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-lac.del', '100-123-4')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlAddDelLACCI(self):
+ # without ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-lac-ci.add', '100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-lac-ci.del', '100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # with ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-lac-ci.add', '100-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-lac-ci.del', '100-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlAddDelCGI(self):
+ # without ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-cgi.add', '001-01-100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-cgi.del', '001-01-100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # with ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-cgi.add', '001-01-100-200-123-4')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-cgi.del', '001-01-100-200-123-4')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlAddDelCGIPS(self):
+ # without ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-cgi-ps.add', '001-01-100-33-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.del')
+ self.assertEqual(r['value'], 'OK')
+
+ # with ARFCN+BSIC:
+ r = self.do_set('bts.0.neighbor-cgi-ps.add', '001-01-100-33-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.add')
+ self.assertEqual(r['value'], 'OK')
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.del')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlClearNeighbors(self):
+ r = self.do_set('bts.0.neighbor-clear', 'ignored')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'SET_REPLY')
+ self.assertEqual(r['var'], 'bts.0.neighbor-clear')
+ self.assertEqual(r['value'], 'OK')
+
+ def testCtrlErrs(self):
+ # Missing BSIC
+ r = self.do_set('bts.0.neighbor-lac.add', '100-123')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Short value (missing RAC)
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-200-123-1')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Long value
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-1-2')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Out of range values
+ r = self.do_set('bts.0.neighbor-cgi.add', '100001-1123401-100-200')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Garbage
+ r = self.do_set('bts.0.neighbor-lac-ci.add', '0G1-Z1-1U0-a3-2p0')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Value failed verification.')
+
+ # Delete something that shouldn't be there
+ r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-any')
+ print('respose: ' + str(r))
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Failed to delete neighbor')
+
+
+def add_bsc_test(suite, workdir, klass):
if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")):
print("Skipping the BSC test")
return
- test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlBSC)
+ test = unittest.TestLoader().loadTestsFromTestCase(klass)
suite.addTest(test)
if __name__ == '__main__':
@@ -526,6 +829,8 @@ if __name__ == '__main__':
os.chdir(workdir)
print("Running tests for specific control commands")
suite = unittest.TestSuite()
- add_bsc_test(suite, workdir)
+ add_bsc_test(suite, workdir, TestCtrlBSC)
+ add_bsc_test(suite, workdir, TestCtrlBSCNeighbor)
+ add_bsc_test(suite, workdir, TestCtrlBSCNeighborCell)
res = unittest.TextTestRunner(verbosity=verbose_level).run(suite)
sys.exit(len(res.errors) + len(res.failures))
diff --git a/tests/gsm0408/Makefile.am b/tests/gsm0408/Makefile.am
index 66e407259..480202587 100644
--- a/tests/gsm0408/Makefile.am
+++ b/tests/gsm0408/Makefile.am
@@ -7,10 +7,13 @@ AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
gsm0408_test \
$(NULL)
@@ -23,18 +26,9 @@ gsm0408_test_SOURCES = \
$(NULL)
gsm0408_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/abis_nm.o \
- $(top_builddir)/src/osmo-bsc/acc.o \
- $(top_builddir)/src/osmo-bsc/gsm_04_08_rr.o \
- $(top_builddir)/src/osmo-bsc/arfcn_range_encode.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/net_init.o \
- $(top_builddir)/src/osmo-bsc/rest_octets.o \
- $(top_builddir)/src/osmo-bsc/system_information.o \
- $(top_builddir)/src/osmo-bsc/neighbor_ident.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOCTRL_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(NULL)
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index 8ee29f70b..999ee805a 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -26,10 +26,10 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/debug.h>
-#include <osmocom/bsc/arfcn_range_encode.h>
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bss.h>
#include <osmocom/core/application.h>
#include <osmocom/core/byteswap.h>
@@ -58,8 +58,6 @@
__FILE__, __LINE__, (int) res, # cmp, (int) wanted); \
}
-
-
static inline void gen(struct gsm_bts *bts, const char *s)
{
int r;
@@ -123,7 +121,8 @@ static inline void _bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t
#define bts_init(net) _bts_init(net, __func__)
static inline struct gsm_bts *_bts_init(struct gsm_network *net, const char *msg)
{
- struct gsm_bts *bts = gsm_bts_alloc(net, 0);
+ struct gsm_bts_sm *bts_sm = gsm_bts_sm_alloc(net, 0);
+ struct gsm_bts *bts = bts_sm->bts[0];
if (!bts) {
printf("BTS allocation failure in %s()\n", msg);
exit(1);
@@ -138,12 +137,9 @@ static inline struct gsm_bts *_bts_init(struct gsm_network *net, const char *msg
#define bts_del(bts) _bts_del(bts, __func__)
static inline void _bts_del(struct gsm_bts *bts, const char *msg)
{
- osmo_stat_item_group_free(bts->bts_statg);
- rate_ctr_group_free(bts->bts_ctrs);
- if (osmo_timer_pending(&bts->acc_mgr.rotate_timer))
- osmo_timer_del(&bts->acc_mgr.rotate_timer);
+ osmo_timer_del(&bts->acc_mgr.rotate_timer);
/* no need to llist_del(&bts->list), we never registered the bts there. */
- talloc_free(bts);
+ talloc_free(bts->site_mgr);
printf("BTS deallocated OK in %s()\n", msg);
}
@@ -272,338 +268,6 @@ static inline void test_si2q_long(struct gsm_network *net)
bts_del(bts);
}
-struct {
- int range;
- int arfcns_num;
- int arfcns[RANGE_ENC_MAX_ARFCNS];
-} arfcn_test_ranges[] = {
- {ARFCN_RANGE_512, 12,
- { 1, 12, 31, 51, 57, 91, 97, 98, 113, 117, 120, 125 }},
- {ARFCN_RANGE_512, 17,
- { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }},
- {ARFCN_RANGE_512, 18,
- { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }},
- {ARFCN_RANGE_512, 18,
- { 1, 17, 31, 45, 58, 79, 81, 97,
- 113, 127, 213, 277, 287, 311, 331, 391,
- 417, 511 }},
- {ARFCN_RANGE_512, 6,
- { 1, 17, 31, 45, 58, 79 }},
- {ARFCN_RANGE_512, 6,
- { 10, 17, 31, 45, 58, 79 }},
- {ARFCN_RANGE_1024, 17,
- { 0, 17, 31, 45, 58, 79, 81, 97,
- 113, 127, 213, 277, 287, 311, 331, 391,
- 1023 }},
- {ARFCN_RANGE_1024, 16,
- { 17, 31, 45, 58, 79, 81, 97, 113,
- 127, 213, 277, 287, 311, 331, 391, 1023 }},
- {-1}
-};
-
-static int test_single_range_encoding(int range, const int *orig_arfcns,
- int arfcns_num, int silent)
-{
- int arfcns[RANGE_ENC_MAX_ARFCNS];
- int w[RANGE_ENC_MAX_ARFCNS];
- int f0_included = 0;
- int rc, f0;
- uint8_t chan_list[16] = {0};
- struct gsm_sysinfo_freq dec_freq[1024] = {{0}};
- int dec_arfcns[RANGE_ENC_MAX_ARFCNS] = {0};
- int dec_arfcns_count = 0;
- int arfcns_used = 0;
- int i;
-
- arfcns_used = arfcns_num;
- memmove(arfcns, orig_arfcns, sizeof(arfcns));
-
- f0 = range == ARFCN_RANGE_1024 ? 0 : arfcns[0];
- /*
- * Manipulate the ARFCN list according to the rules in J4 depending
- * on the selected range.
- */
- arfcns_used = range_enc_filter_arfcns(arfcns, arfcns_used,
- f0, &f0_included);
-
- memset(w, 0, sizeof(w));
- range_enc_arfcns(range, arfcns, arfcns_used, w, 0);
-
- if (!silent)
- fprintf(stderr, "range=%d, arfcns_used=%d, f0=%d, f0_included=%d\n",
- range, arfcns_used, f0, f0_included);
-
- /* Select the range and the amount of bits needed */
- switch (range) {
- case ARFCN_RANGE_128:
- range_enc_range128(chan_list, f0, w);
- break;
- case ARFCN_RANGE_256:
- range_enc_range256(chan_list, f0, w);
- break;
- case ARFCN_RANGE_512:
- range_enc_range512(chan_list, f0, w);
- break;
- case ARFCN_RANGE_1024:
- range_enc_range1024(chan_list, f0, f0_included, w);
- break;
- default:
- return 1;
- };
-
- if (!silent)
- printf("chan_list = %s\n",
- osmo_hexdump(chan_list, sizeof(chan_list)));
-
- rc = gsm48_decode_freq_list(dec_freq, chan_list, sizeof(chan_list),
- 0xfe, 1);
- if (rc != 0) {
- printf("Cannot decode freq list, rc = %d\n", rc);
- return 1;
- }
-
- for (i = 0; i < ARRAY_SIZE(dec_freq); i++) {
- if (dec_freq[i].mask &&
- dec_arfcns_count < ARRAY_SIZE(dec_arfcns))
- dec_arfcns[dec_arfcns_count++] = i;
- }
-
- if (!silent) {
- printf("Decoded freqs %d (expected %d)\n",
- dec_arfcns_count, arfcns_num);
- printf("Decoded: ");
- for (i = 0; i < dec_arfcns_count; i++) {
- printf("%d ", dec_arfcns[i]);
- if (dec_arfcns[i] != orig_arfcns[i])
- printf("(!= %d) ", orig_arfcns[i]);
- }
- printf("\n");
- }
-
- if (dec_arfcns_count != arfcns_num) {
- printf("Wrong number of arfcns\n");
- return 1;
- }
-
- if (memcmp(dec_arfcns, orig_arfcns, sizeof(dec_arfcns)) != 0) {
- printf("Decoding error, got wrong freqs\n");
- fprintf(stderr, " w = ");
- for (i = 0; i < ARRAY_SIZE(w); i++)
- fprintf(stderr, "%d ", w[i]);
- fprintf(stderr, "\n");
- return 1;
- }
-
- return 0;
-}
-
-static void test_random_range_encoding(int range, int max_arfcn_num)
-{
- int arfcns_num = 0;
- int test_idx;
- int rc, max_count;
- int num_tests = 1024;
-
- printf("Random range test: range %d, max num ARFCNs %d\n",
- range, max_arfcn_num);
-
- srandom(1);
-
- for (max_count = 1; max_count < max_arfcn_num; max_count++) {
- for (test_idx = 0; test_idx < num_tests; test_idx++) {
- int count;
- int i;
- int min_freq = 0;
-
- int rnd_arfcns[RANGE_ENC_MAX_ARFCNS] = {0};
- char rnd_arfcns_set[1024] = {0};
-
- if (range < ARFCN_RANGE_1024)
- min_freq = random() % (1023 - range);
-
- for (count = max_count; count; ) {
- int arfcn = min_freq + random() % (range + 1);
- OSMO_ASSERT(arfcn < ARRAY_SIZE(rnd_arfcns_set));
-
- if (!rnd_arfcns_set[arfcn]) {
- rnd_arfcns_set[arfcn] = 1;
- count -= 1;
- }
- }
-
- arfcns_num = 0;
- for (i = 0; i < ARRAY_SIZE(rnd_arfcns_set); i++)
- if (rnd_arfcns_set[i])
- rnd_arfcns[arfcns_num++] = i;
-
- rc = test_single_range_encoding(range, rnd_arfcns,
- arfcns_num, 1);
- if (rc != 0) {
- printf("Failed on test %d, range %d, num ARFCNs %d\n",
- test_idx, range, max_count);
- test_single_range_encoding(range, rnd_arfcns,
- arfcns_num, 0);
- return;
- }
- }
- }
-}
-
-static void test_range_encoding()
-{
- int *arfcns;
- int arfcns_num = 0;
- int test_idx;
- int range;
-
- for (test_idx = 0; arfcn_test_ranges[test_idx].arfcns_num > 0; test_idx++)
- {
- arfcns_num = arfcn_test_ranges[test_idx].arfcns_num;
- arfcns = &arfcn_test_ranges[test_idx].arfcns[0];
- range = arfcn_test_ranges[test_idx].range;
-
- printf("Range test %d: range %d, num ARFCNs %d\n",
- test_idx, range, arfcns_num);
-
- test_single_range_encoding(range, arfcns, arfcns_num, 0);
- }
-
- test_random_range_encoding(ARFCN_RANGE_128, 29);
- test_random_range_encoding(ARFCN_RANGE_256, 22);
- test_random_range_encoding(ARFCN_RANGE_512, 18);
- test_random_range_encoding(ARFCN_RANGE_1024, 16);
-}
-
-static int freqs1[] = {
- 12, 70, 121, 190, 250, 320, 401, 475, 520, 574, 634, 700, 764, 830, 905, 980
-};
-
-static int freqs2[] = {
- 402, 460, 1, 67, 131, 197, 272, 347,
-};
-
-static int freqs3[] = {
- 68, 128, 198, 279, 353, 398, 452,
-
-};
-
-static int w_out[] = {
- 122, 2, 69, 204, 75, 66, 60, 70, 83, 3, 24, 67, 54, 64, 70, 9,
-};
-
-static int range128[] = {
- 1, 1 + 127,
-};
-
-static int range256[] = {
- 1, 1 + 128,
-};
-
-static int range512[] = {
- 1, 1+ 511,
-};
-
-
-static void test_arfcn_filter()
-{
- int arfcns[50], i, res, f0_included;
- for (i = 0; i < ARRAY_SIZE(arfcns); ++i)
- arfcns[i] = (i + 1) * 2;
-
- /* check that the arfcn is taken out. f0_included is only set for Range1024 */
- f0_included = 24;
- res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns),
- arfcns[0], &f0_included);
- VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1);
- VERIFY(f0_included, ==, 1);
- for (i = 0; i < res; ++i)
- VERIFY(arfcns[i], ==, ((i+2) * 2) - (2+1));
-
- /* check with range1024, ARFCN 0 is included */
- for (i = 0; i < ARRAY_SIZE(arfcns); ++i)
- arfcns[i] = i * 2;
- res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns),
- 0, &f0_included);
- VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1);
- VERIFY(f0_included, ==, 1);
- for (i = 0; i < res; ++i)
- VERIFY(arfcns[i], ==, (i + 1) * 2 - 1);
-
- /* check with range1024, ARFCN 0 not included */
- for (i = 0; i < ARRAY_SIZE(arfcns); ++i)
- arfcns[i] = (i + 1) * 2;
- res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns),
- 0, &f0_included);
- VERIFY(res, ==, ARRAY_SIZE(arfcns));
- VERIFY(f0_included, ==, 0);
- for (i = 0; i < res; ++i)
- VERIFY(arfcns[i], ==, ((i + 1) * 2) - 1);
-}
-
-static void test_print_encoding()
-{
- int rc;
- int w[17];
- uint8_t chan_list[16];
- memset(chan_list, 0x23, sizeof(chan_list));
-
- for (rc = 0; rc < ARRAY_SIZE(w); ++rc)
- switch (rc % 3) {
- case 0:
- w[rc] = 0xAAAA;
- break;
- case 1:
- w[rc] = 0x5555;
- break;
- case 2:
- w[rc] = 0x9696;
- break;
- }
-
- range_enc_range512(chan_list, (1 << 9) | 0x96, w);
-
- printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list)));
-}
-
-static void test_si_range_helpers()
-{
- int ws[(sizeof(freqs1)/sizeof(freqs1[0]))];
- int i, f0 = 0xFFFFFF;
-
- memset(&ws[0], 0x23, sizeof(ws));
-
- i = range_enc_find_index(1023, freqs1, ARRAY_SIZE(freqs1));
- printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs1[i] : -1);
- VERIFY(i, ==, 2);
-
- i = range_enc_find_index(511, freqs2, ARRAY_SIZE(freqs2));
- printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs2[i] : -1);
- VERIFY(i, ==, 2);
-
- i = range_enc_find_index(511, freqs3, ARRAY_SIZE(freqs3));
- printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs3[i] : -1);
- VERIFY(i, ==, 0);
-
- range_enc_arfcns(1023, freqs1, ARRAY_SIZE(freqs1), ws, 0);
-
- for (i = 0; i < sizeof(freqs1)/sizeof(freqs1[0]); ++i) {
- printf("w[%d]=%d\n", i, ws[i]);
- VERIFY(ws[i], ==, w_out[i]);
- }
-
- i = range_enc_determine_range(range128, ARRAY_SIZE(range128), &f0);
- VERIFY(i, ==, ARFCN_RANGE_128);
- VERIFY(f0, ==, 1);
-
- i = range_enc_determine_range(range256, ARRAY_SIZE(range256), &f0);
- VERIFY(i, ==, ARFCN_RANGE_256);
- VERIFY(f0, ==, 1);
-
- i = range_enc_determine_range(range512, ARRAY_SIZE(range512), &f0);
- VERIFY(i, ==, ARFCN_RANGE_512);
- VERIFY(f0, ==, 1);
-}
-
static void test_si_ba_ind(struct gsm_network *net)
{
struct gsm_bts *bts = bts_init(net);
@@ -778,10 +442,10 @@ static void test_gsm48_ra_id_by_bts()
static void test_gsm48_multirate_config()
{
- uint8_t lv[7];
struct gsm48_multi_rate_conf *gsm48_ie;
struct amr_multirate_conf mr;
int rc;
+ struct msgb *msg = msgb_alloc(32, "test_gsm48_multirate_config");
memset(&mr, 0, sizeof(mr));
@@ -805,75 +469,153 @@ static void test_gsm48_multirate_config()
mr.ms_mode[1].mode = 4;
mr.ms_mode[2].mode = 5;
mr.ms_mode[3].mode = 7;
- rc = gsm48_multirate_config(lv, gsm48_ie, mr.ms_mode, 4);
+ msgb_trim(msg, 0);
+ rc = gsm48_multirate_config(msg, gsm48_ie, mr.ms_mode, 4);
OSMO_ASSERT(rc == 0);
printf("gsm48_multirate_config(): rc=%i, lv=%s\n", rc,
- osmo_hexdump_nospc(lv, lv[0]));
+ osmo_hexdump_nospc(msg->data, msg->len));
/* Test #2: 4 active set members, but wrong mode order: */
mr.ms_mode[3].mode = 2;
mr.ms_mode[2].mode = 4;
mr.ms_mode[1].mode = 5;
mr.ms_mode[0].mode = 7;
- rc = gsm48_multirate_config(lv, gsm48_ie, mr.ms_mode, 4);
+ msgb_trim(msg, 0);
+ rc = gsm48_multirate_config(msg, gsm48_ie, mr.ms_mode, 4);
OSMO_ASSERT(rc == -EINVAL);
/* Test #3: Normal configuration with 3 active set members */
mr.ms_mode[0].mode = 2;
mr.ms_mode[1].mode = 4;
mr.ms_mode[2].mode = 5;
- mr.ms_mode[3].mode = 0;
+ mr.ms_mode[3].mode = 7;
gsm48_ie->m12_2 = 0;
mr.ms_mode[2].threshold = 0;
mr.ms_mode[2].hysteresis = 0;
- rc = gsm48_multirate_config(lv, gsm48_ie, mr.ms_mode, 3);
+ msgb_trim(msg, 0);
+ rc = gsm48_multirate_config(msg, gsm48_ie, mr.ms_mode, 3);
OSMO_ASSERT(rc == 0);
printf("gsm48_multirate_config(): rc=%i, lv=%s\n", rc,
- osmo_hexdump_nospc(lv, lv[0]));
+ osmo_hexdump_nospc(msg->data, msg->len));
/* Test #4: 3 active set members, but wrong mode order: */
mr.ms_mode[0].mode = 2;
mr.ms_mode[2].mode = 4;
mr.ms_mode[1].mode = 5;
- rc = gsm48_multirate_config(lv, gsm48_ie, mr.ms_mode, 3);
+ msgb_trim(msg, 0);
+ rc = gsm48_multirate_config(msg, gsm48_ie, mr.ms_mode, 3);
OSMO_ASSERT(rc == -EINVAL);
/* Test #5: Normal configuration with 2 active set members */
mr.ms_mode[0].mode = 2;
mr.ms_mode[1].mode = 4;
- mr.ms_mode[2].mode = 0;
+ mr.ms_mode[2].mode = 5;
+ mr.ms_mode[3].mode = 7;
gsm48_ie->m7_95 = 0;
mr.ms_mode[1].threshold = 0;
mr.ms_mode[1].hysteresis = 0;
- rc = gsm48_multirate_config(lv, gsm48_ie, mr.ms_mode, 2);
+ msgb_trim(msg, 0);
+ rc = gsm48_multirate_config(msg, gsm48_ie, mr.ms_mode, 2);
OSMO_ASSERT(rc == 0);
printf("gsm48_multirate_config(): rc=%i, lv=%s\n", rc,
- osmo_hexdump_nospc(lv, lv[0]));
+ osmo_hexdump_nospc(msg->data, msg->len));
/* Test #6: 2 active set members, but wrong mode order: */
mr.ms_mode[1].mode = 2;
mr.ms_mode[0].mode = 4;
- rc = gsm48_multirate_config(lv, gsm48_ie, mr.ms_mode, 2);
+ msgb_trim(msg, 0);
+ rc = gsm48_multirate_config(msg, gsm48_ie, mr.ms_mode, 2);
OSMO_ASSERT(rc == -EINVAL);
/* Test #7: Normal configuration with 1 active set member */
mr.ms_mode[0].mode = 2;
- mr.ms_mode[1].mode = 0;
+ mr.ms_mode[1].mode = 4;
+ mr.ms_mode[2].mode = 5;
+ mr.ms_mode[3].mode = 7;
gsm48_ie->m7_40 = 0;
mr.ms_mode[0].threshold = 0;
mr.ms_mode[0].hysteresis = 0;
- rc = gsm48_multirate_config(lv, gsm48_ie, mr.ms_mode, 1);
+ msgb_trim(msg, 0);
+ rc = gsm48_multirate_config(msg, gsm48_ie, mr.ms_mode, 1);
OSMO_ASSERT(rc == 0);
printf("gsm48_multirate_config(): rc=%i, lv=%s\n", rc,
- osmo_hexdump_nospc(lv, lv[0]));
+ osmo_hexdump_nospc(msg->data, msg->len));
/* Test #8: 0 active set members: */
mr.ms_mode[0].mode = 0;
- rc = gsm48_multirate_config(lv, gsm48_ie, mr.ms_mode, 1);
+ msgb_trim(msg, 0);
+ rc = gsm48_multirate_config(msg, gsm48_ie, mr.ms_mode, 1);
OSMO_ASSERT(rc == -EINVAL);
+
+ msgb_free(msg);
+}
+
+/* Similar to list_arfcn() from system_information.c, but uses printf().
+ * Another difference is that the text is printed even if n is 0. */
+static void print_cell_chan_desc(uint8_t *cd, const char *text)
+{
+ struct gsm_sysinfo_freq freq[1024];
+ unsigned int n = 0, i;
+
+ memset(freq, 0, sizeof(freq));
+ gsm48_decode_freq_list(freq, cd, 16, 0xce, 1);
+
+ printf("%s:", text);
+ for (i = 0; i < 1024; i++) {
+ if (!freq[i].mask)
+ continue;
+ printf(" %u", i);
+ n++;
+ }
+ if (!n)
+ printf(" (empty set)");
+ printf("\n");
+}
+
+static void test_cell_chan_desc(struct gsm_network *net)
+{
+ struct gsm_bts *bts = bts_init(net);
+ uint8_t cell_chan_desc[16];
+
+ printf("Testing generation of the Cell Channel Description IE:\n");
+
+ bts_model_unknown_init();
+ bts->type = GSM_BTS_TYPE_UNKNOWN;
+ bts->model = bts_model_find(bts->type);
+ OSMO_ASSERT(bts->model != NULL);
+
+ bts->band = GSM_BAND_900;
+ bts->c0->arfcn = 10; /* BCCH carrier */
+
+ /* Case a) only the BCCH carrier */
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, bts->c0->arfcn, ONE);
+
+ OSMO_ASSERT(generate_cell_chan_list(&cell_chan_desc[0], bts) == 0);
+ print_cell_chan_desc(&cell_chan_desc[0], "Case a) only the BCCH carrier");
+
+ /* Case b) more carriers from P-GSM band */
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, 1, ONE);
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, 3, ONE);
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, 64, ONE);
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, 99, ONE);
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, 124, ONE);
+
+ OSMO_ASSERT(generate_cell_chan_list(&cell_chan_desc[0], bts) == 0);
+ print_cell_chan_desc(&cell_chan_desc[0], "Case b) more carriers from P-GSM band");
+
+ /* Case c) more carriers from E-GSM band */
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, 0, ONE);
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, 975, ONE);
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, 1001, ONE);
+ bitvec_set_bit_pos(&bts->si_common.cell_alloc, 1023, ONE);
+
+ OSMO_ASSERT(generate_cell_chan_list(&cell_chan_desc[0], bts) == 0);
+ print_cell_chan_desc(&cell_chan_desc[0], "Case c) more carriers from E-GSM band");
+
+ bts_del(bts);
}
static const struct log_info_cat log_categories[] = {
@@ -898,11 +640,7 @@ int main(int argc, char **argv)
printf("Network init failure.\n");
return EXIT_FAILURE;
}
-
- test_si_range_helpers();
- test_arfcn_filter();
- test_print_encoding();
- test_range_encoding();
+ bsc_gsmnet = net;
test_si2q_segfault(net);
test_si2q_e(net);
@@ -916,63 +654,9 @@ int main(int argc, char **argv)
test_gsm48_multirate_config();
+ test_cell_chan_desc(net);
+
printf("Done.\n");
return EXIT_SUCCESS;
}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-void gscon_release_lchans(struct gsm_subscriber_connection *conn, bool do_rr_release) {
- OSMO_ASSERT(0);
-}
-
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts)
-{
- return true;
-}
-
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-
-void bsc_cm_update(struct gsm_subscriber_connection *conn,
- const uint8_t *cm2, uint8_t cm2_len,
- const uint8_t *cm3, uint8_t cm3_len) {}
-
-int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci)
-{ return 0; }
-
-int rsl_chan_mode_modify_req(struct gsm_lchan *ts) { return 0; }
-
-int rsl_tx_ipacc_crcx(const struct gsm_lchan *lchan) { return 0; }
-
-void gscon_submit_rsl_dtap(struct gsm_subscriber_connection *conn,
- struct msgb *msg, int link_id, int allow_sacch) {}
-
-bool lchan_may_receive_data(struct gsm_lchan *lchan) { return true; }
-
-int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel)
-{ return 0; }
-
-void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id,
- struct msgb *msg) {}
-
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn,
- struct msgb *msg, uint8_t chosen_encr) {}
-
-const char *bsc_subscr_name(struct bsc_subscr *bsub) { return NULL; }
-
-void lchan_release(struct gsm_lchan *lchan, bool do_rr_release,
- bool err, enum gsm48_rr_cause cause_rr) {}
-
-int rsl_data_request(struct msgb *msg, uint8_t link_id) { return 0; }
-
-int rsl_encryption_cmd(struct msgb *msg) { return 0; }
-
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok
index 7e054f48e..1a8389d81 100644
--- a/tests/gsm0408/gsm0408_test.ok
+++ b/tests/gsm0408/gsm0408_test.ok
@@ -1,59 +1,3 @@
-Element is: 2 => freqs[i] = 121
-Element is: 2 => freqs[i] = 1
-Element is: 0 => freqs[i] = 68
-w[0]=122
-w[1]=2
-w[2]=69
-w[3]=204
-w[4]=75
-w[5]=66
-w[6]=60
-w[7]=70
-w[8]=83
-w[9]=3
-w[10]=24
-w[11]=67
-w[12]=54
-w[13]=64
-w[14]=70
-w[15]=9
-Range512: 89 4b 2a 95 65 95 55 2c a9 55 aa 55 6a 95 59 55
-Range test 0: range 511, num ARFCNs 12
-chan_list = 88 00 98 34 85 36 7c 50 22 dc 5e ec 00 00 00 00
-Decoded freqs 12 (expected 12)
-Decoded: 1 12 31 51 57 91 97 98 113 117 120 125
-Range test 1: range 511, num ARFCNs 17
-chan_list = 88 00 82 7f 01 3f 7e 04 0b ff ff fc 10 41 07 e0
-Decoded freqs 17 (expected 17)
-Decoded: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
-Range test 2: range 511, num ARFCNs 18
-chan_list = 88 00 82 7f 01 7f 7e 04 0b ff ff fc 10 41 07 ff
-Decoded freqs 18 (expected 18)
-Decoded: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
-Range test 3: range 511, num ARFCNs 18
-chan_list = 88 00 94 3a 44 32 d7 2a 43 2a 13 94 e5 38 39 f6
-Decoded freqs 18 (expected 18)
-Decoded: 1 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 417 511
-Range test 4: range 511, num ARFCNs 6
-chan_list = 88 00 8b 3c 88 b9 6b 00 00 00 00 00 00 00 00 00
-Decoded freqs 6 (expected 6)
-Decoded: 1 17 31 45 58 79
-Range test 5: range 511, num ARFCNs 6
-chan_list = 88 05 08 fc 88 b9 6b 00 00 00 00 00 00 00 00 00
-Decoded freqs 6 (expected 6)
-Decoded: 10 17 31 45 58 79
-Range test 6: range 1023, num ARFCNs 17
-chan_list = 84 71 e4 ab b9 58 05 cb 39 17 fd b0 75 62 0f 2f
-Decoded freqs 17 (expected 17)
-Decoded: 0 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 1023
-Range test 7: range 1023, num ARFCNs 16
-chan_list = 80 71 e4 ab b9 58 05 cb 39 17 fd b0 75 62 0f 2f
-Decoded freqs 16 (expected 16)
-Decoded: 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 1023
-Random range test: range 127, max num ARFCNs 29
-Random range test: range 255, max num ARFCNs 22
-Random range test: range 511, max num ARFCNs 18
-Random range test: range 1023, max num ARFCNs 16
BTS allocation OK in test_si2q_segfault()
Test SI2quater UARFCN (same scrambling code and diversity):
generating SI2quater for 0 EARFCNs and 1 UARFCNs...
@@ -225,7 +169,7 @@ generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 4
BTS deallocated OK in test_si2q_long()
BTS allocation OK in test_si_ba_ind()
Testing if BA-IND is set as expected in SI2xxx and SI5xxx
-SI2: 59 06 1a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e5 04 00
+SI2: 59 06 1a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff e5 04 00
SI2bis: 55 06 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e5 04 00 2b
SI2ter: 49 06 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2b 2b 2b 2b
SI5: 06 1d 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
@@ -239,8 +183,14 @@ test_gsm48_ra_id_by_bts[3]: digits='000000' lac=0x0000=htons(0) rac=0x00=0 pass
test_gsm48_ra_id_by_bts[4]: digits='999999' lac=0xffff=htons(65535) rac=0xff=255 pass
test_gsm48_ra_id_by_bts[5]: digits='09f909' lac=0xcdab=htons(43981) rac=0xab=171 pass
test_gsm48_ra_id_by_bts[6]: digits='090990' lac=0xcdab=htons(43981) rac=0xab=171 pass
-gsm48_multirate_config(): rc=0, lv=0620b40bf330
-gsm48_multirate_config(): rc=0, lv=0520340bf3
-gsm48_multirate_config(): rc=0, lv=0420140b
-gsm48_multirate_config(): rc=0, lv=0220
+gsm48_multirate_config(): rc=0, lv=0620b40bf330d8
+gsm48_multirate_config(): rc=0, lv=0520340bf330
+gsm48_multirate_config(): rc=0, lv=0420140bf0
+gsm48_multirate_config(): rc=0, lv=022004
+BTS allocation OK in test_cell_chan_desc()
+Testing generation of the Cell Channel Description IE:
+Case a) only the BCCH carrier: 10
+Case b) more carriers from P-GSM band: 1 3 10 64 99 124
+Case c) more carriers from E-GSM band: 0 3 10 64 99 124 975 1001 1023
+BTS deallocated OK in test_cell_chan_desc()
Done.
diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am
index 0f7953cd4..123fd61d6 100644
--- a/tests/handover/Makefile.am
+++ b/tests/handover/Makefile.am
@@ -21,14 +21,13 @@ AM_LDFLAGS = \
$(NULL)
EXTRA_DIST = \
- handover_test.ok \
- neighbor_ident_test.ok \
- neighbor_ident_test.err \
+ handover_tests.sh \
+ handover_tests.ok \
+ $(srcdir)/test*.ho_vty \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
handover_test \
- neighbor_ident_test \
$(NULL)
handover_test_SOURCES = \
@@ -41,62 +40,7 @@ handover_test_LDFLAGS = \
$(NULL)
handover_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/a_reset.o \
- $(top_builddir)/src/osmo-bsc/abis_nm.o \
- $(top_builddir)/src/osmo-bsc/abis_nm_vty.o \
- $(top_builddir)/src/osmo-bsc/abis_om2000.o \
- $(top_builddir)/src/osmo-bsc/abis_om2000_vty.o \
- $(top_builddir)/src/osmo-bsc/abis_rsl.o \
- $(top_builddir)/src/osmo-bsc/acc.o \
- $(top_builddir)/src/osmo-bsc/arfcn_range_encode.o \
- $(top_builddir)/src/osmo-bsc/assignment_fsm.o \
- $(top_builddir)/src/osmo-bsc/bsc_ctrl_commands.o \
- $(top_builddir)/src/osmo-bsc/bsc_init.o \
- $(top_builddir)/src/osmo-bsc/bsc_rf_ctrl.o \
- $(top_builddir)/src/osmo-bsc/bsc_rll.o \
- $(top_builddir)/src/osmo-bsc/bsc_subscr_conn_fsm.o \
- $(top_builddir)/src/osmo-bsc/bsc_subscriber.o \
- $(top_builddir)/src/osmo-bsc/bsc_vty.o \
- $(top_builddir)/src/osmo-bsc/bts.o \
- $(top_builddir)/src/osmo-bsc/bts_trx.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/bts_unknown.o \
- $(top_builddir)/src/osmo-bsc/chan_alloc.o \
- $(top_builddir)/src/osmo-bsc/codec_pref.o \
- $(top_builddir)/src/osmo-bsc/gsm_04_08_rr.o \
- $(top_builddir)/src/osmo-bsc/gsm_data.o \
- $(top_builddir)/src/osmo-bsc/handover_cfg.o \
- $(top_builddir)/src/osmo-bsc/handover_decision.o \
- $(top_builddir)/src/osmo-bsc/handover_decision_2.o \
- $(top_builddir)/src/osmo-bsc/handover_fsm.o \
- $(top_builddir)/src/osmo-bsc/handover_logic.o \
- $(top_builddir)/src/osmo-bsc/handover_vty.o \
- $(top_builddir)/src/osmo-bsc/lchan_fsm.o \
- $(top_builddir)/src/osmo-bsc/lchan_rtp_fsm.o \
- $(top_builddir)/src/osmo-bsc/lchan_select.o \
- $(top_builddir)/src/osmo-bsc/meas_feed.o \
- $(top_builddir)/src/osmo-bsc/meas_rep.o \
- $(top_builddir)/src/osmo-bsc/neighbor_ident.o \
- $(top_builddir)/src/osmo-bsc/neighbor_ident_vty.o \
- $(top_builddir)/src/osmo-bsc/net_init.o \
- $(top_builddir)/src/osmo-bsc/osmo_bsc_ctrl.o \
- $(top_builddir)/src/osmo-bsc/osmo_bsc_lcls.o \
- $(top_builddir)/src/osmo-bsc/osmo_bsc_mgcp.o \
- $(top_builddir)/src/osmo-bsc/osmo_bsc_msc.o \
- $(top_builddir)/src/osmo-bsc/paging.o \
- $(top_builddir)/src/osmo-bsc/pcu_sock.o \
- $(top_builddir)/src/osmo-bsc/penalty_timers.o \
- $(top_builddir)/src/osmo-bsc/rest_octets.o \
- $(top_builddir)/src/osmo-bsc/system_information.o \
- $(top_builddir)/src/osmo-bsc/timeslot_fsm.o \
- $(top_builddir)/src/osmo-bsc/smscb.o \
- $(top_builddir)/src/osmo-bsc/cbch_scheduler.o \
- $(top_builddir)/src/osmo-bsc/cbsp_link.o \
- $(top_builddir)/src/osmo-bsc/lcs_loc_req.o \
- $(top_builddir)/src/osmo-bsc/lcs_ta_req.o \
- $(top_builddir)/src/osmo-bsc/lb.o \
- $(top_builddir)/src/osmo-bsc/bsc_sccp.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCTRL_LIBS) \
@@ -107,17 +51,6 @@ handover_test_LDADD = \
$(LIBOSMOMGCPCLIENT_LIBS) \
$(NULL)
-neighbor_ident_test_SOURCES = \
- neighbor_ident_test.c \
- $(NULL)
-
-neighbor_ident_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/neighbor_ident.o \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOCTRL_LIBS) \
- $(NULL)
-
.PHONY: update_exp
update_exp:
- $(builddir)/neighbor_ident_test >$(srcdir)/neighbor_ident_test.ok 2>$(srcdir)/neighbor_ident_test.err
+ $(srcdir)/handover_tests.sh $(srcdir) $(builddir) -u
diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c
index d3f29e163..bf91f024f 100644
--- a/tests/handover/handover_test.c
+++ b/tests/handover/handover_test.c
@@ -20,12 +20,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
+#include <inttypes.h>
#include <assert.h>
#include <osmocom/core/application.h>
#include <osmocom/core/select.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/vty/vty.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
@@ -34,6 +36,7 @@
#include <osmocom/bsc/bsc_subscriber.h>
#include <osmocom/bsc/lchan_select.h>
#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/assignment_fsm.h>
#include <osmocom/bsc/handover_decision.h>
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/handover.h>
@@ -49,10 +52,13 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/paging.h>
+#include <osmocom/bsc/vty.h>
+#include <osmocom/bsc/lchan.h>
+#include <osmocom/mgcp_client/mgcp_client_pool.h>
-void *ctx;
+#include "../../bscconfig.h"
-struct gsm_network *bsc_gsmnet;
+void *ctx;
/* override, requires '-Wl,--wrap=osmo_mgcpc_ep_ci_request'.
* Catch modification of an MGCP connection. */
@@ -77,20 +83,64 @@ void __wrap_osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
/* measurement report */
uint8_t meas_rep_ba = 0, meas_rep_valid = 1, meas_valid = 1, meas_multi_rep = 0;
-uint8_t meas_dl_rxlev = 0, meas_dl_rxqual = 0;
uint8_t meas_ul_rxlev = 0, meas_ul_rxqual = 0;
-uint8_t meas_tx_power_ms = 0, meas_tx_power_bs = 0, meas_ta_ms = 0;
+uint8_t meas_tx_power_ms = 0;
uint8_t meas_dtx_ms = 0, meas_dtx_bs = 0, meas_nr = 0;
-uint8_t meas_num_nc = 0, meas_rxlev_nc[6], meas_bsic_nc[6], meas_bcch_f_nc[6];
+char *codec_tch_f = NULL;
+char *codec_tch_h = NULL;
+
+struct neighbor_meas {
+ uint8_t rxlev;
+ uint8_t bsic;
+ uint8_t bcch_f;
+};
+
+const struct timeval fake_time_start_time = { 123, 456 };
+
+void fake_time_passes(time_t secs, suseconds_t usecs)
+{
+ struct timeval diff;
+ /* Add time to osmo_fsm timers, using osmo_gettimeofday() */
+ osmo_gettimeofday_override_add(secs, usecs);
+ /* Add time to penalty timers, using osmo_clock_gettime() */
+ osmo_clock_override_add(CLOCK_MONOTONIC, secs, usecs * 1000);
+
+ timersub(&osmo_gettimeofday_override_time, &fake_time_start_time, &diff);
+ fprintf(stderr, "Total time passed: %d.%06d s\n", (int)diff.tv_sec, (int)diff.tv_usec);
+
+ osmo_timers_prepare();
+ osmo_timers_update();
+}
+
+void fake_time_start()
+{
+ struct timespec *clock_override;
+
+ /* osmo_fsm uses osmo_gettimeofday(). To affect FSM timeouts, we need osmo_gettimeofday_override. */
+ osmo_gettimeofday_override_time = fake_time_start_time;
+ osmo_gettimeofday_override = true;
+
+ /* Penalty timers use osmo_clock_gettime(CLOCK_MONOTONIC). To affect these timeouts, we need
+ * osmo_gettimeofday_override. */
+ clock_override = osmo_clock_override_gettimespec(CLOCK_MONOTONIC);
+ OSMO_ASSERT(clock_override);
+ clock_override->tv_sec = fake_time_start_time.tv_sec;
+ clock_override->tv_nsec = fake_time_start_time.tv_usec * 1000;
+ osmo_clock_override_enable(CLOCK_MONOTONIC, true);
+ fake_time_passes(0, 0);
+}
-static void gen_meas_rep(struct gsm_lchan *lchan)
+static void gen_meas_rep(struct gsm_lchan *lchan,
+ uint8_t bs_power_db, uint8_t rxlev, uint8_t rxqual, uint8_t ta,
+ int neighbors_count, struct neighbor_meas *neighbors)
{
struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL");
struct abis_rsl_dchan_hdr *dh;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
uint8_t ulm[3], l1i[2], *buf;
struct gsm48_hdr *gh;
struct gsm48_meas_res *mr;
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ OSMO_ASSERT(chan_nr >= 0);
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
@@ -105,10 +155,10 @@ static void gen_meas_rep(struct gsm_lchan *lchan)
ulm[2] = (meas_ul_rxqual << 3) | meas_ul_rxqual;
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, sizeof(ulm), ulm);
- msgb_tv_put(msg, RSL_IE_BS_POWER, meas_tx_power_bs);
+ msgb_tv_put(msg, RSL_IE_BS_POWER, (bs_power_db / 2) & 0xf);
l1i[0] = 0;
- l1i[1] = meas_ta_ms;
+ l1i[1] = ta;
msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, sizeof(l1i), l1i);
buf = msgb_put(msg, 3);
@@ -123,71 +173,94 @@ static void gen_meas_rep(struct gsm_lchan *lchan)
gh->msg_type = GSM48_MT_RR_MEAS_REP;
/* measurement results */
- mr->rxlev_full = meas_dl_rxlev;
- mr->rxlev_sub = meas_dl_rxlev;
- mr->rxqual_full = meas_dl_rxqual;
- mr->rxqual_sub = meas_dl_rxqual;
+ mr->rxlev_full = rxlev;
+ mr->rxlev_sub = rxlev;
+ mr->rxqual_full = rxqual;
+ mr->rxqual_sub = rxqual;
mr->dtx_used = meas_dtx_ms;
mr->ba_used = meas_rep_ba;
- mr->meas_valid = !meas_valid; /* 0 = valid */
- if (meas_rep_valid) {
- mr->no_nc_n_hi = meas_num_nc >> 2;
- mr->no_nc_n_lo = meas_num_nc & 3;
- } else {
- /* no results for serving cells */
- mr->no_nc_n_hi = 1;
- mr->no_nc_n_lo = 3;
- }
- mr->rxlev_nc1 = meas_rxlev_nc[0];
- mr->rxlev_nc2_hi = meas_rxlev_nc[1] >> 1;
- mr->rxlev_nc2_lo = meas_rxlev_nc[1] & 1;
- mr->rxlev_nc3_hi = meas_rxlev_nc[2] >> 2;
- mr->rxlev_nc3_lo = meas_rxlev_nc[2] & 3;
- mr->rxlev_nc4_hi = meas_rxlev_nc[3] >> 3;
- mr->rxlev_nc4_lo = meas_rxlev_nc[3] & 7;
- mr->rxlev_nc5_hi = meas_rxlev_nc[4] >> 4;
- mr->rxlev_nc5_lo = meas_rxlev_nc[4] & 15;
- mr->rxlev_nc6_hi = meas_rxlev_nc[5] >> 5;
- mr->rxlev_nc6_lo = meas_rxlev_nc[5] & 31;
- mr->bsic_nc1_hi = meas_bsic_nc[0] >> 3;
- mr->bsic_nc1_lo = meas_bsic_nc[0] & 7;
- mr->bsic_nc2_hi = meas_bsic_nc[1] >> 4;
- mr->bsic_nc2_lo = meas_bsic_nc[1] & 15;
- mr->bsic_nc3_hi = meas_bsic_nc[2] >> 5;
- mr->bsic_nc3_lo = meas_bsic_nc[2] & 31;
- mr->bsic_nc4 = meas_bsic_nc[3];
- mr->bsic_nc5 = meas_bsic_nc[4];
- mr->bsic_nc6 = meas_bsic_nc[5];
- mr->bcch_f_nc1 = meas_bcch_f_nc[0];
- mr->bcch_f_nc2 = meas_bcch_f_nc[1];
- mr->bcch_f_nc3 = meas_bcch_f_nc[2];
- mr->bcch_f_nc4 = meas_bcch_f_nc[3];
- mr->bcch_f_nc5_hi = meas_bcch_f_nc[4] >> 1;
- mr->bcch_f_nc5_lo = meas_bcch_f_nc[4] & 1;
- mr->bcch_f_nc6_hi = meas_bcch_f_nc[5] >> 2;
- mr->bcch_f_nc6_lo = meas_bcch_f_nc[5] & 3;
-
- msg->dst = lchan->ts->trx->bts->c0->rsl_link;
+ mr->meas_valid = 0; /* 0 = valid */
+ mr->no_nc_n_hi = neighbors_count >> 2;
+ mr->no_nc_n_lo = neighbors_count & 3;
+
+ mr->rxlev_nc1 = neighbors[0].rxlev;
+ mr->rxlev_nc2_hi = neighbors[1].rxlev >> 1;
+ mr->rxlev_nc2_lo = neighbors[1].rxlev & 1;
+ mr->rxlev_nc3_hi = neighbors[2].rxlev >> 2;
+ mr->rxlev_nc3_lo = neighbors[2].rxlev & 3;
+ mr->rxlev_nc4_hi = neighbors[3].rxlev >> 3;
+ mr->rxlev_nc4_lo = neighbors[3].rxlev & 7;
+ mr->rxlev_nc5_hi = neighbors[4].rxlev >> 4;
+ mr->rxlev_nc5_lo = neighbors[4].rxlev & 15;
+ mr->rxlev_nc6_hi = neighbors[5].rxlev >> 5;
+ mr->rxlev_nc6_lo = neighbors[5].rxlev & 31;
+ mr->bsic_nc1_hi = neighbors[0].bsic >> 3;
+ mr->bsic_nc1_lo = neighbors[0].bsic & 7;
+ mr->bsic_nc2_hi = neighbors[1].bsic >> 4;
+ mr->bsic_nc2_lo = neighbors[1].bsic & 15;
+ mr->bsic_nc3_hi = neighbors[2].bsic >> 5;
+ mr->bsic_nc3_lo = neighbors[2].bsic & 31;
+ mr->bsic_nc4 = neighbors[3].bsic;
+ mr->bsic_nc5 = neighbors[4].bsic;
+ mr->bsic_nc6 = neighbors[5].bsic;
+ mr->bcch_f_nc1 = neighbors[0].bcch_f;
+ mr->bcch_f_nc2 = neighbors[1].bcch_f;
+ mr->bcch_f_nc3 = neighbors[2].bcch_f;
+ mr->bcch_f_nc4 = neighbors[3].bcch_f;
+ mr->bcch_f_nc5_hi = neighbors[4].bcch_f >> 1;
+ mr->bcch_f_nc5_lo = neighbors[4].bcch_f & 1;
+ mr->bcch_f_nc6_hi = neighbors[5].bcch_f >> 2;
+ mr->bcch_f_nc6_lo = neighbors[5].bcch_f & 3;
+
+ msg->dst = rsl_chan_link(lchan);
msg->l2h = (unsigned char *)dh;
msg->l3h = (unsigned char *)gh;
abis_rsl_rcvmsg(msg);
}
-static struct gsm_bts *create_bts(int arfcn)
+enum gsm_phys_chan_config pchan_from_str(const char *str)
{
+ enum gsm_phys_chan_config pchan;
+ if (!strcmp(str, "dyn"))
+ return GSM_PCHAN_OSMO_DYN;
+ if (!strcmp(str, "c+s4"))
+ return GSM_PCHAN_CCCH_SDCCH4;
+ if (!strcmp(str, "-"))
+ return GSM_PCHAN_NONE;
+ pchan = gsm_pchan_parse(str);
+ if (pchan < 0) {
+ fprintf(stderr, "Invalid timeslot pchan type: %s\n", str);
+ exit(1);
+ }
+ return pchan;
+}
+
+const char * const bts_default_ts[] = {
+ "c+s4", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "TCH/H", "TCH/H", "-",
+};
+
+static struct gsm_bts *_create_bts(int num_trx, const char * const *ts_args, int ts_args_count)
+{
+ static int arfcn = 870;
+ static int ci = 0;
struct gsm_bts *bts;
struct e1inp_sign_link *rsl_link;
int i;
+ int trx_i;
+ struct gsm_bts_trx *trx;
+
+ fprintf(stderr, "- Creating BTS %d, %d TRX\n", bsc_gsmnet->num_bts, num_trx);
- bts = bsc_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_UNKNOWN, 0x3f);
+ bts = bsc_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_UNKNOWN, HARDCODED_BSIC);
if (!bts) {
- printf("No resource for bts1\n");
+ fprintf(stderr, "No resource for bts1\n");
return NULL;
}
- bts->location_area_code = 23;
- bts->c0->arfcn = arfcn;
+ bts->location_area_code = 0x0017;
+ bts->cell_identity = ci++;
+ bts->c0->arfcn = arfcn++;
bts->codec.efr = 1;
bts->codec.hr = 1;
@@ -195,31 +268,140 @@ static struct gsm_bts *create_bts(int arfcn)
rsl_link = talloc_zero(ctx, struct e1inp_sign_link);
rsl_link->trx = bts->c0;
- bts->c0->rsl_link = rsl_link;
-
- bts->c0->mo.nm_state.operational = NM_OPSTATE_ENABLED;
- bts->c0->mo.nm_state.availability = NM_AVSTATE_OK;
- bts->c0->mo.nm_state.administrative = NM_STATE_UNLOCKED;
- bts->c0->bb_transc.mo.nm_state.operational = NM_OPSTATE_ENABLED;
- bts->c0->bb_transc.mo.nm_state.availability = NM_AVSTATE_OK;
- bts->c0->bb_transc.mo.nm_state.administrative = NM_STATE_UNLOCKED;
-
- /* 4 full rate and 4 half rate channels */
- for (i = 1; i <= 6; i++) {
- bts->c0->ts[i].pchan_from_config = (i < 5) ? GSM_PCHAN_TCH_F : GSM_PCHAN_TCH_H;
- bts->c0->ts[i].mo.nm_state.operational = NM_OPSTATE_ENABLED;
- bts->c0->ts[i].mo.nm_state.availability = NM_AVSTATE_OK;
- bts->c0->ts[i].mo.nm_state.administrative = NM_STATE_UNLOCKED;
+ bts->c0->rsl_link_primary = rsl_link;
+
+ for (trx_i = 0; trx_i < num_trx; trx_i++) {
+ while (!(trx = gsm_bts_trx_num(bts, trx_i)))
+ gsm_bts_trx_alloc(bts);
+
+ trx->mo.nm_state.operational = NM_OPSTATE_ENABLED;
+ trx->mo.nm_state.availability = NM_AVSTATE_OK;
+ trx->mo.nm_state.administrative = NM_STATE_UNLOCKED;
+ trx->bb_transc.mo.nm_state.operational = NM_OPSTATE_ENABLED;
+ trx->bb_transc.mo.nm_state.availability = NM_AVSTATE_OK;
+ trx->bb_transc.mo.nm_state.administrative = NM_STATE_UNLOCKED;
+
+ /* 4 full rate and 4 half rate channels */
+ for (i = 0; i < 8; i++) {
+ int arg_i = trx_i * 8 + i;
+ const char *ts_arg;
+ if (arg_i >= ts_args_count)
+ ts_arg = bts_default_ts[i];
+ else
+ ts_arg = ts_args[arg_i];
+ fprintf(stderr, "\t%s", ts_arg);
+ trx->ts[i].pchan_from_config = pchan_from_str(ts_arg);
+ if (trx->ts[i].pchan_from_config == GSM_PCHAN_NONE)
+ continue;
+ trx->ts[i].mo.nm_state.operational = NM_OPSTATE_ENABLED;
+ trx->ts[i].mo.nm_state.availability = NM_AVSTATE_OK;
+ trx->ts[i].mo.nm_state.administrative = NM_STATE_UNLOCKED;
+ }
+ fprintf(stderr, "\n");
+
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ /* make sure ts->lchans[] get initialized */
+ osmo_fsm_inst_dispatch(trx->ts[i].fi, TS_EV_RSL_READY, 0);
+ osmo_fsm_inst_dispatch(trx->ts[i].fi, TS_EV_OML_READY, 0);
+
+ /* Unused dyn TS start out as used for PDCH */
+ switch (trx->ts[i].pchan_on_init) {
+ case GSM_PCHAN_OSMO_DYN:
+ case GSM_PCHAN_TCH_F_PDCH:
+ ts_set_pchan_is(&trx->ts[i], GSM_PCHAN_PDCH);
+ break;
+ default:
+ break;
+ }
+ }
}
- for (i = 0; i < ARRAY_SIZE(bts->c0->ts); i++) {
- /* make sure ts->lchans[] get initialized */
- osmo_fsm_inst_dispatch(bts->c0->ts[i].fi, TS_EV_RSL_READY, 0);
- osmo_fsm_inst_dispatch(bts->c0->ts[i].fi, TS_EV_OML_READY, 0);
+ for (i = 0; i < bsc_gsmnet->num_bts; i++) {
+ if (gsm_generate_si(gsm_bts_num(bsc_gsmnet, i), SYSINFO_TYPE_2) <= 0)
+ fprintf(stderr, "Error generating SI2\n");
}
return bts;
}
+char *lchans_use_str(struct gsm_bts_trx_ts *ts, const char *established_prefix, char established_char)
+{
+ char state_chars[8] = { 0 };
+ struct gsm_lchan *lchan;
+ bool any_lchans_established = false;
+ bool any_lchans_in_use = false;
+ ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {
+ char state_char;
+ if (lchan_state_is(lchan, LCHAN_ST_UNUSED)) {
+ state_char = '-';
+ } else {
+ any_lchans_in_use = true;
+ if (lchan_state_is(lchan, LCHAN_ST_ESTABLISHED)) {
+ any_lchans_established = true;
+ state_char = established_char;
+ } else {
+ state_char = '!';
+ }
+ }
+ state_chars[lchan->nr] = state_char;
+ }
+ if (!any_lchans_in_use)
+ return "-";
+ if (!any_lchans_established)
+ established_prefix = "";
+ return talloc_asprintf(OTC_SELECT, "%s%s", established_prefix, state_chars);
+}
+
+const char *ts_use_str(struct gsm_bts_trx_ts *ts)
+{
+ switch (ts->pchan_is) {
+ case GSM_PCHAN_CCCH_SDCCH4:
+ return "c+s4";
+
+ case GSM_PCHAN_NONE:
+ return "-";
+
+ case GSM_PCHAN_TCH_F:
+ return lchans_use_str(ts, "TCH/", 'F');
+
+ case GSM_PCHAN_TCH_H:
+ return lchans_use_str(ts, "TCH/", 'H');
+
+ default:
+ return gsm_pchan_name(ts->pchan_is);
+ }
+}
+
+bool _expect_ts_use(struct gsm_bts *bts, struct gsm_bts_trx *trx, const char * const *ts_use)
+{
+ int i;
+ int mismatching_ts = -1;
+
+ fprintf(stderr, "bts %d trx %d: expect:", bts->nr, trx->nr);
+ for (i = 0; i < 8; i++)
+ fprintf(stderr, "\t%s", ts_use[i]);
+ fprintf(stderr, "\nbts %d trx %d: got:", bts->nr, trx->nr);
+
+ for (i = 0; i < 8; i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ const char *use = ts_use_str(ts);
+
+ fprintf(stderr, "\t%s", use);
+
+ if (!strcmp(ts_use[i], "*"))
+ continue;
+ if (strcasecmp(ts_use[i], use) && mismatching_ts < 0)
+ mismatching_ts = i;
+ }
+ fprintf(stderr, "\n");
+
+ if (mismatching_ts >= 0) {
+ fprintf(stderr, "Test failed: mismatching TS use in bts %d trx %d ts %d\n",
+ bts->nr, trx->nr, mismatching_ts);
+ return false;
+ }
+ return true;
+}
+
void create_conn(struct gsm_lchan *lchan)
{
static unsigned int next_imsi = 0;
@@ -246,39 +428,45 @@ void create_conn(struct gsm_lchan *lchan)
snprintf(imsi, sizeof(imsi), "%06u", next_imsi);
lchan->conn->bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers, imsi, BSUB_USE_CONN);
+ /* Set RTP data that the MSC normally would have sent */
+ OSMO_STRLCPY_ARRAY(conn->user_plane.msc_assigned_rtp_addr, "1.2.3.4");
+ conn->user_plane.msc_assigned_rtp_port = 1234;
+
/* kick the FSM from INIT through to the ACTIVE state */
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MO_COMPL_L3, NULL);
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_CFM, NULL);
}
-/* create lchan */
-struct gsm_lchan *create_lchan(struct gsm_bts *bts, int full_rate, char *codec)
+struct gsm_lchan *lchan_act(struct gsm_lchan *lchan, int full_rate, const char *codec)
{
- struct gsm_lchan *lchan;
-
- lchan = lchan_select_by_type(bts, (full_rate) ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H);
- if (!lchan) {
- printf("No resource for lchan\n");
- exit(EXIT_FAILURE);
- }
-
/* serious hack into osmo_fsm */
lchan->fi->state = LCHAN_ST_ESTABLISHED;
lchan->ts->fi->state = TS_ST_IN_USE;
+ lchan->type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H;
+ /* Fake osmo_mgcpc_ep_ci to indicate that the lchan is used for voice */
+ lchan->mgw_endpoint_ci_bts = (void*)1;
+
+ if (lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN)
+ ts_set_pchan_is(lchan->ts, full_rate ? GSM_PCHAN_TCH_F : GSM_PCHAN_TCH_H);
+ if (lchan->ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH) {
+ OSMO_ASSERT(full_rate);
+ ts_set_pchan_is(lchan->ts, GSM_PCHAN_TCH_F);
+ }
+
LOG_LCHAN(lchan, LOGL_DEBUG, "activated by handover_test.c\n");
create_conn(lchan);
if (!strcasecmp(codec, "FR") && full_rate)
- lchan->tch_mode = GSM48_CMODE_SPEECH_V1;
+ lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1;
else if (!strcasecmp(codec, "HR") && !full_rate)
- lchan->tch_mode = GSM48_CMODE_SPEECH_V1;
+ lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1;
else if (!strcasecmp(codec, "EFR") && full_rate)
- lchan->tch_mode = GSM48_CMODE_SPEECH_EFR;
+ lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_EFR;
else if (!strcasecmp(codec, "AMR")) {
- lchan->tch_mode = GSM48_CMODE_SPEECH_AMR;
- lchan->activate.info.s15_s0 = 0x0002;
+ lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_AMR;
+ lchan->current_ch_mode_rate.s15_s0 = 0x0002;
} else {
- printf("Given codec unknown\n");
+ fprintf(stderr, "Given codec unknown\n");
exit(EXIT_FAILURE);
}
@@ -293,63 +481,128 @@ struct gsm_lchan *create_lchan(struct gsm_bts *bts, int full_rate, char *codec)
.len = 5,
};
+ chan_counts_ts_update(lchan->ts);
+
return lchan;
}
-/* parse channel request */
+struct gsm_lchan *create_lchan(struct gsm_bts *bts, int full_rate, const char *codec)
+{
+ struct gsm_lchan *lchan;
-static int got_chan_req = 0;
-static struct gsm_lchan *chan_req_lchan = NULL;
+ lchan = lchan_select_by_type(bts, (full_rate) ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H,
+ SELECT_FOR_HANDOVER, NULL);
+ if (!lchan) {
+ fprintf(stderr, "No resource for lchan\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return lchan_act(lchan, full_rate, codec);
+}
-static int parse_chan_act(struct gsm_lchan *lchan, uint8_t *data)
+static void lchan_release_ack(struct gsm_lchan *lchan)
{
- chan_req_lchan = lchan;
- return 0;
+ if (!lchan->fi || lchan->fi->state != LCHAN_ST_WAIT_BEFORE_RF_RELEASE)
+ return;
+ /* don't wait before release */
+ osmo_fsm_inst_state_chg(lchan->fi, LCHAN_ST_WAIT_RF_RELEASE_ACK, 0, 0);
+ if (lchan->fi->state == LCHAN_ST_UNUSED)
+ return;
+ /* ack the release */
+ osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RSL_RF_CHAN_REL_ACK, 0);
}
-static int parse_chan_rel(struct gsm_lchan *lchan, uint8_t *data)
+static void lchan_clear(struct gsm_lchan *lchan)
{
- chan_req_lchan = lchan;
- return 0;
+ lchan_release(lchan, true, false, 0, NULL);
+ lchan_release_ack(lchan);
}
-/* parse handover request */
+static void ts_clear(struct gsm_bts_trx_ts *ts)
+{
+ struct gsm_lchan *lchan;
-static int got_ho_req = 0;
-static struct gsm_lchan *ho_req_lchan = NULL;
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
+ if (lchan_state_is(lchan, LCHAN_ST_UNUSED))
+ continue;
+ lchan_clear(lchan);
+ }
+ chan_counts_ts_update(ts);
+}
-static int parse_ho_command(struct gsm_lchan *lchan, uint8_t *data, int len)
+bool _set_ts_use(struct gsm_bts *bts, struct gsm_bts_trx *trx, const char * const *ts_use)
{
- struct gsm48_hdr *gh = (struct gsm48_hdr *) data;
- struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *) gh->data;
- int arfcn;
- struct gsm_bts *neigh;
-
- switch (gh->msg_type) {
- case GSM48_MT_RR_HANDO_CMD:
- arfcn = (ho->cell_desc.arfcn_hi << 8) | ho->cell_desc.arfcn_lo;
-
- /* look up trx. since every dummy bts uses different arfcn and
- * only one trx, it is simple */
- llist_for_each_entry(neigh, &bsc_gsmnet->bts_list, list) {
- if (neigh->c0->arfcn != arfcn)
- continue;
- ho_req_lchan = lchan;
- return 0;
+ int i;
+
+ fprintf(stderr, "Setting TS use:");
+ for (i = 0; i < 8; i++)
+ fprintf(stderr, "\t%s", ts_use[i]);
+ fprintf(stderr, "\n");
+
+ for (i = 0; i < 8; i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ const char *want_use = ts_use[i];
+ const char *is_use = ts_use_str(ts);
+
+ if (!strcmp(want_use, "*"))
+ continue;
+
+ /* If it is already as desired, don't change anything */
+ if (!strcasecmp(want_use, is_use))
+ continue;
+
+ if (!strcasecmp(want_use, "tch/f")) {
+ if (!ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_F)) {
+ fprintf(stderr, "Error: bts %d trx %d ts %d cannot be used as TCH/F\n",
+ bts->nr, trx->nr, i);
+ return false;
+ }
+ ts_clear(ts);
+
+ lchan_act(&ts->lchan[0], true, codec_tch_f ? : "AMR");
+ } else if (!strcasecmp(want_use, "tch/h-")
+ || !strcasecmp(want_use, "tch/hh")
+ || !strcasecmp(want_use, "tch/-h")) {
+ bool act[2];
+ int j;
+
+ if (!ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_H)) {
+ fprintf(stderr, "Error: bts %d trx %d ts %d cannot be used as TCH/H\n",
+ bts->nr, trx->nr, i);
+ return false;
+ }
+
+ if (ts->pchan_is != GSM_PCHAN_TCH_H)
+ ts_clear(ts);
+
+ act[0] = (want_use[4] == 'h' || want_use[4] == 'H');
+ act[1] = (want_use[5] == 'h' || want_use[5] == 'H');
+
+ for (j = 0; j < 2; j++) {
+ if (lchan_state_is(&ts->lchan[j], LCHAN_ST_UNUSED)) {
+ if (act[j])
+ lchan_act(&ts->lchan[j], false, codec_tch_h ? : "AMR");
+ } else if (!act[j])
+ lchan_clear(&ts->lchan[j]);
+ }
+ } else if (!strcmp(want_use, "-") || !strcasecmp(want_use, "PDCH")) {
+ ts_clear(ts);
}
- break;
- case GSM48_MT_RR_ASS_CMD:
- ho_req_lchan = lchan;
- return 0;
- break;
- default:
- fprintf(stderr, "Error, expecting HO or AS command\n");
- return -EINVAL;
}
-
- return -1;
+ return true;
}
+/* parse channel request */
+
+static struct gsm_lchan *new_chan_req = NULL;
+static struct gsm_lchan *last_chan_req = NULL;
+
+static struct gsm_lchan *new_ho_cmd = NULL;
+static struct gsm_lchan *last_ho_cmd = NULL;
+
+static struct gsm_lchan *new_as_cmd = NULL;
+static struct gsm_lchan *last_as_cmd = NULL;
+
/* send channel activation ack */
static void send_chan_act_ack(struct gsm_lchan *lchan, int act)
{
@@ -360,20 +613,60 @@ static void send_chan_act_ack(struct gsm_lchan *lchan, int act)
dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
dh->c.msg_type = (act) ? RSL_MT_CHAN_ACTIV_ACK : RSL_MT_RF_CHAN_REL_ACK;
dh->ie_chan = RSL_IE_CHAN_NR;
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
+ dh->chan_nr = gsm_lchan2chan_nr(lchan, true);
- msg->dst = lchan->ts->trx->bts->c0->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
msg->l2h = (unsigned char *)dh;
abis_rsl_rcvmsg(msg);
}
+/* Send RR Assignment Complete for SAPI[0] */
+static void send_assignment_complete(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL");
+ struct abis_rsl_rll_hdr *rh;
+ uint8_t chan_nr = gsm_lchan2chan_nr(lchan, true);
+ uint8_t *buf;
+ struct gsm48_hdr *gh;
+ struct gsm48_ho_cpl *hc;
+
+ fprintf(stderr, "- Send RR Assignment Complete for %s\n", gsm_lchan_name(lchan));
+
+ rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
+ rh->c.msg_discr = ABIS_RSL_MDISC_RLL;
+ rh->c.msg_type = RSL_MT_DATA_IND;
+ rh->ie_chan = RSL_IE_CHAN_NR;
+ rh->chan_nr = chan_nr;
+ rh->ie_link_id = RSL_IE_LINK_IDENT;
+ rh->link_id = 0x00;
+
+ buf = msgb_put(msg, 3);
+ buf[0] = RSL_IE_L3_INFO;
+ buf[1] = (sizeof(*gh) + sizeof(*hc)) >> 8;
+ buf[2] = (sizeof(*gh) + sizeof(*hc)) & 0xff;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ hc = (struct gsm48_ho_cpl *) msgb_put(msg, sizeof(*hc));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_ASS_COMPL;
+
+ msg->dst = rsl_chan_link(lchan);
+ msg->l2h = (unsigned char *)rh;
+ msg->l3h = (unsigned char *)gh;
+
+ abis_rsl_rcvmsg(msg);
+}
+
/* Send RLL Est Ind for SAPI[0] */
static void send_est_ind(struct gsm_lchan *lchan)
{
struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL");
struct abis_rsl_rll_hdr *rh;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr(lchan, true);
+
+ fprintf(stderr, "- Send EST IND for %s\n", gsm_lchan_name(lchan));
rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
rh->c.msg_discr = ABIS_RSL_MDISC_RLL;
@@ -383,24 +676,51 @@ static void send_est_ind(struct gsm_lchan *lchan)
rh->ie_link_id = RSL_IE_LINK_IDENT;
rh->link_id = 0x00;
- msg->dst = lchan->ts->trx->bts->c0->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
msg->l2h = (unsigned char *)rh;
abis_rsl_rcvmsg(msg);
}
-/* send handover complete */
+static void send_ho_detect(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL");
+ struct abis_rsl_rll_hdr *rh;
+ uint8_t chan_nr = gsm_lchan2chan_nr(lchan, true);
+
+ fprintf(stderr, "- Send HO DETECT for %s\n", gsm_lchan_name(lchan));
+
+ rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
+ rh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
+ rh->c.msg_type = RSL_MT_HANDO_DET;
+ rh->ie_chan = RSL_IE_CHAN_NR;
+ rh->chan_nr = chan_nr;
+ rh->ie_link_id = RSL_IE_LINK_IDENT;
+ rh->link_id = 0x00;
+
+ msg->dst = rsl_chan_link(lchan);
+ msg->l2h = (unsigned char *)rh;
+
+ abis_rsl_rcvmsg(msg);
+
+ send_est_ind(lchan);
+ osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RTP_READY, 0);
+
+}
+
static void send_ho_complete(struct gsm_lchan *lchan, bool success)
{
struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL");
struct abis_rsl_rll_hdr *rh;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr(lchan, true);
uint8_t *buf;
struct gsm48_hdr *gh;
struct gsm48_ho_cpl *hc;
- send_est_ind(lchan);
- osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RTP_READY, 0);
+ if (success)
+ fprintf(stderr, "- Send HO COMPLETE for %s\n", gsm_lchan_name(lchan));
+ else
+ fprintf(stderr, "- Send HO FAIL to %s\n", gsm_lchan_name(lchan));
rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
rh->c.msg_discr = ABIS_RSL_MDISC_RLL;
@@ -422,7 +742,7 @@ static void send_ho_complete(struct gsm_lchan *lchan, bool success)
gh->msg_type =
success ? GSM48_MT_RR_HANDO_COMPL : GSM48_MT_RR_HANDO_FAIL;
- msg->dst = lchan->ts->trx->bts->c0->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
msg->l2h = (unsigned char *)rh;
msg->l3h = (unsigned char *)gh;
@@ -438,910 +758,797 @@ int __wrap_abis_rsl_sendmsg(struct msgb *msg)
struct e1inp_sign_link *sign_link = msg->dst;
int rc;
struct gsm_lchan *lchan = rsl_lchan_lookup(sign_link->trx, dh->chan_nr, &rc);
+ struct gsm_lchan *other_lchan;
+ struct gsm48_hdr *gh;
if (rc) {
- printf("rsl_lchan_lookup() failed\n");
+ fprintf(stderr, "rsl_lchan_lookup() failed\n");
exit(1);
}
switch (dh->c.msg_type) {
case RSL_MT_CHAN_ACTIV:
- rc = parse_chan_act(lchan, dh->data);
- if (rc == 0)
- got_chan_req = 1;
+ if (new_chan_req) {
+ fprintf(stderr, "Test script is erratic: a channel is requested"
+ " while a previous channel request is still unhandled\n");
+ exit(1);
+ }
+ new_chan_req = lchan;
break;
case RSL_MT_RF_CHAN_REL:
- rc = parse_chan_rel(lchan, dh->data);
- if (rc == 0)
- send_chan_act_ack(chan_req_lchan, 0);
+ send_chan_act_ack(lchan, 0);
+
+ /* send dyn TS back to PDCH if unused */
+ switch (lchan->ts->pchan_on_init) {
+ case GSM_PCHAN_OSMO_DYN:
+ case GSM_PCHAN_TCH_F_PDCH:
+ switch (lchan->ts->pchan_is) {
+ case GSM_PCHAN_TCH_H:
+ other_lchan = &lchan->ts->lchan[
+ (lchan == &lchan->ts->lchan[0])?
+ 1 : 0];
+ if (lchan_state_is(other_lchan, LCHAN_ST_ESTABLISHED))
+ break;
+ /* else fall thru */
+ case GSM_PCHAN_TCH_F:
+ ts_set_pchan_is(lchan->ts, GSM_PCHAN_PDCH);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
break;
case RSL_MT_DATA_REQ:
- rc = parse_ho_command(lchan, msg->l3h, msgb_l3len(msg));
- if (rc == 0)
- got_ho_req = 1;
+ gh = (struct gsm48_hdr*)msg->l3h;
+ switch (gh->msg_type) {
+ case GSM48_MT_RR_HANDO_CMD:
+ if (new_ho_cmd || new_as_cmd) {
+ fprintf(stderr, "Test script is erratic: seen a Handover Command"
+ " while a previous Assignment or Handover Command is still unhandled\n");
+ exit(1);
+ }
+ new_ho_cmd = lchan;
+ break;
+ case GSM48_MT_RR_ASS_CMD:
+ if (new_ho_cmd || new_as_cmd) {
+ fprintf(stderr, "Test script is erratic: seen an Assignment Command"
+ " while a previous Assignment or Handover Command is still unhandled\n");
+ exit(1);
+ }
+ new_as_cmd = lchan;
+ break;
+ }
break;
case RSL_MT_IPAC_CRCX:
break;
case RSL_MT_DEACTIVATE_SACCH:
break;
default:
- printf("unknown rsl message=0x%x\n", dh->c.msg_type);
+ fprintf(stderr, "unknown rsl message=0x%x\n", dh->c.msg_type);
}
return 0;
}
-/* test cases */
+struct gsm_bts *bts_by_num_str(const char *num_str)
+{
+ struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, atoi(num_str));
+ OSMO_ASSERT(bts);
+ return bts;
+}
-static char *test_case_0[] = {
- "2",
+struct gsm_bts_trx *trx_by_num_str(struct gsm_bts *bts, const char *num_str)
+{
+ struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, atoi(num_str));
+ OSMO_ASSERT(trx);
+ return trx;
+}
- "Stay in better cell\n\n"
- "There are many neighbor cells, but only the current cell is the best\n"
- "cell, so no handover is performed\n",
+#define LCHAN_ARGS "lchan <0-255> <0-255> <0-7> <0-7>"
+#define LCHAN_ARGS_DOC "identify an lchan\nBTS nr\nTRX nr\nTimeslot nr\nSubslot nr\n"
- "create-bts", "7",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "30","0",
- "6","0","20","1","21","2","18","3","20","4","23","5","19",
- "expect-no-chan",
- NULL
-};
+static struct gsm_lchan *parse_lchan_args(const char **argv)
+{
+ struct gsm_bts *bts = bts_by_num_str(argv[0]);
+ struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]);
+ struct gsm_bts_trx_ts *ts = &trx->ts[atoi(argv[2])];
+ return &ts->lchan[atoi(argv[3])];
+}
-static char *test_case_1[] = {
- "2",
-
- "Handover to best better cell\n\n"
- "The best neighbor cell is selected\n",
-
- "create-bts", "7",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "10","0",
- "6","0","20","1","21","2","18","3","20","4","23","5","19",
- "expect-chan", "5", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+#define LCHAN_WILDCARD_ARGS "lchan (<0-255>|*) (<0-255>|*) (<0-7>|*) (<0-7>|*)"
+#define LCHAN_WILDCARD_ARGS_DOC "identify an lchan\nBTS nr\nall BTS\nTRX nr\nall BTS\nTimeslot nr\nall TS\nSubslot nr\nall subslots\n"
-static char *test_case_2[] = {
- "2",
-
- "Handover and Assignment must be enabled\n\n"
- "This test will start with disabled assignment and handover. A\n"
- "better neighbor cell (assignment enabled) will not be selected and \n"
- "also no assignment from TCH/H to TCH/F to improve quality. There\n"
- "will be no handover nor assignment. After enabling assignment on the\n"
- "current cell, the MS will assign to TCH/F. After enabling handover\n"
- "in the current cell, but disabling in the neighbor cell, handover\n"
- "will not be performed, until it is enabled in the neighbor cell too.\n",
-
- "create-bts", "2",
- "afs-rxlev-improve", "0", "5",
- "create-ms", "0", "TCH/H", "AMR",
- "as-enable", "0", "0",
- "ho-enable", "0", "0",
- "meas-rep", "0", "0","0", "1","0","30",
- "expect-no-chan",
- "as-enable", "0", "1",
- "meas-rep", "0", "0","0", "1","0","30",
- "expect-chan", "0", "1",
- "ack-chan",
- "expect-ho", "0", "5",
- "ho-complete",
- "ho-enable", "0", "1",
- "ho-enable", "1", "0",
- "meas-rep", "0", "0","0", "1","0","30",
- "expect-no-chan",
- "ho-enable", "1", "1",
- "meas-rep", "0", "0","0", "1","0","30",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+static void parse_lchan_wildcard_args(const char **argv, void (*cb)(struct gsm_lchan*, void*), void *cb_data)
+{
+ const char *bts_str = argv[0];
+ const char *trx_str = argv[1];
+ const char *ts_str = argv[2];
+ const char *ss_str = argv[3];
+ int bts_num = (strcmp(bts_str, "*") == 0)? -1 : atoi(bts_str);
+ int trx_num = (strcmp(trx_str, "*") == 0)? -1 : atoi(trx_str);
+ int ts_num = (strcmp(ts_str, "*") == 0)? -1 : atoi(ts_str);
+ int ss_num = (strcmp(ss_str, "*") == 0)? -1 : atoi(ss_str);
+
+ int bts_i;
+ int trx_i;
+ int ts_i;
+ int ss_i;
+
+ for (bts_i = ((bts_num == -1) ? 0 : bts_num);
+ bts_i < ((bts_num == -1) ? bsc_gsmnet->num_bts : bts_num + 1);
+ bts_i++) {
+ struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, bts_i);
+
+ for (trx_i = ((trx_num == -1) ? 0 : trx_num);
+ trx_i < ((trx_num == -1) ? bts->num_trx : trx_num + 1);
+ trx_i++) {
+ struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_i);
+
+ for (ts_i = ((ts_num == -1) ? 0 : ts_num);
+ ts_i < ((ts_num == -1) ? 8 : ts_num + 1);
+ ts_i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[ts_i];
+
+ for (ss_i = ((ss_num == -1) ? 0 : ss_num);
+ ss_i < ((ss_num == -1) ? pchan_subslots(ts->pchan_is) : ss_num + 1);
+ ss_i++) {
+ cb(&ts->lchan[ss_i], cb_data);
+ }
+ }
+ }
+ }
+}
-static char *test_case_3[] = {
- "2",
-
- "Penalty timer must not run\n\n"
- "The MS will try to handover to a better cell, but this will fail.\n"
- "Even though the cell is still better, handover will not be performed\n"
- "due to penalty timer after handover failure\n",
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-failed",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-no-chan",
- NULL
-};
+static int vty_step = 1;
+
+#define VTY_ECHO() \
+ fprintf(stderr, "\n%d: %s\n", vty_step++, vty->buf)
+
+#define TS_USE " (TCH/F|TCH/H-|TCH/-H|TCH/HH|PDCH" \
+ "|tch/f|tch/h-|tch/-h|tch/hh|pdch" \
+ "|-|*)"
+#define TS_USE_DOC "'TCH/F': one FR call\n" \
+ "'TCH/H-': HR TS with first subslot used as TCH/H, other subslot unused\n" \
+ "'TCH/HH': HR TS with both subslots used as TCH/H\n" \
+ "'TCH/-H': HR TS with only second subslot used as TCH/H\n" \
+ "'PDCH': TS used for PDCH (e.g. unused dynamic TS)\n" \
+ "'tch/f': one FR call\n" \
+ "'tch/h-': HR TS with first subslot used as TCH/H, other subslot unused\n" \
+ "'tch/hh': HR TS with both subslots used as TCH/H\n" \
+ "'tch/-h': HR TS with only second subslot used as TCH/H\n" \
+ "'pdch': TS used for PDCH (e.g. unused dynamic TS)\n" \
+ "'-': TS unused\n" \
+ "'*': TS allowed to be in any state\n"
+
+DEFUN(create_n_bts, create_n_bts_cmd,
+ "create-n-bts <1-255>",
+ "Create a number of BTS with four TCH/F and four TCH/H timeslots\n"
+ "Number of BTS to create\n")
+{
+ int i;
+ int n = atoi(argv[0]);
+ VTY_ECHO();
+ for (i = 0; i < n; i++)
+ _create_bts(1, NULL, 0);
+ return CMD_SUCCESS;
+}
-static char *test_case_4[] = {
- "2",
-
- "TCH/H keeping with HR codec\n\n"
- "The MS is using half rate V1 codec, but the better cell is congested\n"
- "at TCH/H slots. As the congestion is removed, the handover takes\n"
- "place.\n",
-
- "create-bts", "2",
- "set-min-free", "1", "TCH/H", "4",
- "create-ms", "0", "TCH/H", "HR",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-no-chan",
- "set-min-free", "1", "TCH/H", "3",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-chan", "1", "5",
- "ack-chan",
- "expect-ho", "0", "5",
- "ho-complete",
- NULL
-};
+DEFUN(create_bts, create_bts_cmd,
+ "create-bts trx-count <1-255> timeslots .TS_CFG",
+ "Create a new BTS with specific timeslot configuration\n"
+ "Create N TRX in the new BTS\n"
+ "TRX count\n"
+ "Timeslot config\n"
+ "Timeslot types for 8 * trx-count, each being one of CCCH+SDCCH4|SDCCH8|TCH/F|TCH/H|TCH/F_TCH/H_SDCCH8_PDCH|...;"
+ " shorthands: cs+4 = CCCH+SDCCH4; dyn = TCH/F_TCH/H_SDCCH8_PDCH\n")
+{
+ int num_trx = atoi(argv[0]);
+ VTY_ECHO();
+ _create_bts(num_trx, argv + 1, argc - 1);
+ return CMD_SUCCESS;
+}
-static char *test_case_5[] = {
- "2",
-
- "TCH/F keeping with FR codec\n\n"
- "The MS is using full rate V1 codec, but the better cell is congested\n"
- "at TCH/F slots. As the congestion is removed, the handover takes\n"
- "place.\n",
-
- "create-bts", "2",
- "set-min-free", "1", "TCH/F", "4",
- "create-ms", "0", "TCH/F", "FR",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-no-chan",
- "set-min-free", "1", "TCH/F", "3",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+DEFUN(create_ms, create_ms_cmd,
+ "create-ms bts <0-999> (TCH/F|TCH/H) (AMR|HR|EFR)",
+ "Create an MS using the next free matching lchan on a given BTS\n"
+ "BTS index to subscribe on\n"
+ "lchan type to select\n"
+ "codec\n")
+{
+ const char *bts_nr_str = argv[0];
+ const char *tch_type = argv[1];
+ const char *codec = argv[2];
+ struct gsm_lchan *lchan;
+ VTY_ECHO();
+ fprintf(stderr, "- Creating mobile at BTS %s on "
+ "%s with %s codec\n", bts_nr_str, tch_type, codec);
+ lchan = create_lchan(bts_by_num_str(bts_nr_str),
+ !strcmp(tch_type, "TCH/F"), codec);
+ if (!lchan) {
+ fprintf(stderr, "Failed to create lchan!\n");
+ return CMD_WARNING;
+ }
+ fprintf(stderr, " * New MS is at %s\n", gsm_lchan_name(lchan));
+ return CMD_SUCCESS;
+}
-static char *test_case_6[] = {
- "2",
-
- "TCH/F keeping with EFR codec\n\n"
- "The MS is using full rate V2 codec, but the better cell is congested\n"
- "at TCH/F slots. As the congestion is removed, the handover takes\n"
- "place.\n",
-
- "create-bts", "2",
- "set-min-free", "1", "TCH/F", "4",
- "create-ms", "0", "TCH/F", "EFR",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-no-chan",
- "set-min-free", "1", "TCH/F", "3",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
+struct meas_rep_data {
+ int argc;
+ const char **argv;
+ uint8_t bs_power_db;
};
-static char *test_case_7[] = {
- "2",
-
- "TCH/F to TCH/H changing with AMR codec\n\n"
- "The MS is using AMR V3 codec, the better cell is congested at TCH/F\n"
- "slots. The handover is performed to non-congested TCH/H slots.\n",
-
- "create-bts", "2",
- "set-min-free", "1", "TCH/F", "4",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-chan", "1", "5",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+static void _meas_rep_cb(struct gsm_lchan *lc, void *data)
+{
+ struct meas_rep_data *d = data;
+ int argc = d->argc;
+ const char **argv = d->argv;
+ uint8_t rxlev;
+ uint8_t rxqual;
+ uint8_t ta;
+ int i;
+ struct neighbor_meas nm[6] = {};
-static char *test_case_8[] = {
- "2",
-
- "No handover to a cell with no slots available\n\n"
- "If no slot is available, no handover is performed\n",
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "1", "TCH/F", "AMR",
- "create-ms", "1", "TCH/F", "AMR",
- "create-ms", "1", "TCH/F", "AMR",
- "create-ms", "1", "TCH/F", "AMR",
- "create-ms", "1", "TCH/H", "AMR",
- "create-ms", "1", "TCH/H", "AMR",
- "create-ms", "1", "TCH/H", "AMR",
- "create-ms", "1", "TCH/H", "AMR",
- "meas-rep", "0", "0","0", "1","0","30",
- "expect-no-chan",
- NULL
-};
+ if (!lchan_state_is(lc, LCHAN_ST_ESTABLISHED))
+ return;
-static char *test_case_9[] = {
- "2",
-
- "No more parallel handovers, if max_unsync_ho is defined\n\n"
- "There are tree mobiles that want to handover, but only two can do\n"
- "it at a time, because the maximum number is limited to two.\n",
-
- "create-bts", "2",
- "set-max-ho", "1", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "0","0", "1","0","30",
- "expect-chan", "1", "1",
- "meas-rep", "1", "0","0", "1","0","30",
- "expect-chan", "1", "2",
- "meas-rep", "2", "0","0", "1","0","30",
- "expect-no-chan",
- NULL
-};
+ rxlev = atoi(argv[0]);
+ rxqual = atoi(argv[1]);
+ ta = atoi(argv[2]);
+ argv += 3;
+ argc -= 3;
-static char *test_case_10[] = {
- "2",
-
- "Hysteresis\n\n"
- "If neighbor cell is better, handover is only performed if the\n"
- "amount of improvement is greater or equal hyteresis\n",
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "27","0", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "26","0", "1","0","30",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+ if (!lchan_state_is(lc, LCHAN_ST_ESTABLISHED)) {
+ fprintf(stderr, "Error: sending measurement report for %s which is in state %s\n",
+ gsm_lchan_name(lc), lchan_state_name(lc));
+ exit(1);
+ }
-static char *test_case_11[] = {
- "2",
-
- "No Hysteresis and minimum RX level\n\n"
- "If current cell's RX level is below mimium level, handover must be\n"
- "performed, no matter of the hysteresis. First do not perform\n"
- "handover to better neighbor cell, because the hysteresis is not\n"
- "met. Second do not perform handover because better neighbor cell is\n"
- "below minimum RX level. Third perform handover because current cell\n"
- "is below minimum RX level, even if the better neighbor cell (minimum\n"
- "RX level reached) does not meet the hysteresis.\n",
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "10","0", "1","0","11",
- "expect-no-chan",
- "meas-rep", "0", "8","0", "1","0","9",
- "expect-no-chan",
- "meas-rep", "0", "9","0", "1","0","10",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+ /* skip the optional [neighbors] keyword */
+ if (argc) {
+ argv++;
+ argc--;
+ }
-static char *test_case_12[] = {
- "2",
-
- "No handover to congested cell\n\n"
- "The better neighbor cell is congested, so no handover is performed.\n"
- "After the congestion is over, handover will be performed.\n",
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "set-min-free", "1", "TCH/F", "4",
- "set-min-free", "1", "TCH/H", "4",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-no-chan",
- "set-min-free", "1", "TCH/F", "3",
- "set-min-free", "1", "TCH/H", "3",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+ fprintf(stderr, "- Sending measurement report from %s: rxlev=%u rxqual=%u ta=%u (%d neighbors)\n",
+ gsm_lchan_name(lc), rxlev, rxqual, ta, argc);
+
+ for (i = 0; i < 6; i++) {
+ int neighbor_bts_nr = i;
+ /* since our bts is not in the list of neighbor cells, we need to shift */
+ if (neighbor_bts_nr >= lc->ts->trx->bts->nr)
+ neighbor_bts_nr++;
+ nm[i] = (struct neighbor_meas){
+ .rxlev = argc > i ? atoi(argv[i]) : 0,
+ .bsic = 0x3f,
+ .bcch_f = i,
+ };
+ if (i < argc)
+ fprintf(stderr, " * Neighbor cell #%d, actual BTS %d: rxlev=%d\n", i, neighbor_bts_nr,
+ nm[i].rxlev);
+ }
+ gen_meas_rep(lc, d->bs_power_db, rxlev, rxqual, ta, argc, nm);
+}
-static char *test_case_13[] = {
- "2",
-
- "Handover to balance congestion\n\n"
- "The current and the better cell are congested, so no handover is\n"
- "performed. This is because handover would congest the neighbor cell\n"
- "more. After congestion raises in the current cell, the handover is\n"
- "performed to balance congestion\n",
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "set-min-free", "0", "TCH/F", "4",
- "set-min-free", "0", "TCH/H", "4",
- "set-min-free", "1", "TCH/F", "4",
- "set-min-free", "1", "TCH/H", "4",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-no-chan",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+static int _meas_rep(struct vty *vty, uint8_t bs_power_db, int argc, const char **argv)
+{
+ struct meas_rep_data d = {
+ .argc = argc - 4,
+ .argv = argv + 4,
+ .bs_power_db = bs_power_db,
+ };
+ parse_lchan_wildcard_args(argv, _meas_rep_cb, &d);
+ return CMD_SUCCESS;
+}
-static char *test_case_14[] = {
- "2",
-
- "Handover to congested cell, if RX level is below minimum\n\n"
- "The better neighbor cell is congested, so no handover is performed.\n"
- "If the RX level of the current cell drops below minimum acceptable\n"
- "level, the handover is performed.\n",
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "set-min-free", "1", "TCH/F", "4",
- "set-min-free", "1", "TCH/H", "4",
- "meas-rep", "0", "10","0", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "9","0", "1","0","30",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
-static char *test_case_15[] = {
- "2",
-
- "Handover to cell with worse RXLEV, if RXQUAL is below minimum\n\n"
- "The neighbor cell has worse RXLEV, so no handover is performed.\n"
- "If the RXQUAL of the current cell drops below minimum acceptable\n"
- "level, the handover is performed. It is also required that 10\n"
- "reports are received, before RXQUAL is checked.\n",
- /* (See also test 28, which tests for RXQUAL triggering HO to congested cell.) */
- /* TODO: bad RXQUAL may want to prefer assignment within the same cell to avoid interference.
- * See Performance Enhancements in a Frequency Hopping GSM Network (Nielsen Wigard 2002), Chapter
- * 2.1.1, "Interference" in the list of triggers on p.157. */
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-no-chan",
- "meas-rep", "0", "40","6", "1","0","30",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+#define MEAS_REP_ARGS LCHAN_WILDCARD_ARGS " rxlev <0-255> rxqual <0-7> ta <0-255>" \
+ " [neighbors] [<0-255>] [<0-255>] [<0-255>] [<0-255>] [<0-255>] [<0-255>]"
+#define MEAS_REP_DOC "Send measurement report\n"
+#define MEAS_REP_ARGS_DOC \
+ LCHAN_WILDCARD_ARGS_DOC \
+ "rxlev\nrxlev\n" \
+ "rxqual\nrxqual\n" \
+ "timing advance\ntiming advance\n" \
+ "neighbors list of rxlev reported by each neighbor cell\n" \
+ "neighbor 0 rxlev\n" \
+ "neighbor 1 rxlev\n" \
+ "neighbor 2 rxlev\n" \
+ "neighbor 3 rxlev\n" \
+ "neighbor 4 rxlev\n" \
+ "neighbor 5 rxlev\n"
+
+DEFUN(meas_rep, meas_rep_cmd,
+ "meas-rep " MEAS_REP_ARGS,
+ MEAS_REP_DOC MEAS_REP_ARGS_DOC)
+{
+ VTY_ECHO();
+ return _meas_rep(vty, 0, argc, argv);
+}
-static char *test_case_16[] = {
- "2",
-
- "Handover due to maximum TA exceeded\n\n"
- "The MS in the current (best) cell has reached maximum allowed timing\n"
- "advance. No handover is performed until the timing advance exceeds\n"
- "it. The originating cell is still the best, but no handover is\n"
- "performed back to that cell, because the penalty timer (due to\n"
- "maximum allowed timing advance) is running.\n",
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "set-max-ta", "0", "5", /* of cell */
- "set-ta", "0", "5", /* of ms */
- "meas-rep", "0", "30","0", "1","0","20",
- "expect-no-chan",
- "set-ta", "0", "6", /* of ms */
- "meas-rep", "0", "30","0", "1","0","20",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- "meas-rep", "0", "20","0", "1","0","30",
- "expect-no-chan",
- NULL
-};
+DEFUN(meas_rep_repeat, meas_rep_repeat_cmd,
+ "meas-rep repeat <0-999> " MEAS_REP_ARGS,
+ MEAS_REP_DOC
+ "Resend the same measurement report N times\nN\n"
+ MEAS_REP_ARGS_DOC)
+{
+ int count = atoi(argv[0]);
+ VTY_ECHO();
+ argv += 1;
+ argc -= 1;
+
+ while (count--)
+ _meas_rep(vty, 0, argc, argv);
+ return CMD_SUCCESS;
+}
-static char *test_case_17[] = {
- "2",
-
- "Congestion check: No congestion\n\n"
- "Three cells have different number of used slots, but there is no\n"
- "congestion in any of these cells. No handover is performed.\n",
-
- "create-bts", "3",
- "set-min-free", "0", "TCH/F", "2",
- "set-min-free", "0", "TCH/H", "2",
- "set-min-free", "1", "TCH/F", "2",
- "set-min-free", "1", "TCH/H", "2",
- "set-min-free", "2", "TCH/F", "2",
- "set-min-free", "2", "TCH/H", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "create-ms", "1", "TCH/F", "AMR",
- "create-ms", "1", "TCH/H", "AMR",
- "meas-rep", "0", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "1", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "2", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "3", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "4", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "5", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "congestion-check",
- "expect-no-chan",
- NULL
-};
+DEFUN(meas_rep_repeat_bspower, meas_rep_repeat_bspower_cmd,
+ "meas-rep repeat <0-999> bspower <0-31> " MEAS_REP_ARGS,
+ MEAS_REP_DOC
+ "Resend the same measurement report N times\nN\n"
+ "Send a nonzero BS Power value in the measurement report (downlink power reduction)\nBS Power reduction in dB\n"
+ MEAS_REP_ARGS_DOC)
+{
+ int count = atoi(argv[0]);
+ uint8_t bs_power_db = atoi(argv[1]);
+ VTY_ECHO();
+ argv += 2;
+ argc -= 2;
+
+ while (count--)
+ _meas_rep(vty, bs_power_db, argc, argv);
+ return CMD_SUCCESS;
+}
-static char *test_case_18[] = {
- "2",
-
- "Congestion check: One out of three cells is congested\n\n"
- "Three cells have different number of used slots, but there is\n"
- "congestion at TCH/F in the first cell. Handover is performed with\n"
- "the best candidate.\n",
-
- "create-bts", "3",
- "set-min-free", "0", "TCH/F", "2",
- "set-min-free", "0", "TCH/H", "2",
- "set-min-free", "1", "TCH/F", "2",
- "set-min-free", "1", "TCH/H", "2",
- "set-min-free", "2", "TCH/F", "2",
- "set-min-free", "2", "TCH/H", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "create-ms", "1", "TCH/F", "AMR",
- "create-ms", "1", "TCH/H", "AMR",
- "meas-rep", "0", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "1", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "2", "30","0", "2","0","21","1","20",
- "expect-no-chan",
- "meas-rep", "3", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "4", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "5", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "meas-rep", "6", "30","0", "2","0","20","1","20",
- "expect-no-chan",
- "congestion-check",
- "expect-chan", "1", "2",
- "ack-chan",
- "expect-ho", "0", "3", /* best candidate is MS 2 at BTS 1, TS 3 */
- "ho-complete",
- NULL
-};
+DEFUN(res_ind, res_ind_cmd,
+ "res-ind trx <0-255> <0-255> levels .LEVELS",
+ "Send Resource Indication for a specific TRX, indicating interference levels per lchan\n"
+ "Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n"
+ "Indicate interference levels: each level is an index to bts->interf_meas_params.bounds_dbm[],"
+ " i.e. <0-5> or '-' to omit a report for this timeslot/lchan."
+ " Separate timeslots by spaces, for individual subslots directly concatenate values."
+ " If a timeslot has more subslots than provided, the last given value is repeated."
+ " For example: 'res-ind trx 0 0 levels - 1 23 -': on BTS 0 TRX 0, omit ratings for the entire first timeslot,"
+ " send level=1 for timeslot 1, and for timeslot 2 send level=2 for subslot 0 and level=3 for subslot 1.\n")
+{
+ int i;
+ uint8_t level;
+ struct gsm_bts *bts = bts_by_num_str(argv[0]);
+ struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]);
+ struct msgb *msg = msgb_alloc_headroom(256, 64, "RES-IND");
+ struct abis_rsl_common_hdr *rslh;
+ uint8_t *res_info_len;
+ VTY_ECHO();
+
+ /* In this test suite, always act as if the interf_meas_params_cfg were already sent to the BTS via OML */
+ bts->interf_meas_params_used = bts->interf_meas_params_cfg;
+
+ argv += 2;
+ argc -= 2;
+
+ rslh = (struct abis_rsl_common_hdr*)msgb_put(msg, sizeof(*rslh));
+ rslh->msg_discr = ABIS_RSL_MDISC_TRX;
+ rslh->msg_type = RSL_MT_RF_RES_IND;
+ msgb_put_u8(msg, RSL_IE_RESOURCE_INFO);
+ res_info_len = msg->tail;
+ msgb_put_u8(msg, 0);
+
+ level = 0xff;
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ const char *ts_str = NULL;
+ struct gsm_lchan *lchan;
+ size_t given_subslots = 0;
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+
+ if (i < argc) {
+ ts_str = argv[i];
+ given_subslots = strlen(ts_str);
+ }
-static char *test_case_19[] = {
- "2",
-
- "Congestion check: Balancing over congested cells\n\n"
- "Two cells are congested, but the second cell is less congested.\n"
- "Handover is performed to solve the congestion.\n",
-
- "create-bts", "2",
- "set-min-free", "0", "TCH/F", "4",
- "set-min-free", "1", "TCH/F", "4",
- "create-ms", "0", "TCH/F", "FR",
- "create-ms", "0", "TCH/F", "FR",
- "create-ms", "0", "TCH/F", "FR",
- "create-ms", "1", "TCH/F", "FR",
- "meas-rep", "0", "30","0", "1","0","20",
- "expect-no-chan",
- "meas-rep", "1", "30","0", "1","0","21",
- "expect-no-chan",
- "meas-rep", "2", "30","0", "1","0","20",
- "expect-no-chan",
- "meas-rep", "3", "30","0", "1","0","20",
- "expect-no-chan",
- "congestion-check",
- "expect-chan", "1", "2",
- "ack-chan",
- "expect-ho", "0", "2", /* best candidate is MS 1 at BTS 0, TS 2 */
- "ho-complete",
- NULL
-};
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
+ int chan_nr;
+
+ if (lchan->nr < given_subslots && ts_str) {
+ char subslot_val = ts_str[lchan->nr];
+ switch (subslot_val) {
+ case '-':
+ level = INTERF_BAND_UNKNOWN;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ level = subslot_val - '0';
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+ }
-static char *test_case_20[] = {
- "2",
-
- "Congestion check: Solving congestion by handover TCH/F -> TCH/H\n\n"
- "Two BTS, one MS in the first congested BTS must handover to\n"
- "non-congested TCH/H of second BTS, in order to solve congestion\n",
- "create-bts", "2",
- "set-min-free", "0", "TCH/F", "4",
- "set-min-free", "0", "TCH/H", "4",
- "set-min-free", "1", "TCH/F", "4",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "30","0", "1","0","30",
- "expect-no-chan",
- "congestion-check",
- "expect-chan", "1", "5",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+ if (level == INTERF_BAND_UNKNOWN)
+ continue;
-static char *test_case_21[] = {
- "2",
-
- "Congestion check: Balancing congestion by handover TCH/F -> TCH/H\n\n"
- "Two BTS, one MS in the first congested BTS must handover to\n"
- "less-congested TCH/H of second BTS, in order to balance congestion\n",
- "create-bts", "2",
- "set-min-free", "0", "TCH/F", "4",
- "set-min-free", "0", "TCH/H", "4",
- "set-min-free", "1", "TCH/F", "4",
- "set-min-free", "1", "TCH/H", "4",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "meas-rep", "0", "30","0", "1","0","30",
- "expect-no-chan",
- "congestion-check",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+ chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ continue;
-static char *test_case_22[] = {
- "2",
-
- "Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n"
- "There is only one BTS. The TCH/H slots are congested. Since\n"
- "assignment is performed to less-congested TCH/F, the candidate with\n"
- "the worst RX level is chosen.\n",
-
- "create-bts", "1",
- "set-min-free", "0", "TCH/F", "4",
- "set-min-free", "0", "TCH/H", "4",
- "create-ms", "0", "TCH/H", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "meas-rep", "0", "30","0", "0",
- "meas-rep", "1", "34","0", "0",
- "meas-rep", "2", "20","0", "0",
- "expect-no-chan",
- "congestion-check",
- "expect-chan", "0", "1",
- "ack-chan",
- "expect-ho", "0", "6",
- "ho-complete",
- NULL
-};
+ msgb_put_u8(msg, chan_nr);
+ msgb_put_u8(msg, level << 5);
+ }
+ }
-static char *test_case_23[] = {
- "2",
-
- "Story: 'A neighbor is your friend'\n",
-
- "create-bts", "3",
-
- "print",
- "Andreas is driving along the coast, on a sunny june afternoon.\n"
- "Suddenly he is getting a call from his friend and neighbor Axel.\n"
- "\n"
- "What happens: Two MS are created, #0 for Axel, #1 for Andreas.",
- /* Axel */
- "create-ms", "2", "TCH/F", "AMR",
- /* andreas */
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "1", "40","0", "1","0","30",
- "expect-no-chan",
-
- "print",
- "Axel asks Andreas if he would like to join them for a barbecue.\n"
- "Axel's house is right in the neighborhood and the weather is fine.\n"
- "Andreas agrees, so he drives to a close store to buy some barbecue\n"
- "skewers.\n"
- "\n"
- "What happens: While driving, a different cell (mounted atop the\n"
- "store) becomes better.",
- /* drive to bts 1 */
- "meas-rep", "1", "20","0", "1","0","35",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
-
- "print",
- "While Andreas is walking into the store, Axel asks, if he could also\n"
- "bring some beer. Andreas has problems understanding him: \"I have a\n"
- "bad reception here. The cell tower is right atop the store, but poor\n"
- "coverage inside. Can you repeat please?\"\n"
- "\n"
- "What happens: Inside the store the close cell is so bad, that\n"
- "handover back to the previous cell is required.",
- /* bts 1 becomes bad, so bts 0 helps out */
- "meas-rep", "1", "5","0", "1","0","20",
- "expect-chan", "0", "1",
- "ack-chan",
- "expect-ho", "1", "1",
- "ho-complete",
-
- "print",
- "After Andreas bought skewers and beer, he leaves the store.\n"
- "\n"
- "What happens: Outside the store the close cell is better again, so\n"
- "handover back to the that cell is performed.",
- /* bts 1 becomes better again */
- "meas-rep", "1", "20","0", "1","0","35",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
-
- "print",
- /* bts 2 becomes better */
- "Andreas drives down to the lake where Axel's house is.\n"
- "\n"
- "What happens: There is a small cell at Axel's house, which becomes\n"
- "better, because the current cell has no good comverage at the lake.",
- "meas-rep", "1", "14","0", "2","0","2","1","63",
- "expect-chan", "2", "2",
- "ack-chan",
- "expect-ho", "1", "1",
- "ho-complete",
-
- "print",
- "Andreas wonders why he still has good radio coverage: \"Last time it\n"
- "was so bad\". Axel says: \"I installed a pico cell in my house,\n"
- "now we can use our mobile phones down here at the lake.\"",
-
- NULL
-};
+ *res_info_len = msg->tail - res_info_len - 1;
-static char *test_case_24[] = {
- "2",
- "No (or not enough) measurements for handover\n\n"
- "Do not solve congestion in cell, because there is no measurement.\n"
- "As soon as enough measurements available (1 in our case), perform\n"
- "handover. Afterwards the old cell becomes congested and the new\n"
- "cell is not. Do not perform handover until new measurements are\n"
- "received.\n",
-
- /* two cells, first in congested, but no handover */
- "create-bts", "2",
- "set-min-free", "0", "TCH/F", "4",
- "set-min-free", "0", "TCH/H", "4",
- "create-ms", "0", "TCH/F", "AMR",
- "congestion-check",
- "expect-no-chan",
-
- /* send measurement and trigger congestion check */
- "meas-rep", "0", "20","0", "1","0","20",
- "expect-no-chan",
- "congestion-check",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
-
- /* congest the first cell and remove congestion from second cell */
- "set-min-free", "0", "TCH/F", "0",
- "set-min-free", "0", "TCH/H", "0",
- "set-min-free", "1", "TCH/F", "4",
- "set-min-free", "1", "TCH/H", "4",
-
- /* no handover until measurements applied */
- "congestion-check",
- "expect-no-chan",
- "meas-rep", "0", "20","0", "1","0","20",
- "expect-no-chan",
- "congestion-check",
- "expect-chan", "0", "1",
- "ack-chan",
- "expect-ho", "1", "1",
- "ho-complete",
- NULL
-};
+ msg->dst = trx->rsl_link_primary;
+ msg->l2h = msg->data;
+ abis_rsl_rcvmsg(msg);
-static char *test_case_25[] = {
- "1",
+ return CMD_SUCCESS;
+}
- "Stay in better cell\n\n"
- "There are many neighbor cells, but only the current cell is the best\n"
- "cell, so no handover is performed\n",
+DEFUN(congestion_check, congestion_check_cmd,
+ "congestion-check",
+ "Trigger a congestion check\n")
+{
+ VTY_ECHO();
+ fprintf(stderr, "- Triggering congestion check\n");
+ hodec2_congestion_check(bsc_gsmnet);
+ return CMD_SUCCESS;
+}
- "create-bts", "7",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "30","0",
- "6","0","20","1","21","2","18","3","20","4","23","5","19",
- "expect-no-chan",
- NULL
-};
+DEFUN(expect_no_chan, expect_no_chan_cmd,
+ "expect-no-chan",
+ "Expect that no channel request was sent from BSC to any cell\n")
+{
+ VTY_ECHO();
+ fprintf(stderr, "- Expecting no channel request\n");
+ if (new_chan_req) {
+ fprintf(stderr, " * Got channel request at %s\n", gsm_lchan_name(new_chan_req));
+ fprintf(stderr, "Test failed, because channel was requested\n");
+ exit(1);
+ }
+ fprintf(stderr, " * Got no channel request\n");
+ return CMD_SUCCESS;
+}
-static char *test_case_26[] = {
- "1",
-
- "Handover to best better cell\n\n"
- "The best neighbor cell is selected\n",
-
- "create-bts", "7",
- "create-ms", "0", "TCH/F", "AMR",
- "meas-rep", "0", "10","0",
- "6","0","20","1","21","2","18","3","20","4","23","5","19",
- "expect-chan", "5", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+static void _expect_chan_activ(struct gsm_lchan *lchan)
+{
+ fprintf(stderr, "- Expecting channel request at %s\n",
+ gsm_lchan_name(lchan));
+ if (!new_chan_req) {
+ fprintf(stderr, "Test failed, because no channel was requested\n");
+ exit(1);
+ }
+ last_chan_req = new_chan_req;
+ new_chan_req = NULL;
+ fprintf(stderr, " * Got channel request at %s\n", gsm_lchan_name(last_chan_req));
+ if (lchan != last_chan_req) {
+ fprintf(stderr, "Test failed, because channel was requested on a different lchan than expected\n"
+ "expected: %s got: %s\n",
+ gsm_lchan_name(lchan), gsm_lchan_name(last_chan_req));
+ exit(1);
+ }
+ send_chan_act_ack(lchan, 1);
+}
-static char *test_case_27[] = {
- "2",
-
- "Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n"
- "There is only one BTS. The TCH/H slots are congested. Since\n"
- "assignment is performed to less-congested TCH/F, the candidate with\n"
- "the worst RX level is chosen. (So far like test 22.)\n"
- "After that, trigger more congestion checks to ensure stability.\n",
-
- "create-bts", "1",
- "set-min-free", "0", "TCH/F", "2",
- "set-min-free", "0", "TCH/H", "4",
- "create-ms", "0", "TCH/H", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "meas-rep", "0", "30","0", "0",
- "meas-rep", "1", "34","0", "0",
- "meas-rep", "2", "20","0", "0",
- "expect-no-chan",
- "congestion-check",
- "expect-chan", "0", "1",
- "ack-chan",
- "expect-ho", "0", "6",
- "ho-complete",
- "congestion-check",
- "expect-chan", "0", "2",
- "ack-chan",
- "expect-ho", "0", "5",
- "ho-complete",
- "congestion-check",
- "expect-no-chan",
- "congestion-check",
- "expect-no-chan",
- NULL
-};
+static void _expect_ho_cmd(struct gsm_lchan *lchan)
+{
+ fprintf(stderr, "- Expecting Handover Command at %s\n",
+ gsm_lchan_name(lchan));
-static char *test_case_28[] = {
- "2",
-
- "Handover to congested cell, if RX quality is below minimum\n\n"
- "The better neighbor cell is congested, so no handover is performed.\n"
- "If the RX quality of the current cell drops below minimum acceptable\n"
- "level, the handover is performed. It is also required that 10\n"
- "resports are received, before RX quality is checked.\n",
-
- "create-bts", "2",
- "create-ms", "0", "TCH/F", "AMR",
- "set-min-free", "1", "TCH/F", "4",
- "set-min-free", "1", "TCH/H", "4",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-no-chan",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-no-chan",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-no-chan",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-no-chan",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-no-chan",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-no-chan",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-no-chan",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-no-chan",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-no-chan",
- "meas-rep", "0", "30","6", "1","0","40",
- "expect-chan", "1", "1",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+ if (!new_ho_cmd) {
+ fprintf(stderr, "Test failed, no Handover Command\n");
+ exit(1);
+ }
+ fprintf(stderr, " * Got Handover Command at %s\n", gsm_lchan_name(new_ho_cmd));
+ if (new_ho_cmd != lchan) {
+ fprintf(stderr, "Test failed, Handover Command not on the expected lchan\n");
+ exit(1);
+ }
+ last_ho_cmd = new_ho_cmd;
+ new_ho_cmd = NULL;
+}
-static char *test_case_29[] = {
- "2",
-
- "Congestion check: Balancing congestion by handover TCH/F -> TCH/H\n\n"
- "One BTS, and TCH/F are considered congested, TCH/H are not.\n"
- ,
- "create-bts", "1",
- "set-min-free", "0", "TCH/F", "3",
- "set-min-free", "0", "TCH/H", "0",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/F", "AMR",
- "create-ms", "0", "TCH/H", "AMR",
- "meas-rep", "0", "30","0", "1","0","30",
- "expect-no-chan",
- "congestion-check",
- "expect-chan", "0", "5",
- "ack-chan",
- "expect-ho", "0", "1",
- "ho-complete",
- NULL
-};
+static void _expect_as_cmd(struct gsm_lchan *lchan)
+{
+ fprintf(stderr, "- Expecting Assignment Command at %s\n",
+ gsm_lchan_name(lchan));
+
+ if (!new_as_cmd) {
+ fprintf(stderr, "Test failed, no Assignment Command\n");
+ exit(1);
+ }
+ fprintf(stderr, " * Got Assignment Command at %s\n", gsm_lchan_name(new_as_cmd));
+ if (new_as_cmd != lchan) {
+ fprintf(stderr, "Test failed, Assignment Command not on the expected lchan\n");
+ exit(1);
+ }
+ last_as_cmd = new_as_cmd;
+ new_as_cmd = NULL;
+}
+DEFUN(expect_chan, expect_chan_cmd,
+ "expect-chan " LCHAN_ARGS,
+ "Expect RSL Channel Activation of a specific lchan\n"
+ LCHAN_ARGS_DOC)
+{
+ VTY_ECHO();
+ _expect_chan_activ(parse_lchan_args(argv));
+ return CMD_SUCCESS;
+}
-static char **test_cases[] = {
- test_case_0,
- test_case_1,
- test_case_2,
- test_case_3,
- test_case_4,
- test_case_5,
- test_case_6,
- test_case_7,
- test_case_8,
- test_case_9,
- test_case_10,
- test_case_11,
- test_case_12,
- test_case_13,
- test_case_14,
- test_case_15,
- test_case_16,
- test_case_17,
- test_case_18,
- test_case_19,
- test_case_20,
- test_case_21,
- test_case_22,
- test_case_23,
- test_case_24,
- test_case_25,
- test_case_26,
- test_case_27,
- test_case_28,
- test_case_29,
-};
+DEFUN(expect_handover_command, expect_handover_command_cmd,
+ "expect-ho-cmd " LCHAN_ARGS,
+ "Expect an RR Handover Command sent to a specific lchan\n"
+ LCHAN_ARGS_DOC)
+{
+ VTY_ECHO();
+ _expect_ho_cmd(parse_lchan_args(argv));
+ return CMD_SUCCESS;
+}
+
+DEFUN(expect_assignment_command, expect_assignment_command_cmd,
+ "expect-as-cmd " LCHAN_ARGS,
+ "Expect Assignment Command for a given lchan\n"
+ LCHAN_ARGS_DOC)
+{
+ VTY_ECHO();
+ _expect_as_cmd(parse_lchan_args(argv));
+ return CMD_SUCCESS;
+}
+
+DEFUN(ho_detection, ho_detection_cmd,
+ "ho-detect",
+ "Send Handover Detection to the most recent HO target lchan\n")
+{
+ VTY_ECHO();
+ if (!last_chan_req) {
+ fprintf(stderr, "Cannot ack handover/assignment, because no chan request\n");
+ exit(1);
+ }
+ send_ho_detect(last_chan_req);
+ return CMD_SUCCESS;
+}
+
+DEFUN(ho_complete, ho_complete_cmd,
+ "ho-complete",
+ "Send Handover Complete for the most recent HO target lchan\n")
+{
+ VTY_ECHO();
+ if (!last_chan_req) {
+ fprintf(stderr, "Cannot ack handover/assignment, because no chan request\n");
+ exit(1);
+ }
+ if (!last_ho_cmd) {
+ fprintf(stderr, "Cannot ack handover/assignment, because no ho request\n");
+ exit(1);
+ }
+ send_ho_complete(last_chan_req, true);
+ lchan_release_ack(last_ho_cmd);
+ return CMD_SUCCESS;
+}
+
+DEFUN(expect_ho, expect_ho_cmd,
+ "expect-ho from " LCHAN_ARGS " to " LCHAN_ARGS,
+ "Expect a handover of a specific lchan to a specific target lchan;"
+ " shorthand for expect-chan, ack-chan, expect-ho, ho-complete.\n"
+ "lchan to handover from\n" LCHAN_ARGS_DOC
+ "lchan to handover to\n" LCHAN_ARGS_DOC)
+{
+ struct gsm_lchan *from = parse_lchan_args(argv);
+ struct gsm_lchan *to = parse_lchan_args(argv+4);
+ VTY_ECHO();
+
+ _expect_chan_activ(to);
+ _expect_ho_cmd(from);
+ send_ho_detect(to);
+ send_ho_complete(to, true);
+
+ lchan_release_ack(from);
+ return CMD_SUCCESS;
+}
+
+DEFUN(expect_as, expect_as_cmd,
+ "expect-as from " LCHAN_ARGS " to " LCHAN_ARGS,
+ "Expect an intra-cell re-assignment of a specific lchan to a specific target lchan;"
+ " shorthand for expect-chan, ack-chan, expect-as, TODO.\n"
+ "lchan to be re-assigned elsewhere\n" LCHAN_ARGS_DOC
+ "new lchan to re-assign to\n" LCHAN_ARGS_DOC)
+{
+ struct gsm_lchan *from = parse_lchan_args(argv);
+ struct gsm_lchan *to = parse_lchan_args(argv+4);
+ VTY_ECHO();
+
+ _expect_chan_activ(to);
+ if (from->ts->trx->bts != to->ts->trx->bts) {
+ vty_out(vty, "%% Error: re-assignment only works within the same BTS%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ _expect_as_cmd(from);
+ send_assignment_complete(to);
+ send_est_ind(to);
+
+ lchan_release_ack(from);
+ return CMD_SUCCESS;
+}
+
+DEFUN(ho_failed, ho_failed_cmd,
+ "ho-failed",
+ "Fail the most recent handover request\n")
+{
+ VTY_ECHO();
+ if (!last_chan_req) {
+ fprintf(stderr, "Cannot fail handover, because no chan request\n");
+ exit(1);
+ }
+ if (!last_ho_cmd) {
+ fprintf(stderr, "Cannot fail handover, because no handover request\n");
+ exit(1);
+ }
+ send_ho_complete(last_ho_cmd, false);
+ lchan_release_ack(last_chan_req);
+ return CMD_SUCCESS;
+}
+
+DEFUN(expect_ts_use, expect_ts_use_cmd,
+ "expect-ts-use trx <0-255> <0-255> states" TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE,
+ "Expect timeslots of a BTS' TRX to be in a specific state\n"
+ "Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n"
+ "List of 8 expected TS states\n"
+ TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC)
+{
+ struct gsm_bts *bts = bts_by_num_str(argv[0]);
+ struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]);
+ VTY_ECHO();
+ argv += 2;
+ argc -= 2;
+ if (!_expect_ts_use(bts, trx, argv))
+ exit(1);
+ return CMD_SUCCESS;
+}
+
+DEFUN(codec_f, codec_f_cmd,
+ "codec tch/f (AMR|EFR|FR)",
+ "Define which codec should be used for new TCH/F lchans (for set-ts-use)\n"
+ "Configure the TCH/F codec to use\nAMR\nEFR\nFR\n")
+{
+ VTY_ECHO();
+ osmo_talloc_replace_string(ctx, &codec_tch_f, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(codec_h, codec_h_cmd,
+ "codec tch/h (AMR|HR)",
+ "Define which codec should be used for new TCH/H lchans (for set-ts-use)\n"
+ "Configure the TCH/H codec to use\nAMR\nHR\n")
+{
+ VTY_ECHO();
+ osmo_talloc_replace_string(ctx, &codec_tch_h, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(set_arfcn, set_arfcn_cmd,
+ "set-arfcn trx <0-255> <0-255> <0-1023>",
+ "Set the ARFCN for a BTS' TRX\n"
+ "Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n"
+ "Absolute Radio Frequency Channel Number\n")
+{
+ enum gsm_band unused;
+ struct gsm_bts *bts = bts_by_num_str(argv[0]);
+ struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]);
+ int arfcn = atoi(argv[2]);
+ VTY_ECHO();
+
+ if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ trx->arfcn = arfcn;
+
+ if (generate_cell_chan_alloc(trx->bts) != 0) {
+ vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ for (int i = 0; i < bsc_gsmnet->num_bts; i++) {
+ if (gsm_generate_si(gsm_bts_num(bsc_gsmnet, i), SYSINFO_TYPE_2) <= 0)
+ fprintf(stderr, "Error generating SI2\n");
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(set_band, set_band_cmd,
+ "set-band bts <0-255> BAND",
+ "Set the frequency band for a BTS\n"
+ "Indicate a BTS\n" "BTS nr\n"
+ "Frequency band\n")
+{
+ struct gsm_bts *bts = bts_by_num_str(argv[0]);
+ int band = gsm_band_parse(argv[1]);
+ VTY_ECHO();
+
+ if (band < 0) {
+ vty_out(vty, "%% BAND %d is not a valid GSM band%s",
+ band, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->band = band;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(set_ts_use, set_ts_use_cmd,
+ "set-ts-use trx <0-255> <0-255> states" TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE,
+ "Put timeslots of a BTS' TRX into a specific state\n"
+ "Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n"
+ "List of 8 TS states to apply\n"
+ TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC)
+{
+ struct gsm_bts *bts = bts_by_num_str(argv[0]);
+ struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]);
+ VTY_ECHO();
+ argv += 2;
+ argc -= 2;
+ if (!_set_ts_use(bts, trx, argv))
+ exit(1);
+ if (!_expect_ts_use(bts, trx, argv))
+ exit(1);
+ return CMD_SUCCESS;
+}
+
+DEFUN(wait, wait_cmd,
+ "wait <0-999999> [<0-999>]",
+ "Let some fake time pass. The test continues instantaneously, but this overrides osmo_gettimeofday() to let"
+ " given amount of time pass virtually.\n"
+ "Seconds to fake-wait\n"
+ "Microseconds to fake-wait, in addition to the seconds waited\n")
+{
+ time_t seconds = atoi(argv[0]);
+ suseconds_t useconds = 0;
+ VTY_ECHO();
+ if (argc > 1)
+ useconds = atoi(argv[1]) * 1000;
+ fake_time_passes(seconds, useconds);
+ return CMD_SUCCESS;
+}
+
+static void ho_test_vty_init()
+{
+ install_element(CONFIG_NODE, &create_n_bts_cmd);
+ install_element(CONFIG_NODE, &create_bts_cmd);
+ install_element(CONFIG_NODE, &create_ms_cmd);
+ install_element(CONFIG_NODE, &meas_rep_cmd);
+ install_element(CONFIG_NODE, &meas_rep_repeat_cmd);
+ install_element(CONFIG_NODE, &meas_rep_repeat_bspower_cmd);
+ install_element(CONFIG_NODE, &res_ind_cmd);
+ install_element(CONFIG_NODE, &congestion_check_cmd);
+ install_element(CONFIG_NODE, &expect_no_chan_cmd);
+ install_element(CONFIG_NODE, &expect_chan_cmd);
+ install_element(CONFIG_NODE, &expect_handover_command_cmd);
+ install_element(CONFIG_NODE, &expect_assignment_command_cmd);
+ install_element(CONFIG_NODE, &ho_detection_cmd);
+ install_element(CONFIG_NODE, &ho_complete_cmd);
+ install_element(CONFIG_NODE, &expect_ho_cmd);
+ install_element(CONFIG_NODE, &expect_as_cmd);
+ install_element(CONFIG_NODE, &ho_failed_cmd);
+ install_element(CONFIG_NODE, &expect_ts_use_cmd);
+ install_element(CONFIG_NODE, &codec_f_cmd);
+ install_element(CONFIG_NODE, &codec_h_cmd);
+ install_element(CONFIG_NODE, &set_arfcn_cmd);
+ install_element(CONFIG_NODE, &set_band_cmd);
+ install_element(CONFIG_NODE, &set_ts_use_cmd);
+ install_element(CONFIG_NODE, &wait_cmd);
+}
static const struct log_info_cat log_categories[] = {
[DHO] = {
@@ -1414,48 +1621,71 @@ const struct log_info log_info = {
.num_cat = ARRAY_SIZE(log_categories),
};
+static struct vty_app_info vty_info = {
+ .name = "ho_test",
+ .copyright =
+ "Copyright (C) 2020 sysmocom - s.f.m.c. GmbH\r\n"
+ "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
+ "This is free software: you are free to change and redistribute it.\r\n"
+ "There is NO WARRANTY, to the extent permitted by law.\r\n",
+ .version = PACKAGE_VERSION,
+ .usr_attr_desc = {
+ [BSC_VTY_ATTR_RESTART_ABIS_OML_LINK] = \
+ "This command applies on A-bis OML link (re)establishment",
+ [BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK] = \
+ "This command applies on A-bis RSL link (re)establishment",
+ [BSC_VTY_ATTR_NEW_LCHAN] = \
+ "This command applies for newly created lchans",
+ },
+ .usr_attr_letters = {
+ [BSC_VTY_ATTR_RESTART_ABIS_OML_LINK] = 'o',
+ [BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK] = 'r',
+ [BSC_VTY_ATTR_NEW_LCHAN] = 'l',
+ },
+};
+
int main(int argc, char **argv)
{
- char **test_case;
- struct gsm_bts *bts[256];
- int bts_num = 0;
- struct gsm_lchan *lchan[256];
- int lchan_num = 0;
- int i;
- int algorithm;
- int test_case_i;
- int last_test_i;
+ char *test_file = NULL;
+ int rc;
+
+ if (argc < 2) {
+ fprintf(stderr, "Pass a handover test script as argument\n");
+ exit(1);
+ }
+ test_file = argv[1];
ctx = talloc_named_const(NULL, 0, "handover_test");
msgb_talloc_ctx_init(ctx, 0);
-
- test_case_i = argc > 1? atoi(argv[1]) : -1;
- last_test_i = ARRAY_SIZE(test_cases) - 1;
-
- if (test_case_i < 0 || test_case_i > last_test_i) {
- for (i = 0; i <= last_test_i; i++) {
- printf("Test #%d (algorithm %s):\n%s\n", i,
- test_cases[i][0], test_cases[i][1]);
- }
- printf("\nPlease specify test case number 0..%d\n", last_test_i);
- return EXIT_FAILURE;
- }
+ vty_info.tall_ctx = ctx;
osmo_init_logging2(ctx, &log_info);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
+ log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);
- log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
+ log_set_print_level(osmo_stderr_target, 1);
+ log_set_print_timestamp(osmo_stderr_target, 0);
osmo_fsm_log_addr(false);
+ /* the 'wait' command above, intended to test penalty timers, adds seconds to the monotonic clock in "fake
+ * time". */
+ fake_time_start();
+
bsc_network_alloc();
if (!bsc_gsmnet)
exit(1);
- ts_fsm_init();
- lchan_fsm_init();
- bsc_subscr_conn_fsm_init();
- handover_fsm_init();
+ /* The MGCP client which is handling the pool (mgcp_client_pool_vty_init) is used from the bsc_vty_init, so
+ * we must allocate an empty mgw pool even though we do not need it for this test. */
+ bsc_gsmnet->mgw.mgw_pool = mgcp_client_pool_alloc(bsc_gsmnet);
+ if (!bsc_gsmnet->mgw.mgw_pool)
+ exit(1);
+
+ vty_init(&vty_info);
+ bsc_vty_init(bsc_gsmnet);
+ ho_test_vty_init();
ho_set_algorithm(bsc_gsmnet->ho, 2);
ho_set_ho_active(bsc_gsmnet->ho, true);
@@ -1478,309 +1708,24 @@ int main(int argc, char **argv)
/* We don't really need any specific model here */
bts_model_unknown_init();
- test_case = test_cases[test_case_i];
-
- fprintf(stderr, "--------------------\n");
- fprintf(stderr, "Performing the following test %d (algorithm %s):\n%s",
- test_case_i, test_case[0], test_case[1]);
- algorithm = atoi(test_case[0]);
- test_case += 2;
- fprintf(stderr, "--------------------\n");
-
/* Disable the congestion check timer, we will trigger manually. */
bsc_gsmnet->hodec2.congestion_check_interval_s = 0;
handover_decision_1_init();
hodec2_init(bsc_gsmnet);
- while (*test_case) {
- if (!strcmp(*test_case, "create-bts")) {
- static int arfcn = 870;
- int n = atoi(test_case[1]);
- fprintf(stderr, "- Creating %d BTS (one TRX each, "
- "TS(1-4) are TCH/F, TS(5-6) are TCH/H)\n", n);
- for (i = 0; i < n; i++)
- bts[bts_num + i] = create_bts(arfcn++);
- for (i = 0; i < n; i++) {
- if (gsm_generate_si(bts[bts_num + i], SYSINFO_TYPE_2) <= 0)
- fprintf(stderr, "Error generating SI2\n");
- }
- bts_num += n;
- test_case += 2;
- } else
- if (!strcmp(*test_case, "as-enable")) {
- fprintf(stderr, "- Set assignment enable state at "
- "BTS %s to %s\n", test_case[1], test_case[2]);
- ho_set_hodec2_as_active(bts[atoi(test_case[1])]->ho, atoi(test_case[2]));
- test_case += 3;
- } else
- if (!strcmp(*test_case, "ho-enable")) {
- fprintf(stderr, "- Set handover enable state at "
- "BTS %s to %s\n", test_case[1], test_case[2]);
- ho_set_ho_active(bts[atoi(test_case[1])]->ho, atoi(test_case[2]));
- test_case += 3;
- } else
- if (!strcmp(*test_case, "afs-rxlev-improve")) {
- fprintf(stderr, "- Set afs RX level improvement at "
- "BTS %s to %s\n", test_case[1], test_case[2]);
- ho_set_hodec2_afs_bias_rxlev(bts[atoi(test_case[1])]->ho, atoi(test_case[2]));
- test_case += 3;
- } else
- if (!strcmp(*test_case, "afs-rxqual-improve")) {
- fprintf(stderr, "- Set afs RX quality improvement at "
- "BTS %s to %s\n", test_case[1], test_case[2]);
- ho_set_hodec2_afs_bias_rxqual(bts[atoi(test_case[1])]->ho, atoi(test_case[2]));
- test_case += 3;
- } else
- if (!strcmp(*test_case, "set-min-free")) {
- fprintf(stderr, "- Setting minimum required free %s "
- "slots at BTS %s to %s\n", test_case[2],
- test_case[1], test_case[3]);
- if (!strcmp(test_case[2], "TCH/F"))
- ho_set_hodec2_tchf_min_slots(bts[atoi(test_case[1])]->ho, atoi(test_case[3]));
- else
- ho_set_hodec2_tchh_min_slots(bts[atoi(test_case[1])]->ho, atoi(test_case[3]));
- test_case += 4;
- } else
- if (!strcmp(*test_case, "set-max-ho")) {
- fprintf(stderr, "- Setting maximum parallel handovers "
- "at BTS %s to %s\n", test_case[1],
- test_case[2]);
- ho_set_hodec2_ho_max( bts[atoi(test_case[1])]->ho, atoi(test_case[2]));
- test_case += 3;
- } else
- if (!strcmp(*test_case, "set-max-ta")) {
- fprintf(stderr, "- Setting maximum timing advance "
- "at BTS %s to %s\n", test_case[1],
- test_case[2]);
- ho_set_hodec2_max_distance(bts[atoi(test_case[1])]->ho, atoi(test_case[2]));
- test_case += 3;
- } else
- if (!strcmp(*test_case, "create-ms")) {
- fprintf(stderr, "- Creating mobile #%d at BTS %s on "
- "%s with %s codec\n", lchan_num, test_case[1],
- test_case[2], test_case[3]);
- lchan[lchan_num] = create_lchan(bts[atoi(test_case[1])],
- !strcmp(test_case[2], "TCH/F"), test_case[3]);
- if (!lchan[lchan_num]) {
- printf("Failed to create lchan!\n");
- return EXIT_FAILURE;
- }
- fprintf(stderr, " * New MS is at BTS %d TS %d\n",
- lchan[lchan_num]->ts->trx->bts->nr,
- lchan[lchan_num]->ts->nr);
- lchan_num++;
- test_case += 4;
- } else
- if (!strcmp(*test_case, "set-ta")) {
- fprintf(stderr, "- Setting maximum timing advance "
- "at MS %s to %s\n", test_case[1],
- test_case[2]);
- meas_ta_ms = atoi(test_case[2]);
- test_case += 3;
- } else
- if (!strcmp(*test_case, "meas-rep")) {
- /* meas-rep <lchan-nr> <rxlev> <rxqual> <nr-of-neighbors> [<cell-idx> <rxlev> [...]] */
- int n = atoi(test_case[4]);
- struct gsm_lchan *lc = lchan[atoi(test_case[1])];
- fprintf(stderr, "- Sending measurement report from "
- "mobile #%s (rxlev=%s, rxqual=%s)\n",
- test_case[1], test_case[2], test_case[3]);
- meas_dl_rxlev = atoi(test_case[2]);
- meas_dl_rxqual = atoi(test_case[3]);
- meas_num_nc = n;
- test_case += 5;
- for (i = 0; i < n; i++) {
- int nr = atoi(test_case[0]);
- /* since our bts is not in the list of neighbor
- * cells, we need to shift */
- if (nr >= lc->ts->trx->bts->nr)
- nr++;
- fprintf(stderr, " * Neighbor cell #%s, actual "
- "BTS %d (rxlev=%s)\n", test_case[0], nr,
- test_case[1]);
- meas_bcch_f_nc[i] = atoi(test_case[0]);
- /* bts number, not counting our own */
- meas_rxlev_nc[i] = atoi(test_case[1]);
- meas_bsic_nc[i] = 0x3f;
- test_case += 2;
- }
- got_chan_req = 0;
- gen_meas_rep(lc);
- } else
- if (!strcmp(*test_case, "congestion-check")) {
- fprintf(stderr, "- Triggering congestion check\n");
- got_chan_req = 0;
- if (algorithm == 2)
- hodec2_congestion_check(bsc_gsmnet);
- test_case += 1;
- } else
- if (!strcmp(*test_case, "expect-chan")) {
- fprintf(stderr, "- Expecting channel request at BTS %s "
- "TS %s\n", test_case[1], test_case[2]);
- if (!got_chan_req) {
- printf("Test failed, because no channel was "
- "requested\n");
- return EXIT_FAILURE;
- }
- fprintf(stderr, " * Got channel request at BTS %d "
- "TS %d\n", chan_req_lchan->ts->trx->bts->nr,
- chan_req_lchan->ts->nr);
- if (chan_req_lchan->ts->trx->bts->nr
- != atoi(test_case[1])) {
- printf("Test failed, because channel was not "
- "requested on expected BTS\n");
- return EXIT_FAILURE;
- }
- if (chan_req_lchan->ts->nr != atoi(test_case[2])) {
- printf("Test failed, because channel was not "
- "requested on expected TS\n");
- return EXIT_FAILURE;
- }
- test_case += 3;
- } else
- if (!strcmp(*test_case, "expect-no-chan")) {
- fprintf(stderr, "- Expecting no channel request\n");
- if (got_chan_req) {
- fprintf(stderr, " * Got channel request at "
- "BTS %d TS %d\n",
- chan_req_lchan->ts->trx->bts->nr,
- chan_req_lchan->ts->nr);
- printf("Test failed, because channel was "
- "requested\n");
- return EXIT_FAILURE;
- }
- fprintf(stderr, " * Got no channel request\n");
- test_case += 1;
- } else
- if (!strcmp(*test_case, "expect-ho")) {
- fprintf(stderr, "- Expecting handover/assignment "
- "request at BTS %s TS %s\n", test_case[1],
- test_case[2]);
- if (!got_ho_req) {
- printf("Test failed, because no handover was "
- "requested\n");
- return EXIT_FAILURE;
- }
- fprintf(stderr, " * Got handover/assignment request at "
- "BTS %d TS %d\n",
- ho_req_lchan->ts->trx->bts->nr,
- ho_req_lchan->ts->nr);
- if (ho_req_lchan->ts->trx->bts->nr
- != atoi(test_case[1])) {
- printf("Test failed, because "
- "handover/assignment was not commanded "
- "at the expected BTS\n");
- return EXIT_FAILURE;
- }
- if (ho_req_lchan->ts->nr != atoi(test_case[2])) {
- printf("Test failed, because "
- "handover/assignment was not commanded "
- "at the expected TS\n");
- return EXIT_FAILURE;
- }
- test_case += 3;
- } else
- if (!strcmp(*test_case, "ack-chan")) {
- fprintf(stderr, "- Acknowledging channel request\n");
- if (!got_chan_req) {
- printf("Cannot ack channel, because no "
- "request\n");
- return EXIT_FAILURE;
- }
- test_case += 1;
- got_ho_req = 0;
- send_chan_act_ack(chan_req_lchan, 1);
- } else
- if (!strcmp(*test_case, "ho-complete")) {
- fprintf(stderr, "- Acknowledging handover/assignment "
- "request\n");
- if (!got_chan_req) {
- printf("Cannot ack handover/assignment, "
- "because no chan request\n");
- return EXIT_FAILURE;
- }
- if (!got_ho_req) {
- printf("Cannot ack handover/assignment, "
- "because no ho request\n");
- return EXIT_FAILURE;
- }
- test_case += 1;
- got_chan_req = 0;
- got_ho_req = 0;
- /* switch lchan */
- for (i = 0; i < lchan_num; i++) {
- if (lchan[i] == ho_req_lchan) {
- fprintf(stderr, " * MS %d changes from "
- "BTS=%d TS=%d to BTS=%d "
- "TS=%d\n", i,
- lchan[i]->ts->trx->bts->nr,
- lchan[i]->ts->nr,
- chan_req_lchan->ts->trx->bts->nr,
- chan_req_lchan->ts->nr);
- lchan[i] = chan_req_lchan;
- }
- }
- send_ho_complete(chan_req_lchan, true);
- } else
- if (!strcmp(*test_case, "ho-failed")) {
- fprintf(stderr, "- Making handover fail\n");
- if (!got_chan_req) {
- printf("Cannot fail handover, because no chan "
- "request\n");
- return EXIT_FAILURE;
- }
- test_case += 1;
- got_chan_req = 0;
- got_ho_req = 0;
- send_ho_complete(ho_req_lchan, false);
- } else
- if (!strcmp(*test_case, "print")) {
- fprintf(stderr, "\n%s\n\n", test_case[1]);
- test_case += 2;
- } else {
- printf("Unknown test command '%s', please fix!\n",
- *test_case);
- return EXIT_FAILURE;
- }
-
- {
- /* Help the lchan out of releasing states */
- struct gsm_bts *bts;
- llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
- struct gsm_bts_trx *trx;
- llist_for_each_entry(trx, &bts->trx_list, list) {
- int ts_nr;
- for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
- struct gsm_lchan *lchan;
- ts_for_each_lchan(lchan, &trx->ts[ts_nr]) {
-
- if (lchan->fi && lchan->fi->state == LCHAN_ST_WAIT_BEFORE_RF_RELEASE) {
- osmo_fsm_inst_state_chg(lchan->fi, LCHAN_ST_WAIT_RF_RELEASE_ACK, 0, 0);
- osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RSL_RF_CHAN_REL_ACK, 0);
- }
- }
- }
- }
- }
- }
- }
-
- for (i = 0; i < lchan_num; i++) {
- struct gsm_subscriber_connection *conn = lchan[i]->conn;
- lchan[i]->conn = NULL;
- conn->lchan = NULL;
- osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REGULAR, NULL);
+ rc = vty_read_config_file(test_file, NULL);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to parse the test file: '%s'\n", test_file);
}
- fprintf(stderr, "--------------------\n");
-
- printf("Test OK\n");
-
- fprintf(stderr, "--------------------\n");
-
talloc_free(ctx);
- return EXIT_SUCCESS;
+ fprintf(stderr,"-------------------\n");
+ if (!rc)
+ fprintf(stderr, "pass\n");
+ else
+ fprintf(stderr, "FAIL\n");
+ return rc;
}
void rtp_socket_free() {}
@@ -1793,25 +1738,35 @@ void trau_mux_unmap() {}
void trau_mux_map_lchan() {}
void trau_recv_lchan() {}
void trau_send_frame() {}
-int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; }
+/* Stub */
int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; }
void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, uint8_t dlci, enum gsm0808_cause cause) {}
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) {}
+void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_a5_n) {}
int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel)
{ return 0; }
-int bsc_paging_start(struct bsc_paging_params *params)
-{ return 0; }
void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) {}
void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause) {}
void bsc_cm_update(struct gsm_subscriber_connection *conn,
const uint8_t *cm2, uint8_t cm2_len,
const uint8_t *cm3, uint8_t cm3_len) {}
-struct gsm0808_handover_required;
-int bsc_tx_bssmap_ho_required(struct gsm_lchan *lchan, const struct gsm0808_cell_id_list2 *target_cells)
-{ return 0; }
-int bsc_tx_bssmap_ho_request_ack(struct gsm_subscriber_connection *conn, struct msgb *rr_ho_command)
-{ return 0; }
-int bsc_tx_bssmap_ho_detect(struct gsm_subscriber_connection *conn) { return 0; }
-enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection *conn,
- struct gsm_lchan *lchan) { return HO_RESULT_OK; }
-void bsc_tx_bssmap_ho_failure(struct gsm_subscriber_connection *conn) {}
+const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
+{
+ return "fake-ep";
+}
+const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci)
+{
+ return "fake-ci";
+}
+const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
+{
+ static struct mgcp_conn_peer ret = {
+ .addr = "1.2.3.4",
+ .port = 1234,
+ .endpoint = "fake-endpoint",
+ };
+ return &ret;
+}
+struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep)
+{
+ return NULL;
+}
diff --git a/tests/handover/handover_test.ok b/tests/handover/handover_test.ok
deleted file mode 100644
index 678f9a34e..000000000
--- a/tests/handover/handover_test.ok
+++ /dev/null
@@ -1 +0,0 @@
-Test OK
diff --git a/tests/handover/handover_tests.ok b/tests/handover/handover_tests.ok
new file mode 100644
index 000000000..04241811c
--- /dev/null
+++ b/tests/handover/handover_tests.ok
@@ -0,0 +1,57 @@
+pass test_amr_tch_f_to_h.ho_vty
+pass test_amr_tch_f_to_h_balance_congestion.ho_vty
+pass test_amr_tch_f_to_h_congestion.ho_vty
+pass test_amr_tch_f_to_h_congestion_assignment.ho_vty
+pass test_amr_tch_f_to_h_congestion_assignment_2.ho_vty
+pass test_amr_tch_f_to_h_congestion_assignment_3.ho_vty
+pass test_amr_tch_h_and_afs_bias.ho_vty
+pass test_amr_tch_h_to_f_congestion.ho_vty
+pass test_amr_tch_h_to_f_congestion_two_cells.ho_vty
+pass test_amr_tch_h_to_f_rxlev.ho_vty
+pass test_amr_tch_h_to_f_rxlev_congested.ho_vty
+pass test_amr_tch_h_to_f_rxlev_oscillation.ho_vty
+pass test_amr_tch_h_to_f_rxqual.ho_vty
+pass test_amr_tch_h_to_f_rxqual_congested.ho_vty
+pass test_amr_tch_h_to_f_rxqual_oscillation.ho_vty
+pass test_balance_congestion.ho_vty
+pass test_balance_congestion_2.ho_vty
+pass test_balance_congestion_by_percentage.ho_vty
+pass test_balance_congestion_tchf_tchh.ho_vty
+pass test_bs_power.ho_vty
+pass test_congestion.ho_vty
+pass test_congestion_favor_best_target_rxlev.ho_vty
+pass test_congestion_intra_vs_inter_cell.ho_vty
+pass test_congestion_no_oscillation.ho_vty
+pass test_congestion_no_oscillation2.ho_vty
+pass test_disabled_ho_and_as.ho_vty
+pass test_dyn_ts_amr_tch_f_to_h_congestion_assignment.ho_vty
+pass test_dyn_ts_amr_tch_f_to_h_congestion_assignment_2.ho_vty
+pass test_dyn_ts_amr_tch_h_to_f_congestion_assignment_2.ho_vty
+pass test_dyn_ts_balance_congestion.ho_vty
+pass test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty
+pass test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty
+pass test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty
+pass test_dyn_ts_favor_moving_half_used_tch_h.ho_vty
+pass test_dyn_ts_favor_static_ts_as_target.ho_vty
+pass test_ho_to_better_cell.ho_vty
+pass test_ho_to_better_cell_2.ho_vty
+pass test_hysteresis.ho_vty
+pass test_insufficient_measurements.ho_vty
+pass test_keep_efr_codec.ho_vty
+pass test_keep_fr_codec.ho_vty
+pass test_keep_hr_codec.ho_vty
+pass test_max_handovers.ho_vty
+pass test_max_ta.ho_vty
+pass test_meas_rep_multi_band.ho_vty
+pass test_min_rxlev_vs_congestion.ho_vty
+pass test_min_rxlev_vs_hysteresis.ho_vty
+pass test_neighbor_congested.ho_vty
+pass test_neighbor_full.ho_vty
+pass test_no_congestion.ho_vty
+pass test_penalty_timer.ho_vty
+pass test_resource_indication.ho_vty
+pass test_rxqual.ho_vty
+pass test_rxqual_vs_congestion.ho_vty
+pass test_stay_in_better_cell.ho_vty
+pass test_stay_in_better_cell_2.ho_vty
+pass test_story.ho_vty
diff --git a/tests/handover/handover_tests.sh b/tests/handover/handover_tests.sh
new file mode 100755
index 000000000..4be0c1018
--- /dev/null
+++ b/tests/handover/handover_tests.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+set -e
+tests_dir="${1:-.}"
+build_dir="${2:-.}"
+update="$3"
+test -d "$tests_dir"
+test -d "$build_dir"
+
+if [ -n "$update" -a "x$update" != "x-u" -a "x$update" != "x-U" ]; then
+ echo "unknown argument: $update"
+ exit 1
+fi
+
+one_test() {
+ test_path="$1"
+ test_name="$(basename "$test_path")"
+ got_out="$(mktemp "tmp.$test_name.stdout.XXXXX")"
+ got_err="$(mktemp "tmp.$test_name.stderr.XXXXX")"
+ set +e
+ "$build_dir"/handover_test "$test_path" > "$got_out" 2> "$got_err"
+ rc=$?
+ expect_out="$test_path.ok"
+ expect_err="$test_path.err"
+ if [ "x$rc" = "x0" -a "x$update" = "x-U" ]; then
+ cp "$got_out" "$expect_out"
+ cp "$got_err" "$expect_err"
+ else
+ if [ -f "$expect_out" ]; then
+ diff -u "$expect_out" "$got_out" >&2
+ fi
+ if [ -f "$expect_err" ]; then
+ diff -u "$expect_err" "$got_err" >&2
+ fi
+ fi
+ rm "$got_out"
+ rm "$got_err"
+ set -e
+ return $rc
+}
+
+results="$(mktemp "tmp.handover_test_results.XXXXX")"
+for test_path in "$tests_dir"/test*.ho_vty ; do
+ test_name="$(basename "$test_path")"
+ if one_test "$test_path"; then
+ echo "pass $test_name" >> "$results"
+ else
+ echo "FAIL $test_name" >> "$results"
+ fi
+done
+set +e
+cat "$results"
+failed="$(grep FAIL "$results")"
+if [ -z "$failed" -a "x$update" != "x" ]; then
+ cp "$results" "$tests_dir"/handover_tests.ok
+fi
+rm "$results"
+if [ -n "$failed" ]; then
+ echo "tests failed"
+ exit 1
+fi
+exit 0
diff --git a/tests/handover/neighbor_ident_test.c b/tests/handover/neighbor_ident_test.c
deleted file mode 100644
index 9acbea035..000000000
--- a/tests/handover/neighbor_ident_test.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/* Test the neighbor_ident.h API */
-/*
- * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU 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 <talloc.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <osmocom/gsm/gsm0808.h>
-
-#include <osmocom/bsc/neighbor_ident.h>
-
-static struct neighbor_ident_list *nil;
-
-static const struct neighbor_ident_key *k(int from_bts, uint16_t arfcn, uint8_t bsic)
-{
- static struct neighbor_ident_key key;
- key = (struct neighbor_ident_key){
- .from_bts = from_bts,
- .arfcn = arfcn,
- .bsic = bsic,
- };
- return &key;
-}
-
-static const struct gsm0808_cell_id_list2 cgi1 = {
- .id_discr = CELL_IDENT_WHOLE_GLOBAL,
- .id_list_len = 1,
- .id_list = {
- {
- .global = {
- .lai = {
- .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = false },
- .lac = 3,
- },
- .cell_identity = 4,
- }
- },
- },
-};
-
-static const struct gsm0808_cell_id_list2 cgi2 = {
- .id_discr = CELL_IDENT_WHOLE_GLOBAL,
- .id_list_len = 2,
- .id_list = {
- {
- .global = {
- .lai = {
- .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = false },
- .lac = 3,
- },
- .cell_identity = 4,
- }
- },
- {
- .global = {
- .lai = {
- .plmn = { .mcc = 5, .mnc = 6, .mnc_3_digits = true },
- .lac = 7,
- },
- .cell_identity = 8,
- }
- },
- },
-};
-
-static const struct gsm0808_cell_id_list2 lac1 = {
- .id_discr = CELL_IDENT_LAC,
- .id_list_len = 1,
- .id_list = {
- {
- .lac = 123
- },
- },
-};
-
-static const struct gsm0808_cell_id_list2 lac2 = {
- .id_discr = CELL_IDENT_LAC,
- .id_list_len = 2,
- .id_list = {
- {
- .lac = 456
- },
- {
- .lac = 789
- },
- },
-};
-
-static void print_cil(const struct gsm0808_cell_id_list2 *cil)
-{
- unsigned int i;
- if (!cil) {
- printf(" cell_id_list == NULL\n");
- return;
- }
- switch (cil->id_discr) {
- case CELL_IDENT_WHOLE_GLOBAL:
- printf(" cell_id_list cgi[%u] = {\n", cil->id_list_len);
- for (i = 0; i < cil->id_list_len; i++)
- printf(" %2d: %s\n", i, osmo_cgi_name(&cil->id_list[i].global));
- printf(" }\n");
- break;
- case CELL_IDENT_LAC:
- printf(" cell_id_list lac[%u] = {\n", cil->id_list_len);
- for (i = 0; i < cil->id_list_len; i++)
- printf(" %2d: %u\n", i, cil->id_list[i].lac);
- printf(" }\n");
- break;
- default:
- printf(" Unimplemented id_disc\n");
- }
-}
-
-static int print_nil_i;
-
-static bool nil_cb(const struct neighbor_ident_key *key, const struct gsm0808_cell_id_list2 *val,
- void *cb_data)
-{
- printf(" %2d: %s\n", print_nil_i++, neighbor_ident_key_name(key));
- print_cil(val);
- return true;
-}
-
-static void print_nil()
-{
- print_nil_i = 0;
- neighbor_ident_iter(nil, nil_cb, NULL);
- if (!print_nil_i)
- printf(" (empty)\n");
-}
-
-#define check_add(key, val, expect_rc) \
- do { \
- int rc; \
- rc = neighbor_ident_add(nil, key, val); \
- printf("neighbor_ident_add(" #key ", " #val ") --> expect rc=" #expect_rc ", got %d\n", rc); \
- if (rc != expect_rc) \
- printf("ERROR\n"); \
- print_nil(); \
- } while(0)
-
-#define check_del(key, expect_rc) \
- do { \
- bool rc; \
- rc = neighbor_ident_del(nil, key); \
- printf("neighbor_ident_del(" #key ") --> %s\n", rc ? "entry deleted" : "nothing deleted"); \
- if (rc != expect_rc) \
- printf("ERROR: expected: %s\n", expect_rc ? "entry deleted" : "nothing deleted"); \
- print_nil(); \
- } while(0)
-
-#define check_get(key, expect_rc) \
- do { \
- const struct gsm0808_cell_id_list2 *rc; \
- rc = neighbor_ident_get(nil, key); \
- printf("neighbor_ident_get(" #key ") --> %s\n", \
- rc ? "entry returned" : "NULL"); \
- if (((bool)expect_rc) != ((bool) rc)) \
- printf("ERROR: expected %s\n", expect_rc ? "an entry" : "NULL"); \
- if (rc) \
- print_cil(rc); \
- } while(0)
-
-int main(void)
-{
- void *ctx = talloc_named_const(NULL, 0, "neighbor_ident_test");
-
- printf("\n--- testing NULL neighbor_ident_list\n");
- nil = NULL;
- check_add(k(0, 1, 2), &cgi1, -ENOMEM);
- check_get(k(0, 1, 2), false);
- check_del(k(0, 1, 2), false);
-
- printf("\n--- adding entries, test that no two identical entries are added\n");
- nil = neighbor_ident_init(ctx);
- check_add(k(0, 1, 2), &cgi1, 1);
- check_get(k(0, 1, 2), true);
- check_add(k(0, 1, 2), &cgi1, 1);
- check_add(k(0, 1, 2), &cgi2, 2);
- check_add(k(0, 1, 2), &cgi2, 2);
- check_del(k(0, 1, 2), true);
-
- printf("\n--- Cannot mix cell identifier types for one entry\n");
- check_add(k(0, 1, 2), &cgi1, 1);
- check_add(k(0, 1, 2), &lac1, -EINVAL);
- check_del(k(0, 1, 2), true);
- neighbor_ident_free(nil);
-
- printf("\n--- BTS matching: specific BTS is stronger\n");
- nil = neighbor_ident_init(ctx);
- check_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, 2), &lac1, 1);
- check_add(k(3, 1, 2), &lac2, 2);
- check_get(k(2, 1, 2), true);
- check_get(k(3, 1, 2), true);
- check_get(k(4, 1, 2), true);
- check_get(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, 2), true);
- neighbor_ident_free(nil);
-
- printf("\n--- BSIC matching: 6bit and 9bit are different realms, and wildcard match is weaker\n");
- nil = neighbor_ident_init(ctx);
- check_add(k(0, 1, BSIC_ANY), &cgi1, 1);
- check_add(k(0, 1, 2), &lac1, 1);
- check_add(k(0, 1, 2), &lac2, 2);
- check_get(k(0, 1, 2), true);
- check_get(k(0, 1, 2), true);
- neighbor_ident_free(nil);
-
- printf("\n--- Value ranges\n");
- nil = neighbor_ident_init(ctx);
- check_add(k(0, 6, 1 << 6), &lac1, -ERANGE);
- check_add(k(0, 6, BSIC_ANY - 1), &lac1, -ERANGE);
- check_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS - 1, 1, BSIC_ANY), &cgi2, -ERANGE);
- check_add(k(256, 1, BSIC_ANY), &cgi2, -ERANGE);
- check_add(k(0, 0, BSIC_ANY), &cgi1, 1);
- check_add(k(255, 65535, BSIC_ANY), &lac1, 1);
- check_add(k(0, 0, 0), &cgi2, 2);
- check_add(k(255, 65535, 0x3f), &lac2, 2);
-
- neighbor_ident_free(nil);
-
- printf("\n--- size limits\n");
- {
- int i;
- struct gsm0808_cell_id_list2 a = { .id_discr = CELL_IDENT_LAC };
- struct gsm0808_cell_id_list2 b = {
- .id_discr = CELL_IDENT_LAC,
- .id_list = {
- { .lac = 423 }
- },
- .id_list_len = 1,
- };
- for (i = 0; i < ARRAY_SIZE(a.id_list); i++) {
- a.id_list[a.id_list_len ++].lac = i;
- }
-
- nil = neighbor_ident_init(ctx);
-
- i = neighbor_ident_add(nil, k(0, 1, 2), &a);
- printf("Added first cell identifier list (added %u) --> rc = %d\n", a.id_list_len, i);
- i = neighbor_ident_add(nil, k(0, 1, 2), &b);
- printf("Added second cell identifier list (tried to add %u) --> rc = %d\n", b.id_list_len, i);
- if (i != -ENOSPC)
- printf("ERROR: expected rc=%d\n", -ENOSPC);
- neighbor_ident_free(nil);
- }
-
- OSMO_ASSERT(talloc_total_blocks(ctx) == 1);
- talloc_free(ctx);
-
- return 0;
-}
diff --git a/tests/handover/neighbor_ident_test.err b/tests/handover/neighbor_ident_test.err
deleted file mode 100644
index e69de29bb..000000000
--- a/tests/handover/neighbor_ident_test.err
+++ /dev/null
diff --git a/tests/handover/neighbor_ident_test.ok b/tests/handover/neighbor_ident_test.ok
deleted file mode 100644
index 961a33cdc..000000000
--- a/tests/handover/neighbor_ident_test.ok
+++ /dev/null
@@ -1,186 +0,0 @@
-
---- testing NULL neighbor_ident_list
-neighbor_ident_add(k(0, 1, 2), &cgi1) --> expect rc=-ENOMEM, got -12
- (empty)
-neighbor_ident_get(k(0, 1, 2)) --> NULL
-neighbor_ident_del(k(0, 1, 2)) --> nothing deleted
- (empty)
-
---- adding entries, test that no two identical entries are added
-neighbor_ident_add(k(0, 1, 2), &cgi1) --> expect rc=1, got 1
- 0: BTS 0 to ARFCN 1 BSIC 2
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
-neighbor_ident_get(k(0, 1, 2)) --> entry returned
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
-neighbor_ident_add(k(0, 1, 2), &cgi1) --> expect rc=1, got 1
- 0: BTS 0 to ARFCN 1 BSIC 2
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
-neighbor_ident_add(k(0, 1, 2), &cgi2) --> expect rc=2, got 2
- 0: BTS 0 to ARFCN 1 BSIC 2
- cell_id_list cgi[2] = {
- 0: 001-02-3-4
- 1: 005-006-7-8
- }
-neighbor_ident_add(k(0, 1, 2), &cgi2) --> expect rc=2, got 2
- 0: BTS 0 to ARFCN 1 BSIC 2
- cell_id_list cgi[2] = {
- 0: 001-02-3-4
- 1: 005-006-7-8
- }
-neighbor_ident_del(k(0, 1, 2)) --> entry deleted
- (empty)
-
---- Cannot mix cell identifier types for one entry
-neighbor_ident_add(k(0, 1, 2), &cgi1) --> expect rc=1, got 1
- 0: BTS 0 to ARFCN 1 BSIC 2
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
-neighbor_ident_add(k(0, 1, 2), &lac1) --> expect rc=-EINVAL, got -22
- 0: BTS 0 to ARFCN 1 BSIC 2
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
-neighbor_ident_del(k(0, 1, 2)) --> entry deleted
- (empty)
-
---- BTS matching: specific BTS is stronger
-neighbor_ident_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, 2), &lac1) --> expect rc=1, got 1
- 0: BTS * to ARFCN 1 BSIC 2
- cell_id_list lac[1] = {
- 0: 123
- }
-neighbor_ident_add(k(3, 1, 2), &lac2) --> expect rc=2, got 2
- 0: BTS * to ARFCN 1 BSIC 2
- cell_id_list lac[1] = {
- 0: 123
- }
- 1: BTS 3 to ARFCN 1 BSIC 2
- cell_id_list lac[2] = {
- 0: 456
- 1: 789
- }
-neighbor_ident_get(k(2, 1, 2)) --> entry returned
- cell_id_list lac[1] = {
- 0: 123
- }
-neighbor_ident_get(k(3, 1, 2)) --> entry returned
- cell_id_list lac[2] = {
- 0: 456
- 1: 789
- }
-neighbor_ident_get(k(4, 1, 2)) --> entry returned
- cell_id_list lac[1] = {
- 0: 123
- }
-neighbor_ident_get(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, 2)) --> entry returned
- cell_id_list lac[1] = {
- 0: 123
- }
-
---- BSIC matching: 6bit and 9bit are different realms, and wildcard match is weaker
-neighbor_ident_add(k(0, 1, BSIC_ANY), &cgi1) --> expect rc=1, got 1
- 0: BTS 0 to ARFCN 1 (any BSIC)
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
-neighbor_ident_add(k(0, 1, 2), &lac1) --> expect rc=1, got 1
- 0: BTS 0 to ARFCN 1 (any BSIC)
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
- 1: BTS 0 to ARFCN 1 BSIC 2
- cell_id_list lac[1] = {
- 0: 123
- }
-neighbor_ident_add(k(0, 1, 2), &lac2) --> expect rc=2, got 3
-ERROR
- 0: BTS 0 to ARFCN 1 (any BSIC)
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
- 1: BTS 0 to ARFCN 1 BSIC 2
- cell_id_list lac[3] = {
- 0: 123
- 1: 456
- 2: 789
- }
-neighbor_ident_get(k(0, 1, 2)) --> entry returned
- cell_id_list lac[3] = {
- 0: 123
- 1: 456
- 2: 789
- }
-neighbor_ident_get(k(0, 1, 2)) --> entry returned
- cell_id_list lac[3] = {
- 0: 123
- 1: 456
- 2: 789
- }
-
---- Value ranges
-neighbor_ident_add(k(0, 6, 1 << 6), &lac1) --> expect rc=-ERANGE, got -34
- (empty)
-neighbor_ident_add(k(0, 6, BSIC_ANY - 1), &lac1) --> expect rc=-ERANGE, got -34
- (empty)
-neighbor_ident_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS - 1, 1, BSIC_ANY), &cgi2) --> expect rc=-ERANGE, got -34
- (empty)
-neighbor_ident_add(k(256, 1, BSIC_ANY), &cgi2) --> expect rc=-ERANGE, got -34
- (empty)
-neighbor_ident_add(k(0, 0, BSIC_ANY), &cgi1) --> expect rc=1, got 1
- 0: BTS 0 to ARFCN 0 (any BSIC)
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
-neighbor_ident_add(k(255, 65535, BSIC_ANY), &lac1) --> expect rc=1, got 1
- 0: BTS 0 to ARFCN 0 (any BSIC)
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
- 1: BTS 255 to ARFCN 65535 (any BSIC)
- cell_id_list lac[1] = {
- 0: 123
- }
-neighbor_ident_add(k(0, 0, 0), &cgi2) --> expect rc=2, got 2
- 0: BTS 0 to ARFCN 0 (any BSIC)
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
- 1: BTS 255 to ARFCN 65535 (any BSIC)
- cell_id_list lac[1] = {
- 0: 123
- }
- 2: BTS 0 to ARFCN 0 BSIC 0
- cell_id_list cgi[2] = {
- 0: 001-02-3-4
- 1: 005-006-7-8
- }
-neighbor_ident_add(k(255, 65535, 0x3f), &lac2) --> expect rc=2, got 2
- 0: BTS 0 to ARFCN 0 (any BSIC)
- cell_id_list cgi[1] = {
- 0: 001-02-3-4
- }
- 1: BTS 255 to ARFCN 65535 (any BSIC)
- cell_id_list lac[1] = {
- 0: 123
- }
- 2: BTS 0 to ARFCN 0 BSIC 0
- cell_id_list cgi[2] = {
- 0: 001-02-3-4
- 1: 005-006-7-8
- }
- 3: BTS 255 to ARFCN 65535 BSIC 63
- cell_id_list lac[2] = {
- 0: 456
- 1: 789
- }
-
---- size limits
-Added first cell identifier list (added 127) --> rc = 127
-Added second cell identifier list (tried to add 1) --> rc = -28
diff --git a/tests/handover/test_amr_tch_f_to_h.ho_vty b/tests/handover/test_amr_tch_f_to_h.ho_vty
new file mode 100644
index 000000000..22c5e8752
--- /dev/null
+++ b/tests/handover/test_amr_tch_f_to_h.ho_vty
@@ -0,0 +1,14 @@
+# TCH/F to TCH/H changing with AMR codec
+# The MS is using AMR V3 codec, the better cell is congested at TCH/F
+# slots. The handover is performed to non-congested TCH/H slots.
+
+create-n-bts 2
+network
+ bts 1
+ handover2 min-free-slots tch/f 4
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * - - - - TCH/H- - -
+
diff --git a/tests/handover/test_amr_tch_f_to_h_balance_congestion.ho_vty b/tests/handover/test_amr_tch_f_to_h_balance_congestion.ho_vty
new file mode 100644
index 000000000..1b8969e1c
--- /dev/null
+++ b/tests/handover/test_amr_tch_f_to_h_balance_congestion.ho_vty
@@ -0,0 +1,16 @@
+# Congestion check: Balancing congestion by handover TCH/F -> TCH/H
+# Two BTS, one MS in the first congested BTS must handover to
+# less-congested TCH/H of second BTS, in order to balance congestion
+
+create-n-bts 2
+network
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/H- - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - TCH/F - - TCH/H- - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_amr_tch_f_to_h_congestion.ho_vty b/tests/handover/test_amr_tch_f_to_h_congestion.ho_vty
new file mode 100644
index 000000000..5c934941e
--- /dev/null
+++ b/tests/handover/test_amr_tch_f_to_h_congestion.ho_vty
@@ -0,0 +1,19 @@
+# Congestion check: Solving congestion by handover TCH/F -> TCH/H
+# Two BTS, one MS in the first congested BTS must handover to
+# non-congested TCH/H of second BTS, in order to solve congestion
+
+create-n-bts 2
+network
+ bts 0
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+ bts 1
+ handover2 min-free-slots tch/f 4
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * - - - - TCH/H- - -
+
diff --git a/tests/handover/test_amr_tch_f_to_h_congestion_assignment.ho_vty b/tests/handover/test_amr_tch_f_to_h_congestion_assignment.ho_vty
new file mode 100644
index 000000000..84f34ff61
--- /dev/null
+++ b/tests/handover/test_amr_tch_f_to_h_congestion_assignment.ho_vty
@@ -0,0 +1,18 @@
+# Congestion check: Upgrading worst candidate from TCH/H -> TCH/F
+# There is only one BTS. The TCH/H slots are congested. Since
+# assignment is performed to less-congested TCH/F, the candidate with
+# the worst RX level is chosen.
+
+create-n-bts 1
+network
+ bts 0
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+set-ts-use trx 0 0 states * - - - - TCH/HH TCH/H- -
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0
+meas-rep lchan 0 0 5 1 rxlev 34 rxqual 0 ta 0
+meas-rep lchan 0 0 6 0 rxlev 20 rxqual 0 ta 0
+expect-no-chan
+congestion-check
+expect-as from lchan 0 0 6 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - TCH/HH - -
diff --git a/tests/handover/test_amr_tch_f_to_h_congestion_assignment_2.ho_vty b/tests/handover/test_amr_tch_f_to_h_congestion_assignment_2.ho_vty
new file mode 100644
index 000000000..2fa08da17
--- /dev/null
+++ b/tests/handover/test_amr_tch_f_to_h_congestion_assignment_2.ho_vty
@@ -0,0 +1,27 @@
+# Congestion check: Upgrading worst candidate from TCH/H -> TCH/F
+# There is only one BTS. The TCH/H slots are congested. Since
+# assignment is performed to less-congested TCH/F, the candidate with
+# the worst RX level is chosen. (So far like test 22.)
+# After that, trigger more congestion checks to ensure stability.
+
+create-n-bts 1
+network
+ bts 0
+ handover2 min-free-slots tch/f 2
+ handover2 min-free-slots tch/h 4
+set-ts-use trx 0 0 states * - - - - TCH/HH TCH/H- -
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0
+meas-rep lchan 0 0 5 1 rxlev 34 rxqual 0 ta 0
+meas-rep lchan 0 0 6 0 rxlev 20 rxqual 0 ta 0
+expect-no-chan
+congestion-check
+expect-as from lchan 0 0 6 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - TCH/HH - -
+congestion-check
+expect-as from lchan 0 0 5 0 to lchan 0 0 2 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/-H - -
+congestion-check
+expect-no-chan
+congestion-check
+expect-no-chan
+
diff --git a/tests/handover/test_amr_tch_f_to_h_congestion_assignment_3.ho_vty b/tests/handover/test_amr_tch_f_to_h_congestion_assignment_3.ho_vty
new file mode 100644
index 000000000..0dca250d2
--- /dev/null
+++ b/tests/handover/test_amr_tch_f_to_h_congestion_assignment_3.ho_vty
@@ -0,0 +1,15 @@
+# Congestion check: Balancing congestion by handover TCH/F -> TCH/H
+# One BTS, and TCH/F are considered congested, TCH/H are not.
+
+create-n-bts 1
+network
+ bts 0
+ handover2 min-free-slots tch/f 3
+ handover2 min-free-slots tch/h 0
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/H- - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+congestion-check
+expect-as from lchan 0 0 1 0 to lchan 0 0 5 1
+expect-ts-use trx 0 0 states * - TCH/F - - TCH/HH - -
+
diff --git a/tests/handover/test_amr_tch_h_and_afs_bias.ho_vty b/tests/handover/test_amr_tch_h_and_afs_bias.ho_vty
new file mode 100644
index 000000000..462cb0d59
--- /dev/null
+++ b/tests/handover/test_amr_tch_h_and_afs_bias.ho_vty
@@ -0,0 +1,13 @@
+# TCH/H has good RxLev and RxQual, AFS bias should not move it to TCH/F
+
+network
+ handover2 power budget hysteresis 3
+ handover2 min rxlev -90
+ handover2 min rxqual 5
+ handover2 afs-bias rxlev 1
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+set-ts-use trx 0 0 states * - - - TCH/H- - - *
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 1 ta 0
+# The TCH/H should stay where it is, because its levels are fine.
+expect-no-chan
diff --git a/tests/handover/test_amr_tch_h_to_f_congestion.ho_vty b/tests/handover/test_amr_tch_h_to_f_congestion.ho_vty
new file mode 100644
index 000000000..0252d9f23
--- /dev/null
+++ b/tests/handover/test_amr_tch_h_to_f_congestion.ho_vty
@@ -0,0 +1,14 @@
+# Congestion check: Balancing congestion by handover TCH/H -> TCH/F
+# One BTS, TCH/H are congested and should move to TCH/F.
+
+network
+ handover2 min-free-slots tch/f 0
+ handover2 min-free-slots tch/h 6
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+set-ts-use trx 0 0 states * - - - TCH/H- - - *
+meas-rep lchan 0 0 4 0 rxlev 30 rxqual 0 ta 0
+expect-no-chan
+congestion-check
+expect-as from lchan 0 0 4 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - *
diff --git a/tests/handover/test_amr_tch_h_to_f_congestion_two_cells.ho_vty b/tests/handover/test_amr_tch_h_to_f_congestion_two_cells.ho_vty
new file mode 100644
index 000000000..fecd06853
--- /dev/null
+++ b/tests/handover/test_amr_tch_h_to_f_congestion_two_cells.ho_vty
@@ -0,0 +1,17 @@
+# Congestion check: Balancing congestion by handover TCH/H -> TCH/F
+# TCH/H are congested and should move to TCH/F
+# There are two cells, and the neighbor has weaker rxlev, so stay in the same cell.
+
+network
+ handover2 min-free-slots tch/f 0
+ handover2 min-free-slots tch/h 6
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+set-ts-use trx 1 0 states * - - - TCH/H- - - *
+meas-rep repeat 10 lchan 1 0 4 0 rxlev 30 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+congestion-check
+expect-as from lchan 1 0 4 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - *
+expect-ts-use trx 1 0 states * TCH/F - - - - - *
diff --git a/tests/handover/test_amr_tch_h_to_f_rxlev.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxlev.ho_vty
new file mode 100644
index 000000000..a22ad6d85
--- /dev/null
+++ b/tests/handover/test_amr_tch_h_to_f_rxlev.ho_vty
@@ -0,0 +1,16 @@
+# Low RxLev causes upgrade of TCH/H to TCH/F
+
+network
+ handover2 afs-bias rxlev 0
+ handover2 min rxlev -80
+ handover2 window rxlev averaging 10
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+set-ts-use trx 0 0 states * - - - TCH/H- - - *
+meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# not enough values for rxlev averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev
+expect-as from lchan 0 0 4 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - *
diff --git a/tests/handover/test_amr_tch_h_to_f_rxlev_congested.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxlev_congested.ho_vty
new file mode 100644
index 000000000..776b09311
--- /dev/null
+++ b/tests/handover/test_amr_tch_h_to_f_rxlev_congested.ho_vty
@@ -0,0 +1,63 @@
+# Low RxLev causes upgrade of TCH/H to TCH/F
+
+network
+ handover2 afs-bias rxlev 0
+ handover2 min rxlev -80
+ handover2 window rxlev averaging 10
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H PDCH PDCH
+
+
+set-ts-use trx 0 0 states * - - - TCH/HH TCH/HH * *
+meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# not enough values for rxlev averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev
+expect-as from lchan 0 0 4 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - TCH/-H TCH/HH * *
+
+
+# This situation actually balances congestion
+set-ts-use trx 0 0 states * TCH/F - - TCH/HH TCH/HH * *
+meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# not enough values for rxlev averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev
+expect-as from lchan 0 0 4 0 to lchan 0 0 2 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F - TCH/-H TCH/HH * *
+
+# This situation moves congestion from TCH/H to TCH/F (TCH/H was 100% congested, then makes TCH/F 100% congested)
+# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign.
+set-ts-use trx 0 0 states * TCH/F TCH/F - TCH/HH TCH/HH * *
+meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# not enough values for rxlev averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev
+expect-as from lchan 0 0 4 0 to lchan 0 0 3 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/-H TCH/HH * *
+
+# This situation worsens congestion (TCH/H was 50% congested, then makes TCH/F 100% congested)
+# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign.
+set-ts-use trx 0 0 states * TCH/F TCH/F - TCH/H- TCH/HH * *
+meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# not enough values for rxlev averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev
+expect-as from lchan 0 0 4 0 to lchan 0 0 3 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH * *
+
+
+# This situation creates congestion (TCH/H was not congested, then makes TCH/F 50% congested)
+# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign.
+set-ts-use trx 0 0 states * TCH/F - - TCH/H- - * *
+meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# not enough values for rxlev averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev
+expect-as from lchan 0 0 4 0 to lchan 0 0 2 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F - - - * *
diff --git a/tests/handover/test_amr_tch_h_to_f_rxlev_oscillation.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxlev_oscillation.ho_vty
new file mode 100644
index 000000000..2ef927dde
--- /dev/null
+++ b/tests/handover/test_amr_tch_h_to_f_rxlev_oscillation.ho_vty
@@ -0,0 +1,20 @@
+# Low RxLev causes upgrade of TCH/H to TCH/F.
+# That leads to congestion of TCH/F, but do not handover back to non-congested TCH/H.
+
+network
+ handover2 afs-bias rxlev 0
+ handover2 min rxlev -80
+ handover2 window rxlev averaging 1
+ handover2 min-free-slots tch/f 3
+ handover2 min-free-slots tch/h 4
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+set-ts-use trx 0 0 states * - - - TCH/H- - - *
+meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0
+# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev
+expect-as from lchan 0 0 4 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - *
+
+meas-rep lchan 0 0 1 0 rxlev 23 rxqual 1 ta 0
+congestion-check
+expect-no-chan
diff --git a/tests/handover/test_amr_tch_h_to_f_rxqual.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxqual.ho_vty
new file mode 100644
index 000000000..f3a2a9086
--- /dev/null
+++ b/tests/handover/test_amr_tch_h_to_f_rxqual.ho_vty
@@ -0,0 +1,39 @@
+# Low RxQual causes upgrade of TCH/H to TCH/F
+
+network
+ handover2 afs-bias rxlev 0
+ handover2 min rxqual 5
+ handover2 window rxqual averaging 2
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+set-ts-use trx 0 0 states * - - - TCH/H- - - *
+meas-rep lchan 0 0 4 0 rxlev 30 rxqual 6 ta 0
+# not enough valus for rxqual averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 30 rxqual 6 ta 0
+# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual.
+expect-as from lchan 0 0 4 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - *
+meas-rep repeat 2 lchan 0 0 1 0 rxlev 30 rxqual 5 ta 0
+
+# After the upgrade to TCH/F, there should be a penalty timer against re-assgnment within this cell.
+# Configure congestion resolution so that it would normally want to do a re-assignment:
+network
+ handover2 min-free-slots tch/f 3
+ handover2 window rxlev averaging 1
+ handover2 min rxlev -90
+
+# The penalty timer is still active, no re-assignment from congestion of TCH/H
+congestion-check
+expect-no-chan
+expect-ts-use trx 0 0 states * TCH/F - - - - - *
+
+# But handover to another cell is not held off
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+expect-ts-use trx 0 0 states * TCH/F - - - - - *
+expect-ts-use trx 1 0 states * - - - - - - *
+
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 5 ta 0 neighbors 35
+expect-ho from lchan 0 0 1 0 to lchan 1 0 4 0
+expect-ts-use trx 0 0 states * - - - - - - *
+expect-ts-use trx 1 0 states * - - - TCH/H- - - *
diff --git a/tests/handover/test_amr_tch_h_to_f_rxqual_congested.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxqual_congested.ho_vty
new file mode 100644
index 000000000..79ff0a72f
--- /dev/null
+++ b/tests/handover/test_amr_tch_h_to_f_rxqual_congested.ho_vty
@@ -0,0 +1,68 @@
+# Low RxQual causes upgrade of TCH/H to TCH/F, also when the cell is congested
+
+network
+ handover2 afs-bias rxlev 0
+ handover2 min rxqual 5
+ handover2 min rxlev -90
+ handover2 window rxqual averaging 2
+ handover2 min-free-slots tch/f 2
+ handover2 min-free-slots tch/h 2
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H PDCH PDCH
+
+# This situation actually reduces congestion
+set-ts-use trx 0 0 states * - - - TCH/HH TCH/HH * *
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# not enough values for rxqual averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual.
+expect-as from lchan 0 0 4 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - TCH/-H TCH/HH * *
+
+
+# This situation actually balances congestion
+set-ts-use trx 0 0 states * TCH/F - - TCH/HH TCH/HH * *
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# not enough values for rxqual averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual.
+expect-as from lchan 0 0 4 0 to lchan 0 0 2 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F - TCH/-H TCH/HH * *
+
+
+# This situation moves congestion from TCH/H to TCH/F (TCH/H was 100% congested, then makes TCH/F 100% congested)
+# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign.
+set-ts-use trx 0 0 states * TCH/F TCH/F - TCH/HH TCH/HH * *
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# not enough values for rxqual averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual.
+expect-as from lchan 0 0 4 0 to lchan 0 0 3 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/-H TCH/HH * *
+
+
+# This situation worsens congestion (TCH/H was 50% congested, then makes TCH/F 100% congested)
+# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign.
+set-ts-use trx 0 0 states * TCH/F TCH/F - TCH/H- TCH/HH * *
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# not enough values for rxqual averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual.
+expect-as from lchan 0 0 4 0 to lchan 0 0 3 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH * *
+
+
+# This situation creates congestion (TCH/H was not congested, then makes TCH/F 50% congested)
+# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign.
+set-ts-use trx 0 0 states * TCH/F - - TCH/H- - * *
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# not enough values for rxqual averaging
+expect-no-chan
+meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0
+# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual.
+expect-as from lchan 0 0 4 0 to lchan 0 0 2 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F - - - * *
diff --git a/tests/handover/test_amr_tch_h_to_f_rxqual_oscillation.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxqual_oscillation.ho_vty
new file mode 100644
index 000000000..e628f032a
--- /dev/null
+++ b/tests/handover/test_amr_tch_h_to_f_rxqual_oscillation.ho_vty
@@ -0,0 +1,20 @@
+# Low RxQual causes upgrade of TCH/H to TCH/F.
+# That leads to congestion of TCH/F, but do not handover back to non-congested TCH/H.
+
+network
+ handover2 afs-bias rxlev 0
+ handover2 min rxqual 5
+ handover2 window rxqual averaging 1
+ handover2 min-free-slots tch/f 3
+ handover2 min-free-slots tch/h 4
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+set-ts-use trx 0 0 states * - - - TCH/H- - - *
+meas-rep lchan 0 0 4 0 rxlev 30 rxqual 6 ta 0
+# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev
+expect-as from lchan 0 0 4 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - *
+
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0
+congestion-check
+expect-no-chan
diff --git a/tests/handover/test_balance_congestion.ho_vty b/tests/handover/test_balance_congestion.ho_vty
new file mode 100644
index 000000000..0988f3bb4
--- /dev/null
+++ b/tests/handover/test_balance_congestion.ho_vty
@@ -0,0 +1,20 @@
+# Handover to balance congestion
+# The current and the better cell are congested, so no handover is
+# performed. This is because handover would congest the neighbor cell
+# more. After congestion rises in the current cell, the handover is
+# performed to balance congestion
+
+create-n-bts 2
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+set-ts-use trx 0 0 states * TCH/F TCH/F - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - TCH/F - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_balance_congestion_2.ho_vty b/tests/handover/test_balance_congestion_2.ho_vty
new file mode 100644
index 000000000..b157478f9
--- /dev/null
+++ b/tests/handover/test_balance_congestion_2.ho_vty
@@ -0,0 +1,18 @@
+# Congestion check: Balancing over congested cells
+# Two cells are congested, but the second cell is less congested.
+# Handover is performed to solve the congestion.
+
+create-n-bts 2
+network
+ handover2 min-free-slots tch/f 4
+codec tch/f FR
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - - - -
+set-ts-use trx 1 0 states * TCH/F - - - - - -
+meas-rep lchan * * * * rxlev 30 rxqual 0 ta 0 neighbors 20
+meas-rep lchan 0 0 2 0 rxlev 30 rxqual 0 ta 0 neighbors 21
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 2 0 to lchan 1 0 2 0
+expect-ts-use trx 0 0 states * TCH/F - TCH/F - - - -
+expect-ts-use trx 1 0 states * TCH/F TCH/F - - - - -
+
diff --git a/tests/handover/test_balance_congestion_by_percentage.ho_vty b/tests/handover/test_balance_congestion_by_percentage.ho_vty
new file mode 100644
index 000000000..aba7d3a5b
--- /dev/null
+++ b/tests/handover/test_balance_congestion_by_percentage.ho_vty
@@ -0,0 +1,52 @@
+# To balance congestion, use the remaining free percentage instead of free lchan counts.
+#
+# Cell A has min-free-slots 2, and has all slots occupied.
+# Cell B has min-free-slots 4, and has 2 slots remaining free.
+#
+# If we count congested lchans: cell A has a congestion count of 2: two more lchans in use than "allowed".
+# If we move one lchan over to cell B, it ends up with a congestion count of 3, which is worse than 2.
+# So when counting lchans, we decide that cell A should remain full.
+#
+# Instead, when comparing percentage of remaining lchans, we would see that cell A is loaded 100% above congestion (2 of
+# 2 remaining lchans in use), but when moving one lchan to cell B, it would only be 75% loaded above its treshold (3 of
+# 4 remaining lchans in use). So a percentage comparison would cause a handover to cell B.
+#
+# This test currently expects the behavior of counting lchans; a patch will change to use percentage, which should
+# reflect in this test.
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH
+
+network
+ bts 0
+ handover2 min-free-slots tch/f 2
+ bts 1
+ handover2 min-free-slots tch/f 4
+
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F - - *
+
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+# bts 0 is full, by target_overbooked_after_ho==75% < current_overbooked_before_ho==100%, a congestion balancing to bts
+# 1 is performed.
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0
+
+
+# Make sure that no percentage based handover merely reverses the situation between two cells:
+
+network
+ bts 0
+ handover2 min-free-slots tch/f 4
+ bts 1
+ handover2 min-free-slots tch/f 4
+
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F - - *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F - - - *
+
+# the condition is false: target_overbooked_after_ho==50% < current_overbooked_before_ho==50%, so no congestion
+# balancing is performed.
+congestion-check
+expect-no-chan
diff --git a/tests/handover/test_balance_congestion_tchf_tchh.ho_vty b/tests/handover/test_balance_congestion_tchf_tchh.ho_vty
new file mode 100644
index 000000000..f151b2a41
--- /dev/null
+++ b/tests/handover/test_balance_congestion_tchf_tchh.ho_vty
@@ -0,0 +1,53 @@
+# Balance congestion across cells and across TCH/F and TCH/H.
+
+network
+ handover2 min-free-slots tch/f 3
+ handover2 min-free-slots tch/h 3
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H
+
+# both TCH/H and TCH/F have one lchan = 33% above congestion, nothing happens
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/HH -
+meas-rep lchan * * * * rxlev 10 rxqual 0 ta 0
+congestion-check
+expect-no-chan
+
+# TCH/F = +1 = 33%, TCH/H = +2 = 66% above congestion.
+# Moving a TCH/H to TCH/F would just reverse the situation to F=+2=66%. Nothing happens.
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/HH TCH/H-
+meas-rep lchan * * * * rxlev 10 rxqual 0 ta 0
+congestion-check
+expect-no-chan
+
+# F=+1=33% H=+3=100%. Balance to F=+2=66% (which is < 100%) and H=+2=66%
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/HH TCH/HH
+meas-rep lchan * * * * rxlev 10 rxqual 0 ta 0
+congestion-check
+expect-as from lchan 0 0 5 0 to lchan 0 0 3 0
+
+# Now similar load percentages, just with different min-free-slots settings for tch/f vs tch/h.
+
+network
+ handover2 min-free-slots tch/f 3
+ handover2 min-free-slots tch/h 5
+
+# TCH/F has 1/3 = 33%, TCH/H has 1/5 = 20% overload.
+# Moving one to TCH/H would mean 40% overload on TCH/H, which is above the current TCH/F of 33%.
+# Nothing happens.
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - -
+meas-rep lchan * * * * rxlev 20 rxqual 0 ta 0
+congestion-check
+expect-no-chan
+
+# TCH/F = +1 = 33%, TCH/H = +2 = 40% above congestion. Moving a TCH/H to TCH/F would result
+# in F=+2=66%>40%. Nothing happens.
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/H- -
+meas-rep lchan * * * * rxlev 20 rxqual 0 ta 0
+congestion-check
+expect-no-chan
+
+# F=+1=33% H=+4=80%. Balance to F=+2=66%<80% and H=+3=60%
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/HH TCH/H-
+meas-rep lchan * * * * rxlev 20 rxqual 0 ta 0
+congestion-check
+expect-as from lchan 0 0 5 0 to lchan 0 0 3 0
diff --git a/tests/handover/test_bs_power.ho_vty b/tests/handover/test_bs_power.ho_vty
new file mode 100644
index 000000000..5d8c27846
--- /dev/null
+++ b/tests/handover/test_bs_power.ho_vty
@@ -0,0 +1,11 @@
+# Do not oscillate handover when the BTS applies BS power reduction
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH
+
+set-ts-use trx 0 0 states * TCH/F - - - - - *
+
+meas-rep repeat 10 bspower 20 lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+# there should be no handover, because the bspower reduction of 20 with an rxlev of 20 (= 40) is stronger than the
+# neighbor at 30.
+expect-no-chan
diff --git a/tests/handover/test_congestion.ho_vty b/tests/handover/test_congestion.ho_vty
new file mode 100644
index 000000000..529b8de61
--- /dev/null
+++ b/tests/handover/test_congestion.ho_vty
@@ -0,0 +1,21 @@
+# Congestion check: One out of three cells is congested
+# Three cells have different number of used slots, but there is
+# congestion at TCH/F in the first cell. Handover is performed with
+# the best candidate.
+
+create-n-bts 3
+network
+ handover2 min-free-slots tch/f 2
+ handover2 min-free-slots tch/h 2
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH - -
+set-ts-use trx 1 0 states * TCH/F - - - TCH/H- - -
+meas-rep lchan * * * * rxlev 30 rxqual 0 ta 0 neighbors 20 20
+meas-rep lchan 0 0 3 0 rxlev 30 rxqual 0 ta 0 neighbors 21 20
+expect-no-chan
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH - -
+expect-ts-use trx 1 0 states * TCH/F - - - TCH/H- - -
+congestion-check
+expect-ho from lchan 0 0 3 0 to lchan 1 0 2 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - -
+expect-ts-use trx 1 0 states * TCH/F TCH/F - - TCH/H- - -
+
diff --git a/tests/handover/test_congestion_favor_best_target_rxlev.ho_vty b/tests/handover/test_congestion_favor_best_target_rxlev.ho_vty
new file mode 100644
index 000000000..e31d98d38
--- /dev/null
+++ b/tests/handover/test_congestion_favor_best_target_rxlev.ho_vty
@@ -0,0 +1,33 @@
+# A handover should mostly favor the best target rxlev:
+# Two candidates for congestion resolution both reduce the RXLEV for the MS,
+# candidate A results in 10 RXLEV loss, candidate B only in 5 RXLEV loss.
+# But candidate A still results in a better RXLEV at the target than candidate B.
+# So tolerate more RXLEV reduction if the resulting RXLEV still remains better.
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F
+
+network
+ bts 0
+ handover2 min-free-slots tch/f 6
+
+set-ts-use trx 0 0 states * TCH/F TCH/F - - - - -
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+meas-rep lchan 0 0 2 0 rxlev 30 rxqual 0 ta 0 neighbors 25
+
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - TCH/F - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
+# clear measurements for next run
+set-ts-use trx 0 0 states * - - - - - - -
+set-ts-use trx 1 0 states * - - - - - - -
+
+set-ts-use trx 0 0 states * TCH/F TCH/F - - - - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 25
+meas-rep lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+
+congestion-check
+expect-ho from lchan 0 0 2 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
diff --git a/tests/handover/test_congestion_intra_vs_inter_cell.ho_vty b/tests/handover/test_congestion_intra_vs_inter_cell.ho_vty
new file mode 100644
index 000000000..ddf6802f9
--- /dev/null
+++ b/tests/handover/test_congestion_intra_vs_inter_cell.ho_vty
@@ -0,0 +1,122 @@
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H PDCH
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H PDCH
+
+network
+ handover2 min-free-slots tch/h 4
+
+set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- *
+
+meas-rep lchan 0 0 5 0 rxlev 31 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+meas-rep lchan 0 0 6 0 rxlev 30 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+
+congestion-check
+expect-as from lchan 0 0 6 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - TCH/H- - *
+expect-ts-use trx 1 0 states * - - - - - - *
+
+# clear measurements for next run
+set-ts-use trx 0 0 states * - - - - - - *
+set-ts-use trx 1 0 states * - - - - - - *
+
+set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- *
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+
+congestion-check
+expect-as from lchan 0 0 5 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - TCH/H- *
+expect-ts-use trx 1 0 states * - - - - - - *
+
+# clear measurements for next run
+set-ts-use trx 0 0 states * - - - - - - *
+set-ts-use trx 1 0 states * - - - - - - *
+
+set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- *
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 21
+expect-no-chan
+
+congestion-check
+expect-as from lchan 0 0 5 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - TCH/H- *
+expect-ts-use trx 1 0 states * - - - - - - *
+
+# clear measurements for next run
+set-ts-use trx 0 0 states * - - - - - - *
+set-ts-use trx 1 0 states * - - - - - - *
+
+set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- *
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 21
+expect-no-chan
+meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+
+congestion-check
+expect-as from lchan 0 0 5 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - TCH/H- *
+expect-ts-use trx 1 0 states * - - - - - - *
+
+# clear measurements for next run
+set-ts-use trx 0 0 states * - - - - - - *
+set-ts-use trx 1 0 states * - - - - - - *
+
+set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- *
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 31
+expect-no-chan
+
+congestion-check
+expect-as from lchan 0 0 5 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - TCH/H- *
+expect-ts-use trx 1 0 states * - - - - - - *
+
+# clear measurements for next run
+set-ts-use trx 0 0 states * - - - - - - *
+set-ts-use trx 1 0 states * - - - - - - *
+
+set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- *
+meas-rep lchan 0 0 5 0 rxlev 31 rxqual 0 ta 0 neighbors 31
+expect-no-chan
+meas-rep lchan 0 0 6 0 rxlev 30 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+congestion-check
+expect-as from lchan 0 0 6 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - TCH/H- - *
+expect-ts-use trx 1 0 states * - - - - - - *
+
+# clear measurements for next run
+set-ts-use trx 0 0 states * - - - - - - *
+set-ts-use trx 1 0 states * - - - - - - *
+
+set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- *
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 31
+expect-no-chan
+meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+congestion-check
+expect-ho from lchan 0 0 5 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - TCH/H- *
+expect-ts-use trx 1 0 states * TCH/F - - - - - *
+
+# clear measurements for next run
+set-ts-use trx 0 0 states * - - - - - - *
+set-ts-use trx 1 0 states * - - - - - - *
+
+set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- *
+meas-rep lchan 0 0 5 0 rxlev 31 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 6 0 rxlev 30 rxqual 0 ta 0 neighbors 31
+expect-no-chan
+
+congestion-check
+expect-ho from lchan 0 0 6 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - TCH/H- - *
+expect-ts-use trx 1 0 states * TCH/F - - - - - *
diff --git a/tests/handover/test_congestion_no_oscillation.ho_vty b/tests/handover/test_congestion_no_oscillation.ho_vty
new file mode 100644
index 000000000..a830cbe77
--- /dev/null
+++ b/tests/handover/test_congestion_no_oscillation.ho_vty
@@ -0,0 +1,28 @@
+# Do not oscillate handover from TCH/F to TCH/H on a neighbor due to congestion,
+# and then back to the original cell due to RXLEV.
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH
+network
+ bts 0
+ handover2 min-free-slots tch/f 5
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H PDCH
+
+set-ts-use trx 0 0 states * TCH/F TCH/F - - - - *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F - - *
+
+meas-rep repeat 10 lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+
+# bts 0 wants to lose one TCH/F. The neighbor's TCH/F are full, but TCH/H are available there.
+congestion-check
+expect-ho from lchan 0 0 2 0 to lchan 1 0 5 0
+
+expect-ts-use trx 0 0 states * TCH/F - - - - - *
+expect-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F TCH/H- - *
+
+# measurements continue to be the same
+meas-rep lchan 1 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 40
+
+# despite the better RXLEV, congestion prevents oscillation back to bts 0
+expect-no-chan
diff --git a/tests/handover/test_congestion_no_oscillation2.ho_vty b/tests/handover/test_congestion_no_oscillation2.ho_vty
new file mode 100644
index 000000000..44c4176c2
--- /dev/null
+++ b/tests/handover/test_congestion_no_oscillation2.ho_vty
@@ -0,0 +1,28 @@
+# Almost identical to test_amr_oscillation.ho_vty, this has just two more TCH/H slots in BTS 1, and did not trigger the
+# oscillation bug (which has since been fixed, so that both tests behave identically now).
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH
+network
+ bts 0
+ handover2 min-free-slots tch/f 5
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H
+
+set-ts-use trx 0 0 states * TCH/F TCH/F - - - - *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F - - -
+
+meas-rep repeat 10 lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+
+# bts 0 wants to lose one TCH/F. The neighbor's TCH/F are full, but TCH/H are available there.
+congestion-check
+expect-ho from lchan 0 0 2 0 to lchan 1 0 5 0
+
+expect-ts-use trx 0 0 states * TCH/F - - - - - *
+expect-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F TCH/H- - -
+
+# measurements continue to be the same
+meas-rep lchan 1 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 40
+
+# despite the better RXLEV, congestion prevents oscillation back to bts 0
+expect-no-chan
diff --git a/tests/handover/test_disabled_ho_and_as.ho_vty b/tests/handover/test_disabled_ho_and_as.ho_vty
new file mode 100644
index 000000000..586c3a70d
--- /dev/null
+++ b/tests/handover/test_disabled_ho_and_as.ho_vty
@@ -0,0 +1,36 @@
+# Handover and Assignment must be enabled
+# This test will start with disabled assignment and handover. A
+# better neighbor cell (assignment enabled) will not be selected and
+# also no assignment from TCH/H to TCH/F to improve quality. There
+# will be no handover nor assignment. After enabling assignment on the
+# current cell, the MS will assign to TCH/F. After enabling handover
+# in the current cell, but disabling in the neighbor cell, handover
+# will not be performed, until it is enabled in the neighbor cell too.
+network
+ handover 0
+ handover2 afs-bias rxlev 5
+ handover2 assignment 0
+
+create-n-bts 2
+set-ts-use trx 0 0 states * - - - - TCH/H- - -
+meas-rep lchan 0 0 5 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 0
+ handover2 assignment 1
+meas-rep lchan 0 0 5 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-as from lchan 0 0 5 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 0
+ handover 1
+meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+ handover 1
+meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment.ho_vty b/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment.ho_vty
new file mode 100644
index 000000000..6990132ee
--- /dev/null
+++ b/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment.ho_vty
@@ -0,0 +1,78 @@
+# Congestion check: Balancing congestion by handover TCH/F -> TCH/H
+# With dynamic timeslots.
+# As soon as only one TCH/F is left, there should be HO to a dyn TS.
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH
+
+network
+ bts 0
+ handover2 min-free-slots tch/f 2
+ handover2 min-free-slots tch/h 0
+ handover2 assignment 1
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F pdch pdch pdch
+
+# (there must be at leas one measurement report on each lchan for congestion check to work)
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 5 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F TCH/F pdch *
+
+congestion-check
+expect-as from lchan 0 0 5 0 to lchan 0 0 6 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F pdch TCH/H- *
+
+congestion-check
+expect-as from lchan 0 0 4 0 to lchan 0 0 6 1
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F pdch pdch TCH/HH *
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 4 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F pdch TCH/HH *
+
+congestion-check
+expect-as from lchan 0 0 4 0 to lchan 0 0 5 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F pdch TCH/H- TCH/HH *
+
+congestion-check
+expect-as from lchan 0 0 1 0 to lchan 0 0 5 1
+expect-ts-use trx 0 0 states * - TCH/F TCH/F pdch TCH/HH TCH/HH *
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F pdch TCH/HH TCH/HH *
+
+congestion-check
+expect-as from lchan 0 0 1 0 to lchan 0 0 4 0
+expect-ts-use trx 0 0 states * - TCH/F TCH/F TCH/H- TCH/HH TCH/HH *
+
+congestion-check
+expect-as from lchan 0 0 2 0 to lchan 0 0 4 1
+expect-ts-use trx 0 0 states * - - TCH/F TCH/HH TCH/HH TCH/HH *
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F - TCH/F TCH/HH TCH/HH TCH/HH *
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/HH TCH/HH TCH/HH *
+
+congestion-check
+expect-no-chan
+
diff --git a/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment_2.ho_vty b/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment_2.ho_vty
new file mode 100644
index 000000000..08bc151ec
--- /dev/null
+++ b/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment_2.ho_vty
@@ -0,0 +1,47 @@
+# If a handover from TCH/F to TCH/H frees a dynamic timeslot,
+# take the freed TCH/H from the soure timeslot into account,
+# both when the target is a dynamic timeslot and when the target is a static timeslot.
+
+create-bts trx-count 1 timeslots c+s4 dyn TCH/F TCH/F TCH/H PDCH PDCH PDCH
+
+network
+ bts 0
+ handover2 min-free-slots tch/f 3
+ handover2 min-free-slots tch/h 2
+ handover2 assignment 1
+
+set-ts-use trx 0 0 states * TCH/F - - - * * *
+# (there must be at least one measurement report on each lchan for congestion check to work)
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+congestion-check
+expect-as from lchan 0 0 1 0 to lchan 0 0 4 0
+expect-ts-use trx 0 0 states * pdch - - TCH/H- * * *
+
+# Again with one more TCH/H occupied, there will still be two free TCH/H after HO on the dyn TS
+set-ts-use trx 0 0 states * TCH/F - - TCH/H- * * *
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+congestion-check
+expect-as from lchan 0 0 1 0 to lchan 0 0 4 1
+expect-ts-use trx 0 0 states * pdch - - TCH/HH * * *
+
+# Again, with the target being a dyn TS
+create-bts trx-count 1 timeslots c+s4 dyn TCH/F TCH/F dyn PDCH PDCH PDCH
+
+network
+ bts 1
+ handover2 min-free-slots tch/f 3
+ handover2 min-free-slots tch/h 2
+ handover2 assignment 1
+
+set-ts-use trx 1 0 states * TCH/F TCH/F - pdch * * *
+meas-rep lchan 1 * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+congestion-check
+expect-as from lchan 1 0 1 0 to lchan 1 0 4 0
+expect-ts-use trx 1 0 states * pdch TCH/F - TCH/H- * * *
+
+# Again with one more TCH/H occupied, there will still be two free TCH/H after HO on the dyn TS
+set-ts-use trx 1 0 states * TCH/F TCH/F - TCH/H- * * *
+meas-rep lchan 1 * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+congestion-check
+expect-as from lchan 1 0 1 0 to lchan 1 0 4 1
+expect-ts-use trx 1 0 states * pdch TCH/F - TCH/HH * * *
diff --git a/tests/handover/test_dyn_ts_amr_tch_h_to_f_congestion_assignment_2.ho_vty b/tests/handover/test_dyn_ts_amr_tch_h_to_f_congestion_assignment_2.ho_vty
new file mode 100644
index 000000000..bf1edf22b
--- /dev/null
+++ b/tests/handover/test_dyn_ts_amr_tch_h_to_f_congestion_assignment_2.ho_vty
@@ -0,0 +1,27 @@
+# If a handover from TCH/H to TCH/F frees a dynamic timeslot,
+# take the freed TCH/F from the soure timeslot into account,
+# when the target is a static timeslot.
+
+create-bts trx-count 1 timeslots c+s4 dyn TCH/F TCH/F TCH/F PDCH PDCH PDCH
+
+network
+ bts 0
+ handover2 min-free-slots tch/f 2
+ handover2 min-free-slots tch/h 2
+ handover2 assignment 1
+
+set-ts-use trx 0 0 states * TCH/H- - - - * * *
+# (there must be at least one measurement report on each lchan for congestion check to work)
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+congestion-check
+expect-as from lchan 0 0 1 0 to lchan 0 0 2 0
+expect-ts-use trx 0 0 states * pdch TCH/F - - * * *
+
+# Again with one more TCH/F occupied, there will still be two free TCH/F after HO on the dyn TS
+set-ts-use trx 0 0 states * TCH/H- - - TCH/F * * *
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+congestion-check
+expect-as from lchan 0 0 1 0 to lchan 0 0 2 0
+expect-ts-use trx 0 0 states * pdch TCH/F - TCH/F * * *
+
+# (TCH/H -> TCH/F onto a dyn TS will always make TCH/H congestion worse, so there is no useful test case left here)
diff --git a/tests/handover/test_dyn_ts_balance_congestion.ho_vty b/tests/handover/test_dyn_ts_balance_congestion.ho_vty
new file mode 100644
index 000000000..2fa11b6b0
--- /dev/null
+++ b/tests/handover/test_dyn_ts_balance_congestion.ho_vty
@@ -0,0 +1,37 @@
+# To balance congestion, consider cross effects between TCH/F and TCH/H when occupying a dynamic timeslot in the target:
+# when balancing of TCH/F congestion would take up a dyn TS in the target, reducing TCH/H availability, the handover
+# should not cause worse TCH/H congestion than in the source cell.
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F dyn dyn PDCH
+
+# for this test, avoid changing a TCH/F to a TCH/H by using a non-AMR codec
+codec tch/f FR
+
+network
+ bts 0
+ handover2 min-free-slots tch/f 2
+ bts 1
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F pdch pdch *
+
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+# bts 0 is full for TCH/F. Looking at TCH/F, by target_overbooked_after_ho==75% < current_overbooked_before_ho==100%, a
+# congestion balancing to bts 1 would be performed. But the TCH/F on the target cell would occupy a dynamic timeslot.
+# That would reduce the TCH/H free slots by two and cause TCH/H being overbooked by 50%. On the source cell, TCH/H is
+# not congested. No handover is performed because 50% in the target is more congestion for TCH/H than 0% in the source
+# cell.
+congestion-check
+expect-no-chan
+
+# If there is no constraint on TCH/H in the target cell, the handover does take place.
+network
+ bts 1
+ handover2 min-free-slots tch/h 2
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0
diff --git a/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty b/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty
new file mode 100644
index 000000000..c5890a513
--- /dev/null
+++ b/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty
@@ -0,0 +1,74 @@
+# If a handover from one TCH kind to the other occupies a dynamic timeslot,
+# also adhere to congestion constraints of the other TCH kind, since taking up
+# a dyn TS may reduce the available slot count for both kinds of TCH.
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH
+
+# A TCH/F has better rxlev at a neighbor, and the neighbor's TCH/F slots would
+# not become congested. But taking up a neighbor's dynamic timeslot for TCH/F
+# would reduce the TCH/H availability to cause congestion on TCH/H. No HO.
+
+network
+ handover2 min-free-slots tch/f 0
+ handover2 min-free-slots tch/h 4
+
+set-ts-use trx 0 0 states * TCH/F - - pdch pdch pdch *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/HH pdch pdch *
+
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 40
+# no handover because that results in congestion on TCH/H in bts 1
+expect-no-chan
+
+###
+
+set-ts-use trx 0 0 states * - - - pdch pdch pdch *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/HH TCH/F pdch *
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+
+congestion-check
+expect-ho from lchan 1 0 4 1 to lchan 0 0 4 0
+expect-ts-use trx 0 0 states * - - - TCH/H- pdch pdch *
+expect-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/H- TCH/F pdch *
+
+
+###
+
+set-ts-use trx 0 0 states * - - - pdch pdch pdch *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/H- TCH/F TCH/F *
+
+congestion-check
+# more FAIL: TCH/H moves to worse bts 0 due to congestion
+expect-ho from lchan 1 0 4 0 to lchan 0 0 4 0
+expect-ts-use trx 0 0 states * - - - TCH/H- pdch pdch *
+expect-ts-use trx 1 0 states * TCH/F TCH/F TCH/F pdch TCH/F TCH/F *
+
+
+###
+
+set-ts-use trx 0 0 states * - - - TCH/H- pdch pdch *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F pdch TCH/F TCH/F *
+
+congestion-check
+expect-no-chan
+
+meas-rep lchan 1 * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+meas-rep lchan 0 * * * rxlev 30 rxqual 0 ta 0 neighbors 40
+# no HO because the target is congested on TCH/H. Moving to TCH/F would also
+# reduce TCH/H lchans because it would convert another dyn TS.
+expect-no-chan
+
+###
+
+set-ts-use trx 0 0 states * - - - pdch pdch pdch *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F *
+
+congestion-check
+# FAIL: TCH/F occupy dynamic timeslots -- should hand over to bts 0 to free a
+# dyn TS and reduce TCH/H congestion.
+expect-no-chan
diff --git a/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty b/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty
new file mode 100644
index 000000000..ef71d3e26
--- /dev/null
+++ b/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty
@@ -0,0 +1,34 @@
+# If a handover from one TCH kind to the other occupies a dynamic timeslot,
+# also adhere to congestion constraints of the other TCH kind, since taking up
+# a dyn TS may reduce the available slot count for both kinds of TCH.
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH
+
+# A TCH/H has better rxlev at a neighbor, and the neighbor's TCH/H slots would
+# not become congested. But taking up a neighbor's dynamic timeslot for TCH/H
+# would reduce the TCH/F availability to cause congestion on TCH/F. No HO.
+
+network
+ handover2 min-free-slots tch/f 2
+ handover2 min-free-slots tch/h 2
+
+set-ts-use trx 0 0 states * - - - TCH/H- pdch pdch *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/HH pdch pdch *
+
+meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+meas-rep lchan 0 0 4 0 rxlev 20 rxqual 0 ta 0 neighbors 40
+# no handover because that results in congestion on TCH/F in bts 1
+expect-no-chan
+
+
+# Now the same situation, except there already is a half occupied TCH/H, hence an added TCH/H would not change the TCH/F
+# situation. The handover is performed.
+
+set-ts-use trx 0 0 states * - - - TCH/H- pdch pdch *
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/HH TCH/H- pdch *
+
+meas-rep lchan 0 0 4 0 rxlev 20 rxqual 0 ta 0 neighbors 40
+expect-ho from lchan 0 0 4 0 to lchan 1 0 5 1
diff --git a/tests/handover/test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty b/tests/handover/test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty
new file mode 100644
index 000000000..73c236f20
--- /dev/null
+++ b/tests/handover/test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty
@@ -0,0 +1,12 @@
+# assign new MS: re-use half used TCH/H to avoid switching more dyn TS to TCH/H
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH
+set-ts-use trx 0 0 states * - - - pdch TCH/H- pdch pdch
+create-ms bts 0 TCH/H AMR
+expect-ts-use trx 0 0 states * - - - pdch TCH/HH pdch pdch
+
+# in static timeslots, there is NO preference to fill half-used TCH/H first
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH
+set-ts-use trx 1 0 states * - - - - TCH/H- - pdch
+create-ms bts 1 TCH/H AMR
+set-ts-use trx 1 0 states * - - - TCH/H- TCH/H- - pdch
diff --git a/tests/handover/test_dyn_ts_favor_moving_half_used_tch_h.ho_vty b/tests/handover/test_dyn_ts_favor_moving_half_used_tch_h.ho_vty
new file mode 100644
index 000000000..6548360e8
--- /dev/null
+++ b/tests/handover/test_dyn_ts_favor_moving_half_used_tch_h.ho_vty
@@ -0,0 +1,42 @@
+# Congestion check: favor moving a TCH/H that frees a half-used dyn TS completely.
+# The algorithm should notice that this is about moving an lchan within the same cell, so all candidates will remain
+# with unchanged rxlev after a re-assignment; hence the current rxlev for each candidate should not make a difference.
+
+create-bts trx-count 1 timeslots c+s4 TCH/F dyn dyn dyn dyn - -
+network
+ handover2 min-free-slots tch/h 6
+
+# Test with identical rxlev across lchans (trivial and unrealistic)
+set-ts-use trx 0 0 states * - TCH/HH TCH/H- TCH/HH pdch - -
+meas-rep lchan * * * * rxlev 30 rxqual 0 ta 0
+congestion-check
+expect-as from lchan 0 0 3 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F TCH/HH pdch TCH/HH pdch - -
+
+# clear measurements for the next run
+set-ts-use trx 0 0 states * - pdch pdch pdch pdch - -
+
+# Check that a weaker rxlev coming up earlier in the congestion checking loop does not override the favored half-used
+# TCH/H
+set-ts-use trx 0 0 states * - TCH/HH TCH/H- TCH/HH pdch - -
+meas-rep lchan 0 0 2 1 rxlev 30 rxqual 0 ta 0
+meas-rep lchan 0 0 3 0 rxlev 31 rxqual 0 ta 0
+meas-rep lchan 0 0 4 0 rxlev 32 rxqual 0 ta 0
+meas-rep lchan 0 0 4 1 rxlev 33 rxqual 0 ta 0
+congestion-check
+expect-as from lchan 0 0 3 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F TCH/HH pdch TCH/HH pdch - -
+
+# clear measurements for the next run
+set-ts-use trx 0 0 states * - pdch pdch pdch pdch - -
+
+# Check that a weaker rxlev coming up later in the congestion checking loop does not override the favored half-used
+# TCH/H
+set-ts-use trx 0 0 states * - TCH/HH TCH/H- TCH/HH pdch - -
+meas-rep lchan 0 0 2 1 rxlev 34 rxqual 0 ta 0
+meas-rep lchan 0 0 3 0 rxlev 33 rxqual 0 ta 0
+meas-rep lchan 0 0 4 0 rxlev 32 rxqual 0 ta 0
+meas-rep lchan 0 0 4 1 rxlev 31 rxqual 0 ta 0
+congestion-check
+expect-as from lchan 0 0 3 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F TCH/HH pdch TCH/HH pdch - -
diff --git a/tests/handover/test_dyn_ts_favor_static_ts_as_target.ho_vty b/tests/handover/test_dyn_ts_favor_static_ts_as_target.ho_vty
new file mode 100644
index 000000000..0c55a2c97
--- /dev/null
+++ b/tests/handover/test_dyn_ts_favor_static_ts_as_target.ho_vty
@@ -0,0 +1,38 @@
+# If both a static and a dynamic TCH/H (even without pchan switch!) are available, we always prefer static TS.
+create-bts trx-count 1 timeslots c+s4 dyn TCH/H dyn TCH/H dyn TCH/H PDCH
+
+network
+ bts 0
+ channel allocator mode set-all ascending
+
+set-ts-use trx 0 0 states * TCH/-H TCH/-H TCH/-H TCH/-H TCH/-H TCH/-H *
+
+# the dynamic timeslot is already in TCH/H mode, and needs no pchan switch. It appears first in the list, hence it would
+# be used first -- but we prefer using static TS when still available:
+create-ms bts 0 TCH/H AMR
+expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/-H TCH/-H TCH/-H TCH/-H *
+# ^
+
+# Interference ratings do NOT influence whether a static or dynamic lchan (even without pchan switch) is going to be
+# assigned.
+network
+ bts 0
+ channel allocator avoid-interference 1
+ interference-meas level-bounds -115 -109 -103 -97 -91 -85
+# 0 1 2 3 4 5
+
+# Here the dyn TS lchan happens to have less interference. But still the choice to prefer static over dynamic weighs
+# stronger. The static TS with least interference is picked.
+# dyn TCH/H dyn TCH/H dyn TCH/H
+expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/-H TCH/-H TCH/-H TCH/-H *
+res-ind trx 0 0 levels - 4- -- 1- 4- 3- 2- -
+create-ms bts 0 TCH/H AMR
+expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/-H TCH/-H TCH/-H TCH/HH *
+# ^
+create-ms bts 0 TCH/H AMR
+expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/-H TCH/HH TCH/-H TCH/HH *
+# ^
+# now only dynamic TS are left. The one dyn lchan with least interference is picked
+create-ms bts 0 TCH/H AMR
+expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/HH TCH/HH TCH/-H TCH/HH *
+# ^
diff --git a/tests/handover/test_ho_to_better_cell.ho_vty b/tests/handover/test_ho_to_better_cell.ho_vty
new file mode 100644
index 000000000..afa0a8871
--- /dev/null
+++ b/tests/handover/test_ho_to_better_cell.ho_vty
@@ -0,0 +1,8 @@
+# Handover to best better cell
+# The best neighbor cell is selected
+create-n-bts 7
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 20 21 18 20 23 19
+expect-ho from lchan 0 0 1 0 to lchan 5 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 5 0 states * TCH/F - - - - - -
diff --git a/tests/handover/test_ho_to_better_cell_2.ho_vty b/tests/handover/test_ho_to_better_cell_2.ho_vty
new file mode 100644
index 000000000..c3f554438
--- /dev/null
+++ b/tests/handover/test_ho_to_better_cell_2.ho_vty
@@ -0,0 +1,10 @@
+# Handover to best better cell
+# The best neighbor cell is selected
+
+create-n-bts 7
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 20 21 18 20 23 19
+expect-ho from lchan 0 0 1 0 to lchan 5 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 5 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_hysteresis.ho_vty b/tests/handover/test_hysteresis.ho_vty
new file mode 100644
index 000000000..5c06a360a
--- /dev/null
+++ b/tests/handover/test_hysteresis.ho_vty
@@ -0,0 +1,13 @@
+# Hysteresis
+# If neighbor cell is better, handover is only performed if the
+# amount of improvement is greater or equal hyteresis
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 27 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 26 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_insufficient_measurements.ho_vty b/tests/handover/test_insufficient_measurements.ho_vty
new file mode 100644
index 000000000..b67a2488e
--- /dev/null
+++ b/tests/handover/test_insufficient_measurements.ho_vty
@@ -0,0 +1,46 @@
+# No (or not enough) measurements for handover
+# Do not solve congestion in cell, because there is no measurement.
+# As soon as enough measurements available (1 in our case), perform
+# handover. Afterwards the old cell becomes congested and the new
+# cell is not. Do not perform handover until new measurements are
+# received.
+#
+# two cells, first in congested, but no handover:
+
+create-n-bts 2
+network
+ bts 0
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+congestion-check
+expect-no-chan
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+
+# send measurement and trigger congestion check:
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
+# congest the first cell and remove congestion from second cell:
+network
+ bts 0
+ handover2 min-free-slots tch/f 0
+ handover2 min-free-slots tch/h 0
+ bts 1
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+
+# no handover until measurements applied:
+congestion-check
+expect-no-chan
+meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+congestion-check
+expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+
diff --git a/tests/handover/test_keep_efr_codec.ho_vty b/tests/handover/test_keep_efr_codec.ho_vty
new file mode 100644
index 000000000..f1a9b40e6
--- /dev/null
+++ b/tests/handover/test_keep_efr_codec.ho_vty
@@ -0,0 +1,21 @@
+# TCH/F keeping with EFR codec
+# The MS is using full rate V2 codec, but the better cell is congested
+# at TCH/F slots. As the congestion is removed, the handover takes
+# place.
+
+create-n-bts 2
+network
+ bts 1
+ handover2 min-free-slots tch/f 4
+codec tch/f EFR
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+ handover2 min-free-slots tch/f 3
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_keep_fr_codec.ho_vty b/tests/handover/test_keep_fr_codec.ho_vty
new file mode 100644
index 000000000..4729d2514
--- /dev/null
+++ b/tests/handover/test_keep_fr_codec.ho_vty
@@ -0,0 +1,21 @@
+# TCH/F keeping with FR codec
+# The MS is using full rate V1 codec, but the better cell is congested
+# at TCH/F slots. As the congestion is removed, the handover takes
+# place.
+
+create-n-bts 2
+network
+ bts 1
+ handover2 min-free-slots tch/f 4
+codec tch/f FR
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+ handover2 min-free-slots tch/f 3
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_keep_hr_codec.ho_vty b/tests/handover/test_keep_hr_codec.ho_vty
new file mode 100644
index 000000000..2f514bf44
--- /dev/null
+++ b/tests/handover/test_keep_hr_codec.ho_vty
@@ -0,0 +1,20 @@
+# TCH/H keeping with HR codec
+# The MS is using half rate V1 codec, but the better cell is congested
+# at TCH/H slots. As the congestion is removed, the handover takes
+# place.
+
+create-n-bts 2
+network
+ bts 1
+ handover2 min-free-slots tch/h 4
+codec tch/h HR
+set-ts-use trx 0 0 states * - - - - TCH/H- - -
+meas-rep lchan 0 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+ handover2 min-free-slots tch/h 3
+meas-rep lchan 0 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 5 0 to lchan 1 0 5 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * - - - - TCH/H- - -
diff --git a/tests/handover/test_max_handovers.ho_vty b/tests/handover/test_max_handovers.ho_vty
new file mode 100644
index 000000000..76a9ea9e7
--- /dev/null
+++ b/tests/handover/test_max_handovers.ho_vty
@@ -0,0 +1,18 @@
+# No more parallel handovers, if max_unsync_ho is defined
+# There are three mobiles that want to handover, but only two can do
+# it at a time, because the maximum number is limited to two.
+
+create-n-bts 2
+network
+ bts 1
+ handover2 max-handovers 2
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - - - -
+meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-chan lchan 1 0 1 0
+expect-ho-cmd lchan 0 0 1 0
+meas-rep lchan 0 0 2 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-chan lchan 1 0 2 0
+expect-ho-cmd lchan 0 0 2 0
+meas-rep lchan 0 0 3 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
diff --git a/tests/handover/test_max_ta.ho_vty b/tests/handover/test_max_ta.ho_vty
new file mode 100644
index 000000000..56dbb19d1
--- /dev/null
+++ b/tests/handover/test_max_ta.ho_vty
@@ -0,0 +1,37 @@
+# Handover due to maximum TA exceeded
+# The MS in the current (best) cell has reached maximum allowed timing
+# advance. No handover is performed until the timing advance exceeds
+# it. The originating cell is still the best, but no handover is
+# performed back to that cell, because the penalty timer (due to
+# maximum allowed timing advance) is running.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+
+network
+ bts 0
+ handover2 maximum distance 5
+ handover2 penalty-time max-distance 17
+
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 5 neighbors 20
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 6 neighbors 20
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
+# Penalty timer after TA was exceeded is running, so no handover back to the better cell:
+meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 6 neighbors 30
+expect-no-chan
+
+wait 16
+# Penalty timer still running
+meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 6 neighbors 30
+expect-no-chan
+
+wait 1
+# Now 17 seconds have passed, timeout is done, and a handover is performed again.
+meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 6 neighbors 30
+expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
diff --git a/tests/handover/test_meas_rep_multi_band.ho_vty b/tests/handover/test_meas_rep_multi_band.ho_vty
new file mode 100644
index 000000000..d5fa62448
--- /dev/null
+++ b/tests/handover/test_meas_rep_multi_band.ho_vty
@@ -0,0 +1,47 @@
+# Test ARFCN parsing from measurement report in multi-band BSS (OS#5717)
+
+create-n-bts 5
+
+set-band bts 0 1800
+set-arfcn trx 0 0 600
+
+set-band bts 1 900
+set-arfcn trx 1 0 1000
+
+set-band bts 2 850
+set-arfcn trx 2 0 200
+
+set-band bts 3 900
+set-arfcn trx 3 0 0
+
+set-band bts 4 1800
+set-arfcn trx 4 0 800
+
+# Attach MS to BTS 0, BTS 1-4 are neighbors
+create-ms bts 0 TCH/F AMR
+
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+expect-ts-use trx 2 0 states * - - - - - - -
+expect-ts-use trx 3 0 states * - - - - - - -
+expect-ts-use trx 4 0 states * - - - - - - -
+
+# Send a measurement report where TRX with ARFCN=800 has the best rxqual. If
+# the BSC resolved the indexes in the measurement report correctly according to
+# 3GPP TS 04.08 § 10.5.2.20, then the neighbors are the following:
+# Sub list 1 (band == 1800, same band as the TRX where MS is attached):
+# IDX=0 ARFCN=800 BSIC=63 RXLEV=-75dBm (BTS 4)
+# Sub list 2 (other bands):
+# IDX=1 ARFCN=200 BSIC=63 RXLEV=-110dBm (BTS 2)
+# IDX=2 ARFCN=1000 BSIC=63 RXLEV=-110dBm (BTS 1)
+# IDX=3 ARFCN=0 BSIC=63 RXLEV=-110dBm (BTS 3; at the end because ARFCN=0)
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 35 0 0 0
+
+# If the BSC parsed the list correctly, it will request a handover to BTS 4.
+expect-ho from lchan 0 0 1 0 to lchan 4 0 1 0
+
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+expect-ts-use trx 2 0 states * - - - - - - -
+expect-ts-use trx 3 0 states * - - - - - - -
+expect-ts-use trx 4 0 states * TCH/F - - - - - -
diff --git a/tests/handover/test_min_rxlev_vs_congestion.ho_vty b/tests/handover/test_min_rxlev_vs_congestion.ho_vty
new file mode 100644
index 000000000..d43b89cfa
--- /dev/null
+++ b/tests/handover/test_min_rxlev_vs_congestion.ho_vty
@@ -0,0 +1,18 @@
+# Handover to congested cell, if RX level is below minimum
+# The better neighbor cell is congested, so no handover is performed.
+# If the RX level of the current cell drops below minimum acceptable
+# level, the handover is performed.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 1
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 9 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_min_rxlev_vs_hysteresis.ho_vty b/tests/handover/test_min_rxlev_vs_hysteresis.ho_vty
new file mode 100644
index 000000000..e034fadaa
--- /dev/null
+++ b/tests/handover/test_min_rxlev_vs_hysteresis.ho_vty
@@ -0,0 +1,20 @@
+# No Hysteresis and minimum RX level
+# If current cell's RX level is below mimium level, handover must be
+# performed, no matter of the hysteresis. First do not perform
+# handover to better neighbor cell, because the hysteresis is not
+# met. Second do not perform handover because better neighbor cell is
+# below minimum RX level. Third perform handover because current cell
+# is below minimum RX level, even if the better neighbor cell (minimum
+# RX level reached) does not meet the hysteresis.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 11
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 8 rxqual 0 ta 0 neighbors 9
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 9 rxqual 0 ta 0 neighbors 10
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_neighbor_congested.ho_vty b/tests/handover/test_neighbor_congested.ho_vty
new file mode 100644
index 000000000..b2319d40f
--- /dev/null
+++ b/tests/handover/test_neighbor_congested.ho_vty
@@ -0,0 +1,21 @@
+# No handover to congested cell
+# The better neighbor cell is congested, so no handover is performed.
+# After the congestion is over, handover will be performed.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 1
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+ handover2 min-free-slots tch/f 3
+ handover2 min-free-slots tch/h 3
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_neighbor_full.ho_vty b/tests/handover/test_neighbor_full.ho_vty
new file mode 100644
index 000000000..3b06d3dba
--- /dev/null
+++ b/tests/handover/test_neighbor_full.ho_vty
@@ -0,0 +1,9 @@
+# No handover to a cell with no slots available
+# If no slot is available, no handover is performed
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F TCH/HH TCH/HH -
+meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
diff --git a/tests/handover/test_no_congestion.ho_vty b/tests/handover/test_no_congestion.ho_vty
new file mode 100644
index 000000000..c8328c6f2
--- /dev/null
+++ b/tests/handover/test_no_congestion.ho_vty
@@ -0,0 +1,17 @@
+# Congestion check: No congestion
+# Three cells have different number of used slots, but there is no
+# congestion in any of these cells. No handover is performed.
+
+create-n-bts 3
+network
+ handover2 min-free-slots tch/f 2
+ handover2 min-free-slots tch/h 2
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - -
+set-ts-use trx 1 0 states * TCH/F - - - TCH/H- - -
+meas-rep lchan * * * * rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+congestion-check
+expect-no-chan
+expect-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - -
+expect-ts-use trx 1 0 states * TCH/F - - - TCH/H- - -
+
diff --git a/tests/handover/test_penalty_timer.ho_vty b/tests/handover/test_penalty_timer.ho_vty
new file mode 100644
index 000000000..8d864a2d4
--- /dev/null
+++ b/tests/handover/test_penalty_timer.ho_vty
@@ -0,0 +1,45 @@
+# Penalty timer must not run
+# The MS will try to handover to a better cell, but this will fail.
+# Even though the cell is still better, handover will not be performed
+# due to penalty timer after handover failure
+
+network
+ # set the timeout for LCHAN_ST_WAIT_AFTER_ERROR
+ timer X3111 5
+ # set penalty timeout
+ handover2 penalty-time failed-ho 23
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-chan lchan 1 0 1 0
+expect-ho-cmd lchan 0 0 1 0
+ho-failed
+# first BTS still services the call:
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+
+# lchan 1 0 1 0 is in LCHAN_ST_WAIT_AFTER_ERROR because the handover failed:
+expect-ts-use trx 1 0 states * ! - - - - - -
+wait 4
+expect-ts-use trx 1 0 states * ! - - - - - -
+wait 1
+expect-ts-use trx 1 0 states * - - - - - - -
+# back to UNUSED
+
+# No handover because the penalty timer is still running
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+wait 17
+# at this point, the penalty timer has not yet expired. (4+1+17 = 22 < 23)
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+
+wait 1
+# now the penalty timer of 23 seconds has passed and the handover is attempted again.
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
diff --git a/tests/handover/test_resource_indication.ho_vty b/tests/handover/test_resource_indication.ho_vty
new file mode 100644
index 000000000..724372ee9
--- /dev/null
+++ b/tests/handover/test_resource_indication.ho_vty
@@ -0,0 +1,67 @@
+# Test effects of interference levels reported in Resource Indication.
+# Note, this is not actually a handover test.
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH
+
+# By default, the ordering is most-interference-first
+network
+ bts 0
+ channel allocator avoid-interference 1
+ interference-meas level-bounds -85 -91 -97 -103 -109 -115
+# 0 1 2 3 4 5
+
+res-ind trx 0 0 levels - 1 2 3 4 3 2 -
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * - - - TCH/F - - *
+
+# The ordering may also be configured reversed, still the lowest dBm value should win
+network
+ bts 0
+ interference-meas level-bounds -115 -109 -103 -97 -91 -85
+# 0 1 2 3 4 5
+
+res-ind trx 0 0 levels - 5 4 2 - 3 4 -
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * - - TCH/F TCH/F - - *
+
+# Favor lchans that have an indicated interference level
+res-ind trx 0 0 levels - - - - - 4 3 -
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * - - TCH/F TCH/F - TCH/F *
+
+# For equal levels, pick the first
+res-ind trx 0 0 levels - 2 2 - - 2 - -
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * TCH/F - TCH/F TCH/F - TCH/F *
+
+# Test clamping of indexes > 5
+res-ind trx 0 0 levels - - 6 - - 4 - -
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * TCH/F - TCH/F TCH/F TCH/F TCH/F *
+
+# Also test for TCH/H
+create-bts trx-count 1 timeslots c+s4 TCH/H TCH/H TCH/H TCH/H TCH/H TCH/H PDCH
+network
+ bts 1
+ channel allocator avoid-interference 1
+ interference-meas level-bounds -115 -109 -103 -97 -91 -85
+# 0 1 2 3 4 5
+
+res-ind trx 1 0 levels - 54 32 21 23 45 54 -
+create-ms bts 1 TCH/H AMR
+expect-ts-use trx 1 0 states * - - TCH/-H - - - *
+
+# Favor lchans that have an indicated interference level
+res-ind trx 1 0 levels - - - 4- 3- - - -
+create-ms bts 1 TCH/H AMR
+expect-ts-use trx 1 0 states * - - TCH/-H TCH/H- - - *
+
+# For equal levels, pick the first
+res-ind trx 1 0 levels - -2 22 2- -2 22 2- -
+create-ms bts 1 TCH/H AMR
+expect-ts-use trx 1 0 states * TCH/-H - TCH/-H TCH/H- - - *
+
+# Test clamping of indexes > 5
+res-ind trx 1 0 levels - 7- 67 6- -7 54 6- -
+create-ms bts 1 TCH/H AMR
+expect-ts-use trx 1 0 states * TCH/-H - TCH/-H TCH/H- TCH/-H - *
diff --git a/tests/handover/test_rxqual.ho_vty b/tests/handover/test_rxqual.ho_vty
new file mode 100644
index 000000000..6f86cf47f
--- /dev/null
+++ b/tests/handover/test_rxqual.ho_vty
@@ -0,0 +1,49 @@
+# Handover to cell with worse RXLEV, if RXQUAL is below minimum
+# The neighbor cell has worse RXLEV, so no handover is performed.
+# If the RXQUAL of the current cell drops below minimum acceptable
+# level, the handover is performed. It is also required that 10
+# reports are received, before RXQUAL is checked.
+#
+# (See also test 28, which tests for RXQUAL triggering HO to congested cell.)
+#
+# TODO: bad RXQUAL may want to prefer assignment within the same cell to avoid interference.
+# See Performance Enhancements in a Frequency Hopping GSM Network (Nielsen Wigard 2002), Chapter
+# 2.1.1, "Interference" in the list of triggers on p.157.
+
+# first show undesired oscillation when penalty-time low-rxqual-ho is disabled
+network
+ handover2 penalty-time low-rxqual-ho 0
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep repeat 9 lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
+# Now the channel is on bts 1, which has lower rxlev than bts 0.
+# The result is an undesired ho oscillation, because the penalty timer is zero
+meas-rep lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 40
+expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0
+
+# Set a proper penalty timeout and report bad-rxqual again
+network
+ handover2 penalty-time low-rxqual-ho 10
+meas-rep repeat 10 lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+
+# This time the penalty timer prevents oscillation
+meas-rep repeat 10 lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 40
+expect-no-chan
+
+# After the penalty timeout passes, we do go back to the cell with stronger rxlev
+wait 10
+meas-rep lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 40
+expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0
+# If the rxqual is still bad here after the penalty timeout, well, then we quickly snap back to the weaker cell, once
+meas-rep repeat 10 lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+meas-rep repeat 10 lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 40
+expect-no-chan
diff --git a/tests/handover/test_rxqual_vs_congestion.ho_vty b/tests/handover/test_rxqual_vs_congestion.ho_vty
new file mode 100644
index 000000000..21c4e7e18
--- /dev/null
+++ b/tests/handover/test_rxqual_vs_congestion.ho_vty
@@ -0,0 +1,19 @@
+# Handover to congested cell, if RX quality is below minimum
+# The better neighbor cell is congested, so no handover is performed.
+# If the RX quality of the current cell drops below minimum acceptable
+# level, the handover is performed. It is also required that 10
+# resports are received, before RX quality is checked.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 1
+ handover2 min-free-slots tch/f 4
+ handover2 min-free-slots tch/h 4
+meas-rep repeat 9 lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test_stay_in_better_cell.ho_vty b/tests/handover/test_stay_in_better_cell.ho_vty
new file mode 100644
index 000000000..00e0e1a73
--- /dev/null
+++ b/tests/handover/test_stay_in_better_cell.ho_vty
@@ -0,0 +1,6 @@
+# Stay in better cell
+# There are many neighbor cells, but only the current cell is the best cell, so no handover is performed
+create-n-bts 7
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 21 18 20 23 19
+expect-no-chan
diff --git a/tests/handover/test_stay_in_better_cell_2.ho_vty b/tests/handover/test_stay_in_better_cell_2.ho_vty
new file mode 100644
index 000000000..b3e76f8fc
--- /dev/null
+++ b/tests/handover/test_stay_in_better_cell_2.ho_vty
@@ -0,0 +1,10 @@
+# Stay in better cell
+# There are many neighbor cells, but only the current cell is the best
+# cell, so no handover is performed
+
+create-n-bts 7
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 21 18 20 23 19
+expect-no-chan
+
diff --git a/tests/handover/test_story.ho_vty b/tests/handover/test_story.ho_vty
new file mode 100644
index 000000000..4e827610e
--- /dev/null
+++ b/tests/handover/test_story.ho_vty
@@ -0,0 +1,72 @@
+# Story: 'A neighbor is your friend'
+
+create-n-bts 3
+
+# Andreas is driving along the coast, on a sunny june afternoon.
+# Suddenly he is getting a call from his friend and neighbor Axel.
+#
+# What happens: Two MS are created, #0 for Axel, #1 for Andreas.
+# Axel:
+create-ms bts 2 TCH/F AMR
+# andreas:
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+expect-ts-use trx 2 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+# Axel asks Andreas if he would like to join them for a barbecue.
+# Axel's house is right in the neighborhood and the weather is fine.
+# Andreas agrees, so he drives to a close store to buy some barbecue
+# skewers.
+#
+# What happens: While driving, a different cell (mounted atop the
+# store) becomes better.
+# drive to bts 1:
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 35
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+expect-ts-use trx 2 0 states * TCH/F - - - - - -
+
+# While Andreas is walking into the store, Axel asks, if he could also
+# bring some beer. Andreas has problems understanding him: "I have a
+# bad reception here. The cell tower is right atop the store, but poor
+# coverage inside. Can you repeat please?"
+#
+# What happens: Inside the store the close cell is so bad, that
+# handover back to the previous cell is required.
+# bts 1 becomes bad, so bts 0 helps out:
+meas-rep lchan 1 0 1 0 rxlev 5 rxqual 0 ta 0 neighbors 20
+expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+expect-ts-use trx 2 0 states * TCH/F - - - - - -
+
+# After Andreas bought skewers and beer, he leaves the store.
+#
+# What happens: Outside the store the close cell is better again, so
+# handover back to the that cell is performed.
+# bts 1 becomes better again:
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 35
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+expect-ts-use trx 2 0 states * TCH/F - - - - - -
+
+# bts 2 becomes better:
+# Andreas drives down to the lake where Axel's house is.
+#
+# What happens: There is a small cell at Axel's house, which becomes
+# better, because the current cell has no good comverage at the lake.
+meas-rep lchan 1 0 1 0 rxlev 14 rxqual 0 ta 0 neighbors 2 63
+expect-ho from lchan 1 0 1 0 to lchan 2 0 2 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+expect-ts-use trx 2 0 states * TCH/F TCH/F - - - - -
+
+# Andreas wonders why he still has good radio coverage: "Last time it
+# was so bad". Axel says: "I installed a pico cell in my house,
+# now we can use our mobile phones down here at the lake."
+
diff --git a/tests/handover_cfg.vty b/tests/handover_cfg.vty
index 8fdaf1f08..0f1013319 100644
--- a/tests/handover_cfg.vty
+++ b/tests/handover_cfg.vty
@@ -6,7 +6,7 @@ OsmoBSC> enable
OsmoBSC# ### No handover config present
OsmoBSC# show running-config
-... !handover
+... !^\s+handover
OsmoBSC# ### Toggling handover on network level affects 'show network':
OsmoBSC# configure terminal
@@ -44,17 +44,17 @@ OsmoBSC(config-net)# ### HO is 'on' globally, bts 0 disables it, bts 1 tweaks a
OsmoBSC(config-net)# show running-config
...
network
-... !handover
+... !^\s+handover
handover 1
-... !handover
+... !^\s+handover
bts 0
-... !handover
+... !^\s+handover
handover 0
-... !handover
+... !^\s+handover
bts 1
-... !handover
+... !^\s+handover
handover1 power budget interval 23
-... !handover
+... !^\s+handover
OsmoBSC(config-net)# ### Set global default to 'off', now bts 1 also uses the global default of 'off':
OsmoBSC(config-net)# handover 0
@@ -65,17 +65,17 @@ OsmoBSC(config-net)# do show network
OsmoBSC(config-net)# show running-config
...
network
-... !handover
+... !^\s+handover
handover 0
-... !handover
+... !^\s+handover
bts 0
-... !handover
+... !^\s+handover
handover 0
-... !handover
+... !^\s+handover
bts 1
-... !handover
+... !^\s+handover
handover1 power budget interval 23
-... !handover
+... !^\s+handover
OsmoBSC(config-net)# ### Remove the global setting, i.e. use the factory default net level, with same effect:
OsmoBSC(config-net)# handover default
@@ -89,15 +89,15 @@ OsmoBSC(config-net)# do show network
OsmoBSC(config-net)# show running-config
...
network
-... !handover
+... !^\s+handover
bts 0
-... !handover
+... !^\s+handover
handover 0
-... !handover
+... !^\s+handover
bts 1
-... !handover
+... !^\s+handover
handover1 power budget interval 23
-... !handover
+... !^\s+handover
OsmoBSC(config-net)# ### Re-enable net-level handover, but bts 0 remains disabled explicitly
OsmoBSC(config-net)# handover 1
@@ -108,17 +108,17 @@ OsmoBSC(config-net)# do show network
OsmoBSC(config-net)# show running-config
...
network
-... !handover
+... !^\s+handover
handover 1
-... !handover
+... !^\s+handover
bts 0
-... !handover
+... !^\s+handover
handover 0
-... !handover
+... !^\s+handover
bts 1
-... !handover
+... !^\s+handover
handover1 power budget interval 23
-... !handover
+... !^\s+handover
OsmoBSC(config-net)# ### Remove explicit setting of bts 0 to also use the global setting:
OsmoBSC(config-net)# bts 0
@@ -133,15 +133,15 @@ OsmoBSC(config-net-bts)# do show network
OsmoBSC(config-net-bts)# show running-config
...
network
-... !handover
+... !^\s+handover
handover 1
-... !handover
+... !^\s+handover
bts 0
-... !handover
+... !^\s+handover
bts 1
-... !handover
+... !^\s+handover
handover1 power budget interval 23
-... !handover
+... !^\s+handover
OsmoBSC(config-net-bts)# ### Verify that 'min rxlev' value range stops at -50
OsmoBSC(config-net-bts)# handover2 min rxlev ?
@@ -178,7 +178,7 @@ OsmoBSC(config-net)# list
handover2 power budget hysteresis (<0-999>|default)
handover2 maximum distance (<0-9999>|default)
handover2 assignment (0|1|default)
- handover2 tdma-measurement (full|subset|default)
+ handover2 tdma-measurement (auto|full|subset|default)
handover2 min rxlev (<-110--50>|default)
handover2 min rxqual (<0-7>|default)
handover2 afs-bias rxlev (<0-20>|default)
@@ -189,6 +189,8 @@ OsmoBSC(config-net)# list
handover2 penalty-time max-distance (<0-99999>|default)
handover2 penalty-time failed-ho (<0-99999>|default)
handover2 penalty-time failed-assignment (<0-99999>|default)
+ handover2 penalty-time low-rxqual-assignment (<0-99999>|default)
+ handover2 penalty-time low-rxqual-ho (<0-99999>|default)
handover2 retries (<0-9>|default)
handover2 congestion-check (disabled|<1-999>|now)
...
@@ -335,20 +337,21 @@ OsmoBSC(config-net)# handover2 assignment ?
default Use default (0), remove explicit setting on this node
OsmoBSC(config-net)# handover2 tdma-measurement ?
+ auto Use full set when DTX is not in use, use subset when DTX is in use, as indicated by each Measurement Report
full Full set of 102/104 TDMA frames
subset Sub set of 4 TDMA frames (SACCH)
default Use default (subset), remove explicit setting on this node
OsmoBSC(config-net)# handover2 min ?
rxlev How weak may RxLev of an MS become before triggering HO
- rxqual How bad may RxQual of an MS become before triggering HO
+ rxqual 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.
OsmoBSC(config-net)# handover2 min rxlev ?
<-110--50> minimum RxLev (dBm; note: negative values)
default Use default (-100), remove explicit setting on this node
OsmoBSC(config-net)# handover2 min rxqual ?
- <0-7> minimum RxQual
+ <0-7> worst acceptable RxQual
default Use default (5), remove explicit setting on this node
OsmoBSC(config-net)# handover2 afs-bias ?
@@ -383,9 +386,11 @@ OsmoBSC(config-net)# handover2 max-handovers ?
default Use default (9999), remove explicit setting on this node
OsmoBSC(config-net)# handover2 penalty-time ?
- max-distance Time to suspend handover for a subscriber after leaving this cell due to exceeding max distance; see also 'handover2 retries'
- failed-ho Time to suspend handover for a subscriber after a failed handover into this cell; see also 'handover2 retries'
- failed-assignment Time to suspend handover for a subscriber after a failed re-assignment within this cell; see also 'handover2 retries'
+ max-distance Time to suspend handover for a subscriber after leaving this cell due to exceeding max distance; see also 'handover2 retries'
+ failed-ho Time to suspend handover for a subscriber after a failed handover into this cell; see also 'handover2 retries'
+ failed-assignment Time to suspend handover for a subscriber after a failed re-assignment within this cell; see also 'handover2 retries'
+ low-rxqual-assignment Time to suspend re-assignment after an lchan was re-assigned because of low RxQual
+ low-rxqual-ho Time to suspend handover back to a cell after bad RxQual caused handover away from it
OsmoBSC(config-net)# handover2 penalty-time max-distance ?
<0-99999> Seconds
@@ -399,6 +404,10 @@ OsmoBSC(config-net)# handover2 penalty-time failed-assignment ?
<0-99999> Seconds
default Use default (60), remove explicit setting on this node
+OsmoBSC(config-net)# handover2 penalty-time low-rxqual-assignment ?
+ <0-99999> Seconds
+ default Use default (60), remove explicit setting on this node
+
OsmoBSC(config-net)# handover2 retries ?
<0-9> Number of retries
default Use default (0), remove explicit setting on this node
@@ -553,20 +562,21 @@ OsmoBSC(config-net-bts)# handover2 assignment ?
default Use default (0), remove explicit setting on this node
OsmoBSC(config-net-bts)# handover2 tdma-measurement ?
+ auto Use full set when DTX is not in use, use subset when DTX is in use, as indicated by each Measurement Report
full Full set of 102/104 TDMA frames
subset Sub set of 4 TDMA frames (SACCH)
default Use default (subset), remove explicit setting on this node
OsmoBSC(config-net-bts)# handover2 min ?
rxlev How weak may RxLev of an MS become before triggering HO
- rxqual How bad may RxQual of an MS become before triggering HO
+ rxqual 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.
OsmoBSC(config-net-bts)# handover2 min rxlev ?
<-110--50> minimum RxLev (dBm; note: negative values)
default Use default (-100), remove explicit setting on this node
OsmoBSC(config-net-bts)# handover2 min rxqual ?
- <0-7> minimum RxQual
+ <0-7> worst acceptable RxQual
default Use default (5), remove explicit setting on this node
OsmoBSC(config-net-bts)# handover2 afs-bias ?
@@ -601,9 +611,11 @@ OsmoBSC(config-net-bts)# handover2 max-handovers ?
default Use default (9999), remove explicit setting on this node
OsmoBSC(config-net-bts)# handover2 penalty-time ?
- max-distance Time to suspend handover for a subscriber after leaving this cell due to exceeding max distance; see also 'handover2 retries'
- failed-ho Time to suspend handover for a subscriber after a failed handover into this cell; see also 'handover2 retries'
- failed-assignment Time to suspend handover for a subscriber after a failed re-assignment within this cell; see also 'handover2 retries'
+ max-distance Time to suspend handover for a subscriber after leaving this cell due to exceeding max distance; see also 'handover2 retries'
+ failed-ho Time to suspend handover for a subscriber after a failed handover into this cell; see also 'handover2 retries'
+ failed-assignment Time to suspend handover for a subscriber after a failed re-assignment within this cell; see also 'handover2 retries'
+ low-rxqual-assignment Time to suspend re-assignment after an lchan was re-assigned because of low RxQual
+ low-rxqual-ho Time to suspend handover back to a cell after bad RxQual caused handover away from it
OsmoBSC(config-net-bts)# handover2 penalty-time max-distance ?
<0-99999> Seconds
@@ -617,6 +629,10 @@ OsmoBSC(config-net-bts)# handover2 penalty-time failed-assignment ?
<0-99999> Seconds
default Use default (60), remove explicit setting on this node
+OsmoBSC(config-net-bts)# handover2 penalty-time low-rxqual-assignment ?
+ <0-99999> Seconds
+ default Use default (60), remove explicit setting on this node
+
OsmoBSC(config-net-bts)# handover2 retries ?
<0-9> Number of retries
default Use default (0), remove explicit setting on this node
diff --git a/tests/interf_meas.vty b/tests/interf_meas.vty
new file mode 100644
index 000000000..c6d8fb4e9
--- /dev/null
+++ b/tests/interf_meas.vty
@@ -0,0 +1,42 @@
+OsmoBSC> enable
+
+OsmoBSC# ### Default configuration
+OsmoBSC# show running-config
+... !interference-meas
+
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+
+OsmoBSC(config-net-bts)# interference-meas?
+ interference-meas Interference measurement parameters
+OsmoBSC(config-net-bts)# interference-meas ?
+ avg-period Averaging period (Intave)
+ level-bounds Interference level Boundaries. 3GPP do not specify whether these should be in ascending or descending order (3GPP TS 48.058 9.3.21 / 3GPP TS 52.021 9.4.25). OsmoBSC supports either ordering, but possibly some BTS models only return meaningful interference levels with one specific ordering.
+
+OsmoBSC(config-net-bts)# ### Averaging period
+OsmoBSC(config-net-bts)# interference-meas avg-period ?
+ <1-31> Number of SACCH multiframes
+OsmoBSC(config-net-bts)# interference-meas avg-period 0
+% Unknown command.
+OsmoBSC(config-net-bts)# interference-meas avg-period 30
+OsmoBSC(config-net-bts)# show running-config
+... !interference-meas
+ bts 0
+... !interference-meas
+ interference-meas avg-period 30
+... !interference-meas
+
+OsmoBSC(config-net-bts)# ### Interference level Boundaries
+OsmoBSC(config-net-bts)# interference-meas level-bounds ?
+ <-120-0> Interference boundary 0 (dBm)
+OsmoBSC(config-net-bts)# interference-meas level-bounds -85 -90 -95 -100 -105 ?
+ <-120-0> Interference boundary X5 (dBm)
+OsmoBSC(config-net-bts)# interference-meas level-bounds -85 -90 -95 -100 -105 -110
+OsmoBSC(config-net-bts)# show running-config
+... !interference-meas
+ bts 0
+... !interference-meas
+ interference-meas avg-period 30
+ interference-meas level-bounds -85 -90 -95 -100 -105 -110
+... !interference-meas
diff --git a/tests/msc.vty b/tests/msc.vty
new file mode 100644
index 000000000..74eca2a3f
--- /dev/null
+++ b/tests/msc.vty
@@ -0,0 +1,119 @@
+OsmoBSC> enable
+
+OsmoBSC# configure terminal
+OsmoBSC(config)# msc 0
+
+OsmoBSC(config-msc)# codec-list?
+ codec-list Set the allowed audio codecs and their order of preference
+OsmoBSC(config-msc)# codec-list ?
+ LIST List of audio codecs in order of preference, e.g. 'codec-list fr3 fr2 fr1 hr3 hr1'. (fr3: AMR-FR, hr3: AMR-HR, fr2: GSM-EFR, fr1: GSM-FR, hr1: GSM-HR)
+
+OsmoBSC(config-msc)# # Default list -- should match the default in osmo_msc_data_alloc()
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list fr1 hr1 fr2 fr3 hr3
+...
+
+OsmoBSC(config-msc)# # Change order
+OsmoBSC(config-msc)# codec-list fr3 hr3 fr2 fr1 hr1
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list fr3 hr3 fr2 fr1 hr1
+...
+OsmoBSC(config-msc)# codec-list hr1 hr3 fr1 fr2 fr3
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list hr1 hr3 fr1 fr2 fr3
+...
+
+OsmoBSC(config-msc)# codec-list foo
+Codec name must be hrX or frX. Was 'foo'
+
+OsmoBSC(config-msc)# codec-list fr10
+Codec name must be hrX or frX. Was 'fr10'
+
+OsmoBSC(config-msc)# codec-list hr10
+Codec name must be hrX or frX. Was 'hr10'
+
+OsmoBSC(config-msc)# codec-list FR1
+Codec name must be hrX or frX. Was 'FR1'
+
+OsmoBSC(config-msc)# # Ensure the codec-list with wrong args did not change the config
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list hr1 hr3 fr1 fr2 fr3
+...
+
+OsmoBSC(config-msc)# codec-list fr1 fr1
+duplicate entry in 'msc' / 'codec-support': fr1
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list hr1 hr3 fr1 fr2 fr3
+...
+
+OsmoBSC(config-msc)# codec-list fr0 fr1
+'fr0' is not a valid codec version
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list hr1 hr3 fr1 fr2 fr3
+...
+
+OsmoBSC(config-msc)# codec-list hr0 hr1
+'hr0' is not a valid codec version
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list hr1 hr3 fr1 fr2 fr3
+...
+
+OsmoBSC(config-msc)# codec-list fr8 fr9
+'fr8' is not a valid codec version
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list hr1 hr3 fr1 fr2 fr3
+...
+
+OsmoBSC(config-msc)# codec-list hr8 hr9
+'hr8' is not a valid codec version
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list hr1 hr3 fr1 fr2 fr3
+...
+
+OsmoBSC(config-msc)# codec-list fr2 hr2
+'hr2' is not a valid codec version
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list hr1 hr3 fr1 fr2 fr3
+...
+
+OsmoBSC(config-msc)# codec-list fr1 fr2 fr3 fr4
+OsmoBSC(config-msc)# show running-config
+...
+msc 0
+...
+ codec-list fr1 fr2 fr3 fr4
+...
+OsmoBSC(config-msc)# # TODO: should fr4 thru fr7 be rejected
+
+OsmoBSC(config-msc)# codec-list fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1 fr1
+Too many items in 'msc' / 'codec-list': 17. There can be at most 16 entries.
diff --git a/tests/nanobts_omlattr/Makefile.am b/tests/nanobts_omlattr/Makefile.am
index 1cdcc50a1..8c643561a 100644
--- a/tests/nanobts_omlattr/Makefile.am
+++ b/tests/nanobts_omlattr/Makefile.am
@@ -8,9 +8,11 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
nanobts_omlattr_test \
$(NULL)
@@ -23,12 +25,7 @@ nanobts_omlattr_test_SOURCES = \
$(NULL)
nanobts_omlattr_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/abis_nm.o \
- $(top_builddir)/src/osmo-bsc/acc.o \
- $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.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 \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.c b/tests/nanobts_omlattr/nanobts_omlattr_test.c
index da220c1f4..ba58e45ad 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.c
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.c
@@ -25,6 +25,7 @@
#include <osmocom/bsc/bts.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/tdef.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/sockaddr_str.h>
@@ -32,153 +33,84 @@
#include <stdio.h>
#include <string.h>
-struct gsm_bts_model bts_model_nanobts = {
- .type = GSM_BTS_TYPE_NANOBTS,
- .name = "nanobts",
- .start = NULL,
- .oml_rcvmsg = NULL,
- .e1line_bind_ops = NULL,
- .nm_att_tlvdef = {
- .def = {
- /* ip.access specifics */
- [NM_ATT_IPACC_DST_IP] = {TLV_TYPE_FIXED, 4},
- [NM_ATT_IPACC_DST_IP_PORT] =
- {TLV_TYPE_FIXED, 2},
- [NM_ATT_IPACC_STREAM_ID] = {TLV_TYPE_TV,},
- [NM_ATT_IPACC_SEC_OML_CFG] =
- {TLV_TYPE_FIXED, 6},
- [NM_ATT_IPACC_IP_IF_CFG] =
- {TLV_TYPE_FIXED, 8},
- [NM_ATT_IPACC_IP_GW_CFG] =
- {TLV_TYPE_FIXED, 12},
- [NM_ATT_IPACC_IN_SERV_TIME] =
- {TLV_TYPE_FIXED, 4},
- [NM_ATT_IPACC_LOCATION] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_PAGING_CFG] =
- {TLV_TYPE_FIXED, 2},
- [NM_ATT_IPACC_UNIT_ID] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_UNIT_NAME] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_SNMP_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_PRIM_OML_CFG_LIST] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NV_FLAGS] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_FREQ_CTRL] =
- {TLV_TYPE_FIXED, 2},
- [NM_ATT_IPACC_PRIM_OML_FB_TOUT] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_CUR_SW_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_TIMING_BUS] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_CGI] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_RAC] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_OBJ_VERSION] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_GPRS_PAGING_CFG] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NSEI] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_BVCI] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NSVCI] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NS_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_BSSGP_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_NS_LINK_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_RLC_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_ALM_THRESH_LIST] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_MONIT_VAL_LIST] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_TIB_CONTROL] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_SUPP_FEATURES] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_CODING_SCHEMES] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_RLC_CFG_2] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_HEARTB_TOUT] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_UPTIME] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_RLC_CFG_3] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_SSL_CFG] = {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_SEC_POSSIBLE] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_IML_SSL_STATE] =
- {TLV_TYPE_TL16V},
- [NM_ATT_IPACC_REVOC_DATE] = {TLV_TYPE_TL16V},
- },
- },
-};
+extern struct gsm_bts_model bts_model_nanobts;
-static void test_nanobts_attr_bts_get(struct gsm_bts *bts, uint8_t *expected)
+static void test_nanobts_gen_set_bts_attr(struct gsm_bts *bts, uint8_t *expected)
{
struct msgb *msgb;
- printf("Testing nanobts_attr_bts_get()...\n");
+ printf("Testing nanobts_gen_set_bts_attr()...\n");
- msgb = nanobts_attr_bts_get(bts);
+ msgb = nanobts_gen_set_bts_attr(bts);
printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len));
printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len));
- OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0);
+ OSMO_ASSERT(msgb_eq_data_print(msgb, expected, msgb->len));
msgb_free(msgb);
printf("ok.\n");
printf("\n");
}
-static void test_nanobts_attr_nse_get(struct gsm_bts *bts, uint8_t *expected)
+static void test_nanobts_gen_set_nse_attr(struct gsm_bts *bts, uint8_t *expected)
{
struct msgb *msgb;
- printf("Testing nanobts_attr_nse_get()...\n");
+ printf("Testing nanobts_gen_set_nse_attr()...\n");
- msgb = nanobts_attr_nse_get(bts);
+ msgb = nanobts_gen_set_nse_attr(bts->site_mgr);
printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len));
printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len));
- OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0);
+ OSMO_ASSERT(msgb_eq_data_print(msgb, expected, msgb->len));
msgb_free(msgb);
printf("ok.\n");
printf("\n");
}
-static void test_nanobts_attr_cell_get(struct gsm_bts *bts, uint8_t *expected)
+static void test_nanobts_gen_set_cell_attr(struct gsm_bts *bts, uint8_t *expected)
{
struct msgb *msgb;
- printf("Testing nanobts_attr_cell_get()...\n");
+ printf("Testing nanobts_gen_set_cell_attr()...\n");
- msgb = nanobts_attr_cell_get(bts);
+ msgb = nanobts_gen_set_cell_attr(bts);
printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len));
printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len));
- OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0);
+ OSMO_ASSERT(msgb_eq_data_print(msgb, expected, msgb->len));
msgb_free(msgb);
printf("ok.\n");
printf("\n");
}
-static void test_nanobts_attr_nscv_get(struct gsm_bts *bts, uint8_t *expected)
+static void test_nanobts_gen_set_nsvc_attr(struct gsm_bts *bts, uint8_t *expected)
{
struct msgb *msgb;
- printf("Testing nanobts_attr_nscv_get()...\n");
+ printf("Testing nanobts_gen_set_nsvc_attr()...\n");
- msgb = nanobts_attr_nscv_get(bts);
+ msgb = nanobts_gen_set_nsvc_attr(&bts->site_mgr->gprs.nsvc[0]);
printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len));
printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len));
- OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0);
+ OSMO_ASSERT(msgb_eq_data_print(msgb, expected, msgb->len));
msgb_free(msgb);
printf("ok.\n");
printf("\n");
}
-static void test_nanobts_attr_radio_get(struct gsm_bts *bts,
+static void test_nanobts_gen_set_radio_attr(struct gsm_bts *bts,
struct gsm_bts_trx *trx,
uint8_t *expected)
{
struct msgb *msgb;
- printf("Testing nanobts_attr_nscv_get()...\n");
+ printf("Testing nanobts_gen_set_nsvc_attr()...\n");
- msgb = nanobts_attr_radio_get(bts, trx);
+ msgb = nanobts_gen_set_radio_attr(bts, trx);
printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len));
printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len));
- OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0);
+ OSMO_ASSERT(msgb_eq_data_print(msgb, expected, msgb->len));
msgb_free(msgb);
printf("ok.\n");
@@ -194,9 +126,11 @@ static const struct log_info log_info = {
};
static struct osmo_tdef gsm_network_T_defs[] = {
- { .T=3105, .default_val=100, .val=13, .unit=OSMO_TDEF_MS, .desc="Physical Information" },
- { .T=3212, .default_val=5, .unit=OSMO_TDEF_CUSTOM,
- .desc="Periodic Location Update timer, sent to MS (1 = 6 minutes)" },
+ { .T = 3105, .default_val = GSM_T3105_DEFAULT, .val = GSM_T3105_DEFAULT, .min_val = 0, .max_val = UINT8_MAX, .unit = OSMO_TDEF_MS, .desc = "Physical Information" },
+ { .T = 3212, .default_val = 5, .unit = OSMO_TDEF_CUSTOM, .min_val = 0, .max_val = UINT8_MAX,
+ .desc = "Periodic Location Update timer, sent to MS (1 = 6 minutes)" },
+ { .T = -3105, .default_val = GSM_NY1_DEFAULT, .val = GSM_NY1_DEFAULT, .min_val = 0, .max_val = UINT8_MAX, .unit = OSMO_TDEF_CUSTOM,
+ .desc = "Ny1: Maximum number of Physical Information (re)transmissions" },
{}
};
@@ -223,27 +157,44 @@ int main(int argc, char **argv)
bts->network = net;
trx = talloc_zero(ctx, struct gsm_bts_trx);
- /* Parameters needed by nanobts_attr_bts_get() */
+ /* Parameters needed by nanobts_gen_set_bts_attr() */
bts->rach_b_thresh = -1;
bts->rach_ldavg_slots = -1;
bts->c0->arfcn = 866;
bts->cell_identity = 1337;
bts->network->plmn = (struct osmo_plmn_id){ .mcc=1, .mnc=1 };
- bts->location_area_code = 1;
+ bts->location_area_code = 0x0001;
bts->gprs.rac = 0;
uint8_t attr_bts_expected[] =
- { 0x19, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, 0x18, 0x06, 0x0e, 0x00,
- 0x02, 0x01, 0x20, 0x33, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21,
- 0xa8, 0x1f, 0x3f, 0x25,
- 0x00, 0x01, 0x0a, 0x0c, 0x0a, 0x0b, 0x01, 0x2a, 0x5a, 0x2b,
- 0x03, 0xe8, 0x0a, 0x0d,
- 0x23, 0x0a, 0x08, 0x03, 0x62, 0x09, 0x3f, 0x99, 0x00, 0x07,
- 0x00, 0xf1, 0x10, 0x00,
- 0x01, 0x05, 0x39
+ { 0x19, 0x73, 0x6d, 0x67, 0x61, 0x5b, 0x55,
+ /* 0x18 Intave Parameter */
+ 0x18, 0x06,
+ /* 0x0e: Connection Failure Criterion, rlt == 0x20 */
+ 0x0e, 0x00, 0x02, 0x01, 0x20,
+ /* 0x33: T200 */
+ 0x33, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8,
+ /* 0x1f: Max Timing Advance */
+ 0x1f, 0x3f,
+ /* 0x25: Overload Period */
+ 0x25, 0x00, 0x01, 0x0a,
+ /* 0x0c CCCH Load Threshold */
+ 0x0c, 0x0a,
+ /* 0x0b CCCH Load Indication Period */
+ 0x0b, 0x01,
+ /* 0x2a: RACH Busy Threshold */
+ 0x2a, 0x5a,
+ /* 0x2b: RACH Load Averaging Slots */
+ 0x2b, 0x03, 0xe8,
+ /* 0x0a: BTS Air Timer */
+ 0x0a, 0x0a,
+ /* Ny1 */
+ 0x23, 0x11,
+ 0x08, 0x03, 0x62, 0x09, 0x3f, 0x99, 0x00, 0x07,
+ 0x00, 0xf1, 0x10, 0x00, 0x01, 0x05, 0x39
};
- /* Parameters needed to test nanobts_attr_nse_get() */
- bts->gprs.nse.nsei = 101;
+ /* Parameters needed to test nanobts_gen_set_nse_attr() */
+ bts->site_mgr->gprs.nse.nsei = 101;
uint8_t attr_nse_expected[] =
{ 0x9d, 0x00, 0x02, 0x00, 0x65, 0xa0, 0x00, 0x07, 0x03, 0x03, 0x03,
0x03, 0x1e, 0x03, 0x0a, 0xa1, 0x00, 0x0b, 0x03, 0x03, 0x03,
@@ -251,7 +202,7 @@ int main(int argc, char **argv)
0x0a, 0x03, 0x0a, 0x03
};
- /* Parameters needed to test nanobts_attr_cell_get() */
+ /* Parameters needed to test nanobts_gen_set_cell_attr() */
bts->gprs.rac = 0x00;
bts->gprs.cell.bvci = 2;
bts->gprs.mode = BTS_GPRS_GPRS;
@@ -263,18 +214,18 @@ int main(int argc, char **argv)
0xfa, 0x00, 0xfa, 0x02
};
- /* Parameters needed to test nanobts_attr_nscv_get() */
+ /* Parameters needed to test nanobts_gen_set_nsvc_attr() */
struct osmo_sockaddr_str addr;
osmo_sockaddr_str_from_str(&addr, "10.9.1.101", 23000);
- osmo_sockaddr_str_to_sockaddr(&addr, &bts->gprs.nsvc[0].remote.u.sas);
- bts->gprs.nsvc[0].nsvci = 0x65;
- bts->gprs.nsvc[0].local_port = 0x5a3c;
+ osmo_sockaddr_str_to_sockaddr(&addr, &bts->site_mgr->gprs.nsvc[0].remote.u.sas);
+ bts->site_mgr->gprs.nsvc[0].nsvci = 0x65;
+ bts->site_mgr->gprs.nsvc[0].local_port = 0x5a3c;
uint8_t attr_nscv_expected[] =
{ 0x9f, 0x00, 0x02, 0x00, 0x65, 0xa2, 0x00, 0x08, 0x59, 0xd8, 0x0a,
0x09, 0x01, 0x65, 0x5a, 0x3c
};
- /* Parameters needed to test nanobts_attr_radio_get() */
+ /* Parameters needed to test nanobts_gen_set_radio_attr() */
trx->arfcn = 866;
trx->max_power_red = 22;
bts->c0->max_power_red = 22;
@@ -282,18 +233,18 @@ int main(int argc, char **argv)
{ 0x2d, 0x0b, 0x05, 0x00, 0x02, 0x03, 0x62 };
/* Run tests */
- test_nanobts_attr_bts_get(bts, attr_bts_expected);
- test_nanobts_attr_nse_get(bts, attr_nse_expected);
- test_nanobts_attr_cell_get(bts, attr_cell_expected);
- test_nanobts_attr_nscv_get(bts, attr_nscv_expected);
- test_nanobts_attr_radio_get(bts, trx, attr_radio_expected);
+ test_nanobts_gen_set_bts_attr(bts, attr_bts_expected);
+ test_nanobts_gen_set_nse_attr(bts, attr_nse_expected);
+ test_nanobts_gen_set_cell_attr(bts, attr_cell_expected);
+ test_nanobts_gen_set_nsvc_attr(bts, attr_nscv_expected);
+ test_nanobts_gen_set_radio_attr(bts, trx, attr_radio_expected);
/* NSVC IPv6 test */
struct osmo_sockaddr_str addr6;
osmo_sockaddr_str_from_str(&addr6, "fd00:5678:9012:3456:7890:1234:5678:9012", 23010);
- osmo_sockaddr_str_to_sockaddr(&addr6, &bts->gprs.nsvc[0].remote.u.sas);
- bts->gprs.nsvc[0].nsvci = 0x65;
- bts->gprs.nsvc[0].local_port = 0x5a3c;
+ osmo_sockaddr_str_to_sockaddr(&addr6, &bts->site_mgr->gprs.nsvc[0].remote.u.sas);
+ bts->site_mgr->gprs.nsvc[0].nsvci = 0x65;
+ bts->site_mgr->gprs.nsvc[0].local_port = 0x5a3c;
uint8_t attr_nscv6_expected[] =
/* |- oml attr |-16bit length */
{ 0x9f, 0x00, 0x02, 0x00, 0x65, 0xfd, 0x00, 0x16,
@@ -303,7 +254,7 @@ int main(int argc, char **argv)
0xfd, 0x00, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
};
- test_nanobts_attr_nscv_get(bts, attr_nscv6_expected);
+ test_nanobts_gen_set_nsvc_attr(bts, attr_nscv6_expected);
printf("Done\n");
@@ -323,25 +274,3 @@ int main(int argc, char **argv)
talloc_free(ctx);
return 0;
}
-
-/* stubs */
-struct osmo_prim_hdr;
-int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
-{
- abort();
-}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
- OSMO_ASSERT(0);
-}
-
-bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts)
-{ return true; }
-
-void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
-void pcu_info_update(struct gsm_bts *bts) {};
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{ return 0; }
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.ok b/tests/nanobts_omlattr/nanobts_omlattr_test.ok
index abdb95e99..5f907691e 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.ok
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.ok
@@ -1,29 +1,29 @@
-Testing nanobts_attr_bts_get()...
-result= 19555b61676d7318060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539
-expected=19555b61676d7318060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539
+Testing nanobts_gen_set_bts_attr()...
+result= 19736d67615b5518060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0a2311080362093f99000700f11000010539
+expected=19736d67615b5518060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0a2311080362093f99000700f11000010539
ok.
-Testing nanobts_attr_nse_get()...
+Testing nanobts_gen_set_nse_attr()...
result= 9d00020065a00007030303031e030aa1000b03030303030a030a030a03
expected=9d00020065a00007030303031e030aa1000b03030303030a030a030a03
ok.
-Testing nanobts_attr_cell_get()...
+Testing nanobts_gen_set_cell_attr()...
result= 9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02
expected=9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02
ok.
-Testing nanobts_attr_nscv_get()...
+Testing nanobts_gen_set_nsvc_attr()...
result= 9f00020065a2000859d80a0901655a3c
expected=9f00020065a2000859d80a0901655a3c
ok.
-Testing nanobts_attr_nscv_get()...
+Testing nanobts_gen_set_nsvc_attr()...
result= 2d0b0500020362
expected=2d0b0500020362
ok.
-Testing nanobts_attr_nscv_get()...
+Testing nanobts_gen_set_nsvc_attr()...
result= 9f00020065fd001629005a3c59e2fd005678901234567890123456789012
expected=9f00020065fd001629005a3c59e2fd005678901234567890123456789012
ok.
diff --git a/tests/neighbor_ident.vty b/tests/neighbor_ident.vty
index ce414e124..211159cdd 100644
--- a/tests/neighbor_ident.vty
+++ b/tests/neighbor_ident.vty
@@ -14,10 +14,14 @@ OsmoBSC# list
OsmoBSC# configure terminal
OsmoBSC(config)# network
+
+OsmoBSC(config-net)# neighbor-resolution bind 1.2.3.4 ?
+ [<0-65535>] Port to bind the service to [defaults to 4248 if not provided]
+
OsmoBSC(config-net)# bts 0
-OsmoBSC(config-net-bts)# type sysmobts
+OsmoBSC(config-net-bts)# type osmo-bts
OsmoBSC(config-net-bts)# base_station_id_code 10
-OsmoBSC(config-net-bts)# location_area_code 20
+OsmoBSC(config-net-bts)# location_area_code 0x0014
OsmoBSC(config-net-bts)# cell_identity 30
OsmoBSC(config-net-bts)# trx 0
OsmoBSC(config-net-bts-trx)# arfcn 40
@@ -25,9 +29,9 @@ OsmoBSC(config-net-bts-trx)# exit
OsmoBSC(config-net-bts)# exit
OsmoBSC(config-net)# bts 1
-OsmoBSC(config-net-bts)# type sysmobts
+OsmoBSC(config-net-bts)# type osmo-bts
OsmoBSC(config-net-bts)# base_station_id_code 11
-OsmoBSC(config-net-bts)# location_area_code 21
+OsmoBSC(config-net-bts)# location_area_code 0x0015
OsmoBSC(config-net-bts)# cell_identity 31
OsmoBSC(config-net-bts)# trx 0
OsmoBSC(config-net-bts-trx)# arfcn 41
@@ -35,9 +39,9 @@ OsmoBSC(config-net-bts-trx)# exit
OsmoBSC(config-net-bts)# exit
OsmoBSC(config-net)# bts 2
-OsmoBSC(config-net-bts)# type sysmobts
+OsmoBSC(config-net-bts)# type osmo-bts
OsmoBSC(config-net-bts)# base_station_id_code 12
-OsmoBSC(config-net-bts)# location_area_code 22
+OsmoBSC(config-net-bts)# location_area_code 0x0016
OsmoBSC(config-net-bts)# cell_identity 65535
OsmoBSC(config-net-bts)# trx 0
OsmoBSC(config-net-bts-trx)# arfcn 42
@@ -49,7 +53,7 @@ OsmoBSC(config-net)# show running-config
bts 0
...
cell_identity 30
- location_area_code 20
+ location_area_code 0x0014
base_station_id_code 10
...
trx 0
@@ -59,7 +63,7 @@ OsmoBSC(config-net)# show running-config
bts 1
...
cell_identity 31
- location_area_code 21
+ location_area_code 0x0015
base_station_id_code 11
...
trx 0
@@ -69,7 +73,7 @@ OsmoBSC(config-net)# show running-config
bts 2
...
cell_identity 65535
- location_area_code 22
+ location_area_code 0x0016
base_station_id_code 12
...
trx 0
@@ -84,10 +88,16 @@ OsmoBSC(config-net-bts)# list
neighbor lac <0-65535>
neighbor lac-ci <0-65535> <0-65535>
neighbor cgi <0-999> <0-999> <0-65535> <0-65535>
+ neighbor cgi-ps <0-999> <0-999> <0-65535> <0-255> <0-65535>
neighbor lac <0-65535> arfcn <0-1023> bsic (<0-63>|any)
neighbor lac-ci <0-65535> <0-65535> arfcn <0-1023> bsic (<0-63>|any)
neighbor cgi <0-999> <0-999> <0-65535> <0-65535> arfcn <0-1023> bsic (<0-63>|any)
+ neighbor cgi-ps <0-999> <0-999> <0-65535> <0-255> <0-65535> arfcn <0-1023> bsic (<0-63>|any)
no neighbor bts <0-255>
+ no neighbor lac <0-65535>
+ no neighbor lac-ci <0-65535> <0-65535>
+ no neighbor cgi <0-999> <0-999> <0-65535> <0-65535>
+ no neighbor cgi-ps <0-999> <0-999> <0-65535> <0-255> <0-65535>
no neighbor arfcn <0-1023> bsic (<0-63>|any)
no neighbors
...
@@ -100,6 +110,7 @@ OsmoBSC(config-net-bts)# neighbor ?
lac Add Neighbor cell by LAC
lac-ci Add Neighbor cell by LAC and CI
cgi Add Neighbor cell by cgi
+ cgi-ps Add Neighbor cell by cgi (Packet Switched, with RAC)
OsmoBSC(config-net-bts)# neighbor bts ?
<0-255> BTS number
@@ -166,8 +177,12 @@ OsmoBSC(config-net-bts)# no neighbor?
neighbor Remove local or remote-BSS neighbor cell
OsmoBSC(config-net-bts)# no neighbor ?
- bts Neighbor cell by local BTS number
- arfcn ARFCN of neighbor cell
+ bts Neighbor cell by local BTS number
+ lac Neighbor cell by LAC
+ lac-ci Neighbor cell by LAC and CI
+ cgi Neighbor cell by cgi
+ cgi-ps Neighbor cell by cgi (Packet Switched, with RAC)
+ arfcn ARFCN of neighbor cell
OsmoBSC(config-net-bts)# no neighbor bts ?
<0-255> BTS number
@@ -192,52 +207,64 @@ OsmoBSC(config-net-bts)# neighbor cgi 23 42 423 5 arfcn 23 bsic 64
% Unknown command.
OsmoBSC(config-net-bts)# neighbor bts 0
-% Error: cannot add local BTS 0 as neighbor to BTS 0: Invalid argument
+OsmoBSC(config-net-bts)# no neighbor bts 0
OsmoBSC(config-net-bts)# show running-config
... !neighbor
-OsmoBSC(config-net-bts)# neighbor bts 1
-% BTS 0 now has local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11
+OsmoBSC(config-net-bts)# no neighbor bts 1
+% Error: no such neighbor on BTS 0: BTS 1
+OsmoBSC(config-net-bts)# ### Add non-existing BTS nr -- is allowed, checking plausibility at runtime
+OsmoBSC(config-net-bts)# neighbor bts 123
+
+OsmoBSC(config-net-bts)# ### A neighbor by LAC and by BTS number are two distinct neighbor entries, resolved at runtime
OsmoBSC(config-net-bts)# neighbor lac 22
-% BTS 0 now has local neighbor BTS 2 with LAC 22 CI 65535 and ARFCN 42 BSIC 12
OsmoBSC(config-net-bts)# no neighbor bts 2
+% Error: no such neighbor on BTS 0: BTS 2
+OsmoBSC(config-net-bts)# no neighbor lac 22
+
OsmoBSC(config-net-bts)# neighbor cgi 901 70 22 65535
-% BTS 0 now has local neighbor BTS 2 with LAC 22 CI 65535 and ARFCN 42 BSIC 12
OsmoBSC(config-net-bts)# neighbor cgi 23 42 423 5 arfcn 23 bsic 42
-% BTS 0 to ARFCN 23 BSIC 42 now has 1 remote BSS Cell Identifier List entry
OsmoBSC(config-net-bts)# ### adding the same entry again results in no change
-OsmoBSC(config-net-bts)# neighbor bts 1
-% BTS 0 already had local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11
-OsmoBSC(config-net-bts)# neighbor lac-ci 21 31
-% BTS 0 already had local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11
-OsmoBSC(config-net-bts)# neighbor cgi 23 42 423 5 arfcn 23 bsic 42
-% Error: only one Cell Identifier entry is allowed per remote neighbor. Already have: BTS 0 to ARFCN 23 BSIC 42 -> CGI[1]:{023-42-423-5}
-OsmoBSC(config-net-bts)# neighbor cgi 23 42 423 5 arfcn 23 bsic 42
-% Error: only one Cell Identifier entry is allowed per remote neighbor. Already have: BTS 0 to ARFCN 23 BSIC 42 -> CGI[1]:{023-42-423-5}
+OsmoBSC(config-net-bts)# neighbor bts 123
+% BTS 0 already had neighbor BTS 123
+
+OsmoBSC(config-net-bts)# neighbor lac-ci 21 31 arfcn 41 bsic 11
+OsmoBSC(config-net-bts)# neighbor lac-ci 21 31 arfcn 41 bsic 11
+% BTS 0 already had neighbor LAC-CI:21-31 ARFCN-BSIC:41-11
+OsmoBSC(config-net-bts)# neighbor lac-ci 21 31 arfcn 22 bsic 32
+% BTS 0 already had neighbor LAC-CI:21-31 ARFCN-BSIC:41-11
+% ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: LAC-CI:21-31 ARFCN-BSIC:22-32
+OsmoBSC(config-net-bts)# show running-config
+...
+ neighbor lac-ci 21 31 arfcn 41 bsic 11
+...
+
OsmoBSC(config-net-bts)# neighbor cgi 23 42 423 5 arfcn 23 bsic 42
-% Error: only one Cell Identifier entry is allowed per remote neighbor. Already have: BTS 0 to ARFCN 23 BSIC 42 -> CGI[1]:{023-42-423-5}
+% BTS 0 already had neighbor CGI:023-42-423-5 ARFCN-BSIC:23-42
-OsmoBSC(config-net-bts)# neighbor cgi 23 042 423 6 arfcn 23 bsic 42
-% Error: only one Cell Identifier entry is allowed per remote neighbor. Already have: BTS 0 to ARFCN 23 BSIC 42 -> CGI[1]:{023-42-423-5}
+OsmoBSC(config-net-bts)# neighbor cgi 23 042 423 5 arfcn 23 bsic 42
+% Error: only one Cell Identifier entry is allowed per remote neighbor. Already have: BTS 0 -> CGI:023-42-423-5 ARFCN-BSIC:23-42
-OsmoBSC(config-net-bts)# neighbor lac 456 arfcn 123 bsic 45
-% BTS 0 to ARFCN 123 BSIC 45 now has 1 remote BSS Cell Identifier List entry
+OsmoBSC(config-net-bts)# neighbor cgi 23 42 423 6 arfcn 23 bsic 42
+% Error: only one Cell Identifier entry is allowed per remote neighbor. Already have: BTS 0 -> CGI:023-42-423-5 ARFCN-BSIC:23-42
-OsmoBSC(config-net-bts)# neighbor cgi 23 042 234 56 arfcn 23 bsic 42
-% Error: only one Cell Identifier entry is allowed per remote neighbor. Already have: BTS 0 to ARFCN 23 BSIC 42 -> CGI[1]:{023-42-423-5}
+OsmoBSC(config-net-bts)# neighbor cgi 23 42 423 6 arfcn 42 bsic 1
+
+OsmoBSC(config-net-bts)# neighbor lac 456 arfcn 123 bsic 45
OsmoBSC(config-net-bts)# neighbor lac-ci 789 10 arfcn 423 bsic any
-% BTS 0 to ARFCN 423 (any BSIC) now has 1 remote BSS Cell Identifier List entry
OsmoBSC(config-net-bts)# neighbor lac-ci 789 10 arfcn 423 bsic 63
-% Error: only one Cell Identifier entry is allowed per remote neighbor. Already have: BTS 0 to ARFCN 423 BSIC 63 -> LAC-CI[1]:{789-10}
+% BTS 0 already had neighbor LAC-CI:789-10 ARFCN-BSIC:423-any
+% ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: LAC-CI:789-10 ARFCN-BSIC:423-63
OsmoBSC(config-net-bts)# neighbor lac-ci 789 10 arfcn 423 bsic 1
-% Error: only one Cell Identifier entry is allowed per remote neighbor. Already have: BTS 0 to ARFCN 423 BSIC 1 -> LAC-CI[1]:{789-10}
+% BTS 0 already had neighbor LAC-CI:789-10 ARFCN-BSIC:423-any
+% ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: LAC-CI:789-10 ARFCN-BSIC:423-1
OsmoBSC(config-net-bts)# show running-config
...
@@ -245,115 +272,125 @@ network
... !neighbor
bts 0
... !neighbor
- neighbor bts 1
- neighbor bts 2
+ neighbor bts 123
+ neighbor cgi 901 70 22 65535
neighbor cgi 023 42 423 5 arfcn 23 bsic 42
+ neighbor lac-ci 21 31 arfcn 41 bsic 11
+ neighbor cgi 023 42 423 6 arfcn 42 bsic 1
neighbor lac 456 arfcn 123 bsic 45
neighbor lac-ci 789 10 arfcn 423 bsic any
... !neighbor
OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 99 bsic any
-% No entry for BTS 0 to ARFCN 99 (any BSIC)
+% No entry for BTS 0 -> ARFCN-BSIC:99-any
OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 41 bsic any
-% BTS 0 to ARFCN 41 (any BSIC) resolves to local BTS 1 lac-ci 21 31
+% BTS 0 -> ARFCN-BSIC:41-any resolves to local BTS 1 lac-ci 21 31
OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 423 bsic 1
-% neighbor lac-ci 789 10 arfcn 423 bsic 1
+% BTS 0 -> ARFCN-BSIC:423-1 resolves to remote-BSS neighbors: LAC-CI[1]:{789-10}
OsmoBSC(config-net-bts)# do show bts 0 neighbor arfcn 423 bsic 23
-% neighbor lac-ci 789 10 arfcn 423 bsic 23
+% BTS 0 -> ARFCN-BSIC:423-23 resolves to remote-BSS neighbors: LAC-CI[1]:{789-10}
OsmoBSC(config-net-bts)# no neighbor arfcn 99 bsic 7
-% Cannot remove, no such neighbor: BTS 0 to ARFCN 99 BSIC 7
+% Cannot remove: no such neighbor on BTS 0: ARFCN-BSIC:99-7
OsmoBSC(config-net-bts)# no neighbor arfcn 23 bsic 42
-% Removed remote BSS neighbor BTS 0 to ARFCN 23 BSIC 42
OsmoBSC(config-net-bts)# show running-config
... !neighbor
- neighbor bts 1
- neighbor bts 2
+ neighbor bts 123
+ neighbor cgi 901 70 22 65535
+ neighbor lac-ci 21 31 arfcn 41 bsic 11
+ neighbor cgi 023 42 423 6 arfcn 42 bsic 1
neighbor lac 456 arfcn 123 bsic 45
neighbor lac-ci 789 10 arfcn 423 bsic any
... !neighbor
OsmoBSC(config-net-bts)# no neighbor arfcn 123 bsic 45
-% Removed remote BSS neighbor BTS 0 to ARFCN 123 BSIC 45
OsmoBSC(config-net-bts)# show running-config
... !neighbor
- neighbor bts 1
- neighbor bts 2
+ neighbor bts 123
+ neighbor cgi 901 70 22 65535
+ neighbor lac-ci 21 31 arfcn 41 bsic 11
+ neighbor cgi 023 42 423 6 arfcn 42 bsic 1
neighbor lac-ci 789 10 arfcn 423 bsic any
... !neighbor
OsmoBSC(config-net-bts)# no neighbor arfcn 423 bsic any
-% Removed remote BSS neighbor BTS 0 to ARFCN 423 (any BSIC)
OsmoBSC(config-net-bts)# show running-config
... !neighbor
- neighbor bts 1
- neighbor bts 2
+ neighbor bts 123
+ neighbor cgi 901 70 22 65535
+ neighbor lac-ci 21 31 arfcn 41 bsic 11
+ neighbor cgi 023 42 423 6 arfcn 42 bsic 1
... !neighbor
OsmoBSC(config-net-bts)# no neighbor arfcn 423 bsic 63
-% Cannot remove, no such neighbor: BTS 0 to ARFCN 423 BSIC 63
+% Cannot remove: no such neighbor on BTS 0: ARFCN-BSIC:423-63
OsmoBSC(config-net-bts)# show running-config
... !neighbor
- neighbor bts 1
- neighbor bts 2
+ neighbor bts 123
+ neighbor cgi 901 70 22 65535
+ neighbor lac-ci 21 31 arfcn 41 bsic 11
+ neighbor cgi 023 42 423 6 arfcn 42 bsic 1
... !neighbor
OsmoBSC(config-net-bts)# no neighbor arfcn 423 bsic 1
-% Cannot remove, no such neighbor: BTS 0 to ARFCN 423 BSIC 1
+% Cannot remove: no such neighbor on BTS 0: ARFCN-BSIC:423-1
OsmoBSC(config-net-bts)# show running-config
... !neighbor
- neighbor bts 1
- neighbor bts 2
+ neighbor bts 123
+ neighbor cgi 901 70 22 65535
+ neighbor lac-ci 21 31 arfcn 41 bsic 11
+ neighbor cgi 023 42 423 6 arfcn 42 bsic 1
... !neighbor
OsmoBSC(config-net-bts)# no neighbor arfcn 41 bsic any
-% Removed local neighbor bts 0 to bts 1
OsmoBSC(config-net-bts)# show running-config
... !neighbor
- neighbor bts 2
+ neighbor bts 123
+ neighbor cgi 901 70 22 65535
+ neighbor cgi 023 42 423 6 arfcn 42 bsic 1
... !neighbor
OsmoBSC(config-net-bts)# no neighbor arfcn 41 bsic any
-% Cannot remove, no such neighbor: BTS 0 to ARFCN 41 (any BSIC)
+% Cannot remove: no such neighbor on BTS 0: ARFCN-BSIC:41-any
OsmoBSC(config-net-bts)# show running-config
... !neighbor
- neighbor bts 2
+ neighbor bts 123
+ neighbor cgi 901 70 22 65535
+ neighbor cgi 023 42 423 6 arfcn 42 bsic 1
... !neighbor
-OsmoBSC(config-net-bts)# no neighbor arfcn 42 bsic 12
-% Removed local neighbor bts 0 to bts 2
+OsmoBSC(config-net-bts)# no neighbor bts 123
+OsmoBSC(config-net-bts)# no neighbor cgi 901 70 22 65535
+OsmoBSC(config-net-bts)# no neighbor arfcn 42 bsic 1
OsmoBSC(config-net-bts)# show running-config
... !neighbor
OsmoBSC(config-net-bts)# neighbor bts 1
-% BTS 0 now has local neighbor BTS 1 with LAC 21 CI 31 and ARFCN 41 BSIC 11
OsmoBSC(config-net-bts)# neighbor bts 2
-% BTS 0 now has local neighbor BTS 2 with LAC 22 CI 65535 and ARFCN 42 BSIC 12
OsmoBSC(config-net-bts)# neighbor cgi 023 42 423 5 arfcn 23 bsic 42
-% BTS 0 to ARFCN 23 BSIC 42 now has 1 remote BSS Cell Identifier List entry
OsmoBSC(config-net-bts)# neighbor lac 456 arfcn 123 bsic 45
-% BTS 0 to ARFCN 123 BSIC 45 now has 1 remote BSS Cell Identifier List entry
OsmoBSC(config-net-bts)# neighbor lac-ci 789 10 arfcn 423 bsic any
-% BTS 0 to ARFCN 423 (any BSIC) now has 1 remote BSS Cell Identifier List entry
+OsmoBSC(config-net-bts)# neighbor cgi-ps 23 42 423 2 5 arfcn 23 bsic 32
OsmoBSC(config-net-bts)# no neighbors
-% Removed local neighbor bts 0 to bts 1
-% Removed local neighbor bts 0 to bts 2
-% Removed remote BSS neighbor BTS 0 to ARFCN 23 BSIC 42
-% Removed remote BSS neighbor BTS 0 to ARFCN 123 BSIC 45
-% Removed remote BSS neighbor BTS 0 to ARFCN 423 (any BSIC)
+% Removed neighbor: BTS 0 to BTS 1
+% Removed neighbor: BTS 0 to BTS 2
+% Removed neighbor: BTS 0 to CGI:023-42-423-5 ARFCN-BSIC:23-42
+% Removed neighbor: BTS 0 to LAC:456 ARFCN-BSIC:123-45
+% Removed neighbor: BTS 0 to LAC-CI:789-10 ARFCN-BSIC:423-any
+% Removed neighbor: BTS 0 to CGI-PS:023-42-423-2-5 ARFCN-BSIC:23-32
OsmoBSC(config-net-bts)# show running-config
... !neighbor
diff --git a/tests/nri_cfg.vty b/tests/nri_cfg.vty
index 8287eab38..8e935a488 100644
--- a/tests/nri_cfg.vty
+++ b/tests/nri_cfg.vty
@@ -79,6 +79,19 @@ msc 1
nri add 1024 1025
OsmoBSC(config)# network
+
+OsmoBSC(config-net)# list
+...
+ nri bitlen <1-15>
+...
+
+OsmoBSC(config-net)# nri ?
+ bitlen Set number of bits that an NRI has, to extract from TMSI identities (always starting just after the TMSI's most significant octet).
+ null Define NULL-NRI values that cause re-assignment of an MS to a different MSC, for MSC pooling.
+
+OsmoBSC(config-net)# nri bitlen ?
+ <1-15> bit count (default: 10)
+
OsmoBSC(config-net)# nri bitlen 11
OsmoBSC(config-net)# show running-config
...
@@ -105,6 +118,25 @@ msc 1
nri add 512 767
nri add 1024 1025
...
+
+OsmoBSC(config-net)# nri null ?
+ add Add NULL-NRI value (or range)
+ del Remove NRI value or range from the NRI mapping
+
+OsmoBSC(config-net)# nri null add ?
+ <0-32767> First value of the NRI value range, should not surpass the configured 'nri bitlen'.
+OsmoBSC(config-net)# nri null add 0 ?
+ [<0-32767>] Last value of the NRI value range, should not surpass the configured 'nri bitlen' and be larger than the first value; if omitted, apply only the first value.
+OsmoBSC(config-net)# nri null add 0 0 ?
+ <cr>
+
+OsmoBSC(config-net)# nri null del ?
+ <0-32767> First value of the NRI value range, should not surpass the configured 'nri bitlen'.
+OsmoBSC(config-net)# nri null del 0 ?
+ [<0-32767>] Last value of the NRI value range, should not surpass the configured 'nri bitlen' and be larger than the first value; if omitted, apply only the first value.
+OsmoBSC(config-net)# nri null del 0 0 ?
+ <cr>
+
OsmoBSC(config-net)# exit
OsmoBSC(config)# msc 0
diff --git a/tests/osmo-bsc.vty b/tests/osmo-bsc.vty
index 560fb3683..776b5ada6 100644
--- a/tests/osmo-bsc.vty
+++ b/tests/osmo-bsc.vty
@@ -1,5 +1,139 @@
OsmoBSC> enable
+OsmoBSC# list
+...
+ bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> modify (vamos|non-vamos) [tsc] [<1-4>] [<0-7>]
+...
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 ?
+ activate Manual Channel Activation (e.g. for BER test)
+ activate-vamos Manual Channel Activation, in VAMOS mode
+ deactivate Manual Channel Deactivation (e.g. for BER test)
+ modify Manually send Channel Mode Modify (for debugging)
+ mdcx Modify RTP Connection
+ reassign-to Trigger Assignment to an unused lchan on the same cell
+ ms-power Manually force MS Uplink Power Level in dBm on the lchan (for testing)
+ handover Manually trigger handover (for debugging)
+ assignment Manually trigger assignment (for debugging)
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 modify ?
+ vamos Enable VAMOS channel mode
+ non-vamos Disable VAMOS channel mode
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 modify vamos ?
+ [tsc] Provide specific TSC Set and Training Sequence Code
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 modify vamos tsc ?
+ [<1-4>] TSC Set
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 modify vamos tsc 1 ?
+ [<0-7>] Training Sequence Code
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 modify vamos tsc 1 0 ?
+ <cr>
+
+
+OsmoBSC# list
+...
+ bts <0-255> trx <0-255> timeslot <0-7> (sub-slot|vamos-sub-slot) <0-7> (activate|activate-vamos) (hr|fr|efr|amr|sig) [<0-7>]
+ bts <0-255> trx <0-255> timeslot <0-7> (sub-slot|vamos-sub-slot) <0-7> deactivate
+...
+
+OsmoBSC# bts?
+ bts BTS Specific Commands
+
+OsmoBSC# bts ?
+ <0-255> BTS Number
+
+OsmoBSC# bts 0 ?
+ unblock-setup-ramping Unblock and allow to configure a BTS if kept back by BTS ramping
+ resend-system-information Re-generate + re-send BCCH SYSTEM INFORMATION
+ resend-power-control-defaults Re-generate + re-send default MS/BS Power control parameters
+ c0-power-reduction BCCH carrier power reduction operation
+ trx TRX for manual command
+ oml Manipulate the OML managed objects
+ om2000 Manipulate the OM2000 managed objects
+
+OsmoBSC# bts 0 c0-power-reduction ?
+ <0-6> Power reduction value (in dB, even numbers only)
+
+OsmoBSC# bts 0 trx ?
+ <0-255> TRX Number
+
+OsmoBSC# bts 0 trx 0 ?
+ timeslot Timeslot for manual command
+
+OsmoBSC# bts 0 trx 0 timeslot ?
+ <0-7> Timeslot Number
+
+OsmoBSC# bts 0 trx 0 timeslot 0 ?
+ pdch Packet Data Channel
+ sub-slot Primary sub-slot
+ vamos-sub-slot VAMOS secondary shadow subslot, range <0-1>, only valid for TCH type timeslots
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot ?
+ <0-7> Sub-slot Number
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 ?
+ activate Manual Channel Activation (e.g. for BER test)
+ activate-vamos Manual Channel Activation, in VAMOS mode
+ deactivate Manual Channel Deactivation (e.g. for BER test)
+ modify Manually send Channel Mode Modify (for debugging)
+ mdcx Modify RTP Connection
+ reassign-to Trigger Assignment to an unused lchan on the same cell
+ ms-power Manually force MS Uplink Power Level in dBm on the lchan (for testing)
+ handover Manually trigger handover (for debugging)
+ assignment Manually trigger assignment (for debugging)
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 activate ?
+ hr Half-Rate v1
+ fr Full-Rate
+ efr Enhanced Full Rate
+ amr Adaptive Multi-Rate
+ sig Signalling
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 activate fr ?
+ [<0-7>] AMR Mode
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 activate fr 0 ?
+ <cr>
+
+OsmoBSC# list
+...
+ bts <0-255> trx <0-255> timeslot <0-7> (sub-slot|vamos-sub-slot) <0-7> reassign-to trx <0-255> timeslot <0-7> (sub-slot|vamos-sub-slot) <0-7> [tsc] [<1-4>] [<0-7>]
+...
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to ?
+ trx Target TRX
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to trx ?
+ <0-255> TRX nr
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to trx 0 ?
+ timeslot Target timeslot
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to trx 0 timeslot ?
+ <0-7> timeslot nr
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to trx 0 timeslot 0 ?
+ sub-slot Primary sub-slot
+ vamos-sub-slot VAMOS secondary shadow subslot, range <0-1>, only valid for TCH type timeslots
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to trx 0 timeslot 0 vamos-sub-slot ?
+ <0-7> Sub-slot Number
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to trx 0 timeslot 0 vamos-sub-slot 0 ?
+ [tsc] Provide specific TSC Set and Training Sequence Code
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to trx 0 timeslot 0 vamos-sub-slot 0 tsc ?
+ [<1-4>] TSC Set
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to trx 0 timeslot 0 vamos-sub-slot 0 tsc 1 ?
+ [<0-7>] Training Sequence Code
+
+OsmoBSC# bts 0 trx 0 timeslot 0 sub-slot 0 reassign-to trx 0 timeslot 0 vamos-sub-slot 0 tsc 1 0 ?
+ <cr>
+
OsmoBSC# configure terminal
OsmoBSC(config)# network
OsmoBSC(config-net)# list
@@ -17,3 +151,138 @@ network
meas-feed destination 127.0.0.23 4223
meas-feed scenario foo23
...
+
+
+OsmoBSC(config-net)# bts 0
+
+OsmoBSC(config-net-bts)# list
+...
+ channel allocator avoid-interference (0|1)
+...
+
+OsmoBSC(config-net-bts)# channel?
+ channel Channel Allocator
+
+OsmoBSC(config-net-bts)# channel ?
+ allocator Channel Allocator
+
+OsmoBSC(config-net-bts)# channel allocator ?
+ mode Channel allocation mode
+ dynamic-param Parameters for dynamic channel allocation mode
+ avoid-interference Configure whether reported interference levels from RES IND are used in channel allocation
+ tch-signalling-policy Configure when TCH/H or TCH/F channels can be used to serve signalling if SDCCHs are exhausted
+
+OsmoBSC(config-net-bts)# channel allocator mode ?
+ set-all Set a single mode for all variants
+ chan-req Channel allocation for CHANNEL REQUEST (RACH)
+ assignment Channel allocation for assignment
+ handover Channel allocation for handover
+
+OsmoBSC(config-net-bts)# channel allocator mode set-all ?
+ ascending Allocate Timeslots and Transceivers in ascending order
+ descending Allocate Timeslots and Transceivers in descending order
+
+OsmoBSC(config-net-bts)# channel allocator mode handover ?
+ ascending Allocate Timeslots and Transceivers in ascending order
+ descending Allocate Timeslots and Transceivers in descending order
+
+OsmoBSC(config-net-bts)# channel allocator mode assignment ?
+ ascending Allocate Timeslots and Transceivers in ascending order
+ descending Allocate Timeslots and Transceivers in descending order
+ dynamic Dynamic lchan selection based on configured parameters
+
+OsmoBSC(config-net-bts)# channel allocator dynamic-param ?
+ sort-by-trx-power Whether to sort TRX instances by their respective power levels
+ ul-rxlev Uplink RxLev
+ c0-chan-load C0 (BCCH carrier) channel load
+
+OsmoBSC(config-net-bts)# channel allocator dynamic-param sort-by-trx-power ?
+ 0 Do not sort, use the same order as in the configuration file
+ 1 Sort TRX instances by their power levels in descending order
+
+OsmoBSC(config-net-bts)# channel allocator dynamic-param ul-rxlev thresh ?
+ <0-63> Uplink RxLev threshold
+OsmoBSC(config-net-bts)# channel allocator dynamic-param ul-rxlev thresh 50 avg-num ?
+ <1-10> Minimum number of RxLev samples for averaging
+OsmoBSC(config-net-bts)# channel allocator dynamic-param c0-chan-load thresh ?
+ <0-100> Channel load threshold (in %)
+
+OsmoBSC(config-net-bts)# channel allocator avoid-interference ?
+ 0 Ignore interference levels (default). Always assign lchans in a deterministic order.
+ 1 In channel allocation, prefer lchans with less interference.
+
+OsmoBSC(config-net-bts)# channel allocator tch-signalling-policy ?
+ never Never allow TCH for signalling purposes
+ emergency Only allow TCH for signalling purposes when establishing an emergency call
+ voice Allow TCH for signalling purposes when establishing any voice call
+ always Always allow TCH for signalling purposes (default)
+
+OsmoBSC(config-net-bts)# show running-config
+... !channel allocator avoid-interference
+OsmoBSC(config-net-bts)# channel allocator avoid-interference 1
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ channel allocator avoid-interference 1
+...
+
+OsmoBSC(config-net-bts)# channel allocator avoid-interference 0
+OsmoBSC(config-net-bts)# show running-config
+... !channel allocator avoid-interference
+
+OsmoBSC(config-net-bts)# show running-config
+... !channel allocator tch-signalling-policy
+OsmoBSC(config-net-bts)# channel allocator tch-signalling-policy never
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ channel allocator tch-signalling-policy never
+...
+
+OsmoBSC(config-net-bts)# channel allocator tch-signalling-policy emergency
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ channel allocator tch-signalling-policy emergency
+...
+
+OsmoBSC(config-net-bts)# channel allocator tch-signalling-policy voice
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ channel allocator tch-signalling-policy voice
+...
+
+OsmoBSC(config-net-bts)# channel allocator tch-signalling-policy always
+OsmoBSC(config-net-bts)# show running-config
+... !channel allocator tch-signalling-policy
+
+OsmoBSC(config-net-bts)# immediate-assignment?
+ immediate-assignment Configure time of Immediate Assignment after ChanRqd RACH (Abis optimization)
+OsmoBSC(config-net-bts)# immediate-assignment ?
+ post-chan-ack Send the Immediate Assignment after the Channel Activation ACK (normal sequence)
+ pre-chan-ack Send the Immediate Assignment directly after Channel Activation (early), without waiting for the ACK; This may help with double allocations on high latency Abis links
+ pre-ts-ack EXPERIMENTAL: If a dynamic timeslot switch is necessary, send the Immediate Assignment even before the timeslot is switched, i.e. even before the Channel Activation is sent (very early)
+OsmoBSC(config-net-bts)# show running-config
+... !immediate-assignment
+OsmoBSC(config-net-bts)# immediate-assignment pre-chan-ack
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ immediate-assignment pre-chan-ack
+...
+OsmoBSC(config-net-bts)# immediate-assignment pre-ts-ack
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ immediate-assignment pre-ts-ack
+...
+OsmoBSC(config-net-bts)# immediate-assignment post-chan-ack
+OsmoBSC(config-net-bts)# show running-config
+... !immediate-assignment
diff --git a/tests/paging/Makefile.am b/tests/paging/Makefile.am
new file mode 100644
index 000000000..5472c489a
--- /dev/null
+++ b/tests/paging/Makefile.am
@@ -0,0 +1,38 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ -ggdb3 \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ paging_test.ok \
+ $(NULL)
+
+check_PROGRAMS = \
+ paging_test \
+ $(NULL)
+
+paging_test_SOURCES = \
+ paging_test.c \
+ $(NULL)
+
+paging_test_LDFLAGS = \
+ -Wl,--wrap=abis_rsl_sendmsg \
+ $(NULL)
+
+paging_test_LDADD = \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOABIS_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(NULL)
diff --git a/tests/paging/paging_test.c b/tests/paging/paging_test.c
new file mode 100644
index 000000000..80aaee13a
--- /dev/null
+++ b/tests/paging/paging_test.c
@@ -0,0 +1,296 @@
+/*
+ * (C) 2022 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 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 <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+#include <osmocom/gsm/gsm23003.h>
+#include <osmocom/gsm/rsl.h>
+
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/bss.h>
+
+struct timespec *clk_monotonic_override;
+unsigned int _sent_pg_cmd_rsl;
+
+static void clock_debug(char *str)
+{
+ struct timeval tv;
+ osmo_gettimeofday(&tv, NULL);
+ fprintf(stderr, "sys={%lu.%06lu}: %s\n",
+ tv.tv_sec, tv.tv_usec, str);
+}
+
+
+static void clock_set(uint64_t sec, uint64_t usec)
+{
+ osmo_gettimeofday_override_time.tv_sec = sec;
+ osmo_gettimeofday_override_time.tv_usec = usec;
+ clk_monotonic_override->tv_sec = sec;
+ clk_monotonic_override->tv_nsec = usec * 1000;
+}
+
+static void clock_inc(unsigned int sec, unsigned int usec)
+{
+ osmo_gettimeofday_override_add(sec, usec);
+ osmo_clock_override_add(CLOCK_MONOTONIC, sec, usec * 1000);
+}
+
+#define bts_init(net) _bts_init(net, __func__)
+static inline struct gsm_bts *_bts_init(struct gsm_network *net, const char *msg)
+{
+ struct nm_running_chg_signal_data nsd;
+ struct gsm_bts_sm *bts_sm = gsm_bts_sm_alloc(net, 0);
+ struct gsm_bts *bts = bts_sm->bts[0];
+ if (!bts) {
+ fprintf(stderr, "BTS allocation failure in %s()\n", msg);
+ exit(1);
+ }
+ fprintf(stderr, "BTS allocation OK in %s()\n", msg);
+
+ bts->network = net;
+
+ /* Make sure trx_is_usable() returns true for bts->c0: */
+ bts->c0->mo.nm_state.operational = NM_OPSTATE_ENABLED;
+ bts->c0->mo.nm_state.availability = NM_AVSTATE_OK;
+ bts->c0->mo.nm_state.administrative = NM_STATE_UNLOCKED;
+ bts->c0->bb_transc.mo.nm_state.operational = NM_OPSTATE_ENABLED;
+ bts->c0->bb_transc.mo.nm_state.availability = NM_AVSTATE_OK;
+ bts->c0->bb_transc.mo.nm_state.administrative = NM_STATE_UNLOCKED;
+ bts->c0->rsl_link_primary = (struct e1inp_sign_link *)(intptr_t)0x01; /* Fake RSL is UP */
+
+ /* Emulate signal stating the TRX C0 is ready: */
+ memset(&nsd, 0, sizeof(nsd));
+ nsd.bts = bts;
+ nsd.obj_class = NM_OC_RADIO_CARRIER;
+ nsd.obj = bts->c0;
+ nsd.running = true;
+ osmo_signal_dispatch(SS_NM, S_NM_RUNNING_CHG, &nsd);
+
+ return bts;
+}
+
+#define bts_del(bts) _bts_del(bts, __func__)
+static inline void _bts_del(struct gsm_bts *bts, const char *msg)
+{
+ /* no need to llist_del(&bts->list), we never registered the bts there. */
+ talloc_free(bts->site_mgr);
+ fprintf(stderr, "BTS deallocated OK in %s()\n", msg);
+}
+
+static void emu_bsc_paging_cmd_from_msc(struct gsm_network *net, struct gsm_bts *bts, const char *imsi)
+{
+ int ret;
+ struct bsc_paging_params params = {
+ .reason = BSC_PAGING_FROM_CN,
+ .msc = NULL,
+ .tmsi = GSM_RESERVED_TMSI,
+ .imsi = {
+ .type = GSM_MI_TYPE_IMSI,
+ }
+ };
+ OSMO_STRLCPY_ARRAY(params.imsi.imsi, imsi);
+ params.bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers, params.imsi.imsi,
+ BSUB_USE_PAGING_START);
+ ret = paging_request_bts(&params, bts);
+ OSMO_ASSERT(ret == 1);
+}
+
+static void test_paging500(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ clock_set(0, 0);
+ _sent_pg_cmd_rsl = 0;
+ struct gsm_bts *bts = bts_init(net);
+
+ for (i = 0; i < 500; i++) {
+ char imsi[32];
+ snprintf(imsi, sizeof(imsi), "1234%06u", i);
+ emu_bsc_paging_cmd_from_msc(net, bts, imsi);
+ }
+
+ while (_sent_pg_cmd_rsl < 500) {
+ osmo_timers_prepare();
+ int nearest_ms = osmo_timers_nearest_ms();
+ if (nearest_ms == -1)
+ nearest_ms = 250;
+ clock_inc(0, nearest_ms*1000);
+ clock_debug("select()");
+ osmo_select_main_ctx(0);
+ if (llist_empty(&bts->paging.initial_req_list) && llist_empty(&bts->paging.retrans_req_list)) {
+ fprintf(stderr, "ERROR: some request timed out before being sent! %u\n", _sent_pg_cmd_rsl);
+ OSMO_ASSERT(0);
+ }
+ }
+
+ bts_del(bts);
+}
+
+static void test_paging500_combined(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ clock_set(0, 0);
+ _sent_pg_cmd_rsl = 0;
+ struct gsm_bts *bts = bts_init(net);
+ bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
+
+ for (i = 0; i < 500; i++) {
+ char imsi[32];
+ snprintf(imsi, sizeof(imsi), "1234%06u", i);
+ emu_bsc_paging_cmd_from_msc(net, bts, imsi);
+ }
+
+ while (_sent_pg_cmd_rsl < 500) {
+ osmo_timers_prepare();
+ int nearest_ms = osmo_timers_nearest_ms();
+ if (nearest_ms == -1)
+ nearest_ms = 250;
+ clock_inc(0, nearest_ms*1000);
+ clock_debug("select()");
+ osmo_select_main_ctx(0);
+ if (llist_empty(&bts->paging.initial_req_list) && llist_empty(&bts->paging.retrans_req_list)) {
+ fprintf(stderr, "ERROR: some request timed out before being sent! %u\n", _sent_pg_cmd_rsl);
+ OSMO_ASSERT(0);
+ }
+ }
+
+ bts_del(bts);
+}
+
+static void test_paging500_samepgroup(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ clock_set(0, 0);
+ _sent_pg_cmd_rsl = 0;
+ struct gsm_bts *bts = bts_init(net);
+
+ unsigned int num_pgroups = gsm48_number_of_paging_subchannels(&bts->si_common.chan_desc);
+ fprintf(stderr, "Number of paging groups: %u\n", num_pgroups);
+
+ for (i = 0; i < 500; i++) {
+ char imsi[32];
+ snprintf(imsi, sizeof(imsi), "1234%08u", i*num_pgroups);
+ emu_bsc_paging_cmd_from_msc(net, bts, imsi);
+ }
+
+ while (_sent_pg_cmd_rsl < 500) {
+ osmo_timers_prepare();
+ int nearest_ms = osmo_timers_nearest_ms();
+ if (nearest_ms == -1)
+ nearest_ms = 250;
+ clock_inc(0, nearest_ms*1000);
+ clock_debug("select()");
+ osmo_select_main_ctx(0);
+ if (llist_empty(&bts->paging.initial_req_list) && llist_empty(&bts->paging.retrans_req_list)) {
+ fprintf(stderr, "ERROR: some request timed out before being sent! %u\n", _sent_pg_cmd_rsl);
+ OSMO_ASSERT(0);
+ }
+ }
+
+ bts_del(bts);
+}
+
+static const struct log_info_cat log_categories[] = {
+ [DPAG] = {
+ .name = "DPAG",
+ .description = "",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+};
+
+static const struct log_info log_info = {
+ .cat = log_categories,
+ .num_cat = ARRAY_SIZE(log_categories),
+};
+
+int main(int argc, char **argv)
+{
+ struct gsm_network *net;
+
+ osmo_gettimeofday_override = true;
+ osmo_clock_override_enable(CLOCK_MONOTONIC, true);
+ clk_monotonic_override = osmo_clock_override_gettimespec(CLOCK_MONOTONIC);
+ clock_set(0, 0);
+
+ tall_bsc_ctx = talloc_named_const(NULL, 0, "paging_test");
+ osmo_init_logging2(tall_bsc_ctx, &log_info);
+ log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_parse_category_mask(osmo_stderr_target, "DPAG,1:");
+ osmo_fsm_log_addr(false);
+
+ bsc_network_alloc();
+ net = bsc_gsmnet;
+ if (!net) {
+ fprintf(stderr, "Network init failure.\n");
+ return EXIT_FAILURE;
+ }
+ paging_global_init();
+
+ test_paging500(net);
+ test_paging500_samepgroup(net);
+ test_paging500_combined(net);
+
+ return EXIT_SUCCESS;
+}
+
+/* override, requires '-Wl,--wrap=abis_rsl_sendmsg'.
+ * Catch RSL messages sent towards the BTS. */
+int __real_abis_rsl_sendmsg(struct msgb *msg);
+int __wrap_abis_rsl_sendmsg(struct msgb *msg)
+{
+ struct abis_rsl_cchan_hdr *cch = (struct abis_rsl_cchan_hdr *) msg->data;
+ struct tlv_parsed tp;
+ struct osmo_mobile_identity mi;
+ int rc;
+ char mi_str[64];
+
+ switch (cch->c.msg_type) {
+ case RSL_MT_PAGING_CMD:
+ if (rsl_tlv_parse(&tp, msgb_data(msg) + sizeof(*cch), msgb_length(msg) - sizeof(*cch)) < 0) {
+ LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ OSMO_ASSERT(0);
+ }
+ rc = osmo_mobile_identity_decode(&mi, TLVP_VAL(&tp, RSL_IE_MS_IDENTITY), TLVP_LEN(&tp, RSL_IE_MS_IDENTITY), true);
+ OSMO_ASSERT(rc == 0);
+ mi_str[0] = '\0';
+ osmo_mobile_identity_to_str_buf(mi_str, sizeof(mi_str), &mi);
+ fprintf(stderr, "abis_rsl_sendmsg: Paging CMD %s\n", mi_str);
+ _sent_pg_cmd_rsl++;
+ break;
+ default:
+ fprintf(stderr, "abis_rsl_sendmsg: unknown rsl message=0x%x\n", cch->c.msg_type);
+ }
+ msgb_free(msg);
+ return 0;
+}
diff --git a/tests/paging/paging_test.ok b/tests/paging/paging_test.ok
new file mode 100644
index 000000000..93cb16227
--- /dev/null
+++ b/tests/paging/paging_test.ok
@@ -0,0 +1,9694 @@
+===test_paging500===
+BTS allocation OK in test_paging500()
+(bts=0) C0 becomes available for paging
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000000
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) Paging delayed: retransmission happens in 0.500000s
+(bts=0) Paged 1 subscribers (1 initial, 0 retrans) during last iteration
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) New req arrived: re-scheduling next batch in 0.250000s
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000002: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000002: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000003: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000003: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000004: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000004: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000005: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000005: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000006: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000006: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000007: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000007: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000008: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000008: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000009: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000009: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000010: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000010: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000011: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000011: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000012: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000012: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000013: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000013: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000014: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000014: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000015: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000015: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000016: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000016: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000017: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000017: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000018: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000018: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000019: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000019: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000020: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000020: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000021: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000021: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000022: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000022: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000023: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000023: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000024: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000024: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000025: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000025: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000026: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000026: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000027: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000027: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000028: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000028: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000029: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000029: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000030: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000030: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000031: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000031: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000032: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000032: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000033: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000033: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000034: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000034: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000035: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000035: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000036: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000036: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000037: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000037: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000038: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000038: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000039: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000039: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000040: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000041: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000041: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000042: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000042: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000043: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000043: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000044: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000044: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000045: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000045: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000046: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000046: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000047: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000047: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000048: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000048: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000049: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000049: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000050: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000050: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000051: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000051: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000052: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000052: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000053: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000053: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000054: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000054: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000055: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000055: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000056: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000056: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000057: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000057: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000058: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000058: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000059: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000059: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000060: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000060: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000061: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000061: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000062: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000062: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000063: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000063: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000064: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000064: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000065: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000065: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000066: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000066: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000068: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000068: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000069: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000069: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000070: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000070: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000071: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000071: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000072: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000072: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000073: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000073: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000074: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000074: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000075: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000075: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000076: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000076: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000077: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000077: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000078: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000078: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000079: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000079: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000080: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000081: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000081: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000082: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000082: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000083: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000083: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000084: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000084: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000085: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000085: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000086: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000086: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000087: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000087: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000088: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000088: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000089: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000089: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000090: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000090: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000091: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000091: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000092: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000092: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000093: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000093: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000094: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000094: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000095: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000095: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000096: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000096: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000097: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000097: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000098: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000098: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000099: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000099: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000100: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000100: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000101: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000101: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000102: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000102: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000103: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000103: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000104: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000104: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000105: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000105: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000106: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000106: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000107: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000107: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000108: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000108: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000109: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000109: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000110: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000110: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000111: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000111: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000112: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000112: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000113: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000113: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000114: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000114: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000115: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000115: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000116: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000116: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000117: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000117: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000118: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000118: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000119: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000119: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000120: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000121: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000121: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000122: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000122: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000123: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000123: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000124: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000124: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000125: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000125: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000126: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000126: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000127: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000127: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000128: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000128: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000129: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000129: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000130: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000130: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000131: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000131: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000132: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000132: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000133: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000133: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000134: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000134: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000135: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000135: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000136: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000136: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000137: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000137: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000138: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000138: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000139: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000139: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000140: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000140: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000141: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000141: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000142: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000142: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000143: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000143: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000144: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000144: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000145: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000145: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000146: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000146: (bts=0) Paging request: T3113 expires in 13 seconds (estimated 13)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000147: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000147: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000148: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000148: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000149: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000149: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000150: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000150: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000151: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000151: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000152: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000152: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000153: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000153: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000154: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000154: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000155: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000155: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000156: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000156: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000157: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000157: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000158: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000158: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000159: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000159: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000160: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000161: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000161: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000162: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000162: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000163: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000163: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000164: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000164: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000165: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000165: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000166: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000166: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000167: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000167: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000168: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000168: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000169: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000169: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000170: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000170: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000171: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000171: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000172: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000172: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000173: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000173: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000174: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000174: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000175: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000175: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000176: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000176: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000177: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000177: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000178: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000178: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000179: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000179: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000180: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000180: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000181: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000181: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000182: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000182: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000183: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000183: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000184: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000184: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000185: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000185: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000186: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000186: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000187: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000187: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000188: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000188: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000189: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000189: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000190: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000190: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000191: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000191: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000192: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000192: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000193: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000193: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000194: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000194: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000195: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000195: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000196: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000196: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000197: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000197: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000198: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000198: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000199: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000199: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000200: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000201: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000201: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000202: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000202: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000203: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000203: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000204: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000204: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000205: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000205: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000206: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000206: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000207: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000207: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000208: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000208: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000209: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000209: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000210: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000210: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000211: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000211: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000212: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000212: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000213: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000213: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000214: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000214: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000215: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000215: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000216: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000216: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000217: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000217: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000218: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000218: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000219: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000219: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000220: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000220: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000221: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000221: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000222: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000222: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000223: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000223: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000224: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000224: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000225: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000225: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000226: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000226: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000227: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000227: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000228: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000228: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000229: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000229: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000230: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000230: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000231: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000231: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000232: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000232: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000233: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000233: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000234: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000234: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000235: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000235: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000236: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000236: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000237: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000237: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000238: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000238: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000239: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000239: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000240: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000241: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000241: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000242: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000242: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000243: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000243: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000244: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000244: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000245: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000245: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000246: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000246: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000247: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000247: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000248: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000248: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000249: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000249: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000250: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000250: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000251: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000251: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000252: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000252: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000253: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000253: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000254: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000254: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000255: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000255: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000256: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000256: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000257: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000257: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000258: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000258: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000259: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000259: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000260: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000260: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000261: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000261: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000262: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000262: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000263: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000263: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000264: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000264: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000265: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000265: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000266: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000266: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000267: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000267: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000268: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000268: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000269: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000269: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000270: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000270: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000271: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000271: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000272: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000272: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000273: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000273: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000274: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000274: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000276: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000276: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000277: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000277: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000278: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000278: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000279: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000279: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000280: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000281: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000281: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000282: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000282: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000283: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000283: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000284: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000284: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000285: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000285: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000286: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000286: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000287: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000287: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000288: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000288: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000289: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000289: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000290: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000290: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000291: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000291: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000292: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000292: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000293: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000293: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000294: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000294: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000295: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000295: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000296: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000296: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000297: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000297: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000298: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000298: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000299: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000299: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000300: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000300: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000301: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000301: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000302: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000302: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000303: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000303: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000304: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000304: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000305: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000305: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000306: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000306: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000307: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000307: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000308: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000308: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000309: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000309: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000310: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000310: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000311: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000311: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000312: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000312: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000313: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000313: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000314: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000314: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000315: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000315: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000316: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000316: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000317: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000317: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000318: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000318: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000319: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000319: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000320: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000321: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000321: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000322: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000322: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000323: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000323: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000324: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000324: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000325: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000325: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000326: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000326: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000327: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000327: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000328: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000328: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000329: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000329: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000330: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000330: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000331: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000331: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000332: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000332: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000333: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000333: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000334: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000334: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000335: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000335: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000336: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000336: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000337: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000337: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000338: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000338: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000339: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000339: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000340: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000340: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000341: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000341: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000342: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000342: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000343: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000343: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000344: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000344: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000345: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000345: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000346: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000346: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000347: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000347: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000348: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000348: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000349: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000349: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000350: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000350: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000351: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000351: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000352: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000352: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000353: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000353: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000354: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000354: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000355: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000355: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000356: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000356: (bts=0) Paging request: T3113 expires in 20 seconds (estimated 20)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000357: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000357: (bts=0) Paging request: T3113 expires in 20 seconds (estimated 20)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000358: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000358: (bts=0) Paging request: T3113 expires in 20 seconds (estimated 20)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000359: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000359: (bts=0) Paging request: T3113 expires in 20 seconds (estimated 20)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000360: (bts=0) Paging request: T3113 expires in 20 seconds (estimated 20)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000361: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000361: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000362: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000362: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000363: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000363: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000364: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000364: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000365: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000365: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000366: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000366: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000367: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000367: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000368: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000368: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000369: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000369: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000370: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000370: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000371: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000371: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000372: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000372: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000373: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000373: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000374: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000374: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000375: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000375: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000376: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000376: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000377: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000377: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000378: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000378: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000379: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000379: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000380: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000380: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000381: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000381: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000382: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000382: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000383: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000383: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000384: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000384: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000385: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000385: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000386: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000386: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000387: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000387: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000388: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000388: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000389: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000389: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000390: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000390: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000391: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000391: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000392: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000392: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000393: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000393: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000394: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000394: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000395: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000395: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000396: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000396: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000397: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000397: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000398: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000398: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000399: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000399: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000400: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000401: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000401: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000402: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000402: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000403: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000403: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000404: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000404: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000405: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000405: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000406: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000406: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000407: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000407: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000408: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000408: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000409: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000409: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000410: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000410: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000411: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000411: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000412: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000412: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000413: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000413: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000414: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000414: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000415: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000415: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000416: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000416: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000417: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000417: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000418: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000418: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000419: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000419: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000420: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000420: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000421: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000421: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000422: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000422: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000423: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000423: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000424: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000424: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000425: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000425: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000426: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000426: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000427: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000427: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000428: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000428: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000429: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000429: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000430: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000430: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000431: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000431: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000432: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000432: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000433: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000433: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000434: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000434: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000435: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000435: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000436: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000436: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000437: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000437: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000438: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000438: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000439: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000439: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000440: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000441: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000441: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000442: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000442: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000443: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000443: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000444: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000444: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000445: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000445: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000446: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000446: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000447: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000447: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000448: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000448: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000449: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000449: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000450: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000450: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000451: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000451: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000452: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000452: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000453: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000453: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000454: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000454: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000455: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000455: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000456: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000456: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000457: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000457: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000458: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000458: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000459: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000459: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000460: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000460: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000461: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000461: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000462: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000462: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000463: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000463: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000464: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000464: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000465: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000465: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000466: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000466: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000467: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000467: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000468: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000468: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000469: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000469: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000470: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000470: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000471: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000471: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000472: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000472: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000473: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000473: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000474: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000474: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000475: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000475: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000476: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000476: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000477: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000477: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000478: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000478: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000479: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000479: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000480: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000481: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000481: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000482: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000482: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000483: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000483: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000484: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000484: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000485: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000485: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000486: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000486: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000487: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000487: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000488: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000488: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000489: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000489: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000490: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000490: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000491: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000491: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000492: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000492: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000493: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000493: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000494: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000494: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000495: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000495: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000496: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000496: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000497: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000497: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000498: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000498: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000499: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000499: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+sys={0.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000001
+(msc=-1) Paging: subscr-IMSI-1234000002: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000002
+(msc=-1) Paging: subscr-IMSI-1234000003: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000003
+(msc=-1) Paging: subscr-IMSI-1234000004: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000004
+(msc=-1) Paging: subscr-IMSI-1234000005: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000005
+(msc=-1) Paging: subscr-IMSI-1234000006: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000006
+(msc=-1) Paging: subscr-IMSI-1234000007: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000007
+(msc=-1) Paging: subscr-IMSI-1234000008: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000008
+(msc=-1) Paging: subscr-IMSI-1234000009: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000009
+(msc=-1) Paging: subscr-IMSI-1234000010: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000010
+(bts=0) Scheduling next batch in 0.250000s (available_slots=56)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={0.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000011: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000011
+(msc=-1) Paging: subscr-IMSI-1234000012: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000012
+(msc=-1) Paging: subscr-IMSI-1234000013: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000013
+(msc=-1) Paging: subscr-IMSI-1234000014: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000014
+(msc=-1) Paging: subscr-IMSI-1234000015: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000015
+(msc=-1) Paging: subscr-IMSI-1234000016: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000016
+(msc=-1) Paging: subscr-IMSI-1234000017: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000017
+(msc=-1) Paging: subscr-IMSI-1234000018: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000018
+(msc=-1) Paging: subscr-IMSI-1234000019: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000019
+(msc=-1) Paging: subscr-IMSI-1234000020: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000020
+(bts=0) Scheduling next batch in 0.250000s (available_slots=46)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={0.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000021: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000021
+(msc=-1) Paging: subscr-IMSI-1234000022: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000022
+(msc=-1) Paging: subscr-IMSI-1234000023: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000023
+(msc=-1) Paging: subscr-IMSI-1234000024: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000024
+(msc=-1) Paging: subscr-IMSI-1234000025: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000025
+(msc=-1) Paging: subscr-IMSI-1234000026: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000026
+(msc=-1) Paging: subscr-IMSI-1234000027: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000027
+(msc=-1) Paging: subscr-IMSI-1234000028: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000028
+(msc=-1) Paging: subscr-IMSI-1234000029: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000029
+(msc=-1) Paging: subscr-IMSI-1234000030: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000030
+(bts=0) Scheduling next batch in 0.250000s (available_slots=36)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000031: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000031
+(msc=-1) Paging: subscr-IMSI-1234000032: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000032
+(msc=-1) Paging: subscr-IMSI-1234000033: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000033
+(msc=-1) Paging: subscr-IMSI-1234000034: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000034
+(msc=-1) Paging: subscr-IMSI-1234000035: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000035
+(msc=-1) Paging: subscr-IMSI-1234000036: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000036
+(msc=-1) Paging: subscr-IMSI-1234000037: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000037
+(msc=-1) Paging: subscr-IMSI-1234000038: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000038
+(msc=-1) Paging: subscr-IMSI-1234000039: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000039
+(msc=-1) Paging: subscr-IMSI-1234000040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000040
+(bts=0) Scheduling next batch in 0.250000s (available_slots=26)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000041: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000041
+(msc=-1) Paging: subscr-IMSI-1234000042: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000042
+(msc=-1) Paging: subscr-IMSI-1234000043: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000043
+(msc=-1) Paging: subscr-IMSI-1234000044: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000044
+(msc=-1) Paging: subscr-IMSI-1234000045: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000045
+(msc=-1) Paging: subscr-IMSI-1234000046: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000046
+(msc=-1) Paging: subscr-IMSI-1234000047: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000047
+(msc=-1) Paging: subscr-IMSI-1234000048: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000048
+(msc=-1) Paging: subscr-IMSI-1234000049: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000049
+(msc=-1) Paging: subscr-IMSI-1234000050: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000050
+(bts=0) Scheduling next batch in 0.250000s (available_slots=16)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000051: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000051
+(msc=-1) Paging: subscr-IMSI-1234000052: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000052
+(msc=-1) Paging: subscr-IMSI-1234000053: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000053
+(msc=-1) Paging: subscr-IMSI-1234000054: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000054
+(msc=-1) Paging: subscr-IMSI-1234000055: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000055
+(msc=-1) Paging: subscr-IMSI-1234000056: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000056
+(msc=-1) Paging: subscr-IMSI-1234000057: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000057
+(msc=-1) Paging: subscr-IMSI-1234000058: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000058
+(msc=-1) Paging: subscr-IMSI-1234000059: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000059
+(msc=-1) Paging: subscr-IMSI-1234000060: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000060
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000061: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000061
+(msc=-1) Paging: subscr-IMSI-1234000062: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000062
+(msc=-1) Paging: subscr-IMSI-1234000063: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000063
+(msc=-1) Paging: subscr-IMSI-1234000064: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000064
+(msc=-1) Paging: subscr-IMSI-1234000065: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000065
+(msc=-1) Paging: subscr-IMSI-1234000066: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000066
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={2.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000067
+(msc=-1) Paging: subscr-IMSI-1234000068: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000068
+(msc=-1) Paging: subscr-IMSI-1234000069: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000069
+(msc=-1) Paging: subscr-IMSI-1234000070: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000070
+(msc=-1) Paging: subscr-IMSI-1234000071: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000071
+(msc=-1) Paging: subscr-IMSI-1234000072: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000072
+(msc=-1) Paging: subscr-IMSI-1234000073: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000073
+(msc=-1) Paging: subscr-IMSI-1234000074: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000074
+(msc=-1) Paging: subscr-IMSI-1234000075: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000075
+(msc=-1) Paging: subscr-IMSI-1234000076: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000076
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={2.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000077: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000077
+(msc=-1) Paging: subscr-IMSI-1234000078: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000078
+(msc=-1) Paging: subscr-IMSI-1234000079: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000079
+(msc=-1) Paging: subscr-IMSI-1234000080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000080
+(msc=-1) Paging: subscr-IMSI-1234000081: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000081
+(msc=-1) Paging: subscr-IMSI-1234000082: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000082
+(msc=-1) Paging: subscr-IMSI-1234000083: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000083
+(msc=-1) Paging: subscr-IMSI-1234000084: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000084
+(msc=-1) Paging: subscr-IMSI-1234000085: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000085
+(msc=-1) Paging: subscr-IMSI-1234000086: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000086
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={2.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000087: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000087
+(msc=-1) Paging: subscr-IMSI-1234000088: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000088
+(msc=-1) Paging: subscr-IMSI-1234000089: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000089
+(msc=-1) Paging: subscr-IMSI-1234000090: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000090
+(msc=-1) Paging: subscr-IMSI-1234000091: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000091
+(msc=-1) Paging: subscr-IMSI-1234000092: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000092
+(msc=-1) Paging: subscr-IMSI-1234000093: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000093
+(msc=-1) Paging: subscr-IMSI-1234000094: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000094
+(msc=-1) Paging: subscr-IMSI-1234000095: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000095
+(msc=-1) Paging: subscr-IMSI-1234000096: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000096
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={2.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000097: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000097
+(msc=-1) Paging: subscr-IMSI-1234000098: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000098
+(msc=-1) Paging: subscr-IMSI-1234000099: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000099
+(msc=-1) Paging: subscr-IMSI-1234000100: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000100
+(msc=-1) Paging: subscr-IMSI-1234000101: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000101
+(msc=-1) Paging: subscr-IMSI-1234000102: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000102
+(msc=-1) Paging: subscr-IMSI-1234000103: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000103
+(msc=-1) Paging: subscr-IMSI-1234000104: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000104
+(msc=-1) Paging: subscr-IMSI-1234000105: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000105
+(msc=-1) Paging: subscr-IMSI-1234000106: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000106
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={3.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000107: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000107
+(msc=-1) Paging: subscr-IMSI-1234000108: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000108
+(msc=-1) Paging: subscr-IMSI-1234000109: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000109
+(msc=-1) Paging: subscr-IMSI-1234000110: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000110
+(msc=-1) Paging: subscr-IMSI-1234000111: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000111
+(msc=-1) Paging: subscr-IMSI-1234000112: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000112
+(msc=-1) Paging: subscr-IMSI-1234000113: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000113
+(msc=-1) Paging: subscr-IMSI-1234000114: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000114
+(msc=-1) Paging: subscr-IMSI-1234000115: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000115
+(msc=-1) Paging: subscr-IMSI-1234000116: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000116
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={3.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000117: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000117
+(msc=-1) Paging: subscr-IMSI-1234000118: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000118
+(msc=-1) Paging: subscr-IMSI-1234000119: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000119
+(msc=-1) Paging: subscr-IMSI-1234000120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000120
+(msc=-1) Paging: subscr-IMSI-1234000121: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000121
+(msc=-1) Paging: subscr-IMSI-1234000122: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000122
+(msc=-1) Paging: subscr-IMSI-1234000123: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000123
+(msc=-1) Paging: subscr-IMSI-1234000124: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000124
+(msc=-1) Paging: subscr-IMSI-1234000125: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000125
+(msc=-1) Paging: subscr-IMSI-1234000126: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000126
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={3.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000127: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000127
+(msc=-1) Paging: subscr-IMSI-1234000128: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000128
+(msc=-1) Paging: subscr-IMSI-1234000129: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000129
+(msc=-1) Paging: subscr-IMSI-1234000130: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000130
+(msc=-1) Paging: subscr-IMSI-1234000131: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000131
+(msc=-1) Paging: subscr-IMSI-1234000132: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000132
+(msc=-1) Paging: subscr-IMSI-1234000133: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000133
+(msc=-1) Paging: subscr-IMSI-1234000134: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={4.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-1234000134: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000134
+(msc=-1) Paging: subscr-IMSI-1234000135: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000135
+(msc=-1) Paging: subscr-IMSI-1234000136: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000136
+(msc=-1) Paging: subscr-IMSI-1234000137: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000137
+(msc=-1) Paging: subscr-IMSI-1234000138: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000138
+(msc=-1) Paging: subscr-IMSI-1234000139: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000139
+(msc=-1) Paging: subscr-IMSI-1234000140: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000140
+(msc=-1) Paging: subscr-IMSI-1234000141: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000141
+(msc=-1) Paging: subscr-IMSI-1234000142: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000142
+(msc=-1) Paging: subscr-IMSI-1234000143: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000143
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={4.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000144: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000144
+(msc=-1) Paging: subscr-IMSI-1234000145: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000145
+(msc=-1) Paging: subscr-IMSI-1234000146: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000146
+(msc=-1) Paging: subscr-IMSI-1234000147: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000147
+(msc=-1) Paging: subscr-IMSI-1234000148: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000148
+(msc=-1) Paging: subscr-IMSI-1234000149: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000149
+(msc=-1) Paging: subscr-IMSI-1234000150: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000150
+(msc=-1) Paging: subscr-IMSI-1234000151: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000151
+(msc=-1) Paging: subscr-IMSI-1234000152: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000152
+(msc=-1) Paging: subscr-IMSI-1234000153: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000153
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={4.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000154: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000154
+(msc=-1) Paging: subscr-IMSI-1234000155: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000155
+(msc=-1) Paging: subscr-IMSI-1234000156: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000156
+(msc=-1) Paging: subscr-IMSI-1234000157: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000157
+(msc=-1) Paging: subscr-IMSI-1234000158: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000158
+(msc=-1) Paging: subscr-IMSI-1234000159: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000159
+(msc=-1) Paging: subscr-IMSI-1234000160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000160
+(msc=-1) Paging: subscr-IMSI-1234000161: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000161
+(msc=-1) Paging: subscr-IMSI-1234000162: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000162
+(msc=-1) Paging: subscr-IMSI-1234000163: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000163
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={4.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000164: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000164
+(msc=-1) Paging: subscr-IMSI-1234000165: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000165
+(msc=-1) Paging: subscr-IMSI-1234000166: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000166
+(msc=-1) Paging: subscr-IMSI-1234000167: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000167
+(msc=-1) Paging: subscr-IMSI-1234000168: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000168
+(msc=-1) Paging: subscr-IMSI-1234000169: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000169
+(msc=-1) Paging: subscr-IMSI-1234000170: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000170
+(msc=-1) Paging: subscr-IMSI-1234000171: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000171
+(msc=-1) Paging: subscr-IMSI-1234000172: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000172
+(msc=-1) Paging: subscr-IMSI-1234000173: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000173
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={5.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000174: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000174
+(msc=-1) Paging: subscr-IMSI-1234000175: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000175
+(msc=-1) Paging: subscr-IMSI-1234000176: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000176
+(msc=-1) Paging: subscr-IMSI-1234000177: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000177
+(msc=-1) Paging: subscr-IMSI-1234000178: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000178
+(msc=-1) Paging: subscr-IMSI-1234000179: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000179
+(msc=-1) Paging: subscr-IMSI-1234000180: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000180
+(msc=-1) Paging: subscr-IMSI-1234000181: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000181
+(msc=-1) Paging: subscr-IMSI-1234000182: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000182
+(msc=-1) Paging: subscr-IMSI-1234000183: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000183
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={5.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000184: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000184
+(msc=-1) Paging: subscr-IMSI-1234000185: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000185
+(msc=-1) Paging: subscr-IMSI-1234000186: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000186
+(msc=-1) Paging: subscr-IMSI-1234000187: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000187
+(msc=-1) Paging: subscr-IMSI-1234000188: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000188
+(msc=-1) Paging: subscr-IMSI-1234000189: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000189
+(msc=-1) Paging: subscr-IMSI-1234000190: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000190
+(msc=-1) Paging: subscr-IMSI-1234000191: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000191
+(msc=-1) Paging: subscr-IMSI-1234000192: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000192
+(msc=-1) Paging: subscr-IMSI-1234000193: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000193
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={5.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000194: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000194
+(msc=-1) Paging: subscr-IMSI-1234000195: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000195
+(msc=-1) Paging: subscr-IMSI-1234000196: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000196
+(msc=-1) Paging: subscr-IMSI-1234000197: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000197
+(msc=-1) Paging: subscr-IMSI-1234000198: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000198
+(msc=-1) Paging: subscr-IMSI-1234000199: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000199
+(msc=-1) Paging: subscr-IMSI-1234000200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000200
+(msc=-1) Paging: subscr-IMSI-1234000201: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={6.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-1234000201: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000201
+(msc=-1) Paging: subscr-IMSI-1234000202: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000202
+(msc=-1) Paging: subscr-IMSI-1234000203: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000203
+(msc=-1) Paging: subscr-IMSI-1234000204: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000204
+(msc=-1) Paging: subscr-IMSI-1234000205: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000205
+(msc=-1) Paging: subscr-IMSI-1234000206: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000206
+(msc=-1) Paging: subscr-IMSI-1234000207: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000207
+(msc=-1) Paging: subscr-IMSI-1234000208: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000208
+(msc=-1) Paging: subscr-IMSI-1234000209: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000209
+(msc=-1) Paging: subscr-IMSI-1234000210: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000210
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={6.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000211: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000211
+(msc=-1) Paging: subscr-IMSI-1234000212: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000212
+(msc=-1) Paging: subscr-IMSI-1234000213: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000213
+(msc=-1) Paging: subscr-IMSI-1234000214: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000214
+(msc=-1) Paging: subscr-IMSI-1234000215: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000215
+(msc=-1) Paging: subscr-IMSI-1234000216: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000216
+(msc=-1) Paging: subscr-IMSI-1234000217: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000217
+(msc=-1) Paging: subscr-IMSI-1234000218: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000218
+(msc=-1) Paging: subscr-IMSI-1234000219: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000219
+(msc=-1) Paging: subscr-IMSI-1234000220: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000220
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={6.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000221: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000221
+(msc=-1) Paging: subscr-IMSI-1234000222: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000222
+(msc=-1) Paging: subscr-IMSI-1234000223: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000223
+(msc=-1) Paging: subscr-IMSI-1234000224: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000224
+(msc=-1) Paging: subscr-IMSI-1234000225: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000225
+(msc=-1) Paging: subscr-IMSI-1234000226: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000226
+(msc=-1) Paging: subscr-IMSI-1234000227: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000227
+(msc=-1) Paging: subscr-IMSI-1234000228: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000228
+(msc=-1) Paging: subscr-IMSI-1234000229: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000229
+(msc=-1) Paging: subscr-IMSI-1234000230: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000230
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={6.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000231: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000231
+(msc=-1) Paging: subscr-IMSI-1234000232: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000232
+(msc=-1) Paging: subscr-IMSI-1234000233: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000233
+(msc=-1) Paging: subscr-IMSI-1234000234: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000234
+(msc=-1) Paging: subscr-IMSI-1234000235: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000235
+(msc=-1) Paging: subscr-IMSI-1234000236: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000236
+(msc=-1) Paging: subscr-IMSI-1234000237: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000237
+(msc=-1) Paging: subscr-IMSI-1234000238: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000238
+(msc=-1) Paging: subscr-IMSI-1234000239: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000239
+(msc=-1) Paging: subscr-IMSI-1234000240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000240
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={7.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000241: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000241
+(msc=-1) Paging: subscr-IMSI-1234000242: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000242
+(msc=-1) Paging: subscr-IMSI-1234000243: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000243
+(msc=-1) Paging: subscr-IMSI-1234000244: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000244
+(msc=-1) Paging: subscr-IMSI-1234000245: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000245
+(msc=-1) Paging: subscr-IMSI-1234000246: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000246
+(msc=-1) Paging: subscr-IMSI-1234000247: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000247
+(msc=-1) Paging: subscr-IMSI-1234000248: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000248
+(msc=-1) Paging: subscr-IMSI-1234000249: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000249
+(msc=-1) Paging: subscr-IMSI-1234000250: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000250
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={7.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000251: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000251
+(msc=-1) Paging: subscr-IMSI-1234000252: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000252
+(msc=-1) Paging: subscr-IMSI-1234000253: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000253
+(msc=-1) Paging: subscr-IMSI-1234000254: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000254
+(msc=-1) Paging: subscr-IMSI-1234000255: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000255
+(msc=-1) Paging: subscr-IMSI-1234000256: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000256
+(msc=-1) Paging: subscr-IMSI-1234000257: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000257
+(msc=-1) Paging: subscr-IMSI-1234000258: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000258
+(msc=-1) Paging: subscr-IMSI-1234000259: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000259
+(msc=-1) Paging: subscr-IMSI-1234000260: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000260
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={7.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000261: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000261
+(msc=-1) Paging: subscr-IMSI-1234000262: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000262
+(msc=-1) Paging: subscr-IMSI-1234000263: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000263
+(msc=-1) Paging: subscr-IMSI-1234000264: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000264
+(msc=-1) Paging: subscr-IMSI-1234000265: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000265
+(msc=-1) Paging: subscr-IMSI-1234000266: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000266
+(msc=-1) Paging: subscr-IMSI-1234000267: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000267
+(msc=-1) Paging: subscr-IMSI-1234000268: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={8.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-1234000268: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000268
+(msc=-1) Paging: subscr-IMSI-1234000269: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000269
+(msc=-1) Paging: subscr-IMSI-1234000270: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000270
+(msc=-1) Paging: subscr-IMSI-1234000271: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000271
+(msc=-1) Paging: subscr-IMSI-1234000272: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000272
+(msc=-1) Paging: subscr-IMSI-1234000273: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000273
+(msc=-1) Paging: subscr-IMSI-1234000274: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000274
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000275
+(msc=-1) Paging: subscr-IMSI-1234000276: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000276
+(msc=-1) Paging: subscr-IMSI-1234000277: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000277
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={8.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000278: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000278
+(msc=-1) Paging: subscr-IMSI-1234000279: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000279
+(msc=-1) Paging: subscr-IMSI-1234000280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000280
+(msc=-1) Paging: subscr-IMSI-1234000281: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000281
+(msc=-1) Paging: subscr-IMSI-1234000282: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000282
+(msc=-1) Paging: subscr-IMSI-1234000283: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000283
+(msc=-1) Paging: subscr-IMSI-1234000284: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000284
+(msc=-1) Paging: subscr-IMSI-1234000285: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000285
+(msc=-1) Paging: subscr-IMSI-1234000286: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000286
+(msc=-1) Paging: subscr-IMSI-1234000287: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000287
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={8.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000288: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000288
+(msc=-1) Paging: subscr-IMSI-1234000289: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000289
+(msc=-1) Paging: subscr-IMSI-1234000290: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000290
+(msc=-1) Paging: subscr-IMSI-1234000291: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000291
+(msc=-1) Paging: subscr-IMSI-1234000292: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000292
+(msc=-1) Paging: subscr-IMSI-1234000293: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000293
+(msc=-1) Paging: subscr-IMSI-1234000294: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000294
+(msc=-1) Paging: subscr-IMSI-1234000295: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000295
+(msc=-1) Paging: subscr-IMSI-1234000296: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000296
+(msc=-1) Paging: subscr-IMSI-1234000297: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000297
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={8.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000298: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000298
+(msc=-1) Paging: subscr-IMSI-1234000299: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000299
+(msc=-1) Paging: subscr-IMSI-1234000300: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000300
+(msc=-1) Paging: subscr-IMSI-1234000301: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000301
+(msc=-1) Paging: subscr-IMSI-1234000302: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000302
+(msc=-1) Paging: subscr-IMSI-1234000303: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000303
+(msc=-1) Paging: subscr-IMSI-1234000304: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000304
+(msc=-1) Paging: subscr-IMSI-1234000305: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000305
+(msc=-1) Paging: subscr-IMSI-1234000306: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000306
+(msc=-1) Paging: subscr-IMSI-1234000307: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000307
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={9.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000308: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000308
+(msc=-1) Paging: subscr-IMSI-1234000309: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000309
+(msc=-1) Paging: subscr-IMSI-1234000310: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000310
+(msc=-1) Paging: subscr-IMSI-1234000311: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000311
+(msc=-1) Paging: subscr-IMSI-1234000312: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000312
+(msc=-1) Paging: subscr-IMSI-1234000313: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000313
+(msc=-1) Paging: subscr-IMSI-1234000314: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000314
+(msc=-1) Paging: subscr-IMSI-1234000315: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000315
+(msc=-1) Paging: subscr-IMSI-1234000316: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000316
+(msc=-1) Paging: subscr-IMSI-1234000317: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000317
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000015: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000014: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000013: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000012: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000011: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000010: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000009: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000008: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000007: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000006: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000005: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000004: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000003: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000002: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) T3113 expired
+sys={9.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000318: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000318
+(msc=-1) Paging: subscr-IMSI-1234000319: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000319
+(msc=-1) Paging: subscr-IMSI-1234000320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000320
+(msc=-1) Paging: subscr-IMSI-1234000321: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000321
+(msc=-1) Paging: subscr-IMSI-1234000322: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000322
+(msc=-1) Paging: subscr-IMSI-1234000323: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000323
+(msc=-1) Paging: subscr-IMSI-1234000324: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000324
+(msc=-1) Paging: subscr-IMSI-1234000325: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000325
+(msc=-1) Paging: subscr-IMSI-1234000326: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000326
+(msc=-1) Paging: subscr-IMSI-1234000327: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000327
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={9.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000328: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000328
+(msc=-1) Paging: subscr-IMSI-1234000329: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000329
+(msc=-1) Paging: subscr-IMSI-1234000330: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000330
+(msc=-1) Paging: subscr-IMSI-1234000331: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000331
+(msc=-1) Paging: subscr-IMSI-1234000332: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000332
+(msc=-1) Paging: subscr-IMSI-1234000333: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000333
+(msc=-1) Paging: subscr-IMSI-1234000334: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000334
+(msc=-1) Paging: subscr-IMSI-1234000335: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={10.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-1234000335: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000335
+(msc=-1) Paging: subscr-IMSI-1234000336: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000336
+(msc=-1) Paging: subscr-IMSI-1234000337: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000337
+(msc=-1) Paging: subscr-IMSI-1234000338: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000338
+(msc=-1) Paging: subscr-IMSI-1234000339: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000339
+(msc=-1) Paging: subscr-IMSI-1234000340: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000340
+(msc=-1) Paging: subscr-IMSI-1234000341: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000341
+(msc=-1) Paging: subscr-IMSI-1234000342: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000342
+(msc=-1) Paging: subscr-IMSI-1234000343: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000343
+(msc=-1) Paging: subscr-IMSI-1234000344: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000344
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000042: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000041: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000040: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000039: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000038: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000037: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000036: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000035: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000034: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000033: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000032: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000031: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000030: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000029: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000028: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000027: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000026: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000025: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000024: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000023: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000022: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000021: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000020: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000019: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000018: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000017: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000016: (bts=0) T3113 expired
+sys={10.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000345: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000345
+(msc=-1) Paging: subscr-IMSI-1234000346: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000346
+(msc=-1) Paging: subscr-IMSI-1234000347: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000347
+(msc=-1) Paging: subscr-IMSI-1234000348: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000348
+(msc=-1) Paging: subscr-IMSI-1234000349: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000349
+(msc=-1) Paging: subscr-IMSI-1234000350: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000350
+(msc=-1) Paging: subscr-IMSI-1234000351: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000351
+(msc=-1) Paging: subscr-IMSI-1234000352: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000352
+(msc=-1) Paging: subscr-IMSI-1234000353: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000353
+(msc=-1) Paging: subscr-IMSI-1234000354: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000354
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={10.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000355: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000355
+(msc=-1) Paging: subscr-IMSI-1234000356: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000356
+(msc=-1) Paging: subscr-IMSI-1234000357: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000357
+(msc=-1) Paging: subscr-IMSI-1234000358: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000358
+(msc=-1) Paging: subscr-IMSI-1234000359: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000359
+(msc=-1) Paging: subscr-IMSI-1234000360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000360
+(msc=-1) Paging: subscr-IMSI-1234000361: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000361
+(msc=-1) Paging: subscr-IMSI-1234000362: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000362
+(msc=-1) Paging: subscr-IMSI-1234000363: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000363
+(msc=-1) Paging: subscr-IMSI-1234000364: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000364
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={10.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000365: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000365
+(msc=-1) Paging: subscr-IMSI-1234000366: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000366
+(msc=-1) Paging: subscr-IMSI-1234000367: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000367
+(msc=-1) Paging: subscr-IMSI-1234000368: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000368
+(msc=-1) Paging: subscr-IMSI-1234000369: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000369
+(msc=-1) Paging: subscr-IMSI-1234000370: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000370
+(msc=-1) Paging: subscr-IMSI-1234000371: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000371
+(msc=-1) Paging: subscr-IMSI-1234000372: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000372
+(msc=-1) Paging: subscr-IMSI-1234000373: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000373
+(msc=-1) Paging: subscr-IMSI-1234000374: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000374
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={11.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000375: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000375
+(msc=-1) Paging: subscr-IMSI-1234000376: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000376
+(msc=-1) Paging: subscr-IMSI-1234000377: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000377
+(msc=-1) Paging: subscr-IMSI-1234000378: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000378
+(msc=-1) Paging: subscr-IMSI-1234000379: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000379
+(msc=-1) Paging: subscr-IMSI-1234000380: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000380
+(msc=-1) Paging: subscr-IMSI-1234000381: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000381
+(msc=-1) Paging: subscr-IMSI-1234000382: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000382
+(msc=-1) Paging: subscr-IMSI-1234000383: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000383
+(msc=-1) Paging: subscr-IMSI-1234000384: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000384
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000076: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000075: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000074: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000073: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000072: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000071: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000070: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000069: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000068: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000066: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000065: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000064: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000063: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000062: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000061: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000060: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000059: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000058: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000057: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000056: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000055: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000054: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000053: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000052: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000051: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000050: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000049: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000048: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000047: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000046: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000045: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000044: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000043: (bts=0) T3113 expired
+sys={11.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000385: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000385
+(msc=-1) Paging: subscr-IMSI-1234000386: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000386
+(msc=-1) Paging: subscr-IMSI-1234000387: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000387
+(msc=-1) Paging: subscr-IMSI-1234000388: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000388
+(msc=-1) Paging: subscr-IMSI-1234000389: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000389
+(msc=-1) Paging: subscr-IMSI-1234000390: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000390
+(msc=-1) Paging: subscr-IMSI-1234000391: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000391
+(msc=-1) Paging: subscr-IMSI-1234000392: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000392
+(msc=-1) Paging: subscr-IMSI-1234000393: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000393
+(msc=-1) Paging: subscr-IMSI-1234000394: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000394
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={11.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000395: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000395
+(msc=-1) Paging: subscr-IMSI-1234000396: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000396
+(msc=-1) Paging: subscr-IMSI-1234000397: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000397
+(msc=-1) Paging: subscr-IMSI-1234000398: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000398
+(msc=-1) Paging: subscr-IMSI-1234000399: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000399
+(msc=-1) Paging: subscr-IMSI-1234000400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000400
+(msc=-1) Paging: subscr-IMSI-1234000401: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000401
+(msc=-1) Paging: subscr-IMSI-1234000402: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={12.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-1234000402: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000402
+(msc=-1) Paging: subscr-IMSI-1234000403: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000403
+(msc=-1) Paging: subscr-IMSI-1234000404: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000404
+(msc=-1) Paging: subscr-IMSI-1234000405: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000405
+(msc=-1) Paging: subscr-IMSI-1234000406: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000406
+(msc=-1) Paging: subscr-IMSI-1234000407: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000407
+(msc=-1) Paging: subscr-IMSI-1234000408: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000408
+(msc=-1) Paging: subscr-IMSI-1234000409: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000409
+(msc=-1) Paging: subscr-IMSI-1234000410: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000410
+(msc=-1) Paging: subscr-IMSI-1234000411: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000411
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000111: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000110: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000109: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000108: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000107: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000106: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000105: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000104: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000103: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000102: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000101: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000100: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000099: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000098: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000097: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000096: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000095: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000094: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000093: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000092: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000091: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000090: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000089: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000088: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000087: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000086: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000085: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000084: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000083: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000082: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000081: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000080: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000079: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000078: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000077: (bts=0) T3113 expired
+sys={12.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000412: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000412
+(msc=-1) Paging: subscr-IMSI-1234000413: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000413
+(msc=-1) Paging: subscr-IMSI-1234000414: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000414
+(msc=-1) Paging: subscr-IMSI-1234000415: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000415
+(msc=-1) Paging: subscr-IMSI-1234000416: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000416
+(msc=-1) Paging: subscr-IMSI-1234000417: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000417
+(msc=-1) Paging: subscr-IMSI-1234000418: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000418
+(msc=-1) Paging: subscr-IMSI-1234000419: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000419
+(msc=-1) Paging: subscr-IMSI-1234000420: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000420
+(msc=-1) Paging: subscr-IMSI-1234000421: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000421
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={12.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000422: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000422
+(msc=-1) Paging: subscr-IMSI-1234000423: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000423
+(msc=-1) Paging: subscr-IMSI-1234000424: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000424
+(msc=-1) Paging: subscr-IMSI-1234000425: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000425
+(msc=-1) Paging: subscr-IMSI-1234000426: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000426
+(msc=-1) Paging: subscr-IMSI-1234000427: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000427
+(msc=-1) Paging: subscr-IMSI-1234000428: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000428
+(msc=-1) Paging: subscr-IMSI-1234000429: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000429
+(msc=-1) Paging: subscr-IMSI-1234000430: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000430
+(msc=-1) Paging: subscr-IMSI-1234000431: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000431
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={12.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000432: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000432
+(msc=-1) Paging: subscr-IMSI-1234000433: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000433
+(msc=-1) Paging: subscr-IMSI-1234000434: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000434
+(msc=-1) Paging: subscr-IMSI-1234000435: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000435
+(msc=-1) Paging: subscr-IMSI-1234000436: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000436
+(msc=-1) Paging: subscr-IMSI-1234000437: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000437
+(msc=-1) Paging: subscr-IMSI-1234000438: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000438
+(msc=-1) Paging: subscr-IMSI-1234000439: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000439
+(msc=-1) Paging: subscr-IMSI-1234000440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000440
+(msc=-1) Paging: subscr-IMSI-1234000441: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000441
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={13.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000442: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000442
+(msc=-1) Paging: subscr-IMSI-1234000443: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000443
+(msc=-1) Paging: subscr-IMSI-1234000444: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000444
+(msc=-1) Paging: subscr-IMSI-1234000445: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000445
+(msc=-1) Paging: subscr-IMSI-1234000446: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000446
+(msc=-1) Paging: subscr-IMSI-1234000447: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000447
+(msc=-1) Paging: subscr-IMSI-1234000448: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000448
+(msc=-1) Paging: subscr-IMSI-1234000449: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000449
+(msc=-1) Paging: subscr-IMSI-1234000450: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000450
+(msc=-1) Paging: subscr-IMSI-1234000451: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000451
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000146: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000145: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000144: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000143: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000142: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000141: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000140: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000139: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000138: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000137: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000136: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000135: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000134: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000133: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000132: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000131: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000130: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000129: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000128: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000127: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000126: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000125: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000124: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000123: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000122: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000121: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000120: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000119: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000118: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000117: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000116: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000115: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000114: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000113: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000112: (bts=0) T3113 expired
+sys={13.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000452: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000452
+(msc=-1) Paging: subscr-IMSI-1234000453: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000453
+(msc=-1) Paging: subscr-IMSI-1234000454: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000454
+(msc=-1) Paging: subscr-IMSI-1234000455: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000455
+(msc=-1) Paging: subscr-IMSI-1234000456: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000456
+(msc=-1) Paging: subscr-IMSI-1234000457: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000457
+(msc=-1) Paging: subscr-IMSI-1234000458: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000458
+(msc=-1) Paging: subscr-IMSI-1234000459: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000459
+(msc=-1) Paging: subscr-IMSI-1234000460: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000460
+(msc=-1) Paging: subscr-IMSI-1234000461: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000461
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={13.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000462: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000462
+(msc=-1) Paging: subscr-IMSI-1234000463: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000463
+(msc=-1) Paging: subscr-IMSI-1234000464: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000464
+(msc=-1) Paging: subscr-IMSI-1234000465: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000465
+(msc=-1) Paging: subscr-IMSI-1234000466: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000466
+(msc=-1) Paging: subscr-IMSI-1234000467: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000467
+(msc=-1) Paging: subscr-IMSI-1234000468: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000468
+(msc=-1) Paging: subscr-IMSI-1234000469: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={14.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-1234000469: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000469
+(msc=-1) Paging: subscr-IMSI-1234000470: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000470
+(msc=-1) Paging: subscr-IMSI-1234000471: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000471
+(msc=-1) Paging: subscr-IMSI-1234000472: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000472
+(msc=-1) Paging: subscr-IMSI-1234000473: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000473
+(msc=-1) Paging: subscr-IMSI-1234000474: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000474
+(msc=-1) Paging: subscr-IMSI-1234000475: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000475
+(msc=-1) Paging: subscr-IMSI-1234000476: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000476
+(msc=-1) Paging: subscr-IMSI-1234000477: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000477
+(msc=-1) Paging: subscr-IMSI-1234000478: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000478
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000181: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000180: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000179: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000178: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000177: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000176: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000175: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000174: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000173: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000172: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000171: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000170: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000169: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000168: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000167: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000166: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000165: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000164: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000163: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000162: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000161: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000160: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000159: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000158: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000157: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000156: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000155: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000154: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000153: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000152: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000151: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000150: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000149: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000148: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000147: (bts=0) T3113 expired
+sys={14.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000479: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000479
+(msc=-1) Paging: subscr-IMSI-1234000480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000480
+(msc=-1) Paging: subscr-IMSI-1234000481: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000481
+(msc=-1) Paging: subscr-IMSI-1234000482: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000482
+(msc=-1) Paging: subscr-IMSI-1234000483: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000483
+(msc=-1) Paging: subscr-IMSI-1234000484: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000484
+(msc=-1) Paging: subscr-IMSI-1234000485: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000485
+(msc=-1) Paging: subscr-IMSI-1234000486: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000486
+(msc=-1) Paging: subscr-IMSI-1234000487: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000487
+(msc=-1) Paging: subscr-IMSI-1234000488: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000488
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={14.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000489: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000489
+(msc=-1) Paging: subscr-IMSI-1234000490: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000490
+(msc=-1) Paging: subscr-IMSI-1234000491: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000491
+(msc=-1) Paging: subscr-IMSI-1234000492: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000492
+(msc=-1) Paging: subscr-IMSI-1234000493: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000493
+(msc=-1) Paging: subscr-IMSI-1234000494: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000494
+(msc=-1) Paging: subscr-IMSI-1234000495: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000495
+(msc=-1) Paging: subscr-IMSI-1234000496: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000496
+(msc=-1) Paging: subscr-IMSI-1234000497: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000497
+(msc=-1) Paging: subscr-IMSI-1234000498: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000498
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={14.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000499: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000499
+(msc=-1) Paging: subscr-IMSI-1234000182: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000182
+(msc=-1) Paging: subscr-IMSI-1234000183: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000183
+(msc=-1) Paging: subscr-IMSI-1234000184: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000184
+(msc=-1) Paging: subscr-IMSI-1234000185: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000185
+(msc=-1) Paging: subscr-IMSI-1234000186: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000186
+(msc=-1) Paging: subscr-IMSI-1234000187: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000187
+(msc=-1) Paging: subscr-IMSI-1234000188: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000188
+(msc=-1) Paging: subscr-IMSI-1234000189: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000189
+(msc=-1) Paging: subscr-IMSI-1234000190: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000190
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (1 initial, 9 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000191: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000192: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000193: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000194: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000195: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000196: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000197: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000198: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000199: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000201: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000202: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000203: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000204: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000205: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000206: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000207: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000208: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000209: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000210: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000211: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000212: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000213: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000214: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000215: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000216: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000217: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000218: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000219: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000220: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000221: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000222: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000223: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000224: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000225: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000226: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000227: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000228: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000229: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000230: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000231: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000232: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000233: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000234: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000235: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000236: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000237: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000238: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000239: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000241: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000242: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000243: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000244: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000245: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000246: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000247: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000248: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000249: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000250: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000251: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000252: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000253: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000254: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000255: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000256: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000257: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000258: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000259: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000260: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000261: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000262: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000263: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000264: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000265: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000266: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000267: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000268: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000269: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000270: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000271: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000272: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000273: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000274: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000276: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000277: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000278: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000279: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000281: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000282: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000283: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000284: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000285: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000286: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000287: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000288: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000289: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000290: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000291: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000292: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000293: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000294: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000295: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000296: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000297: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000298: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000299: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000300: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000301: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000302: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000303: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000304: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000305: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000306: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000307: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000308: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000309: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000310: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000311: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000312: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000313: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000314: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000315: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000316: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000317: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000318: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000319: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000321: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000322: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000323: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000324: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000325: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000326: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000327: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000328: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000329: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000330: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000331: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000332: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000333: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000334: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000335: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000336: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000337: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000338: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000339: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000340: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000341: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000342: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000343: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000344: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000345: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000346: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000347: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000348: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000349: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000350: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000351: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000352: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000353: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000354: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000355: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000356: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000357: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000358: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000359: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000361: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000362: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000363: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000364: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000365: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000366: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000367: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000368: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000369: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000370: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000371: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000372: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000373: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000374: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000375: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000376: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000377: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000378: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000379: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000380: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000381: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000382: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000383: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000384: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000385: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000386: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000387: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000388: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000389: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000390: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000391: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000392: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000393: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000394: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000395: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000396: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000397: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000398: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000399: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000401: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000402: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000403: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000404: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000405: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000406: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000407: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000408: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000409: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000410: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000411: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000412: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000413: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000414: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000415: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000416: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000417: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000418: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000419: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000420: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000421: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000422: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000423: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000424: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000425: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000426: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000427: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000428: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000429: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000430: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000431: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000432: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000433: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000434: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000435: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000436: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000437: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000438: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000439: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000441: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000442: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000443: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000444: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000445: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000446: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000447: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000448: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000449: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000450: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000451: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000452: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000453: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000454: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000455: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000456: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000457: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000458: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000459: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000460: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000461: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000462: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000463: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000464: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000465: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000466: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000467: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000468: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000469: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000470: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000471: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000472: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000473: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000474: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000475: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000476: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000477: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000478: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000479: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000481: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000482: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000483: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000484: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000485: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000486: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000487: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000488: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000489: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000490: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000491: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000492: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000493: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000494: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000495: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000496: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000497: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000498: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000499: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000182: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000183: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000184: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000185: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000186: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000187: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000188: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000189: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000190: (bts=0) Stop paging (flush)
+BTS deallocated OK in test_paging500()
+===test_paging500_samepgroup===
+BTS allocation OK in test_paging500_samepgroup()
+(bts=0) C0 becomes available for paging
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+Number of paging groups: 40
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000000: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(msc=-1) Paging: subscr-IMSI-123400000000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000000
+(msc=-1) Paging: subscr-IMSI-123400000000: (bts=0) Paging delayed: retransmission happens in 0.500000s
+(bts=0) Paged 1 subscribers (1 initial, 0 retrans) during last iteration
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000040: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(msc=-1) Paging: subscr-IMSI-123400000040: (bts=0) New req arrived: re-scheduling next batch in 0.250000s
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000080: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000120: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000160: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000200: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000240: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000280: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000320: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000360: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000400: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000440: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000480: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000520: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000560: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000600: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000640: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000680: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000720: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000760: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000800: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000840: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000880: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000920: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400000960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400000960: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001000: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001040: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001080: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001120: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001160: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001200: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001240: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001280: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001320: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001360: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001400: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001440: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001480: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001520: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001560: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001600: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001640: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001680: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001720: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001760: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001800: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001840: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001880: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001920: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400001960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400001960: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002000: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002040: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002080: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002120: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002160: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002200: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002240: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002280: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002320: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002360: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002400: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002440: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002480: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002520: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002560: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002600: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002640: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002680: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002720: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002760: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002800: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002840: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002880: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002920: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400002960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400002960: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003000: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003040: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003080: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003120: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003160: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003200: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003240: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003280: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003320: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003360: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003400: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003440: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003480: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003520: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003560: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003600: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003640: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003680: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003720: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003760: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003800: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003840: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003880: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003920: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400003960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400003960: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004000: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004040: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004080: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004120: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004160: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004200: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004240: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004280: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004320: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004360: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004400: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004440: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004480: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004520: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004560: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004600: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004640: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004680: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004720: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004760: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004800: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004840: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004880: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004920: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400004960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400004960: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005000: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005040: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005080: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005120: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005160: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005200: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005240: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005280: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005320: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005360: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005400: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005440: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005480: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005520: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005560: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005600: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005640: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005680: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005720: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005760: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005800: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005840: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005880: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005920: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400005960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400005960: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006000: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006040: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006080: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006120: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006160: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006200: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006240: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006280: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006320: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006360: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006400: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006440: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006480: (bts=0) Paging request: T3113 expires in 38 seconds (estimated 38)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006520: (bts=0) Paging request: T3113 expires in 38 seconds (estimated 38)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006560: (bts=0) Paging request: T3113 expires in 38 seconds (estimated 38)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006600: (bts=0) Paging request: T3113 expires in 38 seconds (estimated 38)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006640: (bts=0) Paging request: T3113 expires in 38 seconds (estimated 38)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006680: (bts=0) Paging request: T3113 expires in 38 seconds (estimated 38)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006720: (bts=0) Paging request: T3113 expires in 38 seconds (estimated 38)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006760: (bts=0) Paging request: T3113 expires in 38 seconds (estimated 38)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006800: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006840: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006880: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006920: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400006960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400006960: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007000: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007040: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007080: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007120: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007160: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007200: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007240: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007280: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007320: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007360: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007400: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007440: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007480: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007520: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007560: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007600: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007640: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007680: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007720: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007760: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007800: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007840: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007880: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007920: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400007960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400007960: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008000: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008040: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008080: (bts=0) Paging request: T3113 expires in 45 seconds (estimated 45)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008120: (bts=0) Paging request: T3113 expires in 45 seconds (estimated 45)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008160: (bts=0) Paging request: T3113 expires in 45 seconds (estimated 45)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008200: (bts=0) Paging request: T3113 expires in 45 seconds (estimated 45)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008240: (bts=0) Paging request: T3113 expires in 45 seconds (estimated 45)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008280: (bts=0) Paging request: T3113 expires in 45 seconds (estimated 45)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008320: (bts=0) Paging request: T3113 expires in 45 seconds (estimated 45)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008360: (bts=0) Paging request: T3113 expires in 45 seconds (estimated 45)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008400: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008440: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008480: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008520: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008560: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008600: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008640: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008680: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008720: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008760: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008800: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008840: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008880: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008920: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400008960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400008960: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009000: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009040: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009080: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009120: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009160: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009200: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009240: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009280: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009320: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009360: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009400: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009440: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009480: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009520: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009560: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009600: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009640: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009680: (bts=0) Paging request: T3113 expires in 52 seconds (estimated 52)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009720: (bts=0) Paging request: T3113 expires in 52 seconds (estimated 52)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009760: (bts=0) Paging request: T3113 expires in 52 seconds (estimated 52)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009800: (bts=0) Paging request: T3113 expires in 52 seconds (estimated 52)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009840: (bts=0) Paging request: T3113 expires in 52 seconds (estimated 52)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009880: (bts=0) Paging request: T3113 expires in 52 seconds (estimated 52)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009920: (bts=0) Paging request: T3113 expires in 52 seconds (estimated 52)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400009960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400009960: (bts=0) Paging request: T3113 expires in 52 seconds (estimated 52)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010000: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010040: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010080: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010120: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010160: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010200: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010240: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010280: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010320: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010360: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010400: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010440: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010480: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010520: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010560: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010600: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010640: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010680: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010720: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010760: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010800: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010840: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010880: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010920: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400010960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400010960: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011000: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011040: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011080: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011120: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011160: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011200: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011240: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011280: (bts=0) Paging request: T3113 expires in 59 seconds (estimated 59)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011320: (bts=0) Paging request: T3113 expires in 59 seconds (estimated 59)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011360: (bts=0) Paging request: T3113 expires in 59 seconds (estimated 59)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011400: (bts=0) Paging request: T3113 expires in 59 seconds (estimated 59)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011440: (bts=0) Paging request: T3113 expires in 59 seconds (estimated 59)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011480: (bts=0) Paging request: T3113 expires in 59 seconds (estimated 59)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011520: (bts=0) Paging request: T3113 expires in 59 seconds (estimated 59)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011560: (bts=0) Paging request: T3113 expires in 59 seconds (estimated 59)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011600: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011640: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011680: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011720: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011760: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011800: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011840: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011880: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011920: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400011960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400011960: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012000: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012040: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012080: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012120: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012160: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012200: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012240: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012280: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012320: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012360: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012400: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012440: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012480: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012520: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012560: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012600: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012640: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012680: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012720: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012760: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012800: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012840: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012880: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 66)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012920: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 66)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400012960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400012960: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 66)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013000: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 66)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013040: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 66)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013080: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 66)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013120: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 66)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013160: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 66)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013200: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013240: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013280: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013320: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013360: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013400: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013440: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013480: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013520: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013560: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013600: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013640: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013680: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013720: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013760: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013800: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013840: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013880: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013920: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400013960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400013960: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014000: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014040: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014080: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014120: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014160: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014200: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014240: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014280: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014320: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014360: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014400: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014440: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014480: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 73)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014520: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 73)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014560: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 73)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014600: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 73)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014640: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 73)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014680: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 73)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014720: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 73)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014760: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 73)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014800: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014840: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014880: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014920: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400014960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400014960: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015000: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015040: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015080: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015120: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015160: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015200: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015240: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015280: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015320: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015360: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015400: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015440: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 77)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015480: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 77)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015520: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 77)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015560: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 77)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015600: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 77)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015640: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 77)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015680: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 77)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015720: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 77)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015760: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015800: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015840: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015880: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015920: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400015960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400015960: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016000: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016040: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016080: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 80)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016120: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 80)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016160: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 80)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016200: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 80)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016240: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 80)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016280: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 80)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016320: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 80)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016360: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 80)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016400: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016440: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016480: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016520: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016560: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016600: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016640: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016680: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016720: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016760: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016800: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016840: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016880: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016920: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400016960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400016960: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017000: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017040: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 84)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017080: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 84)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017120: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 84)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017160: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 84)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017200: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 84)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017240: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 84)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017280: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 84)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017320: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 84)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017360: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017400: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017440: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017480: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017520: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017560: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017600: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017640: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017680: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 87)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017720: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 87)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017760: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 87)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017800: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 87)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017840: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 87)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017880: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 87)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017920: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 87)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400017960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400017960: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 87)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018000: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018040: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018080: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018120: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018160: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018200: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018240: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018280: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018320: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018360: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018400: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018440: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018480: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018520: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018560: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018600: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018640: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 91)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018680: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 91)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018720: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 91)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018760: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 91)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018800: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 91)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018840: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 91)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018880: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 91)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018920: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 91)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400018960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400018960: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019000: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019040: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019080: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019120: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019160: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019200: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019240: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019280: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019320: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019360: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019400: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019440: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019480: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019520: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019520: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019560: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019560: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019600: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019600: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019640: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019640: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019680: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019680: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019720: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019720: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019760: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019760: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019800: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019800: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019840: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019840: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019880: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019880: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019920: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019920: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 97)
+(bts=0) Estimated 2039 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-123400019960: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-123400019960: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 97)
+sys={0.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400000040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000040
+(msc=-1) Paging: subscr-IMSI-123400000080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000080
+(msc=-1) Paging: subscr-IMSI-123400000120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000120
+(msc=-1) Paging: subscr-IMSI-123400000160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000160
+(msc=-1) Paging: subscr-IMSI-123400000200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000200
+(msc=-1) Paging: subscr-IMSI-123400000240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000240
+(msc=-1) Paging: subscr-IMSI-123400000280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000280
+(msc=-1) Paging: subscr-IMSI-123400000320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000320
+(msc=-1) Paging: subscr-IMSI-123400000360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000360
+(msc=-1) Paging: subscr-IMSI-123400000400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000400
+(bts=0) Scheduling next batch in 0.250000s (available_slots=56)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={0.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400000440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000440
+(msc=-1) Paging: subscr-IMSI-123400000480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000480
+(msc=-1) Paging: subscr-IMSI-123400000520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000520
+(msc=-1) Paging: subscr-IMSI-123400000560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000560
+(msc=-1) Paging: subscr-IMSI-123400000600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000600
+(msc=-1) Paging: subscr-IMSI-123400000640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000640
+(msc=-1) Paging: subscr-IMSI-123400000680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000680
+(msc=-1) Paging: subscr-IMSI-123400000720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000720
+(msc=-1) Paging: subscr-IMSI-123400000760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000760
+(msc=-1) Paging: subscr-IMSI-123400000800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000800
+(bts=0) Scheduling next batch in 0.250000s (available_slots=46)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={0.750000}: select()
+(msc=-1) Paging: subscr-IMSI-123400000840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000840
+(msc=-1) Paging: subscr-IMSI-123400000880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000880
+(msc=-1) Paging: subscr-IMSI-123400000920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000920
+(msc=-1) Paging: subscr-IMSI-123400000960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400000960
+(msc=-1) Paging: subscr-IMSI-123400001000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001000
+(msc=-1) Paging: subscr-IMSI-123400001040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001040
+(msc=-1) Paging: subscr-IMSI-123400001080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001080
+(msc=-1) Paging: subscr-IMSI-123400001120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001120
+(msc=-1) Paging: subscr-IMSI-123400001160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001160
+(msc=-1) Paging: subscr-IMSI-123400001200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001200
+(bts=0) Scheduling next batch in 0.250000s (available_slots=36)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.000000}: select()
+(msc=-1) Paging: subscr-IMSI-123400001240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001240
+(msc=-1) Paging: subscr-IMSI-123400001280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001280
+(msc=-1) Paging: subscr-IMSI-123400001320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001320
+(msc=-1) Paging: subscr-IMSI-123400001360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001360
+(msc=-1) Paging: subscr-IMSI-123400001400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001400
+(msc=-1) Paging: subscr-IMSI-123400001440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001440
+(msc=-1) Paging: subscr-IMSI-123400001480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001480
+(msc=-1) Paging: subscr-IMSI-123400001520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001520
+(msc=-1) Paging: subscr-IMSI-123400001560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001560
+(msc=-1) Paging: subscr-IMSI-123400001600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001600
+(bts=0) Scheduling next batch in 0.250000s (available_slots=26)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400001640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001640
+(msc=-1) Paging: subscr-IMSI-123400001680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001680
+(msc=-1) Paging: subscr-IMSI-123400001720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001720
+(msc=-1) Paging: subscr-IMSI-123400001760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001760
+(msc=-1) Paging: subscr-IMSI-123400001800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001800
+(msc=-1) Paging: subscr-IMSI-123400001840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001840
+(msc=-1) Paging: subscr-IMSI-123400001880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001880
+(msc=-1) Paging: subscr-IMSI-123400001920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001920
+(msc=-1) Paging: subscr-IMSI-123400001960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001960
+(msc=-1) Paging: subscr-IMSI-123400002000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002000
+(bts=0) Scheduling next batch in 0.250000s (available_slots=16)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400002040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002040
+(msc=-1) Paging: subscr-IMSI-123400002080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002080
+(msc=-1) Paging: subscr-IMSI-123400002120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002120
+(msc=-1) Paging: subscr-IMSI-123400002160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002160
+(msc=-1) Paging: subscr-IMSI-123400002200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002200
+(msc=-1) Paging: subscr-IMSI-123400002240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002240
+(msc=-1) Paging: subscr-IMSI-123400002280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002280
+(msc=-1) Paging: subscr-IMSI-123400002320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002320
+(msc=-1) Paging: subscr-IMSI-123400002360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002360
+(msc=-1) Paging: subscr-IMSI-123400002400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002400
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.750000}: select()
+(msc=-1) Paging: subscr-IMSI-123400002440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002440
+(msc=-1) Paging: subscr-IMSI-123400002480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002480
+(msc=-1) Paging: subscr-IMSI-123400002520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002520
+(msc=-1) Paging: subscr-IMSI-123400002560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002560
+(msc=-1) Paging: subscr-IMSI-123400002600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002600
+(msc=-1) Paging: subscr-IMSI-123400002640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002640
+(msc=-1) Paging: subscr-IMSI-123400002680: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={2.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-123400002680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002680
+(msc=-1) Paging: subscr-IMSI-123400002720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002720
+(msc=-1) Paging: subscr-IMSI-123400002760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002760
+(msc=-1) Paging: subscr-IMSI-123400002800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002800
+(msc=-1) Paging: subscr-IMSI-123400002840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002840
+(msc=-1) Paging: subscr-IMSI-123400002880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002880
+(msc=-1) Paging: subscr-IMSI-123400002920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002920
+(msc=-1) Paging: subscr-IMSI-123400002960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400002960
+(msc=-1) Paging: subscr-IMSI-123400003000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003000
+(msc=-1) Paging: subscr-IMSI-123400003040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003040
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={2.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400003080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003080
+(msc=-1) Paging: subscr-IMSI-123400003120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003120
+(msc=-1) Paging: subscr-IMSI-123400003160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003160
+(msc=-1) Paging: subscr-IMSI-123400003200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003200
+(msc=-1) Paging: subscr-IMSI-123400003240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003240
+(msc=-1) Paging: subscr-IMSI-123400003280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003280
+(msc=-1) Paging: subscr-IMSI-123400003320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003320
+(msc=-1) Paging: subscr-IMSI-123400003360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003360
+(msc=-1) Paging: subscr-IMSI-123400003400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003400
+(msc=-1) Paging: subscr-IMSI-123400003440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003440
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={2.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400003480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003480
+(msc=-1) Paging: subscr-IMSI-123400003520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003520
+(msc=-1) Paging: subscr-IMSI-123400003560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003560
+(msc=-1) Paging: subscr-IMSI-123400003600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003600
+(msc=-1) Paging: subscr-IMSI-123400003640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003640
+(msc=-1) Paging: subscr-IMSI-123400003680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003680
+(msc=-1) Paging: subscr-IMSI-123400003720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003720
+(msc=-1) Paging: subscr-IMSI-123400003760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003760
+(msc=-1) Paging: subscr-IMSI-123400003800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003800
+(msc=-1) Paging: subscr-IMSI-123400003840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003840
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={2.750000}: select()
+(msc=-1) Paging: subscr-IMSI-123400003880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003880
+(msc=-1) Paging: subscr-IMSI-123400003920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003920
+(msc=-1) Paging: subscr-IMSI-123400003960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400003960
+(msc=-1) Paging: subscr-IMSI-123400004000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004000
+(msc=-1) Paging: subscr-IMSI-123400004040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004040
+(msc=-1) Paging: subscr-IMSI-123400004080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004080
+(msc=-1) Paging: subscr-IMSI-123400004120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004120
+(msc=-1) Paging: subscr-IMSI-123400004160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004160
+(msc=-1) Paging: subscr-IMSI-123400004200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004200
+(msc=-1) Paging: subscr-IMSI-123400004240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004240
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={3.000000}: select()
+(msc=-1) Paging: subscr-IMSI-123400004280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004280
+(msc=-1) Paging: subscr-IMSI-123400004320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004320
+(msc=-1) Paging: subscr-IMSI-123400004360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004360
+(msc=-1) Paging: subscr-IMSI-123400004400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004400
+(msc=-1) Paging: subscr-IMSI-123400004440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004440
+(msc=-1) Paging: subscr-IMSI-123400004480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004480
+(msc=-1) Paging: subscr-IMSI-123400004520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004520
+(msc=-1) Paging: subscr-IMSI-123400004560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004560
+(msc=-1) Paging: subscr-IMSI-123400004600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004600
+(msc=-1) Paging: subscr-IMSI-123400004640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004640
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={3.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400004680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004680
+(msc=-1) Paging: subscr-IMSI-123400004720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004720
+(msc=-1) Paging: subscr-IMSI-123400004760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004760
+(msc=-1) Paging: subscr-IMSI-123400004800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004800
+(msc=-1) Paging: subscr-IMSI-123400004840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004840
+(msc=-1) Paging: subscr-IMSI-123400004880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004880
+(msc=-1) Paging: subscr-IMSI-123400004920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004920
+(msc=-1) Paging: subscr-IMSI-123400004960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400004960
+(msc=-1) Paging: subscr-IMSI-123400005000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005000
+(msc=-1) Paging: subscr-IMSI-123400005040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005040
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={3.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400005080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005080
+(msc=-1) Paging: subscr-IMSI-123400005120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005120
+(msc=-1) Paging: subscr-IMSI-123400005160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005160
+(msc=-1) Paging: subscr-IMSI-123400005200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005200
+(msc=-1) Paging: subscr-IMSI-123400005240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005240
+(msc=-1) Paging: subscr-IMSI-123400005280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005280
+(msc=-1) Paging: subscr-IMSI-123400005320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005320
+(msc=-1) Paging: subscr-IMSI-123400005360: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={4.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-123400005360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005360
+(msc=-1) Paging: subscr-IMSI-123400005400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005400
+(msc=-1) Paging: subscr-IMSI-123400005440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005440
+(msc=-1) Paging: subscr-IMSI-123400005480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005480
+(msc=-1) Paging: subscr-IMSI-123400005520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005520
+(msc=-1) Paging: subscr-IMSI-123400005560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005560
+(msc=-1) Paging: subscr-IMSI-123400005600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005600
+(msc=-1) Paging: subscr-IMSI-123400005640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005640
+(msc=-1) Paging: subscr-IMSI-123400005680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005680
+(msc=-1) Paging: subscr-IMSI-123400005720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005720
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={4.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400005760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005760
+(msc=-1) Paging: subscr-IMSI-123400005800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005800
+(msc=-1) Paging: subscr-IMSI-123400005840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005840
+(msc=-1) Paging: subscr-IMSI-123400005880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005880
+(msc=-1) Paging: subscr-IMSI-123400005920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005920
+(msc=-1) Paging: subscr-IMSI-123400005960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400005960
+(msc=-1) Paging: subscr-IMSI-123400006000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006000
+(msc=-1) Paging: subscr-IMSI-123400006040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006040
+(msc=-1) Paging: subscr-IMSI-123400006080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006080
+(msc=-1) Paging: subscr-IMSI-123400006120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006120
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={4.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400006160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006160
+(msc=-1) Paging: subscr-IMSI-123400006200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006200
+(msc=-1) Paging: subscr-IMSI-123400006240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006240
+(msc=-1) Paging: subscr-IMSI-123400006280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006280
+(msc=-1) Paging: subscr-IMSI-123400006320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006320
+(msc=-1) Paging: subscr-IMSI-123400006360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006360
+(msc=-1) Paging: subscr-IMSI-123400006400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006400
+(msc=-1) Paging: subscr-IMSI-123400006440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006440
+(msc=-1) Paging: subscr-IMSI-123400006480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006480
+(msc=-1) Paging: subscr-IMSI-123400006520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006520
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={4.750000}: select()
+(msc=-1) Paging: subscr-IMSI-123400006560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006560
+(msc=-1) Paging: subscr-IMSI-123400006600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006600
+(msc=-1) Paging: subscr-IMSI-123400006640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006640
+(msc=-1) Paging: subscr-IMSI-123400006680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006680
+(msc=-1) Paging: subscr-IMSI-123400006720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006720
+(msc=-1) Paging: subscr-IMSI-123400006760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006760
+(msc=-1) Paging: subscr-IMSI-123400006800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006800
+(msc=-1) Paging: subscr-IMSI-123400006840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006840
+(msc=-1) Paging: subscr-IMSI-123400006880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006880
+(msc=-1) Paging: subscr-IMSI-123400006920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006920
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={5.000000}: select()
+(msc=-1) Paging: subscr-IMSI-123400006960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400006960
+(msc=-1) Paging: subscr-IMSI-123400007000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007000
+(msc=-1) Paging: subscr-IMSI-123400007040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007040
+(msc=-1) Paging: subscr-IMSI-123400007080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007080
+(msc=-1) Paging: subscr-IMSI-123400007120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007120
+(msc=-1) Paging: subscr-IMSI-123400007160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007160
+(msc=-1) Paging: subscr-IMSI-123400007200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007200
+(msc=-1) Paging: subscr-IMSI-123400007240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007240
+(msc=-1) Paging: subscr-IMSI-123400007280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007280
+(msc=-1) Paging: subscr-IMSI-123400007320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007320
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={5.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400007360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007360
+(msc=-1) Paging: subscr-IMSI-123400007400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007400
+(msc=-1) Paging: subscr-IMSI-123400007440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007440
+(msc=-1) Paging: subscr-IMSI-123400007480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007480
+(msc=-1) Paging: subscr-IMSI-123400007520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007520
+(msc=-1) Paging: subscr-IMSI-123400007560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007560
+(msc=-1) Paging: subscr-IMSI-123400007600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007600
+(msc=-1) Paging: subscr-IMSI-123400007640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007640
+(msc=-1) Paging: subscr-IMSI-123400007680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007680
+(msc=-1) Paging: subscr-IMSI-123400007720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007720
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={5.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400007760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007760
+(msc=-1) Paging: subscr-IMSI-123400007800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007800
+(msc=-1) Paging: subscr-IMSI-123400007840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007840
+(msc=-1) Paging: subscr-IMSI-123400007880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007880
+(msc=-1) Paging: subscr-IMSI-123400007920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007920
+(msc=-1) Paging: subscr-IMSI-123400007960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400007960
+(msc=-1) Paging: subscr-IMSI-123400008000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008000
+(msc=-1) Paging: subscr-IMSI-123400008040: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={6.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-123400008040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008040
+(msc=-1) Paging: subscr-IMSI-123400008080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008080
+(msc=-1) Paging: subscr-IMSI-123400008120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008120
+(msc=-1) Paging: subscr-IMSI-123400008160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008160
+(msc=-1) Paging: subscr-IMSI-123400008200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008200
+(msc=-1) Paging: subscr-IMSI-123400008240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008240
+(msc=-1) Paging: subscr-IMSI-123400008280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008280
+(msc=-1) Paging: subscr-IMSI-123400008320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008320
+(msc=-1) Paging: subscr-IMSI-123400008360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008360
+(msc=-1) Paging: subscr-IMSI-123400008400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008400
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={6.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400008440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008440
+(msc=-1) Paging: subscr-IMSI-123400008480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008480
+(msc=-1) Paging: subscr-IMSI-123400008520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008520
+(msc=-1) Paging: subscr-IMSI-123400008560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008560
+(msc=-1) Paging: subscr-IMSI-123400008600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008600
+(msc=-1) Paging: subscr-IMSI-123400008640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008640
+(msc=-1) Paging: subscr-IMSI-123400008680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008680
+(msc=-1) Paging: subscr-IMSI-123400008720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008720
+(msc=-1) Paging: subscr-IMSI-123400008760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008760
+(msc=-1) Paging: subscr-IMSI-123400008800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008800
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={6.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400008840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008840
+(msc=-1) Paging: subscr-IMSI-123400008880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008880
+(msc=-1) Paging: subscr-IMSI-123400008920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008920
+(msc=-1) Paging: subscr-IMSI-123400008960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400008960
+(msc=-1) Paging: subscr-IMSI-123400009000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009000
+(msc=-1) Paging: subscr-IMSI-123400009040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009040
+(msc=-1) Paging: subscr-IMSI-123400009080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009080
+(msc=-1) Paging: subscr-IMSI-123400009120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009120
+(msc=-1) Paging: subscr-IMSI-123400009160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009160
+(msc=-1) Paging: subscr-IMSI-123400009200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009200
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={6.750000}: select()
+(msc=-1) Paging: subscr-IMSI-123400009240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009240
+(msc=-1) Paging: subscr-IMSI-123400009280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009280
+(msc=-1) Paging: subscr-IMSI-123400009320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009320
+(msc=-1) Paging: subscr-IMSI-123400009360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009360
+(msc=-1) Paging: subscr-IMSI-123400009400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009400
+(msc=-1) Paging: subscr-IMSI-123400009440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009440
+(msc=-1) Paging: subscr-IMSI-123400009480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009480
+(msc=-1) Paging: subscr-IMSI-123400009520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009520
+(msc=-1) Paging: subscr-IMSI-123400009560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009560
+(msc=-1) Paging: subscr-IMSI-123400009600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009600
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={7.000000}: select()
+(msc=-1) Paging: subscr-IMSI-123400009640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009640
+(msc=-1) Paging: subscr-IMSI-123400009680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009680
+(msc=-1) Paging: subscr-IMSI-123400009720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009720
+(msc=-1) Paging: subscr-IMSI-123400009760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009760
+(msc=-1) Paging: subscr-IMSI-123400009800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009800
+(msc=-1) Paging: subscr-IMSI-123400009840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009840
+(msc=-1) Paging: subscr-IMSI-123400009880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009880
+(msc=-1) Paging: subscr-IMSI-123400009920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009920
+(msc=-1) Paging: subscr-IMSI-123400009960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400009960
+(msc=-1) Paging: subscr-IMSI-123400010000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010000
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={7.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400010040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010040
+(msc=-1) Paging: subscr-IMSI-123400010080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010080
+(msc=-1) Paging: subscr-IMSI-123400010120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010120
+(msc=-1) Paging: subscr-IMSI-123400010160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010160
+(msc=-1) Paging: subscr-IMSI-123400010200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010200
+(msc=-1) Paging: subscr-IMSI-123400010240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010240
+(msc=-1) Paging: subscr-IMSI-123400010280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010280
+(msc=-1) Paging: subscr-IMSI-123400010320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010320
+(msc=-1) Paging: subscr-IMSI-123400010360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010360
+(msc=-1) Paging: subscr-IMSI-123400010400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010400
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={7.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400010440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010440
+(msc=-1) Paging: subscr-IMSI-123400010480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010480
+(msc=-1) Paging: subscr-IMSI-123400010520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010520
+(msc=-1) Paging: subscr-IMSI-123400010560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010560
+(msc=-1) Paging: subscr-IMSI-123400010600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010600
+(msc=-1) Paging: subscr-IMSI-123400010640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010640
+(msc=-1) Paging: subscr-IMSI-123400010680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010680
+(msc=-1) Paging: subscr-IMSI-123400010720: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={8.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-123400010720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010720
+(msc=-1) Paging: subscr-IMSI-123400010760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010760
+(msc=-1) Paging: subscr-IMSI-123400010800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010800
+(msc=-1) Paging: subscr-IMSI-123400010840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010840
+(msc=-1) Paging: subscr-IMSI-123400010880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010880
+(msc=-1) Paging: subscr-IMSI-123400010920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010920
+(msc=-1) Paging: subscr-IMSI-123400010960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400010960
+(msc=-1) Paging: subscr-IMSI-123400011000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011000
+(msc=-1) Paging: subscr-IMSI-123400011040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011040
+(msc=-1) Paging: subscr-IMSI-123400011080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011080
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={8.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400011120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011120
+(msc=-1) Paging: subscr-IMSI-123400011160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011160
+(msc=-1) Paging: subscr-IMSI-123400011200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011200
+(msc=-1) Paging: subscr-IMSI-123400011240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011240
+(msc=-1) Paging: subscr-IMSI-123400011280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011280
+(msc=-1) Paging: subscr-IMSI-123400011320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011320
+(msc=-1) Paging: subscr-IMSI-123400011360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011360
+(msc=-1) Paging: subscr-IMSI-123400011400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011400
+(msc=-1) Paging: subscr-IMSI-123400011440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011440
+(msc=-1) Paging: subscr-IMSI-123400011480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011480
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={8.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400011520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011520
+(msc=-1) Paging: subscr-IMSI-123400011560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011560
+(msc=-1) Paging: subscr-IMSI-123400011600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011600
+(msc=-1) Paging: subscr-IMSI-123400011640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011640
+(msc=-1) Paging: subscr-IMSI-123400011680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011680
+(msc=-1) Paging: subscr-IMSI-123400011720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011720
+(msc=-1) Paging: subscr-IMSI-123400011760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011760
+(msc=-1) Paging: subscr-IMSI-123400011800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011800
+(msc=-1) Paging: subscr-IMSI-123400011840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011840
+(msc=-1) Paging: subscr-IMSI-123400011880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011880
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={8.750000}: select()
+(msc=-1) Paging: subscr-IMSI-123400011920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011920
+(msc=-1) Paging: subscr-IMSI-123400011960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400011960
+(msc=-1) Paging: subscr-IMSI-123400012000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012000
+(msc=-1) Paging: subscr-IMSI-123400012040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012040
+(msc=-1) Paging: subscr-IMSI-123400012080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012080
+(msc=-1) Paging: subscr-IMSI-123400012120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012120
+(msc=-1) Paging: subscr-IMSI-123400012160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012160
+(msc=-1) Paging: subscr-IMSI-123400012200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012200
+(msc=-1) Paging: subscr-IMSI-123400012240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012240
+(msc=-1) Paging: subscr-IMSI-123400012280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012280
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={9.000000}: select()
+(msc=-1) Paging: subscr-IMSI-123400012320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012320
+(msc=-1) Paging: subscr-IMSI-123400012360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012360
+(msc=-1) Paging: subscr-IMSI-123400012400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012400
+(msc=-1) Paging: subscr-IMSI-123400012440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012440
+(msc=-1) Paging: subscr-IMSI-123400012480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012480
+(msc=-1) Paging: subscr-IMSI-123400012520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012520
+(msc=-1) Paging: subscr-IMSI-123400012560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012560
+(msc=-1) Paging: subscr-IMSI-123400012600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012600
+(msc=-1) Paging: subscr-IMSI-123400012640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012640
+(msc=-1) Paging: subscr-IMSI-123400012680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012680
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-123400000360: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000320: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000280: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000240: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000200: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000160: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000120: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000080: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000040: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000000: (bts=0) T3113 expired
+sys={9.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400012720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012720
+(msc=-1) Paging: subscr-IMSI-123400012760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012760
+(msc=-1) Paging: subscr-IMSI-123400012800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012800
+(msc=-1) Paging: subscr-IMSI-123400012840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012840
+(msc=-1) Paging: subscr-IMSI-123400012880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012880
+(msc=-1) Paging: subscr-IMSI-123400012920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012920
+(msc=-1) Paging: subscr-IMSI-123400012960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400012960
+(msc=-1) Paging: subscr-IMSI-123400013000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013000
+(msc=-1) Paging: subscr-IMSI-123400013040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013040
+(msc=-1) Paging: subscr-IMSI-123400013080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013080
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={9.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400013120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013120
+(msc=-1) Paging: subscr-IMSI-123400013160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013160
+(msc=-1) Paging: subscr-IMSI-123400013200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013200
+(msc=-1) Paging: subscr-IMSI-123400013240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013240
+(msc=-1) Paging: subscr-IMSI-123400013280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013280
+(msc=-1) Paging: subscr-IMSI-123400013320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013320
+(msc=-1) Paging: subscr-IMSI-123400013360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013360
+(msc=-1) Paging: subscr-IMSI-123400013400: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={10.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-123400013400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013400
+(msc=-1) Paging: subscr-IMSI-123400013440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013440
+(msc=-1) Paging: subscr-IMSI-123400013480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013480
+(msc=-1) Paging: subscr-IMSI-123400013520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013520
+(msc=-1) Paging: subscr-IMSI-123400013560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013560
+(msc=-1) Paging: subscr-IMSI-123400013600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013600
+(msc=-1) Paging: subscr-IMSI-123400013640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013640
+(msc=-1) Paging: subscr-IMSI-123400013680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013680
+(msc=-1) Paging: subscr-IMSI-123400013720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013720
+(msc=-1) Paging: subscr-IMSI-123400013760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013760
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={10.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400013800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013800
+(msc=-1) Paging: subscr-IMSI-123400013840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013840
+(msc=-1) Paging: subscr-IMSI-123400013880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013880
+(msc=-1) Paging: subscr-IMSI-123400013920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013920
+(msc=-1) Paging: subscr-IMSI-123400013960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400013960
+(msc=-1) Paging: subscr-IMSI-123400014000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014000
+(msc=-1) Paging: subscr-IMSI-123400014040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014040
+(msc=-1) Paging: subscr-IMSI-123400014080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014080
+(msc=-1) Paging: subscr-IMSI-123400014120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014120
+(msc=-1) Paging: subscr-IMSI-123400014160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014160
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={10.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400014200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014200
+(msc=-1) Paging: subscr-IMSI-123400014240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014240
+(msc=-1) Paging: subscr-IMSI-123400014280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014280
+(msc=-1) Paging: subscr-IMSI-123400014320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014320
+(msc=-1) Paging: subscr-IMSI-123400014360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014360
+(msc=-1) Paging: subscr-IMSI-123400014400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014400
+(msc=-1) Paging: subscr-IMSI-123400014440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014440
+(msc=-1) Paging: subscr-IMSI-123400014480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014480
+(msc=-1) Paging: subscr-IMSI-123400014520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014520
+(msc=-1) Paging: subscr-IMSI-123400014560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014560
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={10.750000}: select()
+(msc=-1) Paging: subscr-IMSI-123400014600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014600
+(msc=-1) Paging: subscr-IMSI-123400014640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014640
+(msc=-1) Paging: subscr-IMSI-123400014680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014680
+(msc=-1) Paging: subscr-IMSI-123400014720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014720
+(msc=-1) Paging: subscr-IMSI-123400014760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014760
+(msc=-1) Paging: subscr-IMSI-123400014800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014800
+(msc=-1) Paging: subscr-IMSI-123400014840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014840
+(msc=-1) Paging: subscr-IMSI-123400014880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014880
+(msc=-1) Paging: subscr-IMSI-123400014920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014920
+(msc=-1) Paging: subscr-IMSI-123400014960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400014960
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={11.000000}: select()
+(msc=-1) Paging: subscr-IMSI-123400015000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015000
+(msc=-1) Paging: subscr-IMSI-123400015040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015040
+(msc=-1) Paging: subscr-IMSI-123400015080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015080
+(msc=-1) Paging: subscr-IMSI-123400015120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015120
+(msc=-1) Paging: subscr-IMSI-123400015160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015160
+(msc=-1) Paging: subscr-IMSI-123400015200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015200
+(msc=-1) Paging: subscr-IMSI-123400015240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015240
+(msc=-1) Paging: subscr-IMSI-123400015280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015280
+(msc=-1) Paging: subscr-IMSI-123400015320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015320
+(msc=-1) Paging: subscr-IMSI-123400015360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015360
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-123400000680: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000640: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000600: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000560: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000520: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000480: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000440: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000400: (bts=0) T3113 expired
+sys={11.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400015400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015400
+(msc=-1) Paging: subscr-IMSI-123400015440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015440
+(msc=-1) Paging: subscr-IMSI-123400015480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015480
+(msc=-1) Paging: subscr-IMSI-123400015520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015520
+(msc=-1) Paging: subscr-IMSI-123400015560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015560
+(msc=-1) Paging: subscr-IMSI-123400015600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015600
+(msc=-1) Paging: subscr-IMSI-123400015640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015640
+(msc=-1) Paging: subscr-IMSI-123400015680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015680
+(msc=-1) Paging: subscr-IMSI-123400015720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015720
+(msc=-1) Paging: subscr-IMSI-123400015760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015760
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={11.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400015800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015800
+(msc=-1) Paging: subscr-IMSI-123400015840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015840
+(msc=-1) Paging: subscr-IMSI-123400015880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015880
+(msc=-1) Paging: subscr-IMSI-123400015920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015920
+(msc=-1) Paging: subscr-IMSI-123400015960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400015960
+(msc=-1) Paging: subscr-IMSI-123400016000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016000
+(msc=-1) Paging: subscr-IMSI-123400016040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016040
+(msc=-1) Paging: subscr-IMSI-123400016080: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={12.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-123400016080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016080
+(msc=-1) Paging: subscr-IMSI-123400016120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016120
+(msc=-1) Paging: subscr-IMSI-123400016160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016160
+(msc=-1) Paging: subscr-IMSI-123400016200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016200
+(msc=-1) Paging: subscr-IMSI-123400016240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016240
+(msc=-1) Paging: subscr-IMSI-123400016280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016280
+(msc=-1) Paging: subscr-IMSI-123400016320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016320
+(msc=-1) Paging: subscr-IMSI-123400016360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016360
+(msc=-1) Paging: subscr-IMSI-123400016400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016400
+(msc=-1) Paging: subscr-IMSI-123400016440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016440
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-123400001000: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000960: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000920: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000880: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000840: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000800: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000760: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400000720: (bts=0) T3113 expired
+sys={12.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400016480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016480
+(msc=-1) Paging: subscr-IMSI-123400016520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016520
+(msc=-1) Paging: subscr-IMSI-123400016560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016560
+(msc=-1) Paging: subscr-IMSI-123400016600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016600
+(msc=-1) Paging: subscr-IMSI-123400016640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016640
+(msc=-1) Paging: subscr-IMSI-123400016680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016680
+(msc=-1) Paging: subscr-IMSI-123400016720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016720
+(msc=-1) Paging: subscr-IMSI-123400016760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016760
+(msc=-1) Paging: subscr-IMSI-123400016800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016800
+(msc=-1) Paging: subscr-IMSI-123400016840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016840
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={12.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400016880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016880
+(msc=-1) Paging: subscr-IMSI-123400016920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016920
+(msc=-1) Paging: subscr-IMSI-123400016960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400016960
+(msc=-1) Paging: subscr-IMSI-123400017000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017000
+(msc=-1) Paging: subscr-IMSI-123400017040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017040
+(msc=-1) Paging: subscr-IMSI-123400017080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017080
+(msc=-1) Paging: subscr-IMSI-123400017120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017120
+(msc=-1) Paging: subscr-IMSI-123400017160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017160
+(msc=-1) Paging: subscr-IMSI-123400017200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017200
+(msc=-1) Paging: subscr-IMSI-123400017240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017240
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={12.750000}: select()
+(msc=-1) Paging: subscr-IMSI-123400017280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017280
+(msc=-1) Paging: subscr-IMSI-123400017320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017320
+(msc=-1) Paging: subscr-IMSI-123400017360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017360
+(msc=-1) Paging: subscr-IMSI-123400017400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017400
+(msc=-1) Paging: subscr-IMSI-123400017440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017440
+(msc=-1) Paging: subscr-IMSI-123400017480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017480
+(msc=-1) Paging: subscr-IMSI-123400017520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017520
+(msc=-1) Paging: subscr-IMSI-123400017560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017560
+(msc=-1) Paging: subscr-IMSI-123400017600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017600
+(msc=-1) Paging: subscr-IMSI-123400017640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017640
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={13.000000}: select()
+(msc=-1) Paging: subscr-IMSI-123400017680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017680
+(msc=-1) Paging: subscr-IMSI-123400017720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017720
+(msc=-1) Paging: subscr-IMSI-123400017760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017760
+(msc=-1) Paging: subscr-IMSI-123400017800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017800
+(msc=-1) Paging: subscr-IMSI-123400017840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017840
+(msc=-1) Paging: subscr-IMSI-123400017880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017880
+(msc=-1) Paging: subscr-IMSI-123400017920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017920
+(msc=-1) Paging: subscr-IMSI-123400017960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400017960
+(msc=-1) Paging: subscr-IMSI-123400018000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018000
+(msc=-1) Paging: subscr-IMSI-123400018040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018040
+(bts=0) Scheduling next batch in 0.250000s (available_slots=17)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={13.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400018080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018080
+(msc=-1) Paging: subscr-IMSI-123400018120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018120
+(msc=-1) Paging: subscr-IMSI-123400018160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018160
+(msc=-1) Paging: subscr-IMSI-123400018200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018200
+(msc=-1) Paging: subscr-IMSI-123400018240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018240
+(msc=-1) Paging: subscr-IMSI-123400018280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018280
+(msc=-1) Paging: subscr-IMSI-123400018320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018320
+(msc=-1) Paging: subscr-IMSI-123400018360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018360
+(msc=-1) Paging: subscr-IMSI-123400018400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018400
+(msc=-1) Paging: subscr-IMSI-123400018440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018440
+(bts=0) Scheduling next batch in 0.250000s (available_slots=7)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={13.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400018480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018480
+(msc=-1) Paging: subscr-IMSI-123400018520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018520
+(msc=-1) Paging: subscr-IMSI-123400018560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018560
+(msc=-1) Paging: subscr-IMSI-123400018600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018600
+(msc=-1) Paging: subscr-IMSI-123400018640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018640
+(msc=-1) Paging: subscr-IMSI-123400018680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018680
+(msc=-1) Paging: subscr-IMSI-123400018720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018720
+(msc=-1) Paging: subscr-IMSI-123400018760: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 7 subscribers (7 initial, 0 retrans) during last iteration
+sys={14.000000}: select()
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 67)
+(msc=-1) Paging: subscr-IMSI-123400018760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018760
+(msc=-1) Paging: subscr-IMSI-123400018800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018800
+(msc=-1) Paging: subscr-IMSI-123400018840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018840
+(msc=-1) Paging: subscr-IMSI-123400018880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018880
+(msc=-1) Paging: subscr-IMSI-123400018920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018920
+(msc=-1) Paging: subscr-IMSI-123400018960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400018960
+(msc=-1) Paging: subscr-IMSI-123400019000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019000
+(msc=-1) Paging: subscr-IMSI-123400019040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019040
+(msc=-1) Paging: subscr-IMSI-123400019080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019080
+(msc=-1) Paging: subscr-IMSI-123400019120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019120
+(bts=0) Scheduling next batch in 0.250000s (available_slots=57)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-123400001320: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400001280: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400001240: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400001200: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400001160: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400001120: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400001080: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-123400001040: (bts=0) T3113 expired
+sys={14.250000}: select()
+(msc=-1) Paging: subscr-IMSI-123400019160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019160
+(msc=-1) Paging: subscr-IMSI-123400019200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019200
+(msc=-1) Paging: subscr-IMSI-123400019240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019240
+(msc=-1) Paging: subscr-IMSI-123400019280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019280
+(msc=-1) Paging: subscr-IMSI-123400019320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019320
+(msc=-1) Paging: subscr-IMSI-123400019360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019360
+(msc=-1) Paging: subscr-IMSI-123400019400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019400
+(msc=-1) Paging: subscr-IMSI-123400019440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019440
+(msc=-1) Paging: subscr-IMSI-123400019480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019480
+(msc=-1) Paging: subscr-IMSI-123400019520: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019520
+(bts=0) Scheduling next batch in 0.250000s (available_slots=47)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={14.500000}: select()
+(msc=-1) Paging: subscr-IMSI-123400019560: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019560
+(msc=-1) Paging: subscr-IMSI-123400019600: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019600
+(msc=-1) Paging: subscr-IMSI-123400019640: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019640
+(msc=-1) Paging: subscr-IMSI-123400019680: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019680
+(msc=-1) Paging: subscr-IMSI-123400019720: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019720
+(msc=-1) Paging: subscr-IMSI-123400019760: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019760
+(msc=-1) Paging: subscr-IMSI-123400019800: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019800
+(msc=-1) Paging: subscr-IMSI-123400019840: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019840
+(msc=-1) Paging: subscr-IMSI-123400019880: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019880
+(msc=-1) Paging: subscr-IMSI-123400019920: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019920
+(bts=0) Scheduling next batch in 0.250000s (available_slots=37)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={14.750000}: select()
+(msc=-1) Paging: subscr-IMSI-123400019960: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-123400019960
+(msc=-1) Paging: subscr-IMSI-123400001360: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001360
+(msc=-1) Paging: subscr-IMSI-123400001400: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001400
+(msc=-1) Paging: subscr-IMSI-123400001440: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001440
+(msc=-1) Paging: subscr-IMSI-123400001480: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001480
+(msc=-1) Paging: subscr-IMSI-123400001520: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001520
+(msc=-1) Paging: subscr-IMSI-123400001560: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001560
+(msc=-1) Paging: subscr-IMSI-123400001600: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001600
+(msc=-1) Paging: subscr-IMSI-123400001640: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001640
+(msc=-1) Paging: subscr-IMSI-123400001680: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-123400001680
+(bts=0) Scheduling next batch in 0.250000s (available_slots=27)
+(bts=0) Paged 10 subscribers (1 initial, 9 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-123400001720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400002960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400003960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400004960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400005960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400006960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400007960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400008960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400009960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400010960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400011960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400012960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400013960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400014960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400015960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400016960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400017960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400018960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019000: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019040: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019080: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019120: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019160: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019200: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019240: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019680: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019720: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019760: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019800: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019840: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019880: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019920: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400019960: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001520: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001560: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001600: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001640: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-123400001680: (bts=0) Stop paging (flush)
+BTS deallocated OK in test_paging500_samepgroup()
+===test_paging500_combined===
+BTS allocation OK in test_paging500_combined()
+(bts=0) C0 becomes available for paging
+(bts=0) Estimated 67 paging available_slots over 2 seconds
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000000
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) Paging delayed: retransmission happens in 0.500000s
+(bts=0) Paged 1 subscribers (1 initial, 0 retrans) during last iteration
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) New req arrived: re-scheduling next batch in 0.250000s
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000002: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000002: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000003: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000003: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000004: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000004: (bts=0) Paging request: T3113 expires in 9 seconds (estimated 9)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000005: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000005: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000006: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000006: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000007: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000007: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000008: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000008: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000009: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000009: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000010: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000010: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000011: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000011: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000012: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000012: (bts=0) Paging request: T3113 expires in 10 seconds (estimated 10)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000013: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000013: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000014: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000014: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000015: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000015: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000016: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000016: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000017: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000017: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000018: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000018: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000019: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000019: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000020: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000020: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000021: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000021: (bts=0) Paging request: T3113 expires in 11 seconds (estimated 11)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000022: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000022: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000023: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000023: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000024: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000024: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000025: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000025: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000026: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000026: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000027: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000027: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000028: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000028: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000029: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000029: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000030: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000030: (bts=0) Paging request: T3113 expires in 12 seconds (estimated 12)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000031: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000031: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000032: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000032: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000033: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000033: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000034: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000034: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000035: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000035: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000036: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000036: (bts=0) Paging request: T3113 expires in 14 seconds (estimated 14)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000037: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000037: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000038: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000038: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000039: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000039: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000040: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000040: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000041: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000041: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000042: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000042: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000043: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000043: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000044: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000044: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000045: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000045: (bts=0) Paging request: T3113 expires in 15 seconds (estimated 15)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000046: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000046: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000047: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000047: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000048: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000048: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000049: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000049: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000050: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000050: (bts=0) Paging request: T3113 expires in 16 seconds (estimated 16)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000051: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000051: (bts=0) Paging request: T3113 expires in 17 seconds (estimated 17)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000052: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000052: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000053: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000053: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000054: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000054: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000055: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000055: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000056: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000056: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000057: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000057: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000058: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000058: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000059: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000059: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000060: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000060: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000061: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000061: (bts=0) Paging request: T3113 expires in 18 seconds (estimated 18)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000062: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000062: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000063: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000063: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000064: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000064: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000065: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000065: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000066: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000066: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000068: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000068: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000069: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000069: (bts=0) Paging request: T3113 expires in 19 seconds (estimated 19)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000070: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000070: (bts=0) Paging request: T3113 expires in 20 seconds (estimated 20)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000071: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000071: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000072: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000072: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000073: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000073: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000074: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000074: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000075: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000075: (bts=0) Paging request: T3113 expires in 21 seconds (estimated 21)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000076: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000076: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000077: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000077: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000078: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000078: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000079: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000079: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000080: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000080: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000081: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000081: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000082: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000082: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000083: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000083: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000084: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000084: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000085: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000085: (bts=0) Paging request: T3113 expires in 22 seconds (estimated 22)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000086: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000086: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000087: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000087: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000088: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000088: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000089: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000089: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000090: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000090: (bts=0) Paging request: T3113 expires in 23 seconds (estimated 23)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000091: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000091: (bts=0) Paging request: T3113 expires in 24 seconds (estimated 24)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000092: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000092: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000093: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000093: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000094: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000094: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000095: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000095: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000096: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000096: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000097: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000097: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000098: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000098: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000099: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000099: (bts=0) Paging request: T3113 expires in 25 seconds (estimated 25)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000100: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000100: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000101: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000101: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000102: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000102: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000103: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000103: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000104: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000104: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000105: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000105: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000106: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000106: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000107: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000107: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000108: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000108: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000109: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000109: (bts=0) Paging request: T3113 expires in 26 seconds (estimated 26)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000110: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000110: (bts=0) Paging request: T3113 expires in 27 seconds (estimated 27)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000111: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000111: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000112: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000112: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000113: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000113: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000114: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000114: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000115: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000115: (bts=0) Paging request: T3113 expires in 28 seconds (estimated 28)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000116: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000116: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000117: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000117: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000118: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000118: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000119: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000119: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000120: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000120: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000121: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000121: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000122: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000122: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000123: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000123: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000124: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000124: (bts=0) Paging request: T3113 expires in 29 seconds (estimated 29)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000125: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000125: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000126: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000126: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000127: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000127: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000128: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000128: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000129: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000129: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000130: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000130: (bts=0) Paging request: T3113 expires in 30 seconds (estimated 30)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000131: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000131: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000132: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000132: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000133: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000133: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000134: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000134: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000135: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000135: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000136: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000136: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000137: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000137: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000138: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000138: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000139: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000139: (bts=0) Paging request: T3113 expires in 32 seconds (estimated 32)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000140: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000140: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000141: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000141: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000142: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000142: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000143: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000143: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000144: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000144: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000145: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000145: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000146: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000146: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000147: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000147: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000148: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000148: (bts=0) Paging request: T3113 expires in 33 seconds (estimated 33)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000149: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000149: (bts=0) Paging request: T3113 expires in 34 seconds (estimated 34)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000150: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000150: (bts=0) Paging request: T3113 expires in 34 seconds (estimated 34)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000151: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000151: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000152: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000152: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000153: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000153: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000154: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000154: (bts=0) Paging request: T3113 expires in 35 seconds (estimated 35)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000155: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000155: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000156: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000156: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000157: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000157: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000158: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000158: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000159: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000159: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000160: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000160: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000161: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000161: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000162: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000162: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000163: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000163: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000164: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000164: (bts=0) Paging request: T3113 expires in 36 seconds (estimated 36)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000165: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000165: (bts=0) Paging request: T3113 expires in 37 seconds (estimated 37)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000166: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000166: (bts=0) Paging request: T3113 expires in 37 seconds (estimated 37)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000167: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000167: (bts=0) Paging request: T3113 expires in 37 seconds (estimated 37)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000168: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000168: (bts=0) Paging request: T3113 expires in 37 seconds (estimated 37)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000169: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000169: (bts=0) Paging request: T3113 expires in 37 seconds (estimated 37)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000170: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000170: (bts=0) Paging request: T3113 expires in 37 seconds (estimated 37)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000171: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000171: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000172: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000172: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000173: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000173: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000174: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000174: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000175: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000175: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000176: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000176: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000177: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000177: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000178: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000178: (bts=0) Paging request: T3113 expires in 39 seconds (estimated 39)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000179: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000179: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000180: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000180: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000181: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000181: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000182: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000182: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000183: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000183: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000184: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000184: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000185: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000185: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000186: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000186: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000187: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000187: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000188: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000188: (bts=0) Paging request: T3113 expires in 40 seconds (estimated 40)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000189: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000189: (bts=0) Paging request: T3113 expires in 41 seconds (estimated 41)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000190: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000190: (bts=0) Paging request: T3113 expires in 41 seconds (estimated 41)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000191: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000191: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000192: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000192: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000193: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000193: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000194: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000194: (bts=0) Paging request: T3113 expires in 42 seconds (estimated 42)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000195: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000195: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000196: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000196: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000197: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000197: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000198: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000198: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000199: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000199: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000200: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000200: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000201: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000201: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000202: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000202: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000203: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000203: (bts=0) Paging request: T3113 expires in 43 seconds (estimated 43)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000204: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000204: (bts=0) Paging request: T3113 expires in 44 seconds (estimated 44)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000205: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000205: (bts=0) Paging request: T3113 expires in 44 seconds (estimated 44)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000206: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000206: (bts=0) Paging request: T3113 expires in 44 seconds (estimated 44)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000207: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000207: (bts=0) Paging request: T3113 expires in 44 seconds (estimated 44)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000208: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000208: (bts=0) Paging request: T3113 expires in 44 seconds (estimated 44)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000209: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000209: (bts=0) Paging request: T3113 expires in 44 seconds (estimated 44)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000210: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000210: (bts=0) Paging request: T3113 expires in 44 seconds (estimated 44)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000211: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000211: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000212: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000212: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000213: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000213: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000214: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000214: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000215: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000215: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000216: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000216: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000217: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000217: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000218: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000218: (bts=0) Paging request: T3113 expires in 46 seconds (estimated 46)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000219: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000219: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000220: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000220: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000221: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000221: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000222: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000222: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000223: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000223: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000224: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000224: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000225: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000225: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000226: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000226: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000227: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000227: (bts=0) Paging request: T3113 expires in 47 seconds (estimated 47)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000228: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000228: (bts=0) Paging request: T3113 expires in 48 seconds (estimated 48)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000229: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000229: (bts=0) Paging request: T3113 expires in 48 seconds (estimated 48)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000230: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000230: (bts=0) Paging request: T3113 expires in 48 seconds (estimated 48)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000231: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000231: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000232: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000232: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000233: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000233: (bts=0) Paging request: T3113 expires in 49 seconds (estimated 49)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000234: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000234: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000235: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000235: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000236: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000236: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000237: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000237: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000238: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000238: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000239: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000239: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000240: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000240: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000241: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000241: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000242: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000242: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000243: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000243: (bts=0) Paging request: T3113 expires in 50 seconds (estimated 50)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000244: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000244: (bts=0) Paging request: T3113 expires in 51 seconds (estimated 51)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000245: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000245: (bts=0) Paging request: T3113 expires in 51 seconds (estimated 51)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000246: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000246: (bts=0) Paging request: T3113 expires in 51 seconds (estimated 51)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000247: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000247: (bts=0) Paging request: T3113 expires in 51 seconds (estimated 51)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000248: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000248: (bts=0) Paging request: T3113 expires in 51 seconds (estimated 51)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000249: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000249: (bts=0) Paging request: T3113 expires in 51 seconds (estimated 51)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000250: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000250: (bts=0) Paging request: T3113 expires in 51 seconds (estimated 51)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000251: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000251: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000252: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000252: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000253: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000253: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000254: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000254: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000255: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000255: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000256: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000256: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000257: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000257: (bts=0) Paging request: T3113 expires in 53 seconds (estimated 53)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000258: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000258: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000259: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000259: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000260: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000260: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000261: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000261: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000262: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000262: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000263: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000263: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000264: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000264: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000265: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000265: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000266: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000266: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000267: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000267: (bts=0) Paging request: T3113 expires in 54 seconds (estimated 54)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000268: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000268: (bts=0) Paging request: T3113 expires in 55 seconds (estimated 55)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000269: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000269: (bts=0) Paging request: T3113 expires in 55 seconds (estimated 55)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000270: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000270: (bts=0) Paging request: T3113 expires in 55 seconds (estimated 55)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000271: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000271: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000272: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000272: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000273: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000273: (bts=0) Paging request: T3113 expires in 56 seconds (estimated 56)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000274: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000274: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000276: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000276: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000277: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000277: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000278: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000278: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000279: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000279: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000280: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000280: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000281: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000281: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000282: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000282: (bts=0) Paging request: T3113 expires in 57 seconds (estimated 57)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000283: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000283: (bts=0) Paging request: T3113 expires in 58 seconds (estimated 58)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000284: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000284: (bts=0) Paging request: T3113 expires in 58 seconds (estimated 58)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000285: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000285: (bts=0) Paging request: T3113 expires in 58 seconds (estimated 58)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000286: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000286: (bts=0) Paging request: T3113 expires in 58 seconds (estimated 58)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000287: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000287: (bts=0) Paging request: T3113 expires in 58 seconds (estimated 58)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000288: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000288: (bts=0) Paging request: T3113 expires in 58 seconds (estimated 58)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000289: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000289: (bts=0) Paging request: T3113 expires in 58 seconds (estimated 58)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000290: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000290: (bts=0) Paging request: T3113 expires in 58 seconds (estimated 58)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000291: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000291: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000292: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000292: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000293: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000293: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000294: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000294: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000295: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000295: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000296: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000296: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000297: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000297: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 60)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000298: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000298: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 61)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000299: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000299: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 61)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000300: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000300: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 61)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000301: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000301: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 61)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000302: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000302: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 61)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000303: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000303: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 61)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000304: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000304: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 61)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000305: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000305: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 61)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000306: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000306: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 61)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000307: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000307: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000308: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000308: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000309: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000309: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000310: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000310: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 62)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000311: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000311: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000312: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000312: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 63)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000313: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000313: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000314: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000314: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000315: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000315: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000316: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000316: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000317: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000317: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000318: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000318: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000319: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000319: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000320: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000320: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000321: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000321: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000322: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000322: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 64)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000323: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000323: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 65)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000324: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000324: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 65)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000325: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000325: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 65)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000326: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000326: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 65)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000327: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000327: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 65)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000328: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000328: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 65)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000329: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000329: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 65)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000330: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000330: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 65)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000331: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000331: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000332: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000332: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000333: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000333: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000334: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000334: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000335: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000335: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000336: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000336: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 67)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000337: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000337: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000338: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000338: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000339: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000339: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000340: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000340: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000341: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000341: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000342: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000342: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000343: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000343: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000344: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000344: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000345: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000345: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000346: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000346: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 68)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000347: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000347: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000348: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000348: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000349: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000349: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000350: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000350: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 69)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000351: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000351: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000352: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000352: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 70)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000353: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000353: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000354: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000354: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000355: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000355: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000356: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000356: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000357: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000357: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000358: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000358: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000359: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000359: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000360: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000360: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000361: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000361: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 71)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000362: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000362: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 72)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000363: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000363: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 72)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000364: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000364: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 72)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000365: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000365: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 72)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000366: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000366: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 72)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000367: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000367: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 72)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000368: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000368: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 72)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000369: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000369: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 72)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000370: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000370: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 72)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000371: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000371: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000372: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000372: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000373: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000373: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000374: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000374: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000375: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000375: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000376: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000376: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 74)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000377: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000377: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 75)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000378: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000378: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 75)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000379: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000379: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 75)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000380: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000380: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 75)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000381: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000381: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 75)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000382: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000382: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 75)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000383: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000383: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 75)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000384: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000384: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 75)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000385: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000385: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 75)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000386: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000386: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000387: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000387: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000388: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000388: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000389: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000389: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000390: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000390: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 76)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000391: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000391: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 77)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000392: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000392: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000393: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000393: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000394: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000394: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000395: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000395: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000396: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000396: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000397: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000397: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000398: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000398: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000399: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000399: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000400: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000400: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000401: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000401: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 78)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000402: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000402: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 79)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000403: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000403: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 79)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000404: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000404: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 79)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000405: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000405: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 79)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000406: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000406: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 79)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000407: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000407: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 79)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000408: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000408: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 79)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000409: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000409: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 79)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000410: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000410: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 80)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000411: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000411: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000412: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000412: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000413: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000413: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000414: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000414: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000415: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000415: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 81)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000416: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000416: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000417: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000417: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000418: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000418: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000419: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000419: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000420: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000420: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000421: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000421: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000422: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000422: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000423: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000423: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000424: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000424: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000425: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000425: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 82)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000426: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000426: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000427: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000427: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000428: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000428: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000429: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000429: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000430: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000430: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 83)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000431: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000431: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 84)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000432: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000432: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 85)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000433: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000433: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 85)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000434: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000434: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 85)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000435: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000435: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 85)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000436: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000436: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 85)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000437: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000437: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 85)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000438: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000438: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 85)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000439: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000439: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 85)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000440: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000440: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000441: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000441: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000442: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000442: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000443: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000443: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000444: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000444: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000445: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000445: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000446: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000446: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000447: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000447: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000448: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000448: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000449: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000449: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 86)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000450: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000450: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 87)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000451: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000451: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000452: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000452: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000453: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000453: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000454: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000454: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 88)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000455: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000455: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000456: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000456: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000457: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000457: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000458: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000458: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000459: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000459: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000460: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000460: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000461: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000461: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000462: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000462: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000463: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000463: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000464: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000464: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 89)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000465: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000465: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000466: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000466: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000467: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000467: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000468: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000468: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000469: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000469: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000470: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000470: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 90)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000471: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000471: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 92)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000472: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000472: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 92)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000473: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000473: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 92)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000474: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000474: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 92)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000475: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000475: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 92)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000476: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000476: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 92)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000477: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000477: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 92)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000478: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000478: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 92)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000479: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000479: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000480: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000480: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000481: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000481: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000482: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000482: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000483: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000483: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000484: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000484: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000485: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000485: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000486: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000486: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000487: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000487: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000488: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000488: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 93)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000489: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000489: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000490: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000490: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 94)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000491: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000491: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000492: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000492: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000493: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000493: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000494: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000494: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 95)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000495: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000495: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 96)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000496: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000496: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 96)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000497: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000497: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 96)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000498: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000498: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 96)
+(bts=0) Estimated 509 paging available_slots over 60 seconds
+(msc=-1) Paging: subscr-IMSI-1234000499: (bts=0) Start paging
+(msc=-1) Paging: subscr-IMSI-1234000499: (bts=0) Paging request: T3113 expires in 60 seconds (estimated 96)
+sys={0.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000001
+(msc=-1) Paging: subscr-IMSI-1234000002: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000002
+(msc=-1) Paging: subscr-IMSI-1234000003: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000003
+(msc=-1) Paging: subscr-IMSI-1234000004: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000004
+(msc=-1) Paging: subscr-IMSI-1234000005: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000005
+(msc=-1) Paging: subscr-IMSI-1234000006: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000006
+(msc=-1) Paging: subscr-IMSI-1234000007: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000007
+(msc=-1) Paging: subscr-IMSI-1234000008: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000008
+(msc=-1) Paging: subscr-IMSI-1234000009: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000009
+(msc=-1) Paging: subscr-IMSI-1234000010: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000010
+(bts=0) Scheduling next batch in 0.250000s (available_slots=56)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={0.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000011: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000011
+(msc=-1) Paging: subscr-IMSI-1234000012: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000012
+(msc=-1) Paging: subscr-IMSI-1234000013: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000013
+(msc=-1) Paging: subscr-IMSI-1234000014: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000014
+(msc=-1) Paging: subscr-IMSI-1234000015: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000015
+(msc=-1) Paging: subscr-IMSI-1234000016: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000016
+(msc=-1) Paging: subscr-IMSI-1234000017: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000017
+(msc=-1) Paging: subscr-IMSI-1234000018: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000018
+(msc=-1) Paging: subscr-IMSI-1234000019: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000019
+(msc=-1) Paging: subscr-IMSI-1234000020: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000020
+(bts=0) Scheduling next batch in 0.250000s (available_slots=46)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={0.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000021: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000021
+(msc=-1) Paging: subscr-IMSI-1234000022: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000022
+(msc=-1) Paging: subscr-IMSI-1234000023: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000023
+(msc=-1) Paging: subscr-IMSI-1234000024: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000024
+(msc=-1) Paging: subscr-IMSI-1234000025: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000025
+(msc=-1) Paging: subscr-IMSI-1234000026: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000026
+(msc=-1) Paging: subscr-IMSI-1234000027: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000027
+(msc=-1) Paging: subscr-IMSI-1234000028: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000028
+(msc=-1) Paging: subscr-IMSI-1234000029: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000029
+(msc=-1) Paging: subscr-IMSI-1234000030: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000030
+(bts=0) Scheduling next batch in 0.250000s (available_slots=36)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000031: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000031
+(msc=-1) Paging: subscr-IMSI-1234000032: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000032
+(msc=-1) Paging: subscr-IMSI-1234000033: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000033
+(msc=-1) Paging: subscr-IMSI-1234000034: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000034
+(msc=-1) Paging: subscr-IMSI-1234000035: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000035
+(msc=-1) Paging: subscr-IMSI-1234000036: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000036
+(msc=-1) Paging: subscr-IMSI-1234000037: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000037
+(msc=-1) Paging: subscr-IMSI-1234000038: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000038
+(msc=-1) Paging: subscr-IMSI-1234000039: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000039
+(msc=-1) Paging: subscr-IMSI-1234000040: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000040
+(bts=0) Scheduling next batch in 0.250000s (available_slots=26)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000041: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000041
+(msc=-1) Paging: subscr-IMSI-1234000042: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000042
+(msc=-1) Paging: subscr-IMSI-1234000043: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000043
+(msc=-1) Paging: subscr-IMSI-1234000044: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000044
+(msc=-1) Paging: subscr-IMSI-1234000045: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000045
+(msc=-1) Paging: subscr-IMSI-1234000046: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000046
+(msc=-1) Paging: subscr-IMSI-1234000047: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000047
+(msc=-1) Paging: subscr-IMSI-1234000048: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000048
+(msc=-1) Paging: subscr-IMSI-1234000049: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000049
+(msc=-1) Paging: subscr-IMSI-1234000050: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000050
+(bts=0) Scheduling next batch in 0.250000s (available_slots=16)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.500000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000051: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000051
+(msc=-1) Paging: subscr-IMSI-1234000052: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000052
+(msc=-1) Paging: subscr-IMSI-1234000053: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000053
+(msc=-1) Paging: subscr-IMSI-1234000054: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000054
+(msc=-1) Paging: subscr-IMSI-1234000055: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000055
+(msc=-1) Paging: subscr-IMSI-1234000056: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000056
+(msc=-1) Paging: subscr-IMSI-1234000057: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000057
+(msc=-1) Paging: subscr-IMSI-1234000058: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000058
+(msc=-1) Paging: subscr-IMSI-1234000059: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000059
+(msc=-1) Paging: subscr-IMSI-1234000060: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000060
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={1.750000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000061: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000061
+(msc=-1) Paging: subscr-IMSI-1234000062: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000062
+(msc=-1) Paging: subscr-IMSI-1234000063: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000063
+(msc=-1) Paging: subscr-IMSI-1234000064: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000064
+(msc=-1) Paging: subscr-IMSI-1234000065: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000065
+(msc=-1) Paging: subscr-IMSI-1234000066: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000066
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={2.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000067
+(msc=-1) Paging: subscr-IMSI-1234000068: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000068
+(msc=-1) Paging: subscr-IMSI-1234000069: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000069
+(msc=-1) Paging: subscr-IMSI-1234000070: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000070
+(msc=-1) Paging: subscr-IMSI-1234000071: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000071
+(msc=-1) Paging: subscr-IMSI-1234000072: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000072
+(msc=-1) Paging: subscr-IMSI-1234000073: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000073
+(msc=-1) Paging: subscr-IMSI-1234000074: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000074
+(msc=-1) Paging: subscr-IMSI-1234000075: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000075
+(msc=-1) Paging: subscr-IMSI-1234000076: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000076
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={2.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000077: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000077
+(msc=-1) Paging: subscr-IMSI-1234000078: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000078
+(msc=-1) Paging: subscr-IMSI-1234000079: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000079
+(msc=-1) Paging: subscr-IMSI-1234000080: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000080
+(msc=-1) Paging: subscr-IMSI-1234000081: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000081
+(msc=-1) Paging: subscr-IMSI-1234000082: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000082
+(msc=-1) Paging: subscr-IMSI-1234000083: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={3.1000000}: select()
+sys={4.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000083: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000083
+(msc=-1) Paging: subscr-IMSI-1234000084: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000084
+(msc=-1) Paging: subscr-IMSI-1234000085: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000085
+(msc=-1) Paging: subscr-IMSI-1234000086: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000086
+(msc=-1) Paging: subscr-IMSI-1234000087: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000087
+(msc=-1) Paging: subscr-IMSI-1234000088: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000088
+(msc=-1) Paging: subscr-IMSI-1234000089: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000089
+(msc=-1) Paging: subscr-IMSI-1234000090: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000090
+(msc=-1) Paging: subscr-IMSI-1234000091: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000091
+(msc=-1) Paging: subscr-IMSI-1234000092: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000092
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={4.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000093: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000093
+(msc=-1) Paging: subscr-IMSI-1234000094: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000094
+(msc=-1) Paging: subscr-IMSI-1234000095: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000095
+(msc=-1) Paging: subscr-IMSI-1234000096: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000096
+(msc=-1) Paging: subscr-IMSI-1234000097: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000097
+(msc=-1) Paging: subscr-IMSI-1234000098: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000098
+(msc=-1) Paging: subscr-IMSI-1234000099: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={5.1000000}: select()
+sys={6.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000099: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000099
+(msc=-1) Paging: subscr-IMSI-1234000100: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000100
+(msc=-1) Paging: subscr-IMSI-1234000101: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000101
+(msc=-1) Paging: subscr-IMSI-1234000102: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000102
+(msc=-1) Paging: subscr-IMSI-1234000103: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000103
+(msc=-1) Paging: subscr-IMSI-1234000104: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000104
+(msc=-1) Paging: subscr-IMSI-1234000105: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000105
+(msc=-1) Paging: subscr-IMSI-1234000106: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000106
+(msc=-1) Paging: subscr-IMSI-1234000107: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000107
+(msc=-1) Paging: subscr-IMSI-1234000108: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000108
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={6.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000109: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000109
+(msc=-1) Paging: subscr-IMSI-1234000110: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000110
+(msc=-1) Paging: subscr-IMSI-1234000111: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000111
+(msc=-1) Paging: subscr-IMSI-1234000112: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000112
+(msc=-1) Paging: subscr-IMSI-1234000113: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000113
+(msc=-1) Paging: subscr-IMSI-1234000114: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000114
+(msc=-1) Paging: subscr-IMSI-1234000115: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={7.1000000}: select()
+sys={8.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000115: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000115
+(msc=-1) Paging: subscr-IMSI-1234000116: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000116
+(msc=-1) Paging: subscr-IMSI-1234000117: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000117
+(msc=-1) Paging: subscr-IMSI-1234000118: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000118
+(msc=-1) Paging: subscr-IMSI-1234000119: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000119
+(msc=-1) Paging: subscr-IMSI-1234000120: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000120
+(msc=-1) Paging: subscr-IMSI-1234000121: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000121
+(msc=-1) Paging: subscr-IMSI-1234000122: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000122
+(msc=-1) Paging: subscr-IMSI-1234000123: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000123
+(msc=-1) Paging: subscr-IMSI-1234000124: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000124
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={8.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000125: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000125
+(msc=-1) Paging: subscr-IMSI-1234000126: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000126
+(msc=-1) Paging: subscr-IMSI-1234000127: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000127
+(msc=-1) Paging: subscr-IMSI-1234000128: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000128
+(msc=-1) Paging: subscr-IMSI-1234000129: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000129
+(msc=-1) Paging: subscr-IMSI-1234000130: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000130
+(msc=-1) Paging: subscr-IMSI-1234000131: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={9.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000004: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000003: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000002: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000001: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000000: (bts=0) T3113 expired
+sys={10.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000131: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000131
+(msc=-1) Paging: subscr-IMSI-1234000132: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000132
+(msc=-1) Paging: subscr-IMSI-1234000133: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000133
+(msc=-1) Paging: subscr-IMSI-1234000134: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000134
+(msc=-1) Paging: subscr-IMSI-1234000135: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000135
+(msc=-1) Paging: subscr-IMSI-1234000136: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000136
+(msc=-1) Paging: subscr-IMSI-1234000137: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000137
+(msc=-1) Paging: subscr-IMSI-1234000138: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000138
+(msc=-1) Paging: subscr-IMSI-1234000139: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000139
+(msc=-1) Paging: subscr-IMSI-1234000140: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000140
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000012: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000011: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000010: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000009: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000008: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000007: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000006: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000005: (bts=0) T3113 expired
+sys={10.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000141: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000141
+(msc=-1) Paging: subscr-IMSI-1234000142: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000142
+(msc=-1) Paging: subscr-IMSI-1234000143: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000143
+(msc=-1) Paging: subscr-IMSI-1234000144: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000144
+(msc=-1) Paging: subscr-IMSI-1234000145: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000145
+(msc=-1) Paging: subscr-IMSI-1234000146: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000146
+(msc=-1) Paging: subscr-IMSI-1234000147: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={11.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000021: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000020: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000019: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000018: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000017: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000016: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000015: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000014: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000013: (bts=0) T3113 expired
+sys={12.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000147: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000147
+(msc=-1) Paging: subscr-IMSI-1234000148: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000148
+(msc=-1) Paging: subscr-IMSI-1234000149: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000149
+(msc=-1) Paging: subscr-IMSI-1234000150: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000150
+(msc=-1) Paging: subscr-IMSI-1234000151: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000151
+(msc=-1) Paging: subscr-IMSI-1234000152: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000152
+(msc=-1) Paging: subscr-IMSI-1234000153: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000153
+(msc=-1) Paging: subscr-IMSI-1234000154: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000154
+(msc=-1) Paging: subscr-IMSI-1234000155: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000155
+(msc=-1) Paging: subscr-IMSI-1234000156: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000156
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000030: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000029: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000028: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000027: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000026: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000025: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000024: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000023: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000022: (bts=0) T3113 expired
+sys={12.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000157: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000157
+(msc=-1) Paging: subscr-IMSI-1234000158: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000158
+(msc=-1) Paging: subscr-IMSI-1234000159: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000159
+(msc=-1) Paging: subscr-IMSI-1234000160: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000160
+(msc=-1) Paging: subscr-IMSI-1234000161: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000161
+(msc=-1) Paging: subscr-IMSI-1234000162: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000162
+(msc=-1) Paging: subscr-IMSI-1234000163: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={13.1000000}: select()
+sys={14.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000163: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000163
+(msc=-1) Paging: subscr-IMSI-1234000164: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000164
+(msc=-1) Paging: subscr-IMSI-1234000165: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000165
+(msc=-1) Paging: subscr-IMSI-1234000166: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000166
+(msc=-1) Paging: subscr-IMSI-1234000167: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000167
+(msc=-1) Paging: subscr-IMSI-1234000168: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000168
+(msc=-1) Paging: subscr-IMSI-1234000169: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000169
+(msc=-1) Paging: subscr-IMSI-1234000170: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000170
+(msc=-1) Paging: subscr-IMSI-1234000171: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000171
+(msc=-1) Paging: subscr-IMSI-1234000172: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000172
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000036: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000035: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000034: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000033: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000032: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000031: (bts=0) T3113 expired
+sys={14.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000173: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000173
+(msc=-1) Paging: subscr-IMSI-1234000174: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000174
+(msc=-1) Paging: subscr-IMSI-1234000175: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000175
+(msc=-1) Paging: subscr-IMSI-1234000176: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000176
+(msc=-1) Paging: subscr-IMSI-1234000177: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000177
+(msc=-1) Paging: subscr-IMSI-1234000178: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000178
+(msc=-1) Paging: subscr-IMSI-1234000179: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={15.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000045: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000044: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000043: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000042: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000041: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000040: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000039: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000038: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000037: (bts=0) T3113 expired
+sys={16.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000179: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000179
+(msc=-1) Paging: subscr-IMSI-1234000180: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000180
+(msc=-1) Paging: subscr-IMSI-1234000181: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000181
+(msc=-1) Paging: subscr-IMSI-1234000182: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000182
+(msc=-1) Paging: subscr-IMSI-1234000183: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000183
+(msc=-1) Paging: subscr-IMSI-1234000184: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000184
+(msc=-1) Paging: subscr-IMSI-1234000185: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000185
+(msc=-1) Paging: subscr-IMSI-1234000186: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000186
+(msc=-1) Paging: subscr-IMSI-1234000187: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000187
+(msc=-1) Paging: subscr-IMSI-1234000188: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000188
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000050: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000049: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000048: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000047: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000046: (bts=0) T3113 expired
+sys={16.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000189: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000189
+(msc=-1) Paging: subscr-IMSI-1234000190: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000190
+(msc=-1) Paging: subscr-IMSI-1234000191: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000191
+(msc=-1) Paging: subscr-IMSI-1234000192: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000192
+(msc=-1) Paging: subscr-IMSI-1234000193: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000193
+(msc=-1) Paging: subscr-IMSI-1234000194: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000194
+(msc=-1) Paging: subscr-IMSI-1234000195: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={17.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000051: (bts=0) T3113 expired
+sys={18.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000195: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000195
+(msc=-1) Paging: subscr-IMSI-1234000196: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000196
+(msc=-1) Paging: subscr-IMSI-1234000197: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000197
+(msc=-1) Paging: subscr-IMSI-1234000198: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000198
+(msc=-1) Paging: subscr-IMSI-1234000199: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000199
+(msc=-1) Paging: subscr-IMSI-1234000200: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000200
+(msc=-1) Paging: subscr-IMSI-1234000201: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000201
+(msc=-1) Paging: subscr-IMSI-1234000202: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000202
+(msc=-1) Paging: subscr-IMSI-1234000203: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000203
+(msc=-1) Paging: subscr-IMSI-1234000204: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000204
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000061: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000060: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000059: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000058: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000057: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000056: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000055: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000054: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000053: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000052: (bts=0) T3113 expired
+sys={18.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000205: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000205
+(msc=-1) Paging: subscr-IMSI-1234000206: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000206
+(msc=-1) Paging: subscr-IMSI-1234000207: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000207
+(msc=-1) Paging: subscr-IMSI-1234000208: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000208
+(msc=-1) Paging: subscr-IMSI-1234000209: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000209
+(msc=-1) Paging: subscr-IMSI-1234000210: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000210
+(msc=-1) Paging: subscr-IMSI-1234000211: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={19.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000069: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000068: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000067: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000066: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000065: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000064: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000063: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000062: (bts=0) T3113 expired
+sys={20.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000211: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000211
+(msc=-1) Paging: subscr-IMSI-1234000212: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000212
+(msc=-1) Paging: subscr-IMSI-1234000213: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000213
+(msc=-1) Paging: subscr-IMSI-1234000214: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000214
+(msc=-1) Paging: subscr-IMSI-1234000215: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000215
+(msc=-1) Paging: subscr-IMSI-1234000216: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000216
+(msc=-1) Paging: subscr-IMSI-1234000217: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000217
+(msc=-1) Paging: subscr-IMSI-1234000218: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000218
+(msc=-1) Paging: subscr-IMSI-1234000219: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000219
+(msc=-1) Paging: subscr-IMSI-1234000220: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000220
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000070: (bts=0) T3113 expired
+sys={20.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000221: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000221
+(msc=-1) Paging: subscr-IMSI-1234000222: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000222
+(msc=-1) Paging: subscr-IMSI-1234000223: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000223
+(msc=-1) Paging: subscr-IMSI-1234000224: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000224
+(msc=-1) Paging: subscr-IMSI-1234000225: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000225
+(msc=-1) Paging: subscr-IMSI-1234000226: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000226
+(msc=-1) Paging: subscr-IMSI-1234000227: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={21.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000075: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000074: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000073: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000072: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000071: (bts=0) T3113 expired
+sys={22.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000227: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000227
+(msc=-1) Paging: subscr-IMSI-1234000228: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000228
+(msc=-1) Paging: subscr-IMSI-1234000229: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000229
+(msc=-1) Paging: subscr-IMSI-1234000230: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000230
+(msc=-1) Paging: subscr-IMSI-1234000231: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000231
+(msc=-1) Paging: subscr-IMSI-1234000232: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000232
+(msc=-1) Paging: subscr-IMSI-1234000233: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000233
+(msc=-1) Paging: subscr-IMSI-1234000234: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000234
+(msc=-1) Paging: subscr-IMSI-1234000235: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000235
+(msc=-1) Paging: subscr-IMSI-1234000236: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000236
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000085: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000084: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000083: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000082: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000081: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000080: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000079: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000078: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000077: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000076: (bts=0) T3113 expired
+sys={22.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000237: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000237
+(msc=-1) Paging: subscr-IMSI-1234000238: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000238
+(msc=-1) Paging: subscr-IMSI-1234000239: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000239
+(msc=-1) Paging: subscr-IMSI-1234000240: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000240
+(msc=-1) Paging: subscr-IMSI-1234000241: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000241
+(msc=-1) Paging: subscr-IMSI-1234000242: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000242
+(msc=-1) Paging: subscr-IMSI-1234000243: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={23.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000090: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000089: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000088: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000087: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000086: (bts=0) T3113 expired
+sys={24.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000243: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000243
+(msc=-1) Paging: subscr-IMSI-1234000244: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000244
+(msc=-1) Paging: subscr-IMSI-1234000245: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000245
+(msc=-1) Paging: subscr-IMSI-1234000246: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000246
+(msc=-1) Paging: subscr-IMSI-1234000247: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000247
+(msc=-1) Paging: subscr-IMSI-1234000248: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000248
+(msc=-1) Paging: subscr-IMSI-1234000249: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000249
+(msc=-1) Paging: subscr-IMSI-1234000250: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000250
+(msc=-1) Paging: subscr-IMSI-1234000251: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000251
+(msc=-1) Paging: subscr-IMSI-1234000252: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000252
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000091: (bts=0) T3113 expired
+sys={24.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000253: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000253
+(msc=-1) Paging: subscr-IMSI-1234000254: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000254
+(msc=-1) Paging: subscr-IMSI-1234000255: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000255
+(msc=-1) Paging: subscr-IMSI-1234000256: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000256
+(msc=-1) Paging: subscr-IMSI-1234000257: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000257
+(msc=-1) Paging: subscr-IMSI-1234000258: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000258
+(msc=-1) Paging: subscr-IMSI-1234000259: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={25.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000099: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000098: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000097: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000096: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000095: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000094: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000093: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000092: (bts=0) T3113 expired
+sys={26.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000259: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000259
+(msc=-1) Paging: subscr-IMSI-1234000260: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000260
+(msc=-1) Paging: subscr-IMSI-1234000261: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000261
+(msc=-1) Paging: subscr-IMSI-1234000262: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000262
+(msc=-1) Paging: subscr-IMSI-1234000263: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000263
+(msc=-1) Paging: subscr-IMSI-1234000264: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000264
+(msc=-1) Paging: subscr-IMSI-1234000265: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000265
+(msc=-1) Paging: subscr-IMSI-1234000266: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000266
+(msc=-1) Paging: subscr-IMSI-1234000267: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000267
+(msc=-1) Paging: subscr-IMSI-1234000268: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000268
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000109: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000108: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000107: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000106: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000105: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000104: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000103: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000102: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000101: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000100: (bts=0) T3113 expired
+sys={26.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000269: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000269
+(msc=-1) Paging: subscr-IMSI-1234000270: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000270
+(msc=-1) Paging: subscr-IMSI-1234000271: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000271
+(msc=-1) Paging: subscr-IMSI-1234000272: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000272
+(msc=-1) Paging: subscr-IMSI-1234000273: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000273
+(msc=-1) Paging: subscr-IMSI-1234000274: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000274
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={27.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000110: (bts=0) T3113 expired
+sys={28.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000275
+(msc=-1) Paging: subscr-IMSI-1234000276: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000276
+(msc=-1) Paging: subscr-IMSI-1234000277: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000277
+(msc=-1) Paging: subscr-IMSI-1234000278: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000278
+(msc=-1) Paging: subscr-IMSI-1234000279: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000279
+(msc=-1) Paging: subscr-IMSI-1234000280: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000280
+(msc=-1) Paging: subscr-IMSI-1234000281: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000281
+(msc=-1) Paging: subscr-IMSI-1234000282: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000282
+(msc=-1) Paging: subscr-IMSI-1234000283: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000283
+(msc=-1) Paging: subscr-IMSI-1234000284: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000284
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000115: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000114: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000113: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000112: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000111: (bts=0) T3113 expired
+sys={28.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000285: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000285
+(msc=-1) Paging: subscr-IMSI-1234000286: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000286
+(msc=-1) Paging: subscr-IMSI-1234000287: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000287
+(msc=-1) Paging: subscr-IMSI-1234000288: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000288
+(msc=-1) Paging: subscr-IMSI-1234000289: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000289
+(msc=-1) Paging: subscr-IMSI-1234000290: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000290
+(msc=-1) Paging: subscr-IMSI-1234000291: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={29.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000124: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000123: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000122: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000121: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000120: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000119: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000118: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000117: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000116: (bts=0) T3113 expired
+sys={30.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000291: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000291
+(msc=-1) Paging: subscr-IMSI-1234000292: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000292
+(msc=-1) Paging: subscr-IMSI-1234000293: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000293
+(msc=-1) Paging: subscr-IMSI-1234000294: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000294
+(msc=-1) Paging: subscr-IMSI-1234000295: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000295
+(msc=-1) Paging: subscr-IMSI-1234000296: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000296
+(msc=-1) Paging: subscr-IMSI-1234000297: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000297
+(msc=-1) Paging: subscr-IMSI-1234000298: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000298
+(msc=-1) Paging: subscr-IMSI-1234000299: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000299
+(msc=-1) Paging: subscr-IMSI-1234000300: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000300
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000130: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000129: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000128: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000127: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000126: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000125: (bts=0) T3113 expired
+sys={30.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000301: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000301
+(msc=-1) Paging: subscr-IMSI-1234000302: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000302
+(msc=-1) Paging: subscr-IMSI-1234000303: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000303
+(msc=-1) Paging: subscr-IMSI-1234000304: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000304
+(msc=-1) Paging: subscr-IMSI-1234000305: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000305
+(msc=-1) Paging: subscr-IMSI-1234000306: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000306
+(msc=-1) Paging: subscr-IMSI-1234000307: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={31.000000}: select()
+sys={32.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000307: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000307
+(msc=-1) Paging: subscr-IMSI-1234000308: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000308
+(msc=-1) Paging: subscr-IMSI-1234000309: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000309
+(msc=-1) Paging: subscr-IMSI-1234000310: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000310
+(msc=-1) Paging: subscr-IMSI-1234000311: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000311
+(msc=-1) Paging: subscr-IMSI-1234000312: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000312
+(msc=-1) Paging: subscr-IMSI-1234000313: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000313
+(msc=-1) Paging: subscr-IMSI-1234000314: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000314
+(msc=-1) Paging: subscr-IMSI-1234000315: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000315
+(msc=-1) Paging: subscr-IMSI-1234000316: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000316
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000139: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000138: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000137: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000136: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000135: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000134: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000133: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000132: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000131: (bts=0) T3113 expired
+sys={32.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000317: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000317
+(msc=-1) Paging: subscr-IMSI-1234000318: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000318
+(msc=-1) Paging: subscr-IMSI-1234000319: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000319
+(msc=-1) Paging: subscr-IMSI-1234000320: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000320
+(msc=-1) Paging: subscr-IMSI-1234000321: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000321
+(msc=-1) Paging: subscr-IMSI-1234000322: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000322
+(msc=-1) Paging: subscr-IMSI-1234000323: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={33.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000148: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000147: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000146: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000145: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000144: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000143: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000142: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000141: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000140: (bts=0) T3113 expired
+sys={34.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000323: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000323
+(msc=-1) Paging: subscr-IMSI-1234000324: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000324
+(msc=-1) Paging: subscr-IMSI-1234000325: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000325
+(msc=-1) Paging: subscr-IMSI-1234000326: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000326
+(msc=-1) Paging: subscr-IMSI-1234000327: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000327
+(msc=-1) Paging: subscr-IMSI-1234000328: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000328
+(msc=-1) Paging: subscr-IMSI-1234000329: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000329
+(msc=-1) Paging: subscr-IMSI-1234000330: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000330
+(msc=-1) Paging: subscr-IMSI-1234000331: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000331
+(msc=-1) Paging: subscr-IMSI-1234000332: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000332
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000150: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000149: (bts=0) T3113 expired
+sys={34.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000333: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000333
+(msc=-1) Paging: subscr-IMSI-1234000334: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000334
+(msc=-1) Paging: subscr-IMSI-1234000335: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000335
+(msc=-1) Paging: subscr-IMSI-1234000336: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000336
+(msc=-1) Paging: subscr-IMSI-1234000337: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000337
+(msc=-1) Paging: subscr-IMSI-1234000338: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000338
+(msc=-1) Paging: subscr-IMSI-1234000339: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={35.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000154: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000153: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000152: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000151: (bts=0) T3113 expired
+sys={36.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000339: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000339
+(msc=-1) Paging: subscr-IMSI-1234000340: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000340
+(msc=-1) Paging: subscr-IMSI-1234000341: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000341
+(msc=-1) Paging: subscr-IMSI-1234000342: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000342
+(msc=-1) Paging: subscr-IMSI-1234000343: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000343
+(msc=-1) Paging: subscr-IMSI-1234000344: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000344
+(msc=-1) Paging: subscr-IMSI-1234000345: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000345
+(msc=-1) Paging: subscr-IMSI-1234000346: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000346
+(msc=-1) Paging: subscr-IMSI-1234000347: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000347
+(msc=-1) Paging: subscr-IMSI-1234000348: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000348
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000164: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000163: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000162: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000161: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000160: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000159: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000158: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000157: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000156: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000155: (bts=0) T3113 expired
+sys={36.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000349: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000349
+(msc=-1) Paging: subscr-IMSI-1234000350: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000350
+(msc=-1) Paging: subscr-IMSI-1234000351: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000351
+(msc=-1) Paging: subscr-IMSI-1234000352: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000352
+(msc=-1) Paging: subscr-IMSI-1234000353: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000353
+(msc=-1) Paging: subscr-IMSI-1234000354: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000354
+(msc=-1) Paging: subscr-IMSI-1234000355: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={37.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000170: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000169: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000168: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000167: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000166: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000165: (bts=0) T3113 expired
+sys={38.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000355: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000355
+(msc=-1) Paging: subscr-IMSI-1234000356: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000356
+(msc=-1) Paging: subscr-IMSI-1234000357: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000357
+(msc=-1) Paging: subscr-IMSI-1234000358: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000358
+(msc=-1) Paging: subscr-IMSI-1234000359: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000359
+(msc=-1) Paging: subscr-IMSI-1234000360: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000360
+(msc=-1) Paging: subscr-IMSI-1234000361: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000361
+(msc=-1) Paging: subscr-IMSI-1234000362: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000362
+(msc=-1) Paging: subscr-IMSI-1234000363: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000363
+(msc=-1) Paging: subscr-IMSI-1234000364: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000364
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={38.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000365: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000365
+(msc=-1) Paging: subscr-IMSI-1234000366: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000366
+(msc=-1) Paging: subscr-IMSI-1234000367: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000367
+(msc=-1) Paging: subscr-IMSI-1234000368: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000368
+(msc=-1) Paging: subscr-IMSI-1234000369: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000369
+(msc=-1) Paging: subscr-IMSI-1234000370: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000370
+(msc=-1) Paging: subscr-IMSI-1234000371: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={39.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000178: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000177: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000176: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000175: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000174: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000173: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000172: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000171: (bts=0) T3113 expired
+sys={40.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000371: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000371
+(msc=-1) Paging: subscr-IMSI-1234000372: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000372
+(msc=-1) Paging: subscr-IMSI-1234000373: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000373
+(msc=-1) Paging: subscr-IMSI-1234000374: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000374
+(msc=-1) Paging: subscr-IMSI-1234000375: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000375
+(msc=-1) Paging: subscr-IMSI-1234000376: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000376
+(msc=-1) Paging: subscr-IMSI-1234000377: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000377
+(msc=-1) Paging: subscr-IMSI-1234000378: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000378
+(msc=-1) Paging: subscr-IMSI-1234000379: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000379
+(msc=-1) Paging: subscr-IMSI-1234000380: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000380
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000188: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000187: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000186: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000185: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000184: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000183: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000182: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000181: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000180: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000179: (bts=0) T3113 expired
+sys={40.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000381: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000381
+(msc=-1) Paging: subscr-IMSI-1234000382: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000382
+(msc=-1) Paging: subscr-IMSI-1234000383: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000383
+(msc=-1) Paging: subscr-IMSI-1234000384: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000384
+(msc=-1) Paging: subscr-IMSI-1234000385: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000385
+(msc=-1) Paging: subscr-IMSI-1234000386: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000386
+(msc=-1) Paging: subscr-IMSI-1234000387: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={41.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000190: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000189: (bts=0) T3113 expired
+sys={42.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000387: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000387
+(msc=-1) Paging: subscr-IMSI-1234000388: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000388
+(msc=-1) Paging: subscr-IMSI-1234000389: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000389
+(msc=-1) Paging: subscr-IMSI-1234000390: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000390
+(msc=-1) Paging: subscr-IMSI-1234000391: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000391
+(msc=-1) Paging: subscr-IMSI-1234000392: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000392
+(msc=-1) Paging: subscr-IMSI-1234000393: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000393
+(msc=-1) Paging: subscr-IMSI-1234000394: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000394
+(msc=-1) Paging: subscr-IMSI-1234000395: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000395
+(msc=-1) Paging: subscr-IMSI-1234000396: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000396
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000194: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000193: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000192: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000191: (bts=0) T3113 expired
+sys={42.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000397: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000397
+(msc=-1) Paging: subscr-IMSI-1234000398: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000398
+(msc=-1) Paging: subscr-IMSI-1234000399: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000399
+(msc=-1) Paging: subscr-IMSI-1234000400: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000400
+(msc=-1) Paging: subscr-IMSI-1234000401: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000401
+(msc=-1) Paging: subscr-IMSI-1234000402: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000402
+(msc=-1) Paging: subscr-IMSI-1234000403: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={43.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000203: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000202: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000201: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000200: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000199: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000198: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000197: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000196: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000195: (bts=0) T3113 expired
+sys={44.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000403: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000403
+(msc=-1) Paging: subscr-IMSI-1234000404: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000404
+(msc=-1) Paging: subscr-IMSI-1234000405: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000405
+(msc=-1) Paging: subscr-IMSI-1234000406: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000406
+(msc=-1) Paging: subscr-IMSI-1234000407: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000407
+(msc=-1) Paging: subscr-IMSI-1234000408: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000408
+(msc=-1) Paging: subscr-IMSI-1234000409: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000409
+(msc=-1) Paging: subscr-IMSI-1234000410: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000410
+(msc=-1) Paging: subscr-IMSI-1234000411: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000411
+(msc=-1) Paging: subscr-IMSI-1234000412: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000412
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000210: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000209: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000208: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000207: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000206: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000205: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000204: (bts=0) T3113 expired
+sys={44.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000413: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000413
+(msc=-1) Paging: subscr-IMSI-1234000414: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000414
+(msc=-1) Paging: subscr-IMSI-1234000415: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000415
+(msc=-1) Paging: subscr-IMSI-1234000416: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000416
+(msc=-1) Paging: subscr-IMSI-1234000417: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000417
+(msc=-1) Paging: subscr-IMSI-1234000418: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000418
+(msc=-1) Paging: subscr-IMSI-1234000419: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={45.000000}: select()
+sys={46.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000419: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000419
+(msc=-1) Paging: subscr-IMSI-1234000420: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000420
+(msc=-1) Paging: subscr-IMSI-1234000421: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000421
+(msc=-1) Paging: subscr-IMSI-1234000422: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000422
+(msc=-1) Paging: subscr-IMSI-1234000423: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000423
+(msc=-1) Paging: subscr-IMSI-1234000424: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000424
+(msc=-1) Paging: subscr-IMSI-1234000425: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000425
+(msc=-1) Paging: subscr-IMSI-1234000426: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000426
+(msc=-1) Paging: subscr-IMSI-1234000427: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000427
+(msc=-1) Paging: subscr-IMSI-1234000428: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000428
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000218: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000217: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000216: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000215: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000214: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000213: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000212: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000211: (bts=0) T3113 expired
+sys={46.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000429: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000429
+(msc=-1) Paging: subscr-IMSI-1234000430: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000430
+(msc=-1) Paging: subscr-IMSI-1234000431: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000431
+(msc=-1) Paging: subscr-IMSI-1234000432: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000432
+(msc=-1) Paging: subscr-IMSI-1234000433: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000433
+(msc=-1) Paging: subscr-IMSI-1234000434: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000434
+(msc=-1) Paging: subscr-IMSI-1234000435: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={47.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000227: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000226: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000225: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000224: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000223: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000222: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000221: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000220: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000219: (bts=0) T3113 expired
+sys={48.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000435: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000435
+(msc=-1) Paging: subscr-IMSI-1234000436: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000436
+(msc=-1) Paging: subscr-IMSI-1234000437: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000437
+(msc=-1) Paging: subscr-IMSI-1234000438: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000438
+(msc=-1) Paging: subscr-IMSI-1234000439: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000439
+(msc=-1) Paging: subscr-IMSI-1234000440: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000440
+(msc=-1) Paging: subscr-IMSI-1234000441: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000441
+(msc=-1) Paging: subscr-IMSI-1234000442: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000442
+(msc=-1) Paging: subscr-IMSI-1234000443: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000443
+(msc=-1) Paging: subscr-IMSI-1234000444: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000444
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000230: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000229: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000228: (bts=0) T3113 expired
+sys={48.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000445: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000445
+(msc=-1) Paging: subscr-IMSI-1234000446: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000446
+(msc=-1) Paging: subscr-IMSI-1234000447: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000447
+(msc=-1) Paging: subscr-IMSI-1234000448: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000448
+(msc=-1) Paging: subscr-IMSI-1234000449: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000449
+(msc=-1) Paging: subscr-IMSI-1234000450: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000450
+(msc=-1) Paging: subscr-IMSI-1234000451: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={49.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000233: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000232: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000231: (bts=0) T3113 expired
+sys={50.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000451: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000451
+(msc=-1) Paging: subscr-IMSI-1234000452: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000452
+(msc=-1) Paging: subscr-IMSI-1234000453: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000453
+(msc=-1) Paging: subscr-IMSI-1234000454: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000454
+(msc=-1) Paging: subscr-IMSI-1234000455: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000455
+(msc=-1) Paging: subscr-IMSI-1234000456: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000456
+(msc=-1) Paging: subscr-IMSI-1234000457: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000457
+(msc=-1) Paging: subscr-IMSI-1234000458: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000458
+(msc=-1) Paging: subscr-IMSI-1234000459: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000459
+(msc=-1) Paging: subscr-IMSI-1234000460: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000460
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000243: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000242: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000241: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000240: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000239: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000238: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000237: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000236: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000235: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000234: (bts=0) T3113 expired
+sys={50.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000461: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000461
+(msc=-1) Paging: subscr-IMSI-1234000462: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000462
+(msc=-1) Paging: subscr-IMSI-1234000463: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000463
+(msc=-1) Paging: subscr-IMSI-1234000464: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000464
+(msc=-1) Paging: subscr-IMSI-1234000465: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000465
+(msc=-1) Paging: subscr-IMSI-1234000466: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000466
+(msc=-1) Paging: subscr-IMSI-1234000467: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={51.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000250: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000249: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000248: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000247: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000246: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000245: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000244: (bts=0) T3113 expired
+sys={52.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000467: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000467
+(msc=-1) Paging: subscr-IMSI-1234000468: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000468
+(msc=-1) Paging: subscr-IMSI-1234000469: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000469
+(msc=-1) Paging: subscr-IMSI-1234000470: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000470
+(msc=-1) Paging: subscr-IMSI-1234000471: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000471
+(msc=-1) Paging: subscr-IMSI-1234000472: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000472
+(msc=-1) Paging: subscr-IMSI-1234000473: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000473
+(msc=-1) Paging: subscr-IMSI-1234000474: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000474
+(msc=-1) Paging: subscr-IMSI-1234000475: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000475
+(msc=-1) Paging: subscr-IMSI-1234000476: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000476
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+sys={52.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000477: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000477
+(msc=-1) Paging: subscr-IMSI-1234000478: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000478
+(msc=-1) Paging: subscr-IMSI-1234000479: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000479
+(msc=-1) Paging: subscr-IMSI-1234000480: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000480
+(msc=-1) Paging: subscr-IMSI-1234000481: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000481
+(msc=-1) Paging: subscr-IMSI-1234000482: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000482
+(msc=-1) Paging: subscr-IMSI-1234000483: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={53.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000257: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000256: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000255: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000254: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000253: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000252: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000251: (bts=0) T3113 expired
+sys={54.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000483: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000483
+(msc=-1) Paging: subscr-IMSI-1234000484: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000484
+(msc=-1) Paging: subscr-IMSI-1234000485: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000485
+(msc=-1) Paging: subscr-IMSI-1234000486: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000486
+(msc=-1) Paging: subscr-IMSI-1234000487: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000487
+(msc=-1) Paging: subscr-IMSI-1234000488: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000488
+(msc=-1) Paging: subscr-IMSI-1234000489: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000489
+(msc=-1) Paging: subscr-IMSI-1234000490: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000490
+(msc=-1) Paging: subscr-IMSI-1234000491: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000491
+(msc=-1) Paging: subscr-IMSI-1234000492: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000492
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (10 initial, 0 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000267: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000266: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000265: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000264: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000263: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000262: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000261: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000260: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000259: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000258: (bts=0) T3113 expired
+sys={54.250000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000493: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000493
+(msc=-1) Paging: subscr-IMSI-1234000494: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000494
+(msc=-1) Paging: subscr-IMSI-1234000495: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000495
+(msc=-1) Paging: subscr-IMSI-1234000496: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000496
+(msc=-1) Paging: subscr-IMSI-1234000497: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000497
+(msc=-1) Paging: subscr-IMSI-1234000498: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000498
+(msc=-1) Paging: subscr-IMSI-1234000499: (bts=0) Paging delayed: waiting for available slots at BTS
+(bts=0) Paged 6 subscribers (6 initial, 0 retrans) during last iteration
+sys={55.000000}: select()
+(msc=-1) Paging: subscr-IMSI-1234000270: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000269: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000268: (bts=0) T3113 expired
+sys={56.000000}: select()
+(bts=0) Estimated 16 paging available_slots over 2 seconds
+(bts=0) Timeout waiting for CCCH Load Indication, assuming BTS is below Load Threshold (available_slots 0 -> 16)
+(msc=-1) Paging: subscr-IMSI-1234000499: (bts=0) Going to send paging command for ch. type 0 (attempt 0)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000499
+(msc=-1) Paging: subscr-IMSI-1234000271: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000271
+(msc=-1) Paging: subscr-IMSI-1234000272: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000272
+(msc=-1) Paging: subscr-IMSI-1234000273: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000273
+(msc=-1) Paging: subscr-IMSI-1234000274: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000274
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000275
+(msc=-1) Paging: subscr-IMSI-1234000276: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000276
+(msc=-1) Paging: subscr-IMSI-1234000277: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000277
+(msc=-1) Paging: subscr-IMSI-1234000278: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000278
+(msc=-1) Paging: subscr-IMSI-1234000279: (bts=0) Going to send paging command for ch. type 0 (attempt 1)
+abis_rsl_sendmsg: Paging CMD IMSI-1234000279
+(bts=0) Scheduling next batch in 0.250000s (available_slots=6)
+(bts=0) Paged 10 subscribers (1 initial, 9 retrans) during last iteration
+(msc=-1) Paging: subscr-IMSI-1234000273: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000272: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000271: (bts=0) T3113 expired
+(msc=-1) Paging: subscr-IMSI-1234000280: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000281: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000282: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000283: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000284: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000285: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000286: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000287: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000288: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000289: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000290: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000291: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000292: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000293: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000294: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000295: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000296: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000297: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000298: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000299: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000300: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000301: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000302: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000303: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000304: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000305: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000306: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000307: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000308: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000309: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000310: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000311: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000312: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000313: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000314: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000315: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000316: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000317: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000318: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000319: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000320: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000321: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000322: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000323: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000324: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000325: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000326: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000327: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000328: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000329: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000330: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000331: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000332: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000333: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000334: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000335: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000336: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000337: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000338: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000339: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000340: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000341: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000342: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000343: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000344: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000345: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000346: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000347: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000348: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000349: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000350: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000351: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000352: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000353: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000354: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000355: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000356: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000357: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000358: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000359: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000360: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000361: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000362: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000363: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000364: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000365: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000366: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000367: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000368: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000369: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000370: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000371: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000372: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000373: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000374: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000375: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000376: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000377: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000378: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000379: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000380: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000381: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000382: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000383: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000384: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000385: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000386: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000387: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000388: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000389: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000390: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000391: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000392: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000393: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000394: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000395: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000396: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000397: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000398: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000399: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000400: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000401: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000402: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000403: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000404: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000405: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000406: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000407: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000408: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000409: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000410: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000411: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000412: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000413: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000414: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000415: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000416: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000417: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000418: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000419: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000420: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000421: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000422: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000423: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000424: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000425: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000426: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000427: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000428: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000429: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000430: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000431: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000432: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000433: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000434: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000435: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000436: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000437: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000438: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000439: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000440: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000441: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000442: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000443: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000444: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000445: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000446: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000447: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000448: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000449: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000450: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000451: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000452: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000453: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000454: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000455: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000456: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000457: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000458: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000459: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000460: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000461: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000462: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000463: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000464: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000465: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000466: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000467: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000468: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000469: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000470: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000471: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000472: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000473: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000474: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000475: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000476: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000477: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000478: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000479: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000480: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000481: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000482: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000483: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000484: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000485: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000486: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000487: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000488: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000489: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000490: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000491: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000492: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000493: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000494: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000495: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000496: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000497: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000498: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000499: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000274: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000275: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000276: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000277: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000278: (bts=0) Stop paging (flush)
+(msc=-1) Paging: subscr-IMSI-1234000279: (bts=0) Stop paging (flush)
+BTS deallocated OK in test_paging500_combined()
diff --git a/tests/power_ctrl.vty b/tests/power_ctrl.vty
new file mode 100644
index 000000000..f0ef46e31
--- /dev/null
+++ b/tests/power_ctrl.vty
@@ -0,0 +1,381 @@
+OsmoBSC> enable
+
+OsmoBSC# ### Default configuration
+OsmoBSC# show running-config
+...
+ bs-power-control
+ mode static
+ ms-power-control
+ mode dyn-bts
+ ctrl-interval 2
+ step-size inc 4 red 2
+ rxlev-thresh lower 32 upper 38
+ rxlev-thresh-comp lower 10 12 upper 19 20
+ rxqual-thresh lower 3 upper 0
+ rxqual-thresh-comp lower 5 7 upper 15 18
+...
+
+OsmoBSC# ### Check that 'power-ctrl' node and its commands exist
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)# list with-flags
+...
+ . ... (bs-power-control|ms-power-control)
+ . ..l no (bs-power-control|ms-power-control)
+...
+
+OsmoBSC(config-net-bts)# bs-power-control
+OsmoBSC(config-bs-power-ctrl)# list with-flags
+...
+ . l. mode (static|dyn-bts|dyn-bsc) [reset]
+ . l. bs-power (static|dyn-max) <0-30>
+ . lv ctrl-interval <0-31>
+ . lv step-size inc <2-6> red <2-4>
+ . lv rxlev-thresh lower <0-63> upper <0-63>
+ . lv rxqual-thresh lower <0-7> upper <0-7>
+ . lv ci-thresh (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs|all) (enable|disable)
+ . lv ci-thresh (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-30> upper <0-30>
+ . lv rxlev-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
+ . lv rxqual-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
+ . lv ci-thresh-comp (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-31> <0-31> upper <0-31> <0-31>
+ . lv no (rxlev-avg|rxqual-avg)
+ . lv (rxlev-avg|rxqual-avg) params hreqave <1-31> hreqt <1-31>
+ . lv (rxlev-avg|rxqual-avg) algo (unweighted|weighted|mod-median)
+ . lv (rxlev-avg|rxqual-avg) algo osmo-ewma beta <1-99>
+ . lv no ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) params hreqave <1-31> hreqt <1-31>
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo (unweighted|weighted|mod-median)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo osmo-ewma beta <1-99>
+
+OsmoBSC(config-bs-power-ctrl)# bs-power ?
+ static Fixed BS Power reduction value (for static mode)
+ dyn-max Maximum BS Power reduction value (for dynamic mode)
+OsmoBSC(config-bs-power-ctrl)# bs-power static ?
+ <0-30> BS Power reduction value (in dB, even numbers only)
+OsmoBSC(config-bs-power-ctrl)# bs-power static 3
+% Incorrect BS Power reduction value, an even number is expected
+OsmoBSC(config-bs-power-ctrl)# bs-power static 6
+OsmoBSC(config-bs-power-ctrl)# show running-config
+...
+ bs-power-control
+ mode static
+ bs-power static 6
+ ms-power-control
+ mode dyn-bts
+... !bs-power
+
+OsmoBSC(config-bs-power-ctrl)# ### Check default BS Power Parameters
+OsmoBSC(config-bs-power-ctrl)# mode dyn-bts
+OsmoBSC(config-bs-power-ctrl)# show running-config
+...
+ bs-power-control
+ mode dyn-bts
+ bs-power dyn-max 12
+ ctrl-interval 1
+ step-size inc 4 red 2
+ rxlev-thresh lower 32 upper 38
+ rxlev-thresh-comp lower 10 12 upper 19 20
+ rxqual-thresh lower 3 upper 0
+ rxqual-thresh-comp lower 5 7 upper 15 18
+ ms-power-control
+ mode dyn-bts
+...
+
+OsmoBSC(config-bs-power-ctrl)# bs-power dyn-max 30
+OsmoBSC(config-bs-power-ctrl)# show running-config
+...
+ bs-power-control
+ mode dyn-bts
+ bs-power dyn-max 30
+... !bs-power
+ ms-power-control
+ mode dyn-bts
+... !bs-power
+
+OsmoBSC(config-bs-power-ctrl)# ctrl-interval 31
+OsmoBSC(config-bs-power-ctrl)# show running-config
+...
+ bs-power-control
+...
+ ctrl-interval 31
+...
+ ms-power-control
+...
+ ctrl-interval 2
+...
+
+OsmoBSC(config-bs-power-ctrl)# exit
+OsmoBSC(config-net-bts)# ms-power-control
+OsmoBSC(config-ms-power-ctrl)# list with-flags
+...
+ . l. mode (static|dyn-bts|dyn-bsc) [reset]
+ . l. bs-power (static|dyn-max) <0-30>
+ . lv ctrl-interval <0-31>
+ . lv step-size inc <2-6> red <2-4>
+ . lv rxlev-thresh lower <0-63> upper <0-63>
+ . lv rxqual-thresh lower <0-7> upper <0-7>
+ . lv ci-thresh (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs|all) (enable|disable)
+ . lv ci-thresh (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-30> upper <0-30>
+ . lv rxlev-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
+ . lv rxqual-thresh-comp lower <0-31> <0-31> upper <0-31> <0-31>
+ . lv ci-thresh-comp (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) lower <0-31> <0-31> upper <0-31> <0-31>
+ . lv no (rxlev-avg|rxqual-avg)
+ . lv (rxlev-avg|rxqual-avg) params hreqave <1-31> hreqt <1-31>
+ . lv (rxlev-avg|rxqual-avg) algo (unweighted|weighted|mod-median)
+ . lv (rxlev-avg|rxqual-avg) algo osmo-ewma beta <1-99>
+ . lv no ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) params hreqave <1-31> hreqt <1-31>
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo (unweighted|weighted|mod-median)
+ . lv ci-avg (fr-efr|hr|amr-fr|amr-hr|sdcch|gprs) algo osmo-ewma beta <1-99>
+
+OsmoBSC(config-ms-power-ctrl)# ### Check default MS Power Parameters
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ bs-power-control
+ mode dyn-bts
+...
+ ms-power-control
+ mode dyn-bts
+ ctrl-interval 2
+ step-size inc 4 red 2
+ rxlev-thresh lower 32 upper 38
+ rxlev-thresh-comp lower 10 12 upper 19 20
+ rxqual-thresh lower 3 upper 0
+ rxqual-thresh-comp lower 5 7 upper 15 18
+ ci-thresh fr-efr disable
+ ci-thresh fr-efr lower 13 upper 17
+ ci-thresh-comp fr-efr lower 5 7 upper 15 18
+ ci-thresh hr disable
+ ci-thresh hr lower 16 upper 21
+ ci-thresh-comp hr lower 5 7 upper 15 18
+ ci-thresh amr-fr disable
+ ci-thresh amr-fr lower 7 upper 11
+ ci-thresh-comp amr-fr lower 5 7 upper 15 18
+ ci-thresh amr-hr disable
+ ci-thresh amr-hr lower 13 upper 17
+ ci-thresh-comp amr-hr lower 5 7 upper 15 18
+ ci-thresh sdcch disable
+ ci-thresh sdcch lower 12 upper 16
+ ci-thresh-comp sdcch lower 5 7 upper 15 18
+ ci-thresh gprs disable
+ ci-thresh gprs lower 18 upper 24
+ ci-thresh-comp gprs lower 5 7 upper 15 18
+...
+
+OsmoBSC(config-ms-power-ctrl)# bs-power static 30
+% This command is only valid for 'bs-power-control' node
+OsmoBSC(config-ms-power-ctrl)# bs-power dyn-max 30
+% This command is only valid for 'bs-power-control' node
+
+OsmoBSC(config-ms-power-ctrl)# ctrl-interval 2
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ bs-power-control
+ mode dyn-bts
+...
+ ctrl-interval 31
+... !ctrl-interval
+ ms-power-control
+ mode dyn-bts
+...
+ ctrl-interval 2
+... !ctrl-interval
+
+OsmoBSC(config-ms-power-ctrl)# step-size inc 5 red 4
+% Power change step size must be an even number
+OsmoBSC(config-ms-power-ctrl)# step-size inc 2 red 3
+% Power change step size must be an even number
+OsmoBSC(config-ms-power-ctrl)# step-size inc 2 red 4
+% Increase step size (2) should be greater than reduce step size (4), consider changing it
+
+OsmoBSC(config-ms-power-ctrl)# step-size inc 6 red 4
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ bs-power-control
+ mode dyn-bts
+...
+ step-size inc 4 red 2
+...
+ ms-power-control
+ mode dyn-bts
+...
+ step-size inc 6 red 4
+...
+
+OsmoBSC(config-ms-power-ctrl)# rxlev-thresh lower?
+ lower Lower RxLev value (default is 32, i.e. -78 dBm)
+OsmoBSC(config-ms-power-ctrl)# rxlev-thresh lower ?
+ <0-63> Lower RxLev value (signal strength, 0 is worst, 63 is best)
+OsmoBSC(config-ms-power-ctrl)# rxlev-thresh lower 30 upper?
+ upper Upper RxLev value (default is 38, i.e. -72 dBm)
+OsmoBSC(config-ms-power-ctrl)# rxlev-thresh lower 30 upper ?
+ <0-63> Upper RxLev value (signal strength, 0 is worst, 63 is best)
+OsmoBSC(config-ms-power-ctrl)# rxlev-thresh lower 30 upper 63
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ bs-power-control
+... !rxlev-thresh
+ rxlev-thresh lower 32 upper 38
+...
+ ms-power-control
+... !rxlev-thresh
+ rxlev-thresh lower 30 upper 63
+...
+
+OsmoBSC(config-ms-power-ctrl)# rxqual-thresh lower?
+ lower Lower RxQual value (default is 3, i.e. 0.8% <= BER < 1.6%)
+OsmoBSC(config-ms-power-ctrl)# rxqual-thresh lower ?
+ <0-7> Lower RxQual value (signal quality, 0 is best, 7 is worst)
+OsmoBSC(config-ms-power-ctrl)# rxqual-thresh lower 7 upper?
+ upper Upper RxQual value (default is 0, i.e. BER < 0.2%)
+OsmoBSC(config-ms-power-ctrl)# rxqual-thresh lower 7 upper ?
+ <0-7> Upper RxQual value (signal quality, 0 is best, 7 is worst)
+OsmoBSC(config-ms-power-ctrl)# rxqual-thresh lower 7 upper 4
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ bs-power-control
+... !rxqual-thresh
+ rxqual-thresh lower 3 upper 0
+...
+ ms-power-control
+... !rxqual-thresh
+ rxqual-thresh lower 7 upper 4
+...
+
+OsmoBSC(config-ms-power-ctrl)# rxlev-avg algo osmo-ewma beta 50
+OsmoBSC(config-ms-power-ctrl)# rxqual-avg algo unweighted
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ bs-power-control
+... !(rxlev-avg|rxqual-avg)
+ ms-power-control
+... !(rxlev-avg|rxqual-avg)
+ rxlev-avg algo osmo-ewma beta 50
+ rxlev-avg params hreqave 4 hreqt 6
+... !(rxlev-avg|rxqual-avg)
+ rxqual-avg algo unweighted
+ rxqual-avg params hreqave 4 hreqt 6
+...
+
+OsmoBSC(config-ms-power-ctrl)# rxlev-avg params hreqave 2 hreqt 20
+% Hreqave (2) * Hreqt (20) = 40 must be < 32
+OsmoBSC(config-ms-power-ctrl)# rxlev-avg params hreqave 2 hreqt 15
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ bs-power-control
+... !rxlev-avg params
+ ms-power-control
+... !rxlev-avg params
+ rxlev-avg params hreqave 2 hreqt 15
+...
+
+OsmoBSC(config-ms-power-ctrl)# exit
+OsmoBSC(config-net-bts)# ### Disable BS/MS Power Control
+OsmoBSC(config-net-bts)# no bs-power-control
+OsmoBSC(config-net-bts)# no ms-power-control
+OsmoBSC(config-net-bts)# show running-config
+...
+ no bs-power-control
+ no ms-power-control
+...
+
+OsmoBSC(config-net-bts)# bs-power-control
+OsmoBSC(config-bs-power-ctrl)# mode static reset
+% Reset to default parameters
+OsmoBSC(config-bs-power-ctrl)# exit
+
+OsmoBSC(config-net-bts)# ms-power-control
+OsmoBSC(config-ms-power-ctrl)# mode dyn-bts reset
+% Reset to default parameters
+OsmoBSC(config-ms-power-ctrl)# exit
+
+OsmoBSC(config-net-bts)# show running-config
+...
+ bs-power-control
+ mode static
+ ms-power-control
+ mode dyn-bts
+ ctrl-interval 2
+ step-size inc 4 red 2
+ rxlev-thresh lower 32 upper 38
+ rxlev-thresh-comp lower 10 12 upper 19 20
+ rxqual-thresh lower 3 upper 0
+ rxqual-thresh-comp lower 5 7 upper 15 18
+...
+
+OsmoBSC(config-net-bts)# ### Check 'ci-thresh-comp (enable|disable) all' works properly:
+OsmoBSC(config-net-bts)# ms-power-control
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ ms-power-control
+...
+ ci-thresh fr-efr disable
+ ci-thresh fr-efr lower 13 upper 17
+ ci-thresh-comp fr-efr lower 5 7 upper 15 18
+ ci-thresh hr disable
+ ci-thresh hr lower 16 upper 21
+ ci-thresh-comp hr lower 5 7 upper 15 18
+ ci-thresh amr-fr disable
+ ci-thresh amr-fr lower 7 upper 11
+ ci-thresh-comp amr-fr lower 5 7 upper 15 18
+ ci-thresh amr-hr disable
+ ci-thresh amr-hr lower 13 upper 17
+ ci-thresh-comp amr-hr lower 5 7 upper 15 18
+ ci-thresh sdcch disable
+ ci-thresh sdcch lower 12 upper 16
+ ci-thresh-comp sdcch lower 5 7 upper 15 18
+ ci-thresh gprs disable
+ ci-thresh gprs lower 18 upper 24
+ ci-thresh-comp gprs lower 5 7 upper 15 18
+...
+
+OsmoBSC(config-ms-power-ctrl)# ci-thresh all enable
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ ms-power-control
+...
+ ci-thresh fr-efr enable
+ ci-thresh fr-efr lower 13 upper 17
+ ci-thresh-comp fr-efr lower 5 7 upper 15 18
+ ci-thresh hr enable
+ ci-thresh hr lower 16 upper 21
+ ci-thresh-comp hr lower 5 7 upper 15 18
+ ci-thresh amr-fr enable
+ ci-thresh amr-fr lower 7 upper 11
+ ci-thresh-comp amr-fr lower 5 7 upper 15 18
+ ci-thresh amr-hr enable
+ ci-thresh amr-hr lower 13 upper 17
+ ci-thresh-comp amr-hr lower 5 7 upper 15 18
+ ci-thresh sdcch enable
+ ci-thresh sdcch lower 12 upper 16
+ ci-thresh-comp sdcch lower 5 7 upper 15 18
+ ci-thresh gprs enable
+ ci-thresh gprs lower 18 upper 24
+ ci-thresh-comp gprs lower 5 7 upper 15 18
+...
+
+OsmoBSC(config-ms-power-ctrl)# ci-thresh all disable
+OsmoBSC(config-ms-power-ctrl)# show running-config
+...
+ ms-power-control
+...
+ ci-thresh fr-efr disable
+ ci-thresh fr-efr lower 13 upper 17
+ ci-thresh-comp fr-efr lower 5 7 upper 15 18
+ ci-thresh hr disable
+ ci-thresh hr lower 16 upper 21
+ ci-thresh-comp hr lower 5 7 upper 15 18
+ ci-thresh amr-fr disable
+ ci-thresh amr-fr lower 7 upper 11
+ ci-thresh-comp amr-fr lower 5 7 upper 15 18
+ ci-thresh amr-hr disable
+ ci-thresh amr-hr lower 13 upper 17
+ ci-thresh-comp amr-hr lower 5 7 upper 15 18
+ ci-thresh sdcch disable
+ ci-thresh sdcch lower 12 upper 16
+ ci-thresh-comp sdcch lower 5 7 upper 15 18
+ ci-thresh gprs disable
+ ci-thresh gprs lower 18 upper 24
+ ci-thresh-comp gprs lower 5 7 upper 15 18
+...
diff --git a/tests/smlc.vty b/tests/smlc.vty
new file mode 100644
index 000000000..559728985
--- /dev/null
+++ b/tests/smlc.vty
@@ -0,0 +1,73 @@
+OsmoBSC> enable
+OsmoBSC# configure terminal
+OsmoBSC(config)# smlc
+OsmoBSC(config-smlc)# list
+...
+ enable
+ no enable
+ bsc-addr NAME
+ smlc-addr NAME
+
+OsmoBSC(config-smlc)# enable?
+ enable Start up Lb interface connection to the remote SMLC
+
+OsmoBSC(config-smlc)# no?
+ no Negate a command or set its defaults
+OsmoBSC(config-smlc)# no ?
+ enable Stop Lb interface connection to the remote SMLC
+
+OsmoBSC(config-smlc)# bsc-addr?
+ bsc-addr Local SCCP address of this BSC towards the SMLC
+OsmoBSC(config-smlc)# bsc-addr ?
+ NAME Name of cs7 addressbook entry
+
+OsmoBSC(config-smlc)# smlc-addr?
+ smlc-addr Remote SCCP address of the SMLC
+OsmoBSC(config-smlc)# smlc-addr ?
+ NAME Name of cs7 addressbook entry
+
+OsmoBSC(config-smlc)# show running-config
+... !smlc
+
+OsmoBSC(config-smlc)# enable
+OsmoBSC(config-smlc)# show running-config
+...
+smlc
+ enable
+...
+
+OsmoBSC(config-smlc)# no enable
+OsmoBSC(config-smlc)# show running-config
+... !smlc
+
+OsmoBSC(config-smlc)# exit
+OsmoBSC(config)# cs7 instance 0
+OsmoBSC(config-cs7)# sccp-addr test-addr
+OsmoBSC(config-cs7-sccpaddr)# point-code 1.23.4
+OsmoBSC(config-cs7-sccpaddr)# exit
+OsmoBSC(config-cs7)# sccp-addr test-addr2
+OsmoBSC(config-cs7-sccpaddr)# point-code 1.23.5
+OsmoBSC(config-cs7-sccpaddr)# exit
+OsmoBSC(config-cs7)# exit
+OsmoBSC(config)# smlc
+
+OsmoBSC(config-smlc)# bsc-addr nonsense
+Error: No such SCCP addressbook entry: 'nonsense'
+% Command incomplete.
+OsmoBSC(config-smlc)# show running-config
+... !smlc
+
+OsmoBSC(config-smlc)# bsc-addr test-addr
+OsmoBSC(config-smlc)# show running-config
+...
+smlc
+ bsc-addr test-addr
+...
+
+OsmoBSC(config-smlc)# smlc-addr test-addr2
+OsmoBSC(config-smlc)# show running-config
+...
+smlc
+ bsc-addr test-addr
+ smlc-addr test-addr2
+...
diff --git a/tests/subscr/Makefile.am b/tests/subscr/Makefile.am
index e56d142bc..e18b96aaa 100644
--- a/tests/subscr/Makefile.am
+++ b/tests/subscr/Makefile.am
@@ -9,6 +9,8 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(LIBSMPP34_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -22,7 +24,7 @@ EXTRA_DIST = \
bsc_subscr_test.err \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
bsc_subscr_test \
$(NULL)
@@ -31,7 +33,7 @@ bsc_subscr_test_SOURCES = \
$(NULL)
bsc_subscr_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/bsc_subscriber.o \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOGSM_LIBS) \
diff --git a/tests/subscr/bsc_subscr_test.c b/tests/subscr/bsc_subscr_test.c
index 619d5e6bf..14e069df9 100644
--- a/tests/subscr/bsc_subscr_test.c
+++ b/tests/subscr/bsc_subscr_test.c
@@ -131,9 +131,10 @@ int main()
void *ctx = talloc_named_const(NULL, 0, "bsc_subscr_test");
printf("Testing BSC subscriber core code.\n");
osmo_init_logging2(ctx, &log_info);
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
bsc_subscribers = talloc_zero(ctx, struct llist_head);
diff --git a/tests/testsuite.at b/tests/testsuite.at
index a0d85ce3e..270d10c85 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -1,19 +1,6 @@
AT_INIT
AT_BANNER([Regression tests.])
-AT_SETUP([gsm0408])
-AT_KEYWORDS([gsm0408])
-cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([bsc_subscr])
-AT_KEYWORDS([bsc_subscr])
-cat $abs_srcdir/subscr/bsc_subscr_test.ok > expout
-cat $abs_srcdir/subscr/bsc_subscr_test.err > experr
-AT_CHECK([$abs_top_builddir/tests/subscr/bsc_subscr_test], [], [expout], [experr])
-AT_CLEANUP
-
AT_SETUP([abis])
AT_KEYWORDS([abis])
cat $abs_srcdir/abis/abis_test.ok > expout
@@ -38,195 +25,33 @@ cat $abs_srcdir/codec_pref/codec_pref_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/codec_pref/codec_pref_test], [], [expout], [ignore])
AT_CLEANUP
-AT_SETUP([nanobts_omlattr])
-AT_KEYWORDS([nanobts_omlattr])
-cat $abs_srcdir/nanobts_omlattr/nanobts_omlattr_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/nanobts_omlattr/nanobts_omlattr_test], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([neighbor_ident])
-AT_KEYWORDS([neighbor_ident])
-cat $abs_srcdir/handover/neighbor_ident_test.ok > expout
-cat $abs_srcdir/handover/neighbor_ident_test.err > experr
-AT_CHECK([$abs_top_builddir/tests/handover/neighbor_ident_test], [], [expout], [experr])
-AT_CLEANUP
-
-AT_SETUP([handover test 0])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 0], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 1])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 1], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 2])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 2], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 3])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 3], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 4])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 4], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 5])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 5], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 6])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 6], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 7])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 7], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 8])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 8], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 9])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 9], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 10])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 10], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 11])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 11], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 12])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 12], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 13])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 13], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 14])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 14], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 15])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 15], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 16])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 16], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 17])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 17], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 18])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 18], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 19])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 19], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 20])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 20], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 21])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 21], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 22])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 22], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 23])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 23], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 24])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 24], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 25])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 25], [], [expout], [ignore])
+AT_SETUP([gsm0408])
+AT_KEYWORDS([gsm0408])
+cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [], [expout], [ignore])
AT_CLEANUP
-AT_SETUP([handover test 26])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 26], [], [expout], [ignore])
+AT_SETUP([handover_tests])
+AT_KEYWORDS([handover_tests])
+cat $abs_srcdir/handover/handover_tests.ok > expout
+AT_CHECK([$abs_srcdir/handover/handover_tests.sh $abs_srcdir/handover $abs_builddir/handover], [], [expout], [ignore])
AT_CLEANUP
-AT_SETUP([handover test 27])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 27], [], [expout], [ignore])
+AT_SETUP([paging])
+AT_KEYWORDS([paging])
+cat $abs_srcdir/paging/paging_test.ok > experr
+AT_CHECK([$abs_top_builddir/tests/paging/paging_test], [], [ignore], [experr])
AT_CLEANUP
-AT_SETUP([handover test 28])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 28], [], [expout], [ignore])
+AT_SETUP([nanobts_omlattr])
+AT_KEYWORDS([nanobts_omlattr])
+cat $abs_srcdir/nanobts_omlattr/nanobts_omlattr_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/nanobts_omlattr/nanobts_omlattr_test], [], [expout], [ignore])
AT_CLEANUP
-AT_SETUP([handover test 29])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 29], [], [expout], [ignore])
+AT_SETUP([subscr])
+AT_KEYWORDS([subscr])
+cat $abs_srcdir/subscr/bsc_subscr_test.ok > expout
+cat $abs_srcdir/subscr/bsc_subscr_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/subscr/bsc_subscr_test], [], [expout], [experr])
AT_CLEANUP
diff --git a/tests/timer.vty b/tests/timer.vty
index 6c26681fc..6d6efe824 100644
--- a/tests/timer.vty
+++ b/tests/timer.vty
@@ -1,6 +1,7 @@
OsmoBSC> enable
OsmoBSC# show timer
+net: T4 = 5 s Timeout to receive BSSMAP RESET ACKNOWLEDGE from the MSC (default: 5 s)
net: T7 = 10 s inter-BSC/MSC Handover outgoing, BSSMAP HO Required to HO Command timeout (default: 10 s)
net: T8 = 10 s inter-BSC/MSC Handover outgoing, BSSMAP HO Command to final Clear timeout (default: 10 s)
net: T10 = 6 s RR Assignment (default: 6 s)
@@ -26,7 +27,16 @@ net: X8 = 5 s Timeout for RSL IPA MDCX ACK after sending RSL IPA MDCX (default:
net: X9 = 5 s Timeout for availability of MGW endpoint (default: 5 s)
net: X10 = 5 s Timeout for fully configured MGW endpoint (default: 5 s)
net: X11 = 5 s Timeout for Perform Location Response from SMLC (default: 5 s)
+net: X12 = 5 s Timeout for obtaining TA after BSSLAP TA Request (default: 5 s)
+net: X13 = 5 s Timeout for RR Channel Mode Modify ACK (BSC <-> MS) (default: 5 s)
+net: X14 = 5 s Timeout for RSL Channel Mode Modify ACK (BSC <-> BTS) (default: 5 s)
+net: X16 = 1000 ms Granularity for all_allocated:* rate counters: amount of milliseconds that one counter increment represents. See also X17, X18 (default: 1000 ms)
+net: X17 = 0 ms Rounding threshold for all_allocated:* rate counters: round up to the next counter increment after this many milliseconds. If set to half of X16 (or 0), employ the usual round() behavior: round up after half of a granularity period. If set to 1, behave like ceil(): already increment the counter immediately when all channels are allocated. If set >= X16, behave like floor(): only increment after a full X16 period of all channels being occupied. See also X16, X18 (default: 0 ms)
+net: X18 = 60000 ms Forget-sum period for all_allocated:* rate counters: after this amount of idle time, forget internally cumulated time remainders. Zero to always keep remainders. See also X16, X17. (default: 60000 ms)
+net: X25 = 5 s Timeout for initial user data after an MSC initiated an SCCP connection to the BSS (default: 5 s)
+net: X3105 = 17 Ny1: Maximum number of Physical Information (re)transmissions (default: 17)
net: X3111 = 4 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
+net: X3113 = 60 s Maximum Paging Request Transmit Delay Threshold: If the estimated transmit delay of the messages in the paging queue surpasses this threshold, then new incoming paging requests will if possible replace a request in retransmission state from the queue or otherwise be discarded, hence limiting the size of the queue and maximum delay of its scheduled requests. X3113 also serves as the upper boundary for dynamic T3113 when estimating the expected maximum delay to get a response (default: 60 s)
net: X3210 = 20 s After L3 Complete, wait for MSC to confirm (default: 20 s)
mgw: X2427 = 5 s timeout for MGCP response from MGW (default: 5 s)
@@ -47,6 +57,7 @@ X4 = 60 s After Clear Request, wait for MSC to Clear Command (sanity) (default:
OsmoBSC# configure terminal
OsmoBSC(config)# network
OsmoBSC(config-net)# timer
+net: T4 = 5 s Timeout to receive BSSMAP RESET ACKNOWLEDGE from the MSC (default: 5 s)
net: T7 = 10 s inter-BSC/MSC Handover outgoing, BSSMAP HO Required to HO Command timeout (default: 10 s)
net: T8 = 10 s inter-BSC/MSC Handover outgoing, BSSMAP HO Command to final Clear timeout (default: 10 s)
net: T10 = 6 s RR Assignment (default: 6 s)
@@ -72,7 +83,16 @@ net: X8 = 5 s Timeout for RSL IPA MDCX ACK after sending RSL IPA MDCX (default:
net: X9 = 5 s Timeout for availability of MGW endpoint (default: 5 s)
net: X10 = 5 s Timeout for fully configured MGW endpoint (default: 5 s)
net: X11 = 5 s Timeout for Perform Location Response from SMLC (default: 5 s)
+net: X12 = 5 s Timeout for obtaining TA after BSSLAP TA Request (default: 5 s)
+net: X13 = 5 s Timeout for RR Channel Mode Modify ACK (BSC <-> MS) (default: 5 s)
+net: X14 = 5 s Timeout for RSL Channel Mode Modify ACK (BSC <-> BTS) (default: 5 s)
+net: X16 = 1000 ms Granularity for all_allocated:* rate counters: amount of milliseconds that one counter increment represents. See also X17, X18 (default: 1000 ms)
+net: X17 = 0 ms Rounding threshold for all_allocated:* rate counters: round up to the next counter increment after this many milliseconds. If set to half of X16 (or 0), employ the usual round() behavior: round up after half of a granularity period. If set to 1, behave like ceil(): already increment the counter immediately when all channels are allocated. If set >= X16, behave like floor(): only increment after a full X16 period of all channels being occupied. See also X16, X18 (default: 0 ms)
+net: X18 = 60000 ms Forget-sum period for all_allocated:* rate counters: after this amount of idle time, forget internally cumulated time remainders. Zero to always keep remainders. See also X16, X17. (default: 60000 ms)
+net: X25 = 5 s Timeout for initial user data after an MSC initiated an SCCP connection to the BSS (default: 5 s)
+net: X3105 = 17 Ny1: Maximum number of Physical Information (re)transmissions (default: 17)
net: X3111 = 4 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
+net: X3113 = 60 s Maximum Paging Request Transmit Delay Threshold: If the estimated transmit delay of the messages in the paging queue surpasses this threshold, then new incoming paging requests will if possible replace a request in retransmission state from the queue or otherwise be discarded, hence limiting the size of the queue and maximum delay of its scheduled requests. X3113 also serves as the upper boundary for dynamic T3113 when estimating the expected maximum delay to get a response (default: 60 s)
net: X3210 = 20 s After L3 Complete, wait for MSC to confirm (default: 20 s)
mgw: X2427 = 5 s timeout for MGCP response from MGW (default: 5 s)
diff --git a/tests/timeslot.vty b/tests/timeslot.vty
new file mode 100644
index 000000000..8d0c70b05
--- /dev/null
+++ b/tests/timeslot.vty
@@ -0,0 +1,73 @@
+OsmoBSC> enable
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)# trx 0
+OsmoBSC(config-net-bts-trx)# timeslot 0
+
+OsmoBSC(config-net-bts-trx-ts)# list
+...
+ phys_chan_config (none|ccch|ccch+sdcch4|tch/f|tch/h|sdcch8|pdch|dynamic/ipaccess|unknown|ccch+sdcch4+cbch|sdcch8+cbch|dynamic/osmocom|tch/f_pdch|tch/f_tch/h_sdcch8_pdch)
+ training_sequence_code <0-7>
+ hopping enabled (0|1)
+ hopping sequence-number <0-63>
+ hopping maio <0-63>
+ hopping arfcn add <0-1023>
+ hopping arfcn del <0-1023>
+ hopping arfcn del-all
+ e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)
+
+OsmoBSC(config-net-bts-trx-ts)# phys_chan_config?
+ phys_chan_config Physical Channel Combination
+
+OsmoBSC(config-net-bts-trx-ts)# phys_chan_config ?
+ none Physical Channel not configured
+ ccch FCCH + SCH + BCCH + CCCH (Comb. IV)
+ ccch+sdcch4 FCCH + SCH + BCCH + CCCH + 4 SDCCH + 2 SACCH (Comb. V)
+ tch/f TCH/F + FACCH/F + SACCH (Comb. I)
+ tch/h 2 TCH/H + 2 FACCH/H + 2 SACCH (Comb. II)
+ sdcch8 8 SDCCH + 4 SACCH (Comb. VII)
+ pdch Packet Data Channel for GPRS/EDGE
+ dynamic/ipaccess Dynamic TCH/F or GPRS PDCH (dynamic/ipaccess is an alias for tch/f_pdch)
+ unknown Unknown / Unsupported channel combination
+ ccch+sdcch4+cbch FCCH + SCH + BCCH + CCCH + CBCH + 3 SDCCH + 2 SACCH (Comb. V)
+ sdcch8+cbch 7 SDCCH + 4 SACCH + CBCH (Comb. VII)
+ dynamic/osmocom Dynamic TCH/F or TCH/H or SDCCH/8 or GPRS PDCH (dynamic/osmocom is an alias for tch/f_tch/h_sdcch8_pdch)
+ tch/f_pdch Dynamic TCH/F or GPRS PDCH (dynamic/ipaccess is an alias for tch/f_pdch)
+ tch/f_tch/h_sdcch8_pdch Dynamic TCH/F or TCH/H or SDCCH/8 or GPRS PDCH (dynamic/osmocom is an alias for tch/f_tch/h_sdcch8_pdch)
+
+OsmoBSC(config-net-bts-trx-ts)# phys_chan_config none
+OsmoBSC(config-net-bts-trx-ts)# show running-config
+...
+ trx 0
+...
+ timeslot 0
+ hopping enabled 0
+...
+
+OsmoBSC(config-net-bts-trx-ts)# phys_chan_config tch/f
+OsmoBSC(config-net-bts-trx-ts)# show running-config
+...
+ trx 0
+...
+ timeslot 0
+ phys_chan_config TCH/F
+...
+
+OsmoBSC(config-net-bts-trx-ts)# phys_chan_config TCH/F
+OsmoBSC(config-net-bts-trx-ts)# show running-config
+...
+ trx 0
+...
+ timeslot 0
+ phys_chan_config TCH/F
+...
+
+OsmoBSC(config-net-bts-trx-ts)# phys_chan_config tch/f_tch/h_sdcch8_pdch
+OsmoBSC(config-net-bts-trx-ts)# show running-config
+...
+ trx 0
+...
+ timeslot 0
+ phys_chan_config DYNAMIC/OSMOCOM
+...
diff --git a/tests/vty_test_runner.py b/tests/vty_test_runner.py
index c96965cdc..34ddcc7c1 100755
--- a/tests/vty_test_runner.py
+++ b/tests/vty_test_runner.py
@@ -172,21 +172,6 @@ class TestVTYBSC(TestVTYGenericBSC):
res = self.vty.command("show network")
self.assertTrue(res.startswith('BSC is on Country Code') >= 0)
- def testMscDataCoreLACCI(self):
- self.vty.enable()
- res = self.vty.command("show running-config")
- self.assertEqual(res.find("core-location-area-code"), -1)
- self.assertEqual(res.find("core-cell-identity"), -1)
-
- self.vty.command("configure terminal")
- self.vty.command("msc 0")
- self.vty.command("core-location-area-code 666")
- self.vty.command("core-cell-identity 333")
-
- res = self.vty.command("show running-config")
- self.assertTrue(res.find("core-location-area-code 666") > 0)
- self.assertTrue(res.find("core-cell-identity 333") > 0)
-
def add_bsc_test(suite, workdir):
if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")):