aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--.gitignore11
-rw-r--r--Makefile.am1
-rw-r--r--README26
-rw-r--r--README.md110
-rw-r--r--README.vty-tests2
-rw-r--r--TODO-RELEASE8
-rw-r--r--configure.ac110
-rwxr-xr-xcontrib/jenkins.sh48
-rw-r--r--contrib/osmo-bsc.spec.in27
-rw-r--r--contrib/systemd/osmo-bsc.service5
-rw-r--r--debian/changelog1660
-rw-r--r--debian/compat2
-rw-r--r--debian/control22
-rw-r--r--debian/copyright27
-rw-r--r--debian/osmo-bsc.install1
-rwxr-xr-xdebian/rules54
-rw-r--r--doc/Makefile.am4
-rw-r--r--doc/assignment-fsm.dot9
-rw-r--r--doc/assignment.msc10
-rw-r--r--doc/bts-features.txt42
-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.cfg217
-rw-r--r--doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-1trx-1pdch-16kbps.cfg117
-rw-r--r--doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-1trx-1pdch-64kbps.cfg106
-rw-r--r--doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-4trx.cfg221
-rw-r--r--doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-8trx.cfg377
-rw-r--r--doc/examples/osmo-bsc/ericsson/osmo-bsc.rbs2308.cfg107
-rw-r--r--doc/examples/osmo-bsc/nokia/osmo-bsc.insite.cfg67
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc-4trx-fh.confmerge216
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc-4trx.cfg170
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc-minimal.cfg13
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc.cfg27
-rw-r--r--doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg20
-rw-r--r--doc/examples/osmo-bsc/siemens/osmo-bsc.bs11.cfg77
-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-fsm.dot16
-rw-r--r--doc/lchan-rtp-fsm.dot7
-rw-r--r--doc/lchan.msc68
-rw-r--r--doc/location_services_fsm_bsc.dot40
-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.adoc113
-rw-r--r--doc/manuals/chapters/bts-examples.adoc255
-rw-r--r--doc/manuals/chapters/bts.adoc785
-rw-r--r--doc/manuals/chapters/chan_alloc.adoc156
-rw-r--r--doc/manuals/chapters/control.adoc83
-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/overview.adoc18
-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.adoc151
-rw-r--r--doc/manuals/chapters/smlc.adoc90
-rw-r--r--doc/manuals/chapters/smscb.adoc88
-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.adoc24
-rw-r--r--doc/mscpool-attach.dot30
-rw-r--r--doc/timeslot.msc20
-rw-r--r--include/osmocom/bsc/Makefile.am22
-rw-r--r--include/osmocom/bsc/a_reset.h2
-rw-r--r--include/osmocom/bsc/abis_nm.h13
-rw-r--r--include/osmocom/bsc/abis_om2000.h33
-rw-r--r--include/osmocom/bsc/abis_osmo.h (renamed from include/osmocom/bsc/openbscdefines.h)29
-rw-r--r--include/osmocom/bsc/abis_rsl.h26
-rw-r--r--include/osmocom/bsc/acc.h (renamed from include/osmocom/bsc/acc_ramp.h)96
-rw-r--r--include/osmocom/bsc/arfcn_range_encode.h26
-rw-r--r--include/osmocom/bsc/assignment_fsm.h13
-rw-r--r--include/osmocom/bsc/bsc_msc_data.h43
-rw-r--r--include/osmocom/bsc/bsc_stats.h116
-rw-r--r--include/osmocom/bsc/bsc_subscr_conn_fsm.h28
-rw-r--r--include/osmocom/bsc/bsc_subscriber.h64
-rw-r--r--include/osmocom/bsc/bss.h2
-rw-r--r--include/osmocom/bsc/bssmap_reset.h32
-rw-r--r--include/osmocom/bsc/bts.h862
-rw-r--r--include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h22
-rw-r--r--include/osmocom/bsc/bts_setup_ramp.h69
-rw-r--r--include/osmocom/bsc/bts_sm.h80
-rw-r--r--include/osmocom/bsc/bts_trx.h102
-rw-r--r--include/osmocom/bsc/chan_counts.h111
-rw-r--r--include/osmocom/bsc/codec_pref.h4
-rw-r--r--include/osmocom/bsc/ctrl.h21
-rw-r--r--include/osmocom/bsc/data_rate_pref.h11
-rw-r--r--include/osmocom/bsc/debug.h4
-rw-r--r--include/osmocom/bsc/gsm_04_08_rr.h23
-rw-r--r--include/osmocom/bsc/gsm_08_08.h7
-rw-r--r--include/osmocom/bsc/gsm_data.h1541
-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.h63
-rw-r--r--include/osmocom/bsc/lchan.h393
-rw-r--r--include/osmocom/bsc/lchan_fsm.h39
-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.h57
-rw-r--r--include/osmocom/bsc/lcs_ta_req.h29
-rw-r--r--include/osmocom/bsc/meas_feed.h3
-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.h11
-rw-r--r--include/osmocom/bsc/osmo_bsc_grace.h5
-rw-r--r--include/osmocom/bsc/osmo_bsc_reset.h34
-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.h113
-rw-r--r--include/osmocom/bsc/pcu_if.h25
-rw-r--r--include/osmocom/bsc/pcuif_proto.h189
-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.h33
-rw-r--r--include/osmocom/bsc/smscb.h38
-rw-r--r--include/osmocom/bsc/system_information.h12
-rw-r--r--include/osmocom/bsc/timeslot_fsm.h26
-rw-r--r--include/osmocom/bsc/vgcs_fsm.h121
-rw-r--r--include/osmocom/bsc/vty.h67
-rw-r--r--m4/ax_check_compile_flag.m474
-rw-r--r--osmoappdesc.py3
-rw-r--r--src/Makefile.am6
-rw-r--r--src/ipaccess/Makefile.am10
-rw-r--r--src/ipaccess/abisip-find.c23
-rw-r--r--src/ipaccess/ipaccess-config.c171
-rw-r--r--src/ipaccess/ipaccess-proxy.c61
-rw-r--r--src/ipaccess/network_listen.c1
-rw-r--r--src/ipaccess/stubs.c24
-rw-r--r--src/osmo-bsc/Makefile.am72
-rw-r--r--src/osmo-bsc/a_reset.c217
-rw-r--r--src/osmo-bsc/abis_nm.c591
-rw-r--r--src/osmo-bsc/abis_nm_vty.c5
-rw-r--r--src/osmo-bsc/abis_om2000.c822
-rw-r--r--src/osmo-bsc/abis_om2000_vty.c231
-rw-r--r--src/osmo-bsc/abis_osmo.c226
-rw-r--r--src/osmo-bsc/abis_rsl.c1964
-rw-r--r--src/osmo-bsc/acc.c575
-rw-r--r--src/osmo-bsc/acc_ramp.c361
-rw-r--r--src/osmo-bsc/arfcn_range_encode.c340
-rw-r--r--src/osmo-bsc/assignment_fsm.c616
-rw-r--r--src/osmo-bsc/bsc_ctrl.c (renamed from src/osmo-bsc/osmo_bsc_ctrl.c)1027
-rw-r--r--src/osmo-bsc/bsc_ctrl_commands.c500
-rw-r--r--src/osmo-bsc/bsc_ctrl_lookup.c33
-rw-r--r--src/osmo-bsc/bsc_init.c298
-rw-r--r--src/osmo-bsc/bsc_rf_ctrl.c101
-rw-r--r--src/osmo-bsc/bsc_sccp.c140
-rw-r--r--src/osmo-bsc/bsc_stats.c236
-rw-r--r--src/osmo-bsc/bsc_subscr_conn_fsm.c642
-rw-r--r--src/osmo-bsc/bsc_subscriber.c343
-rw-r--r--src/osmo-bsc/bsc_vty.c5484
-rw-r--r--src/osmo-bsc/bssmap_reset.c263
-rw-r--r--src/osmo-bsc/bts.c1764
-rw-r--r--src/osmo-bsc/bts_ctrl.c1580
-rw-r--r--src/osmo-bsc/bts_ericsson_rbs2000.c53
-rw-r--r--src/osmo-bsc/bts_init.c2
-rw-r--r--src/osmo-bsc/bts_ipaccess_nanobts.c654
-rw-r--r--src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c349
-rw-r--r--src/osmo-bsc/bts_nokia_site.c128
-rw-r--r--src/osmo-bsc/bts_osmobts.c210
-rw-r--r--src/osmo-bsc/bts_setup_ramp.c249
-rw-r--r--src/osmo-bsc/bts_siemens_bs11.c128
-rw-r--r--src/osmo-bsc/bts_sm.c112
-rw-r--r--src/osmo-bsc/bts_sysmobts.c64
-rw-r--r--src/osmo-bsc/bts_trx.c509
-rw-r--r--src/osmo-bsc/bts_trx_ctrl.c157
-rw-r--r--src/osmo-bsc/bts_trx_ts_ctrl.c151
-rw-r--r--src/osmo-bsc/bts_trx_ts_lchan_ctrl.c152
-rw-r--r--src/osmo-bsc/bts_trx_vty.c881
-rw-r--r--src/osmo-bsc/bts_unknown.c1
-rw-r--r--src/osmo-bsc/bts_vty.c5090
-rw-r--r--src/osmo-bsc/cbch_scheduler.c9
-rw-r--r--src/osmo-bsc/cbsp_link.c293
-rw-r--r--src/osmo-bsc/chan_alloc.c78
-rw-r--r--src/osmo-bsc/chan_counts.c310
-rw-r--r--src/osmo-bsc/codec_pref.c134
-rw-r--r--src/osmo-bsc/data_rate_pref.c165
-rw-r--r--src/osmo-bsc/e1_config.c15
-rw-r--r--src/osmo-bsc/gsm_04_08_rr.c545
-rw-r--r--src/osmo-bsc/gsm_08_08.c512
-rw-r--r--src/osmo-bsc/gsm_data.c1458
-rw-r--r--src/osmo-bsc/handover_ctrl.c216
-rw-r--r--src/osmo-bsc/handover_decision.c19
-rw-r--r--src/osmo-bsc/handover_decision_2.c1308
-rw-r--r--src/osmo-bsc/handover_fsm.c435
-rw-r--r--src/osmo-bsc/handover_logic.c146
-rw-r--r--src/osmo-bsc/handover_vty.c31
-rw-r--r--src/osmo-bsc/lb.c827
-rw-r--r--src/osmo-bsc/lchan.c150
-rw-r--r--src/osmo-bsc/lchan_fsm.c1111
-rw-r--r--src/osmo-bsc/lchan_rtp_fsm.c236
-rw-r--r--src/osmo-bsc/lchan_select.c458
-rw-r--r--src/osmo-bsc/lcs_loc_req.c615
-rw-r--r--src/osmo-bsc/lcs_ta_req.c305
-rw-r--r--src/osmo-bsc/meas_feed.c103
-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.c861
-rw-r--r--src/osmo-bsc/net_init.c94
-rw-r--r--src/osmo-bsc/nm_bb_transc_fsm.c454
-rw-r--r--src/osmo-bsc/nm_bts_fsm.c443
-rw-r--r--src/osmo-bsc/nm_bts_sm_fsm.c326
-rw-r--r--src/osmo-bsc/nm_channel_fsm.c376
-rw-r--r--src/osmo-bsc/nm_common_fsm.c90
-rw-r--r--src/osmo-bsc/nm_gprs_cell_fsm.c432
-rw-r--r--src/osmo-bsc/nm_gprs_nse_fsm.c403
-rw-r--r--src/osmo-bsc/nm_gprs_nsvc_fsm.c434
-rw-r--r--src/osmo-bsc/nm_rcarrier_fsm.c455
-rw-r--r--src/osmo-bsc/osmo_bsc_bssap.c1345
-rw-r--r--src/osmo-bsc/osmo_bsc_filter.c48
-rw-r--r--src/osmo-bsc/osmo_bsc_grace.c40
-rw-r--r--src/osmo-bsc/osmo_bsc_lcls.c18
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c580
-rw-r--r--src/osmo-bsc/osmo_bsc_mgcp.c131
-rw-r--r--src/osmo-bsc/osmo_bsc_msc.c362
-rw-r--r--src/osmo-bsc/osmo_bsc_sigtran.c261
-rw-r--r--src/osmo-bsc/paging.c775
-rw-r--r--src/osmo-bsc/pcu_sock.c765
-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.c898
-rw-r--r--src/osmo-bsc/smscb.c428
-rw-r--r--src/osmo-bsc/smscb_vty.c421
-rw-r--r--src/osmo-bsc/system_information.c741
-rw-r--r--src/osmo-bsc/timeslot_fsm.c244
-rw-r--r--src/osmo-bsc/vgcs_fsm.c1318
-rw-r--r--src/utils/Makefile.am18
-rw-r--r--src/utils/bs11_config.c17
-rw-r--r--src/utils/meas_db.c99
-rw-r--r--src/utils/meas_db.h6
-rw-r--r--src/utils/meas_json.c10
-rw-r--r--src/utils/meas_pcap2db.c12
-rw-r--r--src/utils/meas_udp2db.c13
-rw-r--r--src/utils/meas_vis.c4
-rw-r--r--tests/Makefile.am20
-rw-r--r--tests/abis/Makefile.am12
-rw-r--r--tests/abis/abis_test.c11
-rw-r--r--tests/acc/Makefile.am36
-rw-r--r--tests/acc/acc_test.c554
-rw-r--r--tests/acc/acc_test.ok2318
-rw-r--r--tests/acch_overpower.vty88
-rw-r--r--tests/bsc/Makefile.am20
-rw-r--r--tests/bsc/bsc_test.c89
-rw-r--r--tests/bsc/bsc_test.ok51
-rw-r--r--tests/bts_features.vty58
-rw-r--r--tests/cbc.vty275
-rw-r--r--tests/codec_pref/Makefile.am8
-rw-r--r--tests/codec_pref/codec_pref_test.c134
-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.py329
-rw-r--r--tests/early_ia.vty24
-rw-r--r--tests/gprs_bvci_default.vty13
-rw-r--r--tests/gprs_params.vty113
-rw-r--r--tests/gsm0408/Makefile.am16
-rw-r--r--tests/gsm0408/gsm0408_test.c518
-rw-r--r--tests/gsm0408/gsm0408_test.ok198
-rw-r--r--tests/handover/Makefile.am79
-rw-r--r--tests/handover/handover_test.c2599
-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.am10
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.c227
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.ok23
-rw-r--r--tests/neighbor_ident.vty183
-rw-r--r--tests/nri_cfg.vty32
-rw-r--r--tests/osmo-bsc.vty274
-rw-r--r--tests/paging/Makefile.am39
-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/si2quater_neighbor_list.vty169
-rw-r--r--tests/smlc.vty73
-rw-r--r--tests/subscr/Makefile.am7
-rw-r--r--tests/subscr/bsc_subscr_test.c63
-rw-r--r--tests/subscr/bsc_subscr_test.err40
-rw-r--r--tests/subscr/bsc_subscr_test.ok16
-rw-r--r--tests/testsuite.at217
-rw-r--r--tests/timer.vty172
-rw-r--r--tests/timeslot.vty73
-rwxr-xr-xtests/vty_test_runner.py19
367 files changed, 67638 insertions, 19737 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..7592debf9
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+open_collective: osmocom
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/Makefile.am b/Makefile.am
index 3a2d56f56..0deeb1e4f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,6 +19,7 @@ SUBDIRS = \
BUILT_SOURCES = $(top_srcdir)/.version
EXTRA_DIST = \
.version \
+ README.md \
contrib/osmo-bsc.spec.in \
debian \
git-version-gen \
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..bf9526c92
--- /dev/null
+++ b/README.md
@@ -0,0 +1,110 @@
+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. [osmo-msc](https://osmocom.org/projects/osmomsc/wiki)): 3GPP AoIP or SCCPlite
+ * *Abis* interfaces towards various kinds of BTS (e.g. [osmo-bts](https://osmocom.org/projects/osmobts/wiki/Wiki), 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](https://osmocom.org/projects/osmo-cbc/wiki)).
+ * Lb interface towards a *SMLC* (Serving Mobile Location Centre, such as [osmo-smlc](https://osmocom.org/projects/osmo-smlc/wiki/OsmoSMLC)).
+
+
+Homepage
+--------
+
+You can find the OsmoBSC homepage with issue tracker and wiki online at
+<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.
+
+
+Forum
+-----
+
+We welcome any osmo-bsc related discussions in the
+[Cellular Network Infrastructure -> 2G/3G RAN (GERAN)](https://discourse.osmocom.org/c/cni/geran)
+section of the osmocom discourse (web based Forum).
+
+
+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.
+
+
+Issue Tracker
+-------------
+
+We use the [issue tracker of the osmo-bsc project on osmocom.org](https://osmocom.org/projects/osmobsc/issues) for
+tracking the state of bug reports and feature requests. Feel free to submit any issues you may find, or help
+us out by resolving existing issues.
+
+
+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.
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 b822f8a20..91e8dae1f 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -7,9 +7,5 @@
# 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 from osmo-gsm-manuals > 0.3.0
-libosmocore struct gsm0808_diagnostics Depends on libosmocore > 1.3.0
-libosmocore gsm0808_diagnostics_octet_location_str() Depends on libosmocore > 1.3.0
-libosmocore gsm0808_diagnostics_bit_location_str() Depends on libosmocore > 1.3.0
-libosmocore osmo_mobile_identity Depends on libosmocore > 1.3.0
-osmo-bsc Mobile Identity Coding OsmoBSC is stricter in rejecting invalid coding of Mobile Identity IEs
+libosmocore > 1.9.0 working (compiling) OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL
+libosmocore > 1.9.0 we use the new osmo_cbsp_segmentation_cb
diff --git a/configure.ac b/configure.ac
index 6555f875d..621453943 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])])
@@ -22,7 +24,7 @@ AC_PROG_CC
AC_PROG_INSTALL
LT_INIT
-dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
+dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
AS_CASE(["$LD"],[*clang*],
[AS_CASE(["${host_os}"],
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
@@ -34,47 +36,52 @@ 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, ...])],
+# Enable/disable ipaccess-utils (src/ipacces/)
+AC_ARG_ENABLE([ipaccess-utils], [AS_HELP_STRING([--enable-ipaccess-utils], [Build ipaccess utils: abisip-find, ipaccess-config, ... [default=yes]])],
[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.3.0)
-PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.3.0)
-PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.3.0)
-PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.3.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)
+# Enable/disable osmo-meas-udp2db
+AC_ARG_ENABLE([meas-udp2db], [AS_HELP_STRING([--enable-meas-udp2db], [Build osmo-meas-udp2db: listen to meas_feed on UDP and write it to an sqlite3 database [default=no]])],
+ [osmo_ac_meas_udp2db="$enableval"],[osmo_ac_meas_udp2db="no"])
+AM_CONDITIONAL(BUILD_MEAS_UDP2DB, test "x$osmo_ac_meas_udp2db" = "xyes")
+AC_SUBST(osmo_ac_meas_udp2db)
-dnl checks for header files
-AC_HEADER_STDC
+# Enable/disable osmo-meas-pcap2db
+AC_ARG_ENABLE([meas-pcap2db], [AS_HELP_STRING([--enable-meas-pcap2db], [Build osmo-meas-pcap2db: read PCAP file with meas_feed data and write it to an sqlite3 database [default=no]])],
+ [osmo_ac_meas_pcap2db="$enableval"],[osmo_ac_meas_pcap2db="no"])
+AM_CONDITIONAL(BUILD_MEAS_PCAP2DB, test "x$osmo_ac_meas_pcap2db" = "xyes")
+AC_SUBST(osmo_ac_meas_pcap2db)
-found_pcap=yes
-AC_CHECK_HEADERS(pcap/pcap.h,,found_pcap=no)
-AM_CONDITIONAL(HAVE_PCAP, test "$found_pcap" = yes)
+# Enable/disable meas_vis
+AC_ARG_ENABLE([meas-vis], [AS_HELP_STRING([--enable-meas-vis], [Build meas_vis: curses-visualization of measurements [default=no]])],
+ [osmo_ac_meas_vis="$enableval"],[osmo_ac_meas_vis="no"])
+AM_CONDITIONAL(BUILD_MEAS_VIS, test "x$osmo_ac_meas_vis" = "xyes")
+AC_SUBST(osmo_ac_meas_vis)
-found_cdk=yes
-AC_CHECK_HEADERS(cdk/cdk.h,,found_cdk=no)
-AM_CONDITIONAL(HAVE_LIBCDK, test "$found_cdk" = yes)
+dnl checks for libraries
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
+PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
+PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
+PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
+PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
+PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.8.0)
+PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.12.0)
-found_sqlite3=yes
-PKG_CHECK_MODULES(SQLITE3, sqlite3, ,found_sqlite3=no)
-AM_CONDITIONAL(HAVE_SQLITE3, test "$found_sqlite3" = yes)
-AC_SUBST(found_sqlite3)
+dnl checks for header files
+AC_HEADER_STDC
+if test "$osmo_ac_meas_pcap2db" = "yes" || test "$osmo_ac_meas_udp2db" = "yes"; then
+ PKG_CHECK_MODULES(SQLITE3, sqlite3)
+fi
+if test "$osmo_ac_meas_pcap2db" = "yes"; then
+ AC_CHECK_HEADERS(pcap/pcap.h, [], AC_MSG_ERROR(Unable to find libpcap))
+fi
+if test "$osmo_ac_meas_vis" = "yes"; then
+ AC_CHECK_HEADERS(cdk/cdk.h, [], AC_MSG_ERROR(Unable to find libcdk))
+fi
dnl Checks for typedefs, structures and compiler characteristics
@@ -84,8 +91,7 @@ AC_ARG_ENABLE(sanitize,
[Compile with address sanitizer enabled],
)],
[sanitize=$enableval], [sanitize="no"])
-if test x"$sanitize" = x"yes"
-then
+if test x"$sanitize" = x"yes"; then
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
fi
@@ -99,8 +105,7 @@ AC_ARG_ENABLE(werror,
]
)],
[werror=$enableval], [werror="no"])
-if test x"$werror" = x"yes"
-then
+if test x"$werror" = x"yes"; then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
@@ -119,13 +124,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,
@@ -143,8 +141,7 @@ fi
AC_ARG_ENABLE(profile,
[AS_HELP_STRING([--enable-profile], [Compile with profiling support enabled], )],
[profile=$enableval], [profile="no"])
-if test x"$profile" = x"yes"
-then
+if test x"$profile" = x"yes"; then
CFLAGS="$CFLAGS -pg"
CPPFLAGS="$CPPFLAGS -pg"
fi
@@ -154,13 +151,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])
@@ -177,8 +174,7 @@ AC_ARG_ENABLE(manuals,
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
fallback])
-if test x"$osmo_ac_build_manuals" = x"yes"
-then
+if test x"$osmo_ac_build_manuals" = x"yes"; then
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
@@ -241,13 +237,15 @@ AC_OUTPUT(
src/utils/Makefile
tests/Makefile
tests/atlocal
- tests/gsm0408/Makefile
+ tests/abis/Makefile
+ tests/acc/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..01b0d3389 100755
--- a/contrib/jenkins.sh
+++ b/contrib/jenkins.sh
@@ -1,11 +1,21 @@
#!/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"
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
+# * IS_MASTER_BUILD: set to 1 when running from master-builds (not gerrit-verifications)
#
+exit_tar_workspace() {
+ if [ "$IS_MASTER_BUILD" = "1" ]; then
+ tar -cJf "/tmp/workspace.tar.xz" "$base"
+ mv /tmp/workspace.tar.xz "$base"
+ fi
+
+ cat-testlogs.sh
+}
+
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
exit 2
@@ -27,6 +37,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"
@@ -37,10 +60,15 @@ osmo-build-dep.sh libosmo-sccp
osmo-build-dep.sh osmo-mgw
# Additional configure options and depends
-CONFIG=""
+CONFIG="
+ --enable-external-tests
+ --enable-meas-pcap2db
+ --enable-meas-udp2db
+ --enable-meas-vis
+ --enable-werror
+"
if [ "$WITH_MANUALS" = "1" ]; then
- osmo-build-dep.sh osmo-gsm-manuals
- CONFIG="--enable-manuals"
+ CONFIG="$CONFIG --enable-manuals"
fi
set +x
@@ -53,18 +81,18 @@ set -x
cd "$base"
autoreconf --install --force
-./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
+./configure --enable-sanitize $CONFIG
$MAKE $PARALLEL_MAKE
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
- || cat-testlogs.sh
+ || exit_tar_workspace
LD_LIBRARY_PATH="$inst/lib" \
- DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests --enable-werror $CONFIG" \
- $MAKE distcheck \
- || cat-testlogs.sh
+ DISTCHECK_CONFIGURE_FLAGS="$CONFIG" \
+ $MAKE $PARALLEL_MAKE distcheck \
+ || exit_tar_workspace
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 1d4494068..0a9c9f31f 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.12.0
+BuildRequires: pkgconfig(libosmo-netif) >= 1.4.0
+BuildRequires: pkgconfig(libosmo-sigtran) >= 1.8.0
+BuildRequires: pkgconfig(libosmoabis) >= 1.5.0
+BuildRequires: pkgconfig(libosmocore) >= 1.9.0
+BuildRequires: pkgconfig(libosmoctrl) >= 1.9.0
+BuildRequires: pkgconfig(libosmogb) >= 1.9.0
+BuildRequires: pkgconfig(libosmogsm) >= 1.9.0
+BuildRequires: pkgconfig(libosmovty) >= 1.9.0
BuildRequires: pkgconfig(talloc)
%{?systemd_requires}
@@ -117,13 +116,19 @@ 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
+%dir %{_docdir}/%{name}/examples/osmo-bsc/siemens
+%{_docdir}/%{name}/examples/osmo-bsc/*/osmo-bsc*.cfg
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-bsc.cfg
%{_unitdir}/%{name}.service
diff --git a/contrib/systemd/osmo-bsc.service b/contrib/systemd/osmo-bsc.service
index 67dcd7ed1..c8dc877c3 100644
--- a/contrib/systemd/osmo-bsc.service
+++ b/contrib/systemd/osmo-bsc.service
@@ -1,10 +1,15 @@
[Unit]
Description=Osmocom Base Station Controller (BSC)
Wants=osmo-mgw.service
+After=network-online.target
+Wants=network-online.target
[Service]
Type=simple
Restart=always
+LimitNOFILE=65536
+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..d8b5b2e74 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,1663 @@
+osmo-bsc (1.11.0) unstable; urgency=medium
+
+ [ Max ]
+ * doc: correct reference and add deprecation notice
+ * pcu_connected(): constify parameter
+
+ [ Philipp Maier ]
+ * pcuif_proto: rename PCU_IF_SAPI_AGCH_DT to PCU_IF_SAPI_PCH_DT
+ * pcu_sock: cosmetic: remove space before tab
+ * abis_rsl: add support for sending IMMEDIATE ASSIGNMENT through PCH
+ * pcu_sock: use extract_paging_group() for PCU_IF_SAPI_PCH
+ * abis_rsl: constify parameters of rsl_X_imm_assign_cmd
+ * pcu_sock: transfer sysinfo to PCU
+ * pcu_sock: transfer E1 connection information to PCU
+ * pcu_sock: set direct TLLI flag in info indication
+ * bts_trx_vty: use define constant E1_SUBSLOT_FULL instead magic number
+ * bsc_subscriber_conn_fsm: use subslot 0 in case of E1 full subslot
+ * timeslot_fsm: fix sourcecode indenting
+ * pcu_sock: print OML alerts from PCU
+ * abis_rsl: guard against over long IMMEDIATE ASSIGNMENT Messages
+ * pcu_sock: get rid of leaking message buffer
+ * pcu_sock: activate/deactivate PDCH on pcu reconnect
+ * pcu_sock: use struct to transfer IMMEDIATE ASSIGNMENT for PCH
+ * pcu_sock: improve logging
+ * pcu_sock: rename rc to fd
+ * pcu_sock: cosmetic: remove whitespace after type cast
+ * timeslot_fsm: fix PDCH activation
+ * bts: is_xyz_bts check functions should return bool
+ * bts: add function to check if a BTS has a BSC co located PCU
+ * pcu_sock: use is_ericsson_bts() to check for ericsson BTS
+ * pcu_sock: check BTS type properly in pcu_info_update()
+ * pcu_sock: drop usage of PCUIF flag PCU_IF_FLAG_DT
+ * pcu_sock.c: Call osmo_fd_unregister() before closing and changing listen_bfd->fd
+ * pcu_sock: handle multiple BTSs with one BSC co-located PCU (in theory)
+ * pcuif_proto: increment version number
+ * doc: add sample configuration for GPRS with ericsson RBS
+ * doc: overview: replace section TRAU mapper / E1 sub-channel muxer
+ * bts: bts: Explain Ericsson's interface switch (IS)
+ * doc: running: Describe how to configure a co-located PCU
+ * examples: update erissson GPRS config files
+ * doc: bts-examples: discuss Ericsson RBS EGPRS configuration
+ * pcu_sock: fix PCUIF interface (PCH)
+ * pcu_sock: use correct SAPI in message PCUIF PCU_IF_MSG_DATA_CNF_DT
+ * pcuif_proto: rename tlli to msg_id
+ * pcuif_proto: remove unnecessary members from gsm_pcu_if_data_cnf_dt
+ * pcuif_proto: get rid of _DT, _dt (Direct TLLI)
+ * pcuif_proto: check confirm flag in struct gsm_pcu_if_pch
+ * pcu_sock: use PCU_IF_SAPI_AGCH_2 instead PCU_IF_SAPI_AGCH
+
+ [ Oliver Smith ]
+ * ho: remove timeout for HO_ST_WAIT_LCHAN_ACTIVE
+ * ho: remove timeout for HO_ST_WAIT_LCHAN_ESTABLISHED
+ * fsms: use configurable timers instead of T23042
+ * osmo_bsc_main: don't allocate talloc_ctr_ctx
+ * Cosmetic: fix various typos
+ * Cosmetic: codec_pref: tweak comments
+ * bssmap_handle_assignm_req: split up
+ * bssmap_handle_ass_req_ct_speech: split up
+ * bssmap_handle_ass_req_ct_speech: refactor
+ * codec_pref: split test_codec_support_bts_rate
+ * test_codec_support_bts_rate: add missing breaks
+ * lchan.h: remove enum lchan_csd_mode
+ * channel_mode_from_lchan: add GSM48_CMODE_DATA_3k6
+ * bssmap_handle_ass_req_ct_data: implement
+ * abis_rsl: ipacc_payload_type: handle CSD
+ * bssmap_handle_ass_req_tp_codec_list: tweak log msg
+ * assignment_fsm: chan mode check: support CSD
+ * tests/handover: wrap functions from gsm_08_08.c
+ * requires_voice_stream -> ch_indctr
+ * lchan_select: chan_mode_to_chan_type: support CSD
+ * rsl_tx_ipacc_crcx/mdcx: omit speech mode for CSD
+ * chan_mode_to_mgcp_codec: support CSD
+ * Cosmetic: channel_mode_from_lchan: remove fixme
+ * check_chan_mode_rate_against…: fix never true cond
+ * abis_rsl: CSD: add RTP_CSD_FMT IE to CRCX/MDCX
+ * bsc_mgw_setup: use mgcp_client_pool_empty()
+ * abis_rsl: fix encoding RSL_IE_IPAC_RTP_CSD_FORMAT
+ * CSD: support non-transparent data rates
+ * debian: set compat level to 10
+ * contrib/jenkins: create workspace.tar.xz on error
+ * systemd: depend on networking-online.target
+ * Fix various typos
+ * Cosmetic: configure: move if … then to same line
+ * configure: ipa utils: add default=yes to arg help
+ * Cosmetic: debian/rules: remove boilerplate
+ * contrib/jenkins.sh: deduplicate configure flags
+ * configure: add --enable-meas-udp2db/pcap2db/vis
+
+ [ Harald Welte ]
+ * Work around coverity false positives in macros
+ * abis_nm: Only osmo-bts re-purposes the MANUF_ID for BTS feature flags
+ * cosmetic: Rename is_ipaccess_bts() to is_ipa_abisip_bts()
+ * cosmetic: Clarify language ip.access nanoBTS vs. all IPA Abis/IP
+ * test case fixup: Add missing (void) empty argument list specification
+ * Support (optional) indication of NCH position in SI1 rest octets
+ * nanobts: Request "supported features" attribute from BTS and BB_TRANSC
+ * prevent bogus NCH related error message if no NCH is configured
+
+ [ Michael Iedema ]
+ * utils: store more fields from meas-feed in db
+
+ [ Vadim Yanitskiy ]
+ * utils: fix incorrect string checks in meas_db_insert()
+ * ipaccess_drop_oml(): invalidate the feature vector
+ * Makefile.am: remove unneeded AM_LDFLAGS with LIBS
+ * tests: use -no-install libtool flag to avoid ./lt-* scripts
+ * fix bs11_read_swl_file(): properly clean up stale file list
+ * fix ipacc_rtp_csd_fmt_non_transp(): add missing breaks
+ * tests: $(BUILT_SOURCES) is not defined, depend on osmo-bsc
+ * osmoappdesc.py: add more config files for testing
+ * tests: add VTY transcript tests for 'si2quater neighbor-list'
+ * tests: demonstrate the problems of 'si2quater neighbor-list'
+ * doc/{examples,manuals}: remove dummy 'gprs nsvc 1'
+ * tests: rename and extend gprs_{bvci_default->params}.vty
+ * tests: add more tests for GPRS NSVC parameters
+ * gprs: fix has_valid_nsvc(): permit local udp port 0
+ * cosmetic: bts_uarfcn_add(): pass diversity directly to encode_fdd()
+ * cosmetic: bts_vty: switch is not a function, add a space
+ * si2quater: bts_uarfcn_add(): check if already added first
+ * si2quater: bts_uarfcn_add(): modify existsing UARFCNs
+ * si2quater: bts_earfcn_add(): do not add duplicate EARFCNs
+ * si2quater: add CTRL commands for deleting neighbor [EU]ARFCNs
+ * si2quater: check return value of osmo_earfcn_del()
+ * bts_is_online(): make the BTS pointer const, return bool
+ * struct gsm_bts_model: rename power_ctrl_{set->send}_c0_power_red
+ * bootstrap_rsl(): cosmetic: cache trx->bts and use it directly
+ * bts: st_op_enabled_on_enter(): resume C0 power reduction
+ * gsm_bts_send_c0_power_red(): check if BTS is online first
+ * copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
+ * fixup: contrib/jenkins: create workspace.tar.xz on error
+ * tests/{ctrl,vty}_test_runner.py: raise an exception if proc's rc != 0
+ * contrib/jenkins.sh: remove unrecognized --enable-vty-tests
+ * lchan_select: fix lchan selection for GSM48_CMODE_DATA_{14k5,12k0}
+ * fix send_assignment_complete(): proper SCE encoding for CSD
+
+ [ Neels Hofmeyr ]
+ * examples: osmo-bsc-minimal.cfg: drop codec-list
+ * pick up all *.vty in EXTRA_DIST
+ * add msc.vty to test 'msc' / 'codec-list' cfg
+ * cosmetic: use char literals in cfg_net_bsc_codec_list()
+ * cosmetic: use i++ instead of ++i in for loop
+ * vty: improve doc for 'codec-list'
+ * bsc_vty.c: s/bsc/msc for commands under msc node
+ * bsc_vty.c write_msc(): fix weird printf format
+ * vty: msc / codec-list: tweak error msg
+ * cosmetic: clarify test_codec_support_bts()
+ * select_codecs(): doc tweak
+ * select_codecs(): constify ct arg
+ * add timeslot.vty
+ * implicitly register osmo_fsm definitions
+ * assignment_fsm.c: make two functions static
+ * bsc_test: add 'update_exp'
+ * simplify storage of bsc_msc_data->audio_support
+ * vty: msc / codec-list: forbid duplicate entries
+ * vty: msc / codec-list: forbid invalid codec versions
+ * ensure correct phys_chan_config doc string count on VTY
+ * VTY,CTRL: add pchan dynamic/{osmocom,ipaccess}
+ * stat: change pchan naming to dynamic/{osmocom,ipaccess}
+ * bsc_test.c: test FSM IDs that contain pchan names
+ * tweak gsm_pchan_ids[]: DYNAMIC/{OSMOCOM,IPACCESS}
+ * drop gsm_pchan_ids, use sanitized FSM ids instead
+ * doc: add codec_resolution.msc
+ * cosmetic: timeslot_fsm.c: move some code to separate function
+ * vty: codec-list: fix error message
+ * fix coverity (false) warning in codec-list vty
+ * segfault: verify lchan presence on Assignment Complete
+ * add location_services_fsm_bsc.dot
+ * SCCP N-PCSTATE: trigger MSC status on PC availability
+ * log: N-PCSTATE: use new value_strings
+ * fix comment typo
+ * fix length check in abis_rsl_rx_rll()
+ * fix mscpool for large msc NRs
+
+ [ Pau Espin Pedrol ]
+ * cosmetic: gsm_data.h: Fix typo in comment
+ * constify bsc_conn_by_bsub() ptr param
+ * Clarify type and values of sccp.conn_id
+ * bscc_sccp: Avoid allocating conn_id 0x00FFFFFF
+ * get_bsc_conn_by_conn_id(): Properly match sccp_instance
+ * bscc_sccp: Small optimiztion in bsc_sccp_inst_next_conn_id()
+ * Assert conn_id being looked up is inside expected range
+ * pcu_sock.c: Call osmo_fd_unregister() before closing and changing bfd->fd
+ * Optimize subscr_conns lookup
+ * Move bsc_conn_by_bsub() and make it static
+ * bsc_subscriber: Drop unused function bsc_subscr_find_by_mi()
+ * bsc_subscriber: Mark functions used only internally as static
+ * bsc_subscriber: Introduce bsc_subscriber_store object
+ * bsc_subscriber: Optimize lookup of bsub by TMSI
+ * Fix Lb/A SCCP conn lookup after recent regression in optimization patch
+ * Use new GSM0408 defines for half-octet tags
+ * abis_rsl: Document spec ref of CCCH Load Ind
+ * Move paging queue specific handling to signal callback outside RSL code
+ * bts-rbs2k: Simplify osmo_fsm_inst_alloc_child_id()
+ * ipaccess nm: Delay marking TS as usable until OML reports Enabled state
+ * ipaccess nm: Handle TS_EV_OML_DOWN through NM FSM
+ * UserManual: Include sigtran*.adoc from osmo-gsm-manuals.git
+ * Write explicit role & sctp-role fields in ASP configurations
+ * Use new mgcp_client_conf_alloc() API to alloc mgcp_client_conf
+ * Use new libosmo-sccp APIs osmo_ss7_asp_get_{name,proto}()
+ * SI13: Set DRX_TIMER_MAX value actually transmitted
+ * cosmetic: Document DRX_TIMER_MAX upper limit on BCCH smaller than possible range
+ * meas_feed: Refactor fd/wqueue lifecycle
+ * meas_feed: Increase wqueue max_len to 100 and make it vty-configurable
+ * oml: ipacc: Remove BSSGP value assignment being overwritten afterwards
+ * oml: ipacc: Use new packed struct abis_nm_ipacc_att_bssgp_cfg from libosmcore
+ * oml: ipacc: Use new packed struct abis_nm_ipacc_att_ns_cfg from libosmcore
+ * oml: ipacc: Use new packed struct abis_nm_ipacc_att_rlc_cfg from libosmcore
+
+ [ arehbein ]
+ * gsm_bts_check_ny1: Prevent possible division by zero
+ * main: Give specific error message
+ * PCU interface: Log version when starting listener
+
+ [ Matan Perelman ]
+ * ctrl: Add cell reselection offset control
+ * ctrl: Remove dots from OOM
+ * ctrl: Add penalty time control
+ * ctrl: Add cell reselection hysteresis control
+ * ctrl: Add getting access control class
+ * ctrl: Add setting access control class
+ * ctrl: Add getting neighbor list
+ * ctrl: Add getting SI5 neighbor list
+ * ctrl: Add setting SI5 neighbor list
+ * ctrl: Add bsic
+ * ctrl: Add rach max delay
+ * ctrl: Add getting si2quater uarfcn neighbor list
+ * ctrl: Support adding si2quater uarfcn neighbor
+ * ctrl: Add getting si2quater earfcn neighbor list
+ * ctrl: Support adding si2quater earfcn neighbor
+ * control.adoc: Remove short-name and long-name
+ * control.adoc: Update with recent changes
+
+ [ Daniel Willmann ]
+ * cosmetic: Fix type in VTY description
+
+ [ Andreas Eversberg ]
+ * ASCI: Add new rate counters to support VGCS/VBS messages
+ * Fix typo in rate counters ASSIGMENT->ASSIGNMENT
+ * Cleanup code style of rate counters in osmo_bsc_msc.c
+ * ASCI: Add support for NOTIFICATION COMMAND (RSL) message
+ * ASCI: Add support for Group/Broadcast channel activation
+ * ASCI: Add selection reason for VGCS/VBS channels
+ * ASCI: Add TX support for UPLINK RELEASE message
+ * ASCI: Make function to add OSMUX IE public
+ * ASCI: Prepare bssmap_handle_ass_req_ct_speech() for VGCS/VBS
+ * ASCI: Add new debug category "ASCI" for VGCS/VBS state machines
+ * ASCI: Do not release channel, if SAPI 0 is released
+ * ASCI: Do not wait for RLL establishment
+ * ASCI: Add support for sending RSL UNIT-DATA towards BTS
+ * ASCI: Add TX support for UPLINK FREE/BUSY messages
+ * ASCI: Forward UPLINK RELEASE on dedicated channel to MSC
+ * ASCI: Add encoding of VGCS/VBS A-interface messages
+ * ASCI: Add processing and FSMs for VGCS/VBS
+ * ASCI: Add decoding of VGCS/VBS A-interface messages
+ * ASCI: Forward RLL to VGCS FSM
+ * ASCI: Forward lchan activation states to VGCS FSM
+ * ASCI: Indicate release of subscriber connection to VGCS FSM
+ * ASCI: Add support for reception of TALKER/LISTENER DETECTION
+ * ASCI: Send release on VGCS/VBS channel via unit data
+ * ASCI: Add assignment to a VGCS/VBS channel
+ * Select channel type by enum instead of three boolean
+ * ASCI: Fix uninitialized values in vgcs_fsm.c, found by gcc 13.1.1.20230714
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 16:40:03 +0200
+
+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/compat b/debian/compat
index ec635144f..f599e28b8 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-9
+10
diff --git a/debian/control b/debian/control
index 5a18c2ec6..d2abb4321 100644
--- a/debian/control
+++ b/debian/control
@@ -1,8 +1,8 @@
Source: osmo-bsc
Section: net
Priority: extra
-Maintainer: Alexander Couzens <lynxis@fe80.eu>
-Build-Depends: debhelper (>=9),
+Maintainer: Osmocom team <openbsc@lists.osmocom.org>
+Build-Depends: debhelper (>= 10),
dh-autoreconf,
autotools-dev,
autoconf,
@@ -12,22 +12,22 @@ Build-Depends: debhelper (>=9),
python3-minimal,
libcdk5-dev,
libtalloc-dev,
- libosmocore-dev (>= 1.3.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.9.0),
+ libosmo-sigtran-dev (>= 1.8.0),
+ libosmo-abis-dev (>= 1.5.0),
+ libosmo-netif-dev (>= 1.4.0),
+ libosmo-mgcp-client-dev (>= 1.12.0),
+ osmo-gsm-manuals-dev (>= 1.5.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..ccf3ec0eb 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>
@@ -25,31 +25,6 @@ License: AGPL-3.0+
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/>.
-Files: src/libbsc/bsc_ctrl_lookup.c
- src/libbsc/pcu_sock.c
-Copyright: 2008-2010 Harald Welte <laforge@gnumonks.org>
- 2009-2012 Andreas Eversberg <jolly@eversberg.eu>
- 2010-2011 Daniel Willmann <daniel@totalueberwachung.de>
- 2010-2011 On-Waves
- 2012 Holger Hans Peter Freyther
-License: GPL-2.0+
- 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 2 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, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- .
- On Debian systems, the complete text of the GNU General Public License
- Version 2 can be found in `/usr/share/common-licenses/GPL-2'.
-
Files: osmoappdesc.py
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
License: GPL-3.0+
diff --git a/debian/osmo-bsc.install b/debian/osmo-bsc.install
index 8f91b03bc..e60bb50a5 100644
--- a/debian/osmo-bsc.install
+++ b/debian/osmo-bsc.install
@@ -3,3 +3,4 @@ lib/systemd/system/osmo-bsc.service
usr/bin/osmo-bsc
usr/share/doc/osmo-bsc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg usr/share/doc/osmo-bsc/examples
usr/share/doc/osmo-bsc/examples/osmo-bsc/osmo-bsc.cfg usr/share/doc/osmo-bsc/examples
+usr/share/doc/osmo-bsc/examples/osmo-bsc/*/osmo-bsc*.cfg usr/share/doc/osmo-bsc/examples
diff --git a/debian/rules b/debian/rules
index a6646c10b..9ed337ffc 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,57 +1,15 @@
#!/usr/bin/make -f
-# You must remove unused comment lines for the released package.
-# See debhelper(7) (uncomment to enable)
-# This is an autogenerated template for debian/rules.
-#
-# Output every command that modifies files on the build system.
-#export DH_VERBOSE = 1
-#
-# Copy some variable definitions from pkg-info.mk and vendor.mk
-# under /usr/share/dpkg/ to here if they are useful.
-#
-# See FEATURE AREAS/ENVIRONMENT in dpkg-buildflags(1)
-# Apply all hardening options
-#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
-# Package maintainers to append CFLAGS
-#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
-# Package maintainers to append LDFLAGS
-#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
-#
-# With debhelper version 9 or newer, the dh command exports
-# all buildflags. So there is no need to include the
-# /usr/share/dpkg/buildflags.mk file here if compat is 9 or newer.
-#
-# These are rarely used code. (START)
-#
-# The following include for *.mk magically sets miscellaneous
-# variables while honoring existing values of pertinent
-# environment variables:
-#
-# Architecture-related variables such as DEB_TARGET_MULTIARCH:
-#include /usr/share/dpkg/architecture.mk
-# Vendor-related variables such as DEB_VENDOR:
-#include /usr/share/dpkg/vendor.mk
-# Package-related variables such as DEB_DISTRIBUTION
-#include /usr/share/dpkg/pkg-info.mk
-#
-# You may alternatively set them susing a simple script such as:
-# DEB_VENDOR ?= $(shell dpkg-vendor --query Vendor)
-#
-# These are rarely used code. (END)
-#
-
-# main packaging script based on dh7 syntax
%:
dh $@ --with autoreconf
-# debmake generated override targets
-CONFIGURE_FLAGS += --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
+CONFIGURE_FLAGS += \
+ --enable-manuals \
+ --enable-meas-vis \
+ --with-systemdsystemunitdir=/lib/systemd/system \
+ $(NULL)
+
override_dh_auto_configure:
dh_auto_configure -- $(CONFIGURE_FLAGS)
-#
-# Do not install libtool archive, python .pyc .pyo
-#override_dh_install:
-# dh_install --list-missing -X.la -X.pyc -X.pyo
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg
override_dh_strip:
diff --git a/doc/Makefile.am b/doc/Makefile.am
index d3a04c62f..3324d4665 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,8 @@ 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 \
+ $(builddir)/location_services_fsm_bsc.png \
$(NULL)
$(builddir)/%.png: $(srcdir)/%.msc
diff --git a/doc/assignment-fsm.dot b/doc/assignment-fsm.dot
index 5a3a2b91f..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,7 +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]
- 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]
@@ -39,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 4e690a811..5c3bbfbb5 100644
--- a/doc/assignment.msc
+++ b/doc/assignment.msc
@@ -9,6 +9,14 @@ msc {
gscon note gscon [label="GSCON_EV_ASSIGNMENT_START\n data=struct assignment_request"];
gscon abox gscon [label="ST_ASSIGNMENT"];
ass <- gscon [label="assignment_fsm_start()"];
+ |||;
+ --- [label="IF current lchan supports requested channel mode (re-use)"];
+ lchan <- ass [label="LCHAN_EV_REQUEST_MODE_MODIFY"];
+ ass abox ass [label="ASSIGNMENT_ST_\nWAIT_LCHAN_ESTABLISHED"];
+ ass rbox ass [label="see below"];
+
+ |||;
+ --- [label="ELSE: if current lchan does not support requested channel mode (establish new lchan)"];
ass abox ass [label="ASSIGNMENT_ST_\nWAIT_LCHAN_ACTIVE"];
|||;
@@ -22,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.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
new file mode 100644
index 000000000..22c70b9f3
--- /dev/null
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus01-4trx.cfg
@@ -0,0 +1,217 @@
+!
+! OpenBSC (0.9.11.308-62d46) configuration saved from vty
+!!
+password foo
+!
+line vty
+ no login
+!
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+ logging level lmi info
+ logging level linp info
+ logging level nm debug
+ logging level rsl debug
+ logging level llapd notice
+network
+ network country code 901
+ mobile network code 70
+ neci 0
+ paging any use tch 0
+ handover 0
+ handover window rxlev averaging 10
+ handover window rxqual averaging 1
+ handover window rxlev neighbor averaging 10
+ handover power budget interval 6
+ handover power budget hysteresis 3
+ handover maximum distance 9999
+ timer t3101 10
+ timer t3105 40
+ timer t3109 4
+ timer t3113 60
+ bts 0
+ type rbs2000
+ band GSM900
+ om2000 version-limit oml gen 12 rev 10
+ 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 descending
+ rach tx integer 9
+ rach max transmission 7
+ oml e1 line 0 timeslot 1 sub-slot full
+ oml e1 tei 62
+ neighbor-list mode automatic
+ gprs mode none
+ is-connection-list add 4 512 12
+ 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
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ e1 line 0 timeslot 1 sub-slot full
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 2 sub-slot 1
+ 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
+ trx 1
+ rf_locked 0
+ arfcn 121
+ nominal power 42
+ max_power_red 12
+ rsl e1 line 0 timeslot 4 sub-slot full
+ rsl e1 tei 1
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 3
+ trx 2
+ rf_locked 0
+ arfcn 119
+ nominal power 42
+ max_power_red 12
+ rsl e1 line 0 timeslot 7 sub-slot full
+ rsl e1 tei 2
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 3
+ trx 3
+ rf_locked 0
+ arfcn 119
+ nominal power 42
+ max_power_red 12
+ rsl e1 line 0 timeslot 10 sub-slot full
+ rsl e1 tei 3
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 3
+e1_input
+ e1_line 0 driver dahdi
+ e1_line 0 port 3
diff --git a/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-1trx-1pdch-16kbps.cfg b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-1trx-1pdch-16kbps.cfg
new file mode 100644
index 000000000..79ebca9d2
--- /dev/null
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-1trx-1pdch-16kbps.cfg
@@ -0,0 +1,117 @@
+!
+! OpenBSC (0.9.11.308-62d46) configuration saved from vty
+!!
+! CAUTION: The 16kbps mode of the Ericcson RBS CCU only allows for a very
+! minimal GPRS configuration (C1/C2 only).
+!
+password foo
+!
+line vty
+ no login
+!
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+ logging level lmi info
+ logging level linp info
+ logging level nm debug
+ logging level rsl debug
+ logging level llapd notice
+network
+ network country code 901
+ mobile network code 70
+ neci 0
+ paging any use tch 0
+ handover 0
+ handover window rxlev averaging 10
+ handover window rxqual averaging 1
+ handover window rxlev neighbor averaging 10
+ handover power budget interval 6
+ handover power budget hysteresis 3
+ handover maximum distance 9999
+ timer t3101 10
+ timer t3105 40
+ timer t3109 4
+ timer t3113 60
+ pcu-socket /tmp/pcu_bts
+ bts 0
+ type rbs2000
+ band GSM900
+ om2000 version-limit oml gen 12 rev 10
+ 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 descending
+ rach tx integer 9
+ rach max transmission 7
+ oml e1 line 0 timeslot 1 sub-slot full
+ oml e1 tei 62
+ neighbor-list mode automatic
+ gprs mode gprs
+ gprs routing area 0
+ gprs network-control-order nc0
+ gprs cell bvci 2
+ gprs nsei 101
+ gprs nsvc 0 nsvci 101
+ gprs nsvc 0 local udp port 23100
+ gprs nsvc 0 remote udp port 23000
+ gprs nsvc 0 remote ip 127.0.0.1
+ is-connection-list add 4 512 4
+ is-connection-list add 8 516 1
+ is-connection-list add 12 517 1
+ is-connection-list add 16 518 1
+ is-connection-list add 20 519 1
+ is-connection-list add 24 520 1
+ is-connection-list add 28 521 1
+ is-connection-list add 32 522 1
+ is-connection-list add 36 523 1
+ trx 0
+ rf_locked 0
+ arfcn 123
+ nominal power 44
+ max_power_red 0
+ rsl e1 line 0 timeslot 1 sub-slot full
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ e1 line 0 timeslot 1 sub-slot full
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 3 sub-slot 0
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 4 sub-slot 0
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 0
+ timeslot 4
+ phys_chan_config TCH/F_TCH/H_SDCCH8_PDCH
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 7 sub-slot 0
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 0
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 0
+e1_input
+ e1_line 0 driver dahdi
+ e1_line 0 port 2
diff --git a/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-1trx-1pdch-64kbps.cfg b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-1trx-1pdch-64kbps.cfg
new file mode 100644
index 000000000..4487ff070
--- /dev/null
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-1trx-1pdch-64kbps.cfg
@@ -0,0 +1,106 @@
+!
+! OpenBSC (0.9.11.308-62d46) configuration saved from vty
+!!
+password foo
+!
+line vty
+ no login
+!
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+ logging level lmi info
+ logging level linp info
+ logging level nm debug
+ logging level rsl debug
+ logging level llapd notice
+network
+ network country code 901
+ mobile network code 70
+ neci 0
+ paging any use tch 0
+ handover 0
+ handover window rxlev averaging 10
+ handover window rxqual averaging 1
+ handover window rxlev neighbor averaging 10
+ handover power budget interval 6
+ handover power budget hysteresis 3
+ handover maximum distance 9999
+ timer t3101 10
+ timer t3105 40
+ timer t3109 4
+ timer t3113 60
+ pcu-socket /tmp/pcu_bts
+ bts 0
+ type rbs2000
+ band GSM900
+ om2000 version-limit oml gen 12 rev 10
+ 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 descending
+ rach tx integer 9
+ rach max transmission 7
+ oml e1 line 0 timeslot 1 sub-slot full
+ oml e1 tei 62
+ neighbor-list mode automatic
+ gprs mode egprs
+ gprs routing area 0
+ gprs network-control-order nc0
+ gprs cell bvci 2
+ gprs nsei 101
+ gprs nsvc 0 nsvci 101
+ gprs nsvc 0 local udp port 23100
+ gprs nsvc 0 remote udp port 23000
+ gprs nsvc 0 remote ip 127.0.0.1
+ is-connection-list add 4 712 36
+ trx 0
+ rf_locked 0
+ arfcn 123
+ nominal power 44
+ max_power_red 0
+ rsl e1 line 0 timeslot 1 sub-slot full
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ e1 line 0 timeslot 1 sub-slot full
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 3 sub-slot full
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 4 sub-slot full
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot full
+ timeslot 4
+ phys_chan_config TCH/F_TCH/H_SDCCH8_PDCH
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot full
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 7 sub-slot full
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot full
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot full
+e1_input
+ e1_line 0 driver dahdi
+ e1_line 0 port 2
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
new file mode 100644
index 000000000..7e87babda
--- /dev/null
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-4trx.cfg
@@ -0,0 +1,221 @@
+!
+! OpenBSC (0.9.11.308-62d46) configuration saved from vty
+!!
+password foo
+!
+line vty
+ no login
+!
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+ logging level lmi info
+ logging level linp info
+ logging level nm debug
+ logging level rsl debug
+ logging level llapd notice
+network
+ network country code 901
+ mobile network code 70
+ neci 0
+ paging any use tch 0
+ handover 0
+ handover window rxlev averaging 10
+ handover window rxqual averaging 1
+ handover window rxlev neighbor averaging 10
+ handover power budget interval 6
+ handover power budget hysteresis 3
+ handover maximum distance 9999
+ timer t3101 10
+ timer t3105 40
+ timer t3109 4
+ timer t3113 60
+ bts 0
+ type rbs2000
+ band GSM900
+ om2000 version-limit oml gen 12 rev 10
+ 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 descending
+ rach tx integer 9
+ rach max transmission 7
+ oml e1 line 0 timeslot 1 sub-slot full
+ oml e1 tei 62
+ neighbor-list mode automatic
+ gprs mode none
+ is-connection-list add 4 512 12
+ is-connection-list add 16 524 12
+ is-connection-list add 28 536 12
+ is-connection-list add 40 548 12
+ is-connection-list add 52 560 12
+ is-connection-list add 64 572 12
+ is-connection-list add 76 640 12
+ is-connection-list add 88 652 12
+ trx 0
+ rf_locked 0
+ arfcn 123
+ nominal power 44
+ max_power_red 0
+ rsl e1 line 0 timeslot 1 sub-slot full
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ e1 line 0 timeslot 1 sub-slot full
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 2 sub-slot 1
+ 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
+ trx 1
+ rf_locked 0
+ arfcn 121
+ nominal power 44
+ max_power_red 0
+ rsl e1 line 0 timeslot 4 sub-slot full
+ rsl e1 tei 1
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 3
+ trx 2
+ rf_locked 0
+ arfcn 119
+ nominal power 44
+ max_power_red 0
+ rsl e1 line 0 timeslot 7 sub-slot full
+ rsl e1 tei 2
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 3
+ trx 3
+ rf_locked 0
+ arfcn 119
+ nominal power 44
+ max_power_red 0
+ rsl e1 line 0 timeslot 10 sub-slot full
+ rsl e1 tei 3
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 3
+e1_input
+ e1_line 0 driver dahdi
+ e1_line 0 port 3
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
new file mode 100644
index 000000000..dd015c0e6
--- /dev/null
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.dug20-rus02-8trx.cfg
@@ -0,0 +1,377 @@
+!
+! OpenBSC (0.9.11.308-62d46) configuration saved from vty
+!!
+password foo
+!
+line vty
+ no login
+!
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+ logging level lmi info
+ logging level linp info
+ logging level nm debug
+ logging level rsl debug
+ logging level llapd notice
+network
+ network country code 901
+ mobile network code 70
+ neci 0
+ paging any use tch 0
+ handover 0
+ handover window rxlev averaging 10
+ handover window rxqual averaging 1
+ handover window rxlev neighbor averaging 10
+ handover power budget interval 6
+ handover power budget hysteresis 3
+ handover maximum distance 9999
+ timer t3101 10
+ timer t3105 40
+ timer t3109 4
+ timer t3113 60
+ bts 0
+ type rbs2000
+ band GSM900
+ om2000 version-limit oml gen 12 rev 10
+ 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 descending
+ rach tx integer 9
+ rach max transmission 7
+ oml e1 line 0 timeslot 1 sub-slot full
+ oml e1 tei 62
+ neighbor-list mode automatic
+ gprs mode none
+ is-connection-list add 4 512 12
+ is-connection-list add 16 524 12
+ is-connection-list add 28 536 12
+ is-connection-list add 40 548 12
+ is-connection-list add 52 560 12
+ is-connection-list add 64 572 12
+ is-connection-list add 76 640 12
+ is-connection-list add 88 652 12
+ trx 0
+ rf_locked 0
+ arfcn 123
+ nominal power 40
+ max_power_red 10
+ rsl e1 line 0 timeslot 1 sub-slot full
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ e1 line 0 timeslot 1 sub-slot full
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 2 sub-slot 1
+ 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
+ trx 1
+ rf_locked 0
+ arfcn 121
+ nominal power 40
+ max_power_red 10
+ rsl e1 line 0 timeslot 4 sub-slot full
+ rsl e1 tei 1
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot 3
+ trx 2
+ rf_locked 0
+ arfcn 119
+ nominal power 40
+ max_power_red 10
+ rsl e1 line 0 timeslot 7 sub-slot full
+ rsl e1 tei 2
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot 3
+ trx 3
+ rf_locked 0
+ arfcn 119
+ nominal power 40
+ max_power_red 10
+ rsl e1 line 0 timeslot 10 sub-slot full
+ rsl e1 tei 3
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 11 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 12 sub-slot 3
+ trx 4
+ rf_locked 0
+ arfcn 117
+ nominal power 40
+ max_power_red 10
+ rsl e1 line 0 timeslot 13 sub-slot full
+ rsl e1 tei 4
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 14 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 14 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 14 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 14 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 15 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 15 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 15 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 15 sub-slot 3
+ trx 5
+ rf_locked 0
+ arfcn 115
+ nominal power 40
+ max_power_red 10
+ rsl e1 line 0 timeslot 16 sub-slot full
+ rsl e1 tei 5
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 17 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 17 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 17 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 17 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 18 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 18 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 18 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 18 sub-slot 3
+ trx 6
+ rf_locked 0
+ arfcn 113
+ nominal power 40
+ max_power_red 10
+ rsl e1 line 0 timeslot 19 sub-slot full
+ rsl e1 tei 6
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 20 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 20 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 20 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 20 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 21 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 21 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 21 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 21 sub-slot 3
+ trx 7
+ rf_locked 0
+ arfcn 113
+ nominal power 40
+ max_power_red 10
+ rsl e1 line 0 timeslot 22 sub-slot full
+ rsl e1 tei 7
+ timeslot 0
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 23 sub-slot 0
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 23 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 23 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 23 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 24 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 24 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 24 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 24 sub-slot 3
+e1_input
+ e1_line 0 driver dahdi
+ e1_line 0 port 3
diff --git a/doc/examples/osmo-bsc/ericsson/osmo-bsc.rbs2308.cfg b/doc/examples/osmo-bsc/ericsson/osmo-bsc.rbs2308.cfg
new file mode 100644
index 000000000..e61acc0b3
--- /dev/null
+++ b/doc/examples/osmo-bsc/ericsson/osmo-bsc.rbs2308.cfg
@@ -0,0 +1,107 @@
+!
+! OpenBSC (0.9.11.308-62d46) configuration saved from vty
+!!
+password foo
+!
+line vty
+ no login
+!
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+ logging level lmi info
+ logging level linp info
+ logging level nm debug
+ logging level rsl debug
+ logging level llapd notice
+log file bsc-rbs2k.log
+ logging timestamp 1
+ logging filter all 1
+ logging level lmi info
+ logging level linp info
+ logging level nm debug
+ logging level rsl debug
+network
+ network country code 262
+ mobile network code 42
+ neci 0
+ paging any use tch 0
+ handover 0
+ handover window rxlev averaging 10
+ handover window rxqual averaging 1
+ handover window rxlev neighbor averaging 10
+ handover power budget interval 6
+ handover power budget hysteresis 3
+ handover maximum distance 9999
+ timer t3101 10
+ timer t3105 40
+ timer t3109 4
+ timer t3113 60
+ bts 0
+ type rbs2000
+ band PCS1900
+ cell_identity 0
+ 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 mode set-all descending
+ rach tx integer 9
+ rach max transmission 7
+ oml e1 line 0 timeslot 1 sub-slot full
+ oml e1 tei 62
+ neighbor-list mode automatic
+ gprs mode none
+ is-connection-list add 4 512 12
+ 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 800
+ nominal power 43
+ max_power_red 14
+ rsl e1 line 0 timeslot 1 sub-slot full
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ e1 line 0 timeslot 1 sub-slot full
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 2 sub-slot 1
+ 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
+e1_input
+ e1_line 0 driver dahdi
+ e1_line 0 port 1
+ pcap 20200703-rbs2k-10.pcap
diff --git a/doc/examples/osmo-bsc/nokia/osmo-bsc.insite.cfg b/doc/examples/osmo-bsc/nokia/osmo-bsc.insite.cfg
new file mode 100644
index 000000000..34ffa998d
--- /dev/null
+++ b/doc/examples/osmo-bsc/nokia/osmo-bsc.insite.cfg
@@ -0,0 +1,67 @@
+!
+! OpenBSC configuration saved from vty
+! !
+password foo
+!
+line vty
+ no login
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+ logging level llapd notice
+ logging level nm debug
+e1_input
+ e1_line 0 driver dahdi
+ e1_line 0 port 3
+ pcap 2020004-insite.pcap
+network
+ network country code 1
+ mobile network code 1
+ timer t3101 10
+ timer t3113 60
+ bts 0
+ type nokia_site
+ band GSM1900
+ cell_identity 1
+ location_area_code 0x0001
+ base_station_id_code 63
+ training_sequence_code 7
+
+ oml e1 line 0 timeslot 1 sub-slot full
+ oml e1 tei 1
+
+ trx 0
+ arfcn 810
+ max_power_red 0
+ rsl e1 line 0 timeslot 1 sub-slot full
+ rsl e1 tei 1
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ e1 line 0 timeslot 2 sub-slot full
+ timeslot 1
+ phys_chan_config SDCCH8
+ e1 line 0 timeslot 2 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ e1 line 0 timeslot 2 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ e1 line 0 timeslot 2 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ e1 line 0 timeslot 3 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ e1 line 0 timeslot 3 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ e1 line 0 timeslot 3 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ e1 line 0 timeslot 3 sub-slot 3
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..03c3477f1
--- /dev/null
+++ b/doc/examples/osmo-bsc/osmo-bsc-4trx.cfg
@@ -0,0 +1,170 @@
+! osmo-bsc configuration example with 4 TRX
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+
+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..011b9d7e9 100644
--- a/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg
@@ -1,10 +1,18 @@
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+
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 +38,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 57000ae0b..32a8349f8 100644
--- a/doc/examples/osmo-bsc/osmo-bsc.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc.cfg
@@ -1,6 +1,14 @@
! osmo-bsc default configuration
! (assumes STP to run on 127.0.0.1 and uses default point codes)
!
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+
e1_input
e1_line 0 driver ipa
network
@@ -17,19 +25,23 @@ network
handover1 power budget interval 6
handover1 power budget hysteresis 3
handover1 maximum distance 9999
- dyn_ts_allow_tch_f 0
- 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
@@ -72,7 +84,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
@@ -82,9 +93,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 c250fac3b..f10221e6a 100644
--- a/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg
@@ -1,5 +1,13 @@
! osmo-bsc configuration example for custom SCCP addresses
!
+log stderr
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+
e1_input
e1_line 0 driver ipa
network
@@ -16,19 +24,19 @@ network
handover1 power budget interval 6
handover1 power budget hysteresis 3
handover1 maximum distance 9999
- dyn_ts_allow_tch_f 0
- 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
@@ -74,6 +82,8 @@ cs7 instance 0
point-code 0.42.1
!asp asp-clnt-OsmoBSC 2905 0 m3ua
! remote-ip 10.23.24.1 ! where to reach the STP
+ ! role asp
+ ! sctp-role client
sccp-address msc_remote
point-code 0.23.1
msc 0
diff --git a/doc/examples/osmo-bsc/siemens/osmo-bsc.bs11.cfg b/doc/examples/osmo-bsc/siemens/osmo-bsc.bs11.cfg
new file mode 100644
index 000000000..877ac6af3
--- /dev/null
+++ b/doc/examples/osmo-bsc/siemens/osmo-bsc.bs11.cfg
@@ -0,0 +1,77 @@
+!
+! OpenBSC configuration saved from vty
+! !
+password foo
+!
+line vty
+ no login
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category-hex 0
+ logging print category 1
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
+ logging level llapd notice
+ logging level linp notice
+ logging level lmi notice
+ logging level lmib info
+ logging level rsl debug
+ logging level rll debug
+ logging level nm debug
+ logging level mm debug
+ logging level rr debug
+e1_input
+ e1_line 0 driver dahdi
+ e1_line 0 port 0
+ e1_line 1 driver dahdi
+ e1_line 1 port 1
+ e1_line 2 driver dahdi
+ e1_line 2 port 2
+ pcap 20200714-bs11.pcap
+network
+ network country code 1
+ mobile network code 1
+ timer t3101 10
+ timer t3113 60
+ bts 0
+ type bs11
+ band GSM900
+ cell_identity 1
+ location_area_code 0x0001
+ training_sequence_code 7
+ base_station_id_code 63
+ oml e1 line 2 timeslot 1 sub-slot full
+ oml e1 tei 25
+ trx 0
+ arfcn 121
+ max_power_red 00
+ rsl e1 line 2 timeslot 1 sub-slot full
+ rsl e1 tei 1
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ e1 line 2 timeslot 1 sub-slot full
+ timeslot 1
+ phys_chan_config TCH/F
+ e1 line 2 timeslot 2 sub-slot 1
+ timeslot 2
+ phys_chan_config TCH/F
+ e1 line 2 timeslot 2 sub-slot 2
+ timeslot 3
+ phys_chan_config TCH/F
+ e1 line 2 timeslot 2 sub-slot 3
+ timeslot 4
+ phys_chan_config TCH/F
+ e1 line 2 timeslot 3 sub-slot 0
+ timeslot 5
+ phys_chan_config TCH/F
+ e1 line 2 timeslot 3 sub-slot 1
+ timeslot 6
+ phys_chan_config TCH/F
+ e1 line 2 timeslot 3 sub-slot 2
+ timeslot 7
+ phys_chan_config TCH/F
+ e1 line 2 timeslot 3 sub-slot 3
+
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-fsm.dot b/doc/lchan-fsm.dot
index b726b0c87..82ef922f3 100644
--- a/doc/lchan-fsm.dot
+++ b/doc/lchan-fsm.dot
@@ -32,10 +32,26 @@ labelloc=t; label="lchan FSM"
WAIT_TS_READY -> UNUSED [label="error/timeout",style=dashed,constraint=false]
{WAIT_ACTIV_ACK,WAIT_RF_RELEASE_ACK} -> BORKEN [label="error/timeout",style=dashed]
BORKEN -> WAIT_AFTER_ERROR [label="late RF Release ACK"]
+ BORKEN -> WAIT_RF_RELEASE_ACK [label="late Activation ACK"]
WAIT_RLL_RTP_ESTABLISH -> WAIT_RLL_RTP_RELEASED [label=error,style=dashed]
WAIT_ACTIV_ACK -> rtp [label="LCHAN_RTP_EV_LCHAN_READY",style=dotted]
rtp -> WAIT_RLL_RTP_ESTABLISH [label="LCHAN_EV_RTP_READY",style=dotted]
rtp -> ESTABLISHED [label="LCHAN_EV_RTP_RELEASED",style=dotted]
+ ESTABLISHED -> WAIT_RR_CHAN_MODE_MODIFY_ACK [label="LCHAN_EV_REQUEST_MODE_MODIFY"]
+ WAIT_RR_CHAN_MODE_MODIFY_ACK -> WAIT_RSL_CHAN_MODE_MODIFY_ACK [label="LCHAN_EV_RR_CHAN_MODE_MODIFY_ACK"]
+ WAIT_RSL_CHAN_MODE_MODIFY_ACK -> WAIT_RLL_RTP_ESTABLISH [label="LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK\nwhen adding RTP"]
+ WAIT_RSL_CHAN_MODE_MODIFY_ACK -> ESTABLISHED [label="LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK\nno change to RTP"]
+ WAIT_RR_CHAN_MODE_MODIFY_ACK -> BORKEN [label="error/timeout",style=dashed]
+ WAIT_RSL_CHAN_MODE_MODIFY_ACK -> BORKEN [label="error/timeout",style=dashed]
+
+ BORKEN -> RECOVER_WAIT_ACTIV_ACK [label="X28"]
+ RECOVER_WAIT_ACTIV_ACK -> BORKEN [label="error/timeout",style=dashed]
+
+ RECOVER_WAIT_ACTIV_ACK -> UNUSED [label="rx ACK"]
+ RECOVER_WAIT_ACTIV_ACK -> RECOVER_WAIT_RF_RELEASE_ACK [label="rx NACK"]
+
+ RECOVER_WAIT_RF_RELEASE_ACK -> UNUSED [label="rx ACK"]
+ RECOVER_WAIT_RF_RELEASE_ACK -> BORKEN [label="error/timeout",style=dashed]
}
diff --git a/doc/lchan-rtp-fsm.dot b/doc/lchan-rtp-fsm.dot
index d5df643b5..4c2dd31bf 100644
--- a/doc/lchan-rtp-fsm.dot
+++ b/doc/lchan-rtp-fsm.dot
@@ -1,7 +1,7 @@
digraph G {
rankdir=TB
labelloc=t; label="lchan RTP FSM"
-
+
lchan [label="lchan\nFSM",shape=box3d]
lchan2 [label="lchan\nFSM",shape=box3d]
ho_as [label="Handover or Assignment FSM",shape=box3d]
@@ -23,14 +23,15 @@ labelloc=t; label="lchan RTP FSM"
lchan -> WAIT_LCHAN_READY [label="LCHAN_RTP_EV_LCHAN_READY",style=dashed]
WAIT_LCHAN_READY -> WAIT_IPACC_CRCX_ACK [label="IPACC BTS"]
WAIT_LCHAN_READY -> WAIT_READY_TO_SWITCH_RTP
- WAIT_IPACC_CRCX_ACK -> WAIT_IPACC_MDCX_ACK
- WAIT_IPACC_MDCX_ACK -> WAIT_READY_TO_SWITCH_RTP
+ WAIT_IPACC_CRCX_ACK -> WAIT_READY_TO_SWITCH_RTP
invisible -> ho [label="HO DETECT",style=dashed]
ho -> WAIT_READY_TO_SWITCH_RTP [label="LCHAN_RTP_EV_READY_TO_SWITCH",style=dashed]
WAIT_READY_TO_SWITCH_RTP -> WAIT_MGW_ENDPOINT_CONFIGURED
WAIT_MGW_ENDPOINT_CONFIGURED -> mgwep [label="MDCX",style=dashed]
mgwep -> WAIT_MGW_ENDPOINT_CONFIGURED [label="LCHAN_RTP_EV_\nMGW_ENDPOINT_\nCONFIGURED",style=dashed]
+ WAIT_MGW_ENDPOINT_CONFIGURED -> WAIT_IPACC_MDCX_ACK [label="IPACC BTS"]
WAIT_MGW_ENDPOINT_CONFIGURED -> RTP_READY
+ WAIT_IPACC_MDCX_ACK -> RTP_READY
RTP_READY -> lchan2 [label="LCHAN_EV_\nRTP_READY",style=dashed]
RTP_READY -> RTP_ESTABLISHED
lchan2 -> RTP_ESTABLISHED [label="LCHAN_RTP_EV_\nRELEASE",style=dashed]
diff --git a/doc/lchan.msc b/doc/lchan.msc
index e2caa4875..124037e71 100644
--- a/doc/lchan.msc
+++ b/doc/lchan.msc
@@ -15,7 +15,7 @@ msc {
ts note ts [label="A dyn TS may be in PDCH mode and will asynchronously switch off PDCH first. A
non-dynamic TS is ready immediately."];
|||;
- --- [label="IF requires_voice_stream"];
+ --- [label="IF requires_rtp_stream"];
lchan -> rtp [label="lchan_rtp_fsm_start()"];
rtp abox rtp [label="allocate\n LCHAN_RTP_ST_\nWAIT_MGW_ENDPOINT_\nAVAILABLE"];
--- [label="IF no endpoint-CI yet"];
@@ -29,16 +29,16 @@ msc {
rtp note mgwep [label="The CRCX OK has assigned us a new endpoint CI number"];
rtp abox rtp [label="LCHAN_RTP_ST_WAIT_LCHAN_READY"];
--- [label="END: no endpoint-CI yet"];
- --- [label="END: requires_voice_stream"];
+ --- [label="END: requires_rtp_stream"];
|||;
...;
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"];
@@ -60,7 +60,7 @@ msc {
lchan abox lchan [label="LCHAN_ST_WAIT_\nRLL_RTP_ESTABLISH\nT3101"];
|||;
|||;
- --- [label="IF requires_voice_stream"];
+ --- [label="IF requires_rtp_stream"];
lchan -> rtp [label="LCHAN_RTP_EV_LCHAN_READY"];
|||;
--- [label="IF ip.access style BTS"];
@@ -68,10 +68,6 @@ msc {
ms <= rtp [label="IPACC CRCX"];
...;
ms => rtp [label="IPACC CRCX ACK (BTS RTP port info)"];
- rtp abox rtp [label="LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK"];
- ms <= rtp [label="IPACC MDCX (MGW RTP port info)"];
- ...;
- ms => rtp [label="IPACC MDCX ACK"];
--- [label="END ip.access style BTS"];
|||;
rtp box rtp [label="lchan_rtp_fsm_switch_rtp()"];
@@ -91,13 +87,19 @@ msc {
...;
mgwep rbox mgwep [label="MGCP: MDCX OK"];
rtp <- mgwep [label="LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED"];
+ --- [label="IF ip.access style BTS"];
+ rtp abox rtp [label="LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK"];
+ ms <= rtp [label="IPACC MDCX (MGW RTP port info)"];
+ ...;
+ ms => rtp [label="IPACC MDCX ACK"];
+ --- [label="END ip.access style BTS"];
rtp abox rtp [label="LCHAN_RTP_ST_READY"];
lchan <- rtp [label="LCHAN_EV_RTP_READY"];
rtp note rtp [label="RTP FSM stays ready for Rollback until final establish event"];
...;
lchan -> rtp [label="LCHAN_RTP_EV_ESTABLISHED\nvia gscon_change_primary_lchan()"];
rtp abox rtp [label="LCHAN_RTP_ST_\nESTABLISHED"];
- --- [label="END: requires_voice_stream"];
+ --- [label="END: requires_rtp_stream"];
|||;
|||;
@@ -105,16 +107,16 @@ 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"];
...;
- --- [label="IF requires_voice_stream"];
+ --- [label="IF requires_rtp_stream"];
lchan rbox lchan [label="Assignment or Handover FSM:"];
lchan -> mgwep [label="CRCX/MDCX to-MSC"];
...;
@@ -123,7 +125,27 @@ msc {
lchan -> rtp [label="LCHAN_RTP_EV_ESTABLISHED"];
rtp abox rtp [label="LCHAN_RTP_ST_\nESTABLISHED"];
rtp box rtp [label="Forget any Rollback info"];
- --- [label="END: requires_voice_stream"];
+ --- [label="END: requires_rtp_stream"];
+
+ ...;
+ ...;
+ ...;
+
+ ms rbox mgwep [label="On Mode Modify (e.g. change a TCH lchan from signalling to voice)"];
+ lchan abox lchan [label="LCHAN_ST_\nWAIT_RR_CHAN_\nMODE_MODIFY_ACK"];
+ ms <= lchan [label="RR Chan Mode Modif"];
+ ...;
+ ms => lchan [label="RR Chan Mode Modif Ack"];
+ lchan abox lchan [label="LCHAN_ST_\nWAIT_RSL_CHAN_\nMODE_MODIFY_ACK"];
+ ms <= lchan [label="RSL MT Mode Modify Req"];
+ ...;
+ ms => lchan [label="RSL MT Mode Modify Ack"];
+ --- [label="IF adding RTP stream"];
+ lchan abox lchan [label="LCHAN_ST_WAIT_\nRLL_RTP_ESTABLISH\nT3101"];
+ lchan rbox rtp [label="See above at 'LCHAN_RTP_EV_LCHAN_READY'"];
+ --- [label="IF not adding RTP stream"];
+ lchan abox lchan [label="LCHAN_ST_\nESTABLISHED"];
+ --- [label="END: whether adding voice stream"];
...;
...;
@@ -167,7 +189,7 @@ msc {
ms => lchan [label="RSL RF Channel Release Ack"];
|||;
--- [label="IF release_in_error"];
- lchan abox lchan [label="LCHAN_ST_WAIT_\nAFTER_ERROR\n(timeout: T3111+2 s, T993111)"];
+ lchan abox lchan [label="LCHAN_ST_WAIT_\nAFTER_ERROR\n(timeout: T3111+2 s, X3111)"];
...;
lchan box lchan [label="timer expires"];
--- [label="END: release_in_error"];
@@ -180,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_fsm_bsc.dot b/doc/location_services_fsm_bsc.dot
new file mode 100644
index 000000000..f369c6347
--- /dev/null
+++ b/doc/location_services_fsm_bsc.dot
@@ -0,0 +1,40 @@
+digraph G {
+rankdir=TB
+labelloc=t; label="Location Services FSMs in OsmoBSC"
+
+ MSC [label="MSC\nvia\nA interface",shape=box3d]
+ SMLC [label="SMLC\nvia\nLb interface",shape=box3d]
+ SMLC2 [label="SMLC\nvia\nLb interface",shape=box3d]
+ Paging [shape=box3d]
+
+ subgraph cluster_LCS_LOC_REQ_FSM {
+ label="lcs_loc_req_fsm"
+ INIT -> WAIT_LOCATION_RESPONSE
+ WAIT_LOCATION_RESPONSE -> BSSLAP_TA_REQ_ONGOING -> GOT_LOCATION_RESPONSE -> terminate
+ WAIT_LOCATION_RESPONSE -> GOT_LOCATION_RESPONSE
+ terminate [shape=octagon]
+ }
+
+ MSC -> INIT [label="BSSAP Perform\nLocation Request",style=dashed]
+ WAIT_LOCATION_RESPONSE -> SMLC [label="BSSMAP-LE Perform\nLocation Req",style=dashed]
+ SMLC -> WAIT_LOCATION_RESPONSE [label="BSSMAP-LE Perform\nLocation Resp",style=dashed]
+ GOT_LOCATION_RESPONSE -> MSC [label="BSSAP Perform\nLocation Response",style=dashed]
+
+ subgraph cluster_LCS_TA_REQ_FSM {
+ label="lcs_ta_req_fsm"
+ INIT2 [label="INIT"]
+ INIT2 -> WAIT_TA [label="MS idle"]
+ WAIT_TA -> GOT_TA
+ INIT2 -> GOT_TA [label="MS active"]
+ GOT_TA -> terminate2
+ terminate2 [label="terminate",shape=octagon]
+ }
+
+ SMLC2 -> INIT2 [label="TA Request",style=dashed]
+ WAIT_TA -> Paging [label="launch Paging",style=dashed]
+ Paging -> WAIT_TA [label="EV_TA",style=dashed]
+ GOT_TA -> SMLC2 [label="TA Response",style=dashed]
+
+ WAIT_TA -> BSSLAP_TA_REQ_ONGOING [label="EV_TA_REQ_START",style=dashed]
+ terminate2 -> BSSLAP_TA_REQ_ONGOING [label="EV_TA_REQ_END",style=dashed,constraint=false]
+}
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
new file mode 100644
index 000000000..bdc3d2bb1
--- /dev/null
+++ b/doc/manuals/chapters/bsc.adoc
@@ -0,0 +1,113 @@
+== BSC level configuration
+
+=== Hand-over
+
+==== Hand-over in GSM
+
+Hand-over is the process of changing a MS with a currently active
+dedicated channel from one BTS to another BTS. As opposed to idle mode,
+where the MS autonomously performs cell re-selection, in dedicated mode
+this happens under network control.
+
+In order to determine when to perform hand-over, and to which cells, the
+network requests the MS to perform measurements on a list of neighbor
+cell channels, which the MS then reports back to the network in the form
+of GSM RR 'Measurement Result' messages. Those messages contain the
+downlink measurements as determined by the MS.
+
+Furthermore, the BTS also performs measurements on the uplink, and
+communicates those by means of RSL to the BSC.
+
+The hand-over decision is made by an algorithm that processes those
+measurement results and determines when to perform the hand-over.
+
+==== Configuration of hand-over in OsmoBSC
+
+OsmoBSC only support so-called intra-BSC hand-over, where the hand-over is
+performed between two BTSs within the same BSC.
+
+Hand-over is enabled and configured by the use of a set of `handover`
+commands. Using those, you can tune the key parameters of the hand-over
+algorithm and adapt it to your specific environment.
+
+.Example handover configuration snippet
+----
+ handover 1 <1>
+ handover window rxlev averaging 10 <2>
+ handover window rxqual averaging 1 <3>
+ handover window rxlev neighbor averaging 10 <4>
+ handover power budget interval 6 <5>
+ handover power budget hysteresis 3 <6>
+ handover maximum distance 9999 <7>
+----
+<1> Enable hand-over
+<2> Set the RxLev averaging window for the serving cell to 10 measurements
+<3> Set the RxQual averaging window for the serving cell to 1
+ measurement (no window)
+<4> Set the RxLev averaging for neighbor cells to 10 measurements
+<5> Check for the conditions of a power budget hand-over every 6 SACCH
+ frames
+<6> A neighbor cell must be at least 3 dB stronger than the serving cell
+ to be considered a candidate for hand-over
+<7> Perform a maximum distance hand-over if TA is larger 9999 (i.e. never)
+
+//TODO: Move all to BSC node
+
+=== Timer Configuration
+
+The GSM specification specifies a variety of timers both on the network
+as well as on the mobile station side.
+
+Those timers can be configured using the `timer tXXXX` command.
+
+.Configurable Timers
+|===
+|node|timer|default|description
+|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|10|?
+|===
+
+//TODO: split between BSC and MSC timers
+
+=== Discontinuous Transmission (DTX)
+
+GSM provides a full-duplex voice call service. However, in any
+civilized communication between human beings, only one of the
+participants is speaking at any given point in time. This means that
+most of the time, one of the two directions of the radio link is
+transmitting so-called 'silence frames'.
+
+During such periods of quiescence in one of the two directions, it is
+possible to suppress transmission of most of the radio bursts, as there
+is no voice signal to transport. GSM calls this feature 'Discontinuous
+Transmission'. It exists separately for uplink (DTXu) and downlink
+(DTXd).
+
+Downlink DTX is only permitted on non-primary transceivers (!= TRX0), as
+TRX0 must always transmit at constant output power to ensure it is
+detected during cell selection.
+
+Uplink DTX is 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.
+
+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
+uplink and/or downlink DTX by means of A-bis OML.
+
+//TODO: Test/implement, at least for uplink
+//TODO: Move to BSC
diff --git a/doc/manuals/chapters/bts-examples.adoc b/doc/manuals/chapters/bts-examples.adoc
index 58cb3ab17..d866f4f99 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,250 @@ 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_e1_cfg]]
+=== 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. See also <<cfg_ericsson_rbs_is>>
+
+<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.
+
+=== Example configuration for OsmoBSC with Ericsson RBS E1 BTS and EGPRS
+
+The following example illustrates the usage of Ericsson RBS2000/RBS6000 BTSs.
+This classic E1 BTS has no built in PCU and therefore requires the configuration
+of a BSC co-located OsmoPCU (see also: <<cfg_bsc_co_located_pcu>>).
+
+It should also be noted that the Ericsson RBS2000/RBS6000 series is the first
+BTS of this type to be supported by OsmoBTS and OsmoPCU. The implementation has
+been made possible through funding by the NLnet Foundation.
+
+Ericsson RBS2000/RBS6000 BTSs feature two GPRS modes. A 16kbps GPRS mode where
+only CS1 and CS2 are supported and an EGPRS mode where MCS1 to MCS9 are
+supported. OsmoPCU offers support for both modes but since the 16kbps mode only
+supports classic GPRS with CS1 and CS2 it is more of experimental interest
+and shall not be discussed further. The following example will describe how
+to configure the 64kbps mode with EGPRS.
+
+In the following example we also expect that the user is already familliar
+with the E1 configuration example above (see also: <<example_e1_cfg>>)
+
+.OsmoBSC configured for single-TRX E1 Ericsson DUG20 with EGPRS
+====
+----
+e1_input
+ 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
+ pcu-socket /tmp/pcu_bts <1>
+ bts 0
+ type rbs2000
+ band GSM900
+ om2000 version-limit oml gen 12 rev 10
+ 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
+ oml e1 tei 62
+ gprs mode egprs <2>
+ gprs routing area 0
+ gprs network-control-order nc0
+ gprs cell bvci 2
+ gprs nsei 101
+ gprs nsvc 0 nsvci 101
+ gprs nsvc 0 local udp port 23100
+ gprs nsvc 0 remote udp port 23000
+ gprs nsvc 0 remote ip 127.0.0.1
+ is-connection-list add 4 712 36 <3>
+ trx 0
+ rf_locked 0
+ arfcn 123
+ nominal power 42
+ max_power_red 12
+ rsl e1 line 0 timeslot 1 sub-slot full
+ rsl e1 tei 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ hopping enabled 0
+ e1 line 0 timeslot 1 sub-slot full
+ timeslot 1
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 3 sub-slot full <4>
+ timeslot 2
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 4 sub-slot full
+ timeslot 3
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 5 sub-slot full
+ timeslot 4
+ phys_chan_config TCH/F_TCH/H_SDCCH8_PDCH <5>
+ hopping enabled 0
+ e1 line 0 timeslot 6 sub-slot full
+ timeslot 5
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 7 sub-slot full
+ timeslot 6
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 8 sub-slot full
+ timeslot 7
+ phys_chan_config TCH/F
+ hopping enabled 0
+ e1 line 0 timeslot 9 sub-slot full
+----
+====
+
+<1> This configures the PCU socket path (see also: <<cfg_bsc_co_located_pcu>>)
+
+<2> This configures the general GPRS parameters. The configuration is no
+different from BTS with built-in PCU.
+
+<3> The Ericsson RBS2000/RBS6000 series 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. Since 16kbps subslots cannot
+supply the bandwidth required for EGPRS the IS must be configured to connect
+the 64kbps interface of the TRU to the external E1 line. For a more detailed
+description of the IS see <<cfg_ericsson_rbs_is>>.
+
+<4> Since we are using the 64kbps TRU interface we must configure a full E1
+timeslot per air interface time slot. For Speech this will have no effect on
+the TRAU frame format. The only difference is that always the first 16kbps
+subslot of the assigned E1 timeslot is used. OsmoMGW will be instructed
+accordingly by OsmoBSC, so no re-configuration of OsmoMGW is required.
+
+<5> In this example we will use air interface TS 4 as PDCH. As mentioned
+earlier Ericsson RBS2000/RBS6000 supports the 'DYNAMIC/OSMOCOM' timeslot model.
+PDCH timeslots must be configured as dynamic timeslots. It is not possible to
+configure static PDCHs. Therefore the phys_chan_config must be set to
+TCH/F_TCH/H_SDCCH8_PDCH in order to use the air interface timeslot as PDCH.
+
+NOTE: As of March 2023 the BSC co-located PCU support for Ericsson RBS was
+tested only with a single BTS. Even though OsmoBSC and OsmoPCU should be able
+to handle multiple BTS, unexpected bahviour should be taken into account.
+
+=== 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
new file mode 100644
index 000000000..5371745f2
--- /dev/null
+++ b/doc/manuals/chapters/bts.adoc
@@ -0,0 +1,785 @@
+[[bts]]
+== Reviewing and Provisioning BTS configuration
+
+The main functionality of the BSC component is to manage BTSs. As such,
+provisioning BTSs within the BSC is one of the most common tasks during
+BSC operation. Just like about anything else in OsmoBSC, they are
+configured using the VTY.
+
+BTSs are internally numbered with integer numbers starting from "0" for
+the first BTS. BTS numbers have to be contiguous, so you cannot
+configure 0,1,2 and then 5.
+
+
+=== Reviewing current BTS status and configuration
+
+In order to view the status and properties of a BTS, you can issue the
+`show bts` command. If used without any BTS number, it will display
+information about all provisioned BTS numbers.
+
+----
+OsmoBSC> show bts 0
+BTS 0 is of nanobts type in band DCS1800, has CI 0 LAC 1, BSIC 63, TSC 7 and 1 TRX
+Description: (null)
+MS Max power: 15 dBm
+Minimum Rx Level for Access: -110 dBm
+Cell Reselection Hysteresis: 4 dBm
+RACH TX-Integer: 9
+RACH Max transmissions: 7
+System Information present: 0x0000007e, static: 0x00000000
+ Unit ID: 200/0/0, OML Stream ID 0xff
+ NM State: Oper 'Enabled', Admin 2, Avail 'OK'
+ Site Mgr NM State: Oper 'Enabled', Admin 0, Avail 'OK'
+ Paging: 0 pending requests, 0 free slots
+ OML Link state: connected.
+ Current Channel Load:
+ TCH/F: 0% (0/5)
+ SDCCH8: 0% (0/8)
+----
+
+You can also review the status of the TRXs configured within the BTSs of
+this BSC by using `show trx`:
+
+----
+OsmoBSC> show trx 0 0
+TRX 0 of BTS 0 is on ARFCN 871
+Description: (null)
+ RF Nominal Power: 23 dBm, reduced by 0 dB, resulting BS power: 23 dBm
+ NM State: Oper 'Enabled', Admin 2, Avail 'OK'
+ Baseband Transceiver NM State: Oper 'Enabled', Admin 2, Avail 'OK'
+ IPA Abis/IP stream ID: 0x00
+----
+
+The output can be restricted to the TRXs of one specified BTS number
+(`show trx 0`) or even that of a single specified TRX within a
+specified BTS (`show trx 0 0`).
+
+Furthermore, information on the individual timeslots can be shown by
+means of `show timeslot`. The output can be restricted to the
+timeslots of a single BTS (`show timeslot 0`) or that of a single
+TRX (`show timeslot 0 0`). Finally, you can restrict the output to
+a single timeslot by specifying the BTS, TRX and TS numbers (`show
+timeslot 0 0 4`).
+
+----
+OsmoBSC> show timeslot 0 0 0
+BTS 0, TRX 0, Timeslot 0, phys cfg CCCH, TSC 7
+ NM State: Oper 'Enabled', Admin 2, Avail 'OK'
+OsmoBSC> show timeslot 0 0 1
+BTS 0, TRX 0, Timeslot 1, phys cfg SDCCH8, TSC 7
+ NM State: Oper 'Enabled', Admin 2, Avail 'OK'
+----
+
+
+=== Provisioning a new BTS
+
+In order to provision BTSs, you have to enter the BTS config node of the
+VTY. In order to configure BTS 0, you can issue the following sequence
+of commands:
+
+----
+OsmoBSC> enable
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)#
+----
+
+At this point, you have a plethora of commands, in fact an entire
+hierarchy of commands to configure all aspects of the BTS, as well as
+each of its TRX and each timeslot within each TRX. For a full
+reference, please consult the telnet VTY integrated help or the respective
+chapter in the VTY reference.
+
+BTS configuration depends quite a bit on the specific BTS vendor and
+model. The section below provides just one possible example for the
+case of a sysmoBTS.
+
+Note that from the `configure terminal` command onwards, the telnet VTY
+commands above are identical to configuration file settings, for details see
+<<vty>>.
+
+Starting with `network` as above, your complete sysmoBTS configuration may look
+like this:
+
+----
+network
+ bts 0
+ type osmo-bts
+ band DCS1800
+ description The new BTS in Baikonur
+ location_area_code 0x0926
+ cell_identity 5
+ base_station_id_code 63
+ ip.access unit_id 8888 0
+ ms max power 40
+ trx 0
+ arfcn 871
+ nominal power 23
+ max_power_red 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ timeslot 1
+ phys_chan_config TCH/F
+ timeslot 2
+ phys_chan_config TCH/F
+ timeslot 3
+ phys_chan_config TCH/F
+ timeslot 4
+ phys_chan_config TCH/F
+ timeslot 5
+ phys_chan_config TCH/F
+ timeslot 6
+ phys_chan_config TCH/F
+ timeslot 7
+ phys_chan_config PDCH
+----
+
+
+=== System Information configuration
+
+A GSM BTS periodically transmits a series of 'SYSTEM INFORMATION'
+messages to mobile stations, both via the BCCH in idle mode, was well as
+via the SACCH in dedicated mode. There are many different types of such
+messages. For their detailed contents and encoding, please see _3GPP TS
+24.008_ <<3gpp-ts-24-008>>.
+
+For each of the 'SYSTEM INFORMATION' message types, you can configure to
+have the BSC generate it automatically ('computed'), or you can specify
+the respective binary message as a string of hexadecimal digits.
+
+The default configuration is to compute all (required) 'SYSTEM
+INFORMATION' messages automatically.
+
+Please see the _OsmoBSC VTY Reference Manual_ <<vty-ref-osmobsc>> for
+further information, particularly on the following commands:
+
+* `system-information (1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter) mode (static|computed)`
+* `system-information (1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter) static HEXSTRING`
+
+
+=== Neighbor List configuration
+
+Every BTS sends a list of ARFCNs of neighbor cells
+. within its 'SYSTEM INFORMATION 2' (and 2bis/2ter) messages on the BCCH
+. within its 'SYSTEM INFORMATION 5' messages on SACCH in dedicated mode
+
+For every BTS config node in the VTY, you can specify the behavior of
+the neighbor list using the `neighbor list mode` VTY command:
+
+automatic::
+ Automatically generate a list of neighbor cells using all other
+ BTSs configured in the VTY
+manual::
+ Manually specify the neighbor list by means of `neighbor-list
+(add|del) arfcn <0-1023>` commands, having identical neighbor lists on
+BCCH (SI2) and SACCH (SI5)
+
+manual-si5::
+ Manually specify the neighbor list by means of `neighbor-list
+(add|del) arfcn <0-1023>` for BCCH (SI2) and a separate neighbor list by
+means of `si5 neighbor-list (add|del) arfcn <0-1023>` for SACCH (SI5).
+
+
+[[config_gprs_pcu_pars]]
+=== Configuring GPRS PCU parameters of a BTS
+
+In the case of BTS models using Abis/IP (IPA), the GPRS PCU is located
+inside the BTS. The BTS then establishes a Gb connection to the SGSN.
+
+All the BTS-internal PCU configuration is performed via A-bis OML by
+means of configuring the 'CELL', 'NSVC' (NS Virtual Connection and 'NSE'
+(NS Entity).
+
+There is one 'CELL' node and one 'NSE' node, but there are two 'NSVC'
+nodes. At the time of this writing, only the NSVC 0 is supported by
+OsmoBTS, while both NSVC are supported by the ip.access nanoBTS.
+
+The respective VTY configuration parameters are described below. They
+all exist beneath each BTS VTY config node.
+
+But let's first start with a small example
+
+.Example configuration of GPRS PCU parameters at VTY BTS node
+----
+OsmoBSC(config-net-bts)# gprs mode gprs
+OsmoBSC(config-net-bts)# gprs routing area 1
+OsmoBSC(config-net-bts)# gprs cell bvci 1234
+OsmoBSC(config-net-bts)# gprs nsei 1234
+OsmoBSC(config-net-bts)# gprs nsvc 0 nsvci 1234
+OsmoBSC(config-net-bts)# gprs nsvc 0 local udp port 23000
+OsmoBSC(config-net-bts)# gprs nsvc 0 remote udp port 23000
+OsmoBSC(config-net-bts)# gprs nsvc 0 remote ip 192.168.100.239
+----
+
+
+=== More explanation about the PCU config parameters
+
+//FIXME: should this go into VTY additions?
+
+==== `gprs mode (none|gprs|egprs)`
+
+This command determines if GPRS (or EGPRS) services are to be enabled in
+this cell at all.
+
+
+==== `gprs cell bvci <2-65535>`
+
+Configures the 'BSSGP Virtual Circuit Identifier'. It must be unique
+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.
+
+
+==== `gprs nsei <0-65535>`
+
+Configures the 'NS Entity Identifier'. It must be unique between all NS
+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.
+
+
+==== `gprs nsvc <0-1> nsvci <0-65535>`
+
+Configures the 'NS Virtual Connection Identifier'. It must be unique
+between all NS virtual connections to one SGSN.
+
+NOTE: It is up to the system administrator to ensure all PCUs are
+allocated an unique nsvci. OsmoBSC will not ensure this policy.
+
+
+==== `gprs nsvc <0-1> local udp port <0-65535>`
+
+Configures the local (PCU side) UDP port for the NS-over-UDP link.
+
+
+==== `gprs nsvc <0-1> remote udp port <0-65535>`
+
+Configures the remote (SGSN side) UDP port for the NS-over-UDP link.
+
+
+==== `gprs nsvc <0-1> remote ip A.B.C.D`
+
+Configures the remote (SGSN side) UDP port for the NS-over-UDP link.
+
+
+==== `gprs ns timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries)` <0-255>
+
+Configures the various GPRS NS related timers. Please check the GPRS NS
+specification for the detailed meaning of those timers.
+
+
+=== Dynamic Timeslot Configuration (TCH / PDCH)
+
+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
+MSC requests a logical channel of a given TCH kind from the BSC. The BSC
+assigns such a channel from a BTS' TRX's timeslot of its choice. The knowledge
+that a given timeslot is dynamic exists only on the BSC level. When the MSC
+asks for a logical channel, the BSC may switch off PDCH on a dynamic timeslot
+and then assign a logical TCH channel on it. Hence, though compatibility with
+the BTS needs to be ensured, any MSC is compatible with dynamic timeslots by
+definition.
+
+OsmoBSC supports two kinds of dynamic timeslot handling, configured via the
+`network` / `bts` / `trx` / `timeslot` / `phys_chan_config` configuration. Not
+all BTS models support dynamic channels.
+
+[[dyn_ts_compat]]
+.Dynamic timeslot support by various BTS models
+[cols="50%,25%,25%"]
+|===
+| |`DYNAMIC/OSMOCOM` |`DYNAMIC/IPACCESS`
+|ip.access nanoBTS |- |supported
+|Ericsson RBS |supported |-
+|sysmoBTS using _osmo-bts-sysmo_ |supported |supported
+|various SDR platforms using _osmo-bts-trx_ |supported |supported
+|Nutaq Litecell 1.5 using _osmo-bts-litecell15_ |supported |supported
+|Octasic OctBTS using _osmo-bts-octphy_ | supported | supported
+|===
+
+The _OsmoBTS Abis Protocol Specification_ <<osmobts-abis-spec>> describes the
+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 (DYNAMIC/OSMOCOM)
+
+`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>>.
+
+In the lack of transcoding capabilities, this timeslot type may cause
+mismatching codecs to be selected for two parties of the same call, which would
+cause call routing to fail ("`Cannot patch through call with different channel
+types: local = TCH_F, remote = TCH_H`"). A workaround is to disable TCH/F on
+this timeslot type, i.e. to allow only TCH/H. To disable TCH/F on Osmocom
+style dynamic timeslots, use a configuration of
+
+----
+network
+ dyn_ts_allow_tch_f 0
+----
+
+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 (DYNAMIC/IPACCESS)
+
+`DYNAMIC/IPACCESS` is an alias for `TCH/F_PDCH`.
+
+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>>.
+
+==== Avoid PDCH Exhaustion
+
+To avoid disrupting GPRS, configure at least one timeslot as dedicated PDCH.
+With only dynamic timeslots, a given number of voice calls would convert all
+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
+five Osmocom style dynamic timeslots and one dedicated PDCH may look like this:
+
+----
+network
+ bts 0
+ trx 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ timeslot 1
+ phys_chan_config SDCCH8
+ timeslot 2
+ phys_chan_config DYNAMIC/OSMOCOM
+ timeslot 3
+ phys_chan_config DYNAMIC/OSMOCOM
+ timeslot 4
+ phys_chan_config DYNAMIC/OSMOCOM
+ timeslot 5
+ phys_chan_config DYNAMIC/OSMOCOM
+ timeslot 6
+ phys_chan_config DYNAMIC/OSMOCOM
+ timeslot 7
+ phys_chan_config PDCH
+----
+
+With the ip.access nanoBTS, only `DYNAMIC/IPACCESS` dynamic timeslots are supported,
+and hence a nanoBTS configuration may look like this:
+
+----
+network
+ bts 0
+ trx 0
+ timeslot 0
+ phys_chan_config CCCH+SDCCH4
+ timeslot 1
+ phys_chan_config SDCCH8
+ timeslot 2
+ phys_chan_config DYNAMIC/IPACCESS
+ timeslot 3
+ phys_chan_config DYNAMIC/IPACCESS
+ timeslot 4
+ phys_chan_config DYNAMIC/IPACCESS
+ timeslot 5
+ phys_chan_config DYNAMIC/IPACCESS
+ timeslot 6
+ phys_chan_config DYNAMIC/IPACCESS
+ timeslot 7
+ phys_chan_config PDCH
+----
+
+=== Tuning Access to the BTS
+
+OsmoBSC offers several configuration options to fine-tune access to the BTS.
+It can allow only a portion of the subscribers access to the network.
+This can also be used to ramp up access to the network on startup by slowly
+letting in more and more subscribers. This is especially useful for isolated
+cells with a huge number of subscribers.
+
+Other options control the behaviour of the MS when it needs to access the
+random access channel before a dedicated channel is established.
+
+If the BTS is connected to the BSC via a high-latency connection the MS should
+wait longer for an answer to a RACH request. If it does not the network will
+have to deal with an increased load due to duplicate RACH requests. However,
+in order to minimize the delay when a RACH request or response gets lost the
+MS should not wait too long before retransmitting.
+
+==== Access Control Class Load Management
+
+Every SIM card is member of one of the ten regular ACCs (0-9). Access to the BTS
+can be restricted to SIMs that are members of certain ACCs.
+
+Furthermore, high priority users (such as PLMN staff, public or emergency
+services, etc.) may be members of one or more ACCs from 11-15.
+
+Since the ACCs 0-9 are distributed uniformly across all SIMs, for instance
+allowing only ACCs 0-4 to connect to the BTS should reduce its load by 50% at
+the expense of not serving 50% of the subscribers.
+
+The default is to allow all ACCs to connect.
+
+OsmoBSC supports several levels of ACC management to allow or restrict access
+either permanently or temporarily on each BTS.
+
+The first level of management consists of an access list to flag specific ACCs
+as permanently barred (the list can be updated at any time through VTY as seen
+below). As indicated above, the default is to allow all ACCs (0-15).
+
+.Example: Restrict permanent access to the BTS by ACC
+----
+network
+ bts 0
+ rach access-control-class 1 barred <1>
+ rach access-control-class 9 allowed <2>
+----
+<1> Disallow SIMs with access-class 1 from connecting to the BTS
+<2> Permit SIMs with access-class 9 to connect to the BTS.
+
+On really crowded areas, a BTS may struggle to service all mobile stations
+willing to use it, and which may end up in collapse. In this kind of scenarios
+it is a good idea to temporarily further restrict the amount of allowed ACCs
+(hence restrict the amount of subscribers allowed to reach the BTS).
+However, doing so on a permanent basis would be unfair to subscribers from
+barred ACCs. Hence, OsmoBSC can be configured to temporarily generate ACC
+subsets of the permanent set presented above, and rotate them over time to allow
+fair access to all subscribers. This feature is only aimed at ACCs 0-9,
+since ACCs 11-15 are considered high priority and hence are always configured
+based on permanent list policy.
+
+.Example: Configure rotative access to the BTS
+----
+network
+ bts 0
+ access-control-rotate 3 <1>
+ access-control-rotate-quantum 20 <2>
+----
+<1> Only allow up to 3 concurrent allowed ACCs from the permanent list
+<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 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 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-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> 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:
+
+.Example: Full ACC Load Management config setup
+----
+bts 0
+ rach access-control-class 5 barred <1>
+ rach access-control-class 6 barred
+ rach access-control-class 7 barred
+ rach access-control-class 8 barred
+ rach access-control-class 9 barred
+ access-control-class-rotate 3 <2>
+ 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 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> 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
+
+The following parameters allow control over how the MS can access the random
+access channel (RACH). It is possible to set a minimum receive level under
+which the MS will not even attempt to access the network.
+
+The RACH is a shared channel which means multiple MS can choose to send a
+request at the same time. To minimize the risk of a collision each MS will
+choose a random number of RACH slots to wait before trying to send a RACH
+request.
+
+On very busy networks the range this number is chosen from should be
+high to avoid collisions, but a lower range reduces the overall delay when
+trying to establish a channel.
+
+The option `rach tx integer N` controls the range from which this number X
+is chosen. It is `0 <= X < max(8,N)`.
+
+After sending a RACH request the MS will wait a random amount of slots before
+retransmitting its RACH request. The range it will wait is also determined by
+the option `rach tx integer N`, but calculating it is not so straightforward.
+It is defined as `S <= X < S+N` where `S` is determined from a table.
+
+In particular `S` is lowest when `N` is one of 3, 8, 14 or 50 and highest when
+`N` is 7, 12 or 32.
+
+For more information see _3GPP TA 44.018_ <<3gpp-ts-44-018>> Ch. 3.3.1.1.2 and
+Table 3.3.1.1.2.1 in particular.
+
+The amount of times the MS attempts to retransmit RACH requests can also be
+changed. A higher number means more load on the RACH while a lower number can
+cause channel establishment to fail due to collisions or bad reception.
+
+.Example: Configure RACH Access Parameters
+----
+network
+ bts 0
+ rxlev access min 20 <1>
+ rach tx integer 50<2>
+ rach max transmission <3>
+----
+<1> Allow access to the network if the MS receives the BCCH of the cell at
+-90dBm or better (20dB above -110dBm).
+<2> This number affects how long the MS waits before (re-)transmitting RACH
+requests.
+<3> How often to retransmit the RACH request.
+
+[[cfg_ericsson_rbs_is]]
+=== Configuring Ericsson RBS Interface Switch (IS)
+
+Ericsson RBS2000/RBS6000 base stations feature a so called "Interface Switch" (IS),
+which is a built-in switchboard that interconnects between internal components
+of the BTS. It also connects to the external E1 connections. This allows to
+adapt the BTS to specific E1 networking requirements that may differ from the
+usual timeslot configuration.
+
+The internals of an Ericsson RBS are quite complex. In the following we will
+only explain how to connect transceiver units (TRU) to an E1 interface pointing
+to the outside world.
+
+==== Understanding the is-connection-list VTY option
+
+The IS operates on 16kbps subslots (ICPs), which means that there are no fixed
+borders between E1 timeslots. Any number of consecutive subslots may be
+connected through. However, depending on the components that are connected it
+may still be a requirement to align on E1 timeslot borders.
+
+The configuration of the IS is done using the is-connection-list command. The
+first two numbers are the ICP numbers that specify the first subslot on both
+sides that shall be interconnected. The third number (contiguity index) specifies
+how many of the following subslots shall be connected.
+
+In the following example we connect 4 blocks with 12 subslot each. The numbers
+on the left are the ICP numbers of the E1 connection pointing to the outside.
+The numbers in the middle are the ICP numbers of the subslots occupied by the
+transceivers (one TRX per block). The third number is the contiguity index that
+spans over 12 subslots or 3 E1 timeslots.
+
+.Example: 4 TRX BTS (4 x 12 subslots)
+----
+network
+ bts 0
+ is-connection-list add 4 512 12
+ is-connection-list add 16 524 12
+ is-connection-list add 28 536 12
+ is-connection-list add 40 548 12
+----
+
+==== E1 port and TRU ICP numbers
+
+On the outside connection, the ICP counting begins at E1 timeslot 0 (port A)
+but since E1 TS 0 is reserved for framing and synchronization of the E1 line
+itself the first usable subslot is subslot 4 (beginning of E1 TS 1). Depending
+on the configuration the BTS may have multiple E1 ports. The counting scheme
+will repeat itself. This means the next usable ICP can be found at an offset
+of 128.
+
+.External connections of a BTS with two E1 ports
+[options="header",cols="50%,25%,25%"]
+|===
+|Function |Subslot offset (ICP) |ICP count
+|E1 port A |4 |124
+|E1 port B |132 |124
+|===
+
+Depending on the transceiver configuration, a RBS2000/RBS6000 base station
+usually features two sets of ICPs for each TRX. The reason for this is that with
+the introduction of EGPRS more bandwidth than a single 16kbps subslot could
+deliver was required. The solution to this was to add an entirely new set of IS
+ICPs where full 64kbps E1 timeslots instead of 16kbps subslots could be
+used to serve a single air interface timeslot. The two sets of ICPs must not be
+mixed. Only one set may be used at a time.
+
+.ICPs to use TRU with 16kbps subslots per TRAU
+[options="header",cols="50%,25%,25%"]
+|===
+|Function |Subslot offset (ICP) |ICP count
+|TRU-0, RSL/OML |512 |4
+|TRU-0, TRAU TS0..TS7 |516 |8
+|TRU-1, RSL/OML |524 |4
+|TRU-1, TRAU TS0..TS7 |528 |8
+|TRU-2, RSL/OML |536 |4
+|TRU-2, TRAU TS0..TS7 |540 |8
+|TRU-3, RSL/OML |548 |4
+|TRU-3, TRAU TS0..TS7 |552 |8
+|TRU-4, RSL/OML |560 |4
+|TRU-4, TRAU TS0..TS7 |564 |8
+|TRU-5, RSL/OML |572 |4
+|TRU-5, TRAU TS0..TS7 |576 |8
+|TRU-6, RSL/OML |640 |4
+|TRU-6, TRAU TS0..TS7 |644 |8
+|TRU-7, RSL/OML |652 |4
+|TRU-7, TRAU TS0..TS7 |656 |8
+|TRU-8, RSL/OML |664 |4
+|TRU-8, TRAU TS0..TS7 |668 |8
+|TRU-9, RSL/OML |676 |4
+|TRU-9, TRAU TS0..TS7 |680 |8
+|TRU-10, RSL/OML |688 |4
+|TRU-10, TRAU TS0..TS7 |692 |8
+|TRU-11, RSL/OML |700 |4
+|TRU-11, TRAU TS0..TS7 |704 |8
+|===
+
+NOTE: Each air interface timeslot is served by its individual TRAU, so it is
+possible to route each subslot (ICP) dedicated to TRAU individually. The
+connections on the other end may contain gaps and do not have to be
+consecutive.
+
+.ICPs to use TRU with 64kbps subslots per TRAU
+[options="header",cols="50%,25%,25%"]
+|===
+|Function |Subslot offset (ICP) |ICP count
+|TRU-0, RSL/OML |712 |4
+|TRU-0, TRAU TS0..TS7 |716 |32
+|TRU-1, RSL/OML |748 |4
+|TRU-1, TRAU TS0..TS7 |752 |32
+|TRU-2, RSL/OML |784 |4
+|TRU-2, TRAU TS0..TS7 |788 |32
+|TRU-3, RSL/OML |820 |4
+|TRU-3, TRAU TS0..TS7 |824 |32
+|TRU-4, RSL/OML |856 |4
+|TRU-4, TRAU TS0..TS7 |860 |32
+|TRU-5, RSL/OML |928 |4
+|TRU-5, TRAU TS0..TS7 |932 |32
+|TRU-6, RSL/OML |964 |4
+|TRU-6, TRAU TS0..TS7 |968 |32
+|TRU-7, RSL/OML |1000 |4
+|TRU-7, TRAU TS0..TS7 |1004 |32
+|TRU-8, RSL/OML |1036 |4
+|TRU-8, TRAU TS0..TS7 |1040 |32
+|TRU-9, RSL/OML |1072 |4
+|TRU-9, TRAU TS0..TS7 |1076 |32
+|TRU-10, RSL/OML |1108 |4
+|TRU-10, TRAU TS0..TS7 |1112 |32
+|TRU-11, RSL/OML |1144 |4
+|TRU-11, TRAU TS0..TS7 |1148 |32
+|===
+
+NOTE: In case voice TRAU frames are transferred, only the first of the four
+16kbps subslots is used. When the timeslot is switched to GPRS/EGPRS, the
+full 64kbps bandwidth will be used. This also means that the set of four
+ICPs per TRAU must be connected consecutively. Also the connection
+to the outside must be aligned to E1 timeslot borders. \ No newline at end of file
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..3f0b0f7c6 100644
--- a/doc/manuals/chapters/control.adoc
+++ b/doc/manuals/chapters/control.adoc
@@ -20,24 +20,74 @@ TRX-specific commands are additionally prefixed with TRX number e. g.
|apply-configuration|WO|No|"restart"|Restart all BTSes.
|mnc|RW|No|"<mnc>"|Set/Get MNC (value between (0, 999)).
|mcc|RW|No|"<mcc>"|Set/Get MCC (value between (1, 999)).
-|short-name|RW|No|"<name>"|Set/Get network's short name.
-|long-name|RW|No|"<name>"|Set/Get network's long name.
|mcc-mnc-apply|WO|No|"<mcc>,<mnc>"|Apply new MCC/MNC values if different from currently used one.
|notification|WO|Yes|Arbitrary value| See <<notif>> for details.
|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.bsic|RW|No|"<bsic>"|Set/Get BSIC (value between (0, 63)).
+|bts.N.rach-max-delay|RW|No|"<delay>"|Set/Get RACH max delay (value between (1, 127)).
|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.
|bts.N.gprs-mode|RW|No|"<mode>"|See <<gprsm>> for details.
|bts.N.rf_state|RO|No|"<oper>,<admin>,<pol>"|See <<rfs>> for details.
+|bts.N.cell-reselection-offset|RW|No|"<cro>"|Set/Get cell reselection offset (value between (0, 126), steps of 2).
+|bts.N.cell-reselection-penalty-time|RW|No|"<penalty-time>","reserved"|Set/Get cell reselection penalty time (value between (20, 620), steps of 20).
+|bts.N.cell-reselection-hysteresis|RW|No|"<crh>"|Set/Get cell reselection hysteresis (value between (0, 14), steps of 2).
+|bts.N.rach-access-control-classes|RO|No|"<class>,(barred|allowed)"|Get concatenated pairs of RACH access control classes.
+|bts.N.rach-access-control-class.bar|WO|No|"<class>","emergency"|Set RACH access control class as barred.
+|bts.N.rach-access-control-class.allow|WO|No|"<class>","emergency"|Set RACH access control class as allowed.
|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.
+|bts.N.neighbor-list.si5-add|WO|No|<0-1023>|Add to manual SI5 neighbor list.
+|bts.N.neighbor-list.si5-del|WO|No|<0-1023>|Delete from manual SI5 neighbor list.
+|bts.N.neighbor-list.si2|RO|No|"<arfcn>"|Get space concatenated list of SI2 neighbor ARFCNs.
+|bts.N.neighbor-list.si5|RO|No|"<arfcn>"|Get space concatenated list of SI5 neighbor ARFCNs.
+|bts.N.neighbor-list.si2quater.uarfcns|RO|No|"<uarfcn>,<scrambling code>,<diversity bit>"|Get space concatenated list of UARFCN neighbors.
+|bts.N.neighbor-list.si2quater.earfcns|RO|No|"<earfcn>,<thresh-hi>,<thresh-lo>,<prio>,<qrxlv>,<meas>"|Get space concatenated list of EARFCN neighbors.
+|bts.N.si2quater-neighbor-list.add.uarfcn|WO|No|"<uarfcn>,<scrambling code>,<diversity bit>"|Add UARFCN neighbor.
+|bts.N.si2quater-neighbor-list.del.uarfcn|WO|No|"<uarfcn>,<scrambling code>"|Delete UARFCN neighbor.
+|bts.N.si2quater-neighbor-list.add.earfcn|WO|No|"<earfcn>,<thresh-hi>,<thresh-lo>,<prio>,<qrxlv>,<meas>"|Add EARFCN neighbor.
+|bts.N.si2quater-neighbor-list.del.earfcn|WO|No|"<earfcn>"|Delete EARFCN neighbor (value between (0, 65535)).
|===
[[notif]]
@@ -90,4 +140,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/overview.adoc b/doc/manuals/chapters/overview.adoc
index a27b0bf22..aca0c982f 100644
--- a/doc/manuals/chapters/overview.adoc
+++ b/doc/manuals/chapters/overview.adoc
@@ -135,11 +135,13 @@ Station Controller, i.e.
For more information, see <<net>>, <<bts>> and <<bts-examples>>.
-==== TRAU mapper / E1 sub-channel muxer
-
-Unlike classic GSM networks, OsmoBSC does not perform any transcoding.
-Rather, a compatible codec is selected for both legs of a call, and
-codec frames are passed through transparently. In order to achieve this
-with E1 based BTS, OsmoBSC contains a E1 sub-channel de- and
-re-multiplexer as well as a TRAU mapper that can map uplink to downlink
-frames and vice versa.
+==== Speech traffic
+
+OsmoBSC, by itself, does not perform any transcoding or relaying of user plane
+speech traffic. This task is handled entirely by a BSC co-located media gateway,
+such as OsmoMGW, which will take care of relaying the RTP traffic from the BTS
+into the core network.
+
+In case classic E1 based BTSs are used, OsmoBSC will instruct the MGW to
+convert between TRAU frames on the E1 side and RTP frames on the IP based core
+network side.
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..0c6d4fb9b 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.
@@ -71,6 +69,24 @@ To run multiple OsmoBSC instances on the same A-interface (SCCP/M3UA), each BSC
has to configure a distinct point-code. See <<cs7_config>>.
+=== Configure limits
+
+When connecting hundreds of TRX to OsmoBSC, it may be necessary to adjust the
+operating system's limit on open file descriptors for the osmo-bsc process. A
+typical default limit imposed by operating systems is 1024; this would be
+exceeded by, for example, about 205 BTS with 4 TRX each. (Each BTS with 4 TRX
+requires 5 file descriptors for Abis; 205 * 5 already exceeds 1024, sockets for
+other interfaces not considered yet.)
+
+It should be ok to set an OS limit on open file descriptors as high as 65536
+for osmo-bsc, which practically rules out failure from running out of file
+descriptors anywhere (<50,000 TRX).
+
+When using systemd, the file descriptor limit may be adjusted in the service
+file by the `LimitNOFILE` setting ("Number of Open FILE descriptors"). OsmoBSC
+ships a systemd service file with a high LimitNOFILE setting.
+
+
=== Configure primary links
==== Connect to an MSC's _A_ interface
@@ -89,6 +105,7 @@ cs7 instance 0
point-code 1.23.3
asp asp-clnt-msc-0 2905 0 m3ua
remote-ip 127.0.0.1
+ role asp
sctp-role client
sccp-address msc
point-code 0.23.1
@@ -136,7 +153,131 @@ 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>>.
+
+[[cfg_bsc_co_located_pcu]]
+==== Configure BSC co-located PCU
+
+While small IP based BTSs usually come with a built in PCU (BTS co-located
+PCU), this does not have to be the case with any BTS. Especially larger E1 BTS
+usually make use of a BSC co-located PCU.
+
+In the case of OsmoBSC this means that an instance of OsmoPCU is running next
+to OsmoBSC. Both processes share a unix domain socket to exchange signaling
+traffic and configuration parameters.
+
+.OsmoBSC with co-located OsmoPCU'
+[graphviz]
+----
+digraph G {
+ rankdir=LR;
+ BTS [label="BTS"];
+
+ subgraph cluster_ran {
+ label="RAN";
+ PCU [label="OsmoPCU"];
+ BSC [label="OsmoBSC"];
+ MGW [label="OsmoMGW"];
+ { rank=same BSC MGW PCU }
+ }
+
+ BTS->PCU [label="GPRS/TRAU", style=dotted];
+ BTS->BSC [label="Abis"];
+ BTS->MGW [label="SPEECH/TRAU", style=dotted];
+ BSC->MGW [label="MGCP"];
+ BSC->PCU [label="PCU_SOCK"];
+}
+----
+
+Apart from the configuration of the PCU socket path the configuration is not
+much different from those where the PCU is integrated inside the BTS. See also
+see also <<config_gprs_pcu_pars>> for a detailed description.
+
+.Configure socket path to co-located PCU
+----
+network
+ pcu-socket /tmp/pcu_bts
----
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 f7469a375..6ac5370e6 100644
--- a/doc/manuals/chapters/smscb.adoc
+++ b/doc/manuals/chapters/smscb.adoc
@@ -13,7 +13,7 @@ specification is <<3gpp-ts-23-041>>.
In order to use SMSCB with OsmoBSC, you will need to
-* Configure the CBSP server and/or client
+* Configure OsmoBSC as either CBSP server or client
* Use a channel combination including a CBCH on the BTSs
=== Enabling a CBCH channel combination
@@ -41,42 +41,90 @@ addresses).
In order to comply with the specifications, OsmoBSC supports this mode
of operation as CBSP TCP server. However, to make network operation and
configuration more simple, it also can operate in TCP client mode,
-connecting to the CBC. This way the all the BSCs need to know is the CBC IP
+connecting to the CBC. This way the BSCs need to know the CBC IP
address, but not vice-versa.
-The BSC can operate both CBSP TCP server and CBSP TCP client mode in
-parallel.
+The BSC can operate in either CBSP TCP server mode or CBSP TCP client mode.
The CBC related configuration of OsmoBSC can be found in the `cbc` configuration
node of the VTY interface.
-.Example: Configure CBSP TCP client to connect to CBC at 1.2.3.4:48049
+The default port number for the CBSP server is 48049, according to the CBSP
+specification. Hence it normally suffices to configure only the IP addresses for
+the remote CBC server or the local CBSP server:
+
+.Example: Configure CBSP TCP client to connect to CBC at 1.2.3.4:48049 in osmo-bsc.cfg
----
-OsmoBSC> enable
-OsmoBSC# configure terminal
-OsmoBSC(config)# cbc
-OsmoBSC(config-cbc)# remote-ip 1.2.3.4
-OsmoBSC(config-cbc)# remote-port 48049
-OsmoBSC(config-cbc)# end
+cbc
+ mode client
+ client
+ remote-ip 1.2.3.4
----
-.Example: Disable CBSP TCP client
+In server mode, the default configuration is 127.0.0.1:48049, so it suffices to
+set `mode server` to accept CBSP connections from localhost:
+
----
-OsmoBSC> enable
-OsmoBSC# configure terminal
-OsmoBSC(config)# cbc
-OsmoBSC(config-cbc)# no remote-ip
-OsmoBSC(config-cbc)# end
+cbc
+ mode server
+----
+
+To also listen for inbound CBSP connections on all interfaces, both IPv4 and
+IPv6:
+
+.Example: Configure CBSP TCP server to listen on all interfaces in osmo-bsc.cfg
+----
+cbc
+ mode server
+ server
+ local-ip ::
----
-.Example: Configure CBSP TCP server to listen for CBC at 127.0.0.2:9999
+Should non-standard port numbers be required, these can be configured with the
+`client` / `local-port` or the `server` / `remote-port` settings.
+
+The `client` config also supports an explicit local bind for connecting to the
+remote CBC, using `client` / `local-ip` and `local-port`.
+
+IP addresses for client and server can remain configured at the same time, and
+the `mode` command can be used to switch between client and server operation.
+The `mode` command takes immediate effect, no restart of OsmoBSC is required.
+After changing `cbc` IP addresses in the telnet VTY, it is required to switch
+`mode` to `disabled` and back to `client` or `server` to take effect.
+
+.Example: Disable the CBSP link in the telnet VTY
----
OsmoBSC> enable
OsmoBSC# configure terminal
OsmoBSC(config)# cbc
-OsmoBSC(config-cbc)# listen-ip 127.0.0.2
-OsmoBSC(config-cbc)# listen-port 9999
+OsmoBSC(config-cbc)# mode disabled
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 862c8f906..ea9222143 100644
--- a/doc/manuals/osmobsc-usermanual.adoc
+++ b/doc/manuals/osmobsc-usermanual.adoc
@@ -16,13 +16,23 @@ include::./common/chapters/vty.adoc[]
include::./common/chapters/logging.adoc[]
+include::./common/chapters/sigtran.adoc[]
+
+include::./common/chapters/sigtran-osmocom.adoc[]
+
include::./common/chapters/cs7-config.adoc[]
-include::./common/chapters/bts.adoc[]
+include::{srcdir}/chapters/bts.adoc[]
include::{srcdir}/chapters/bts-examples.adoc[]
-include::./common/chapters/bsc.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[]
@@ -30,6 +40,12 @@ 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[]
@@ -42,6 +58,10 @@ include::{srcdir}/chapters/control.adoc[]
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/doc/timeslot.msc b/doc/timeslot.msc
index 02e7bb3ea..0fd1458e5 100644
--- a/doc/timeslot.msc
+++ b/doc/timeslot.msc
@@ -1,5 +1,5 @@
msc {
- bts [label="MS/BTS"], bsc[label="BSC"], bsc_ts[label="BSC timeslot FSM"], bsc_lchan[label="BSC lchan FSM"];
+ bts [label="MS/BTS"], bsc[label="BSC"], bsc_ts[label="BSC timeslot FSM"], bsc_lchan[label="BSC lchan FSM"], pcu_sock[label="PCU socket"];
bsc_ts abox bsc_ts [label="NOT_INITIALIZED"];
@@ -88,6 +88,24 @@ msc {
bsc_ts rbox bsc_ts [label="Continue at 'IN_USE' above"];
...;
...;
+ bts rbox bsc_lchan [label="on PCU disconnect (Ericsson RBS)
+ for all timeslots in state PDCH:" ];
+ bsc_ts abox bsc_ts [label="PDCH"];
+ bsc_ts <- pcu_sock [label="TS_EV_PDCH_DEACT"];
+ bts note bsc_ts [label="PDCH release, see WAIT_PDCH_DEACT above"];
+ ...;
+ bsc_ts abox bsc_ts [label="UNUSED"];
+ ...;
+ ...;
+ bts rbox bsc_lchan [label="on PCU reconnect (Ericsson RBS)
+ for all timeslots in state UNUSED:" ];
+ bsc_ts abox bsc_ts [label="UNUSED"];
+ bsc_ts <- pcu_sock [label="TS_EV_PDCH_ACT"];
+ bts note bsc_ts [label="PDCH activation, see WAIT_PDCH_ACT above"];
+ ...;
+ bsc_ts abox bsc_ts [label="PDCH"];
+ ...;
+ ...;
bts rbox bsc_lchan [label="on erratic event"];
bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_ERROR"];
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index 7a296e474..a560f23d9 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -2,17 +2,24 @@ noinst_HEADERS = \
a_reset.h \
abis_nm.h \
abis_om2000.h \
+ abis_osmo.h \
abis_rsl.h \
- acc_ramp.h \
- arfcn_range_encode.h \
+ acc.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 \
+ data_rate_pref.h \
ctrl.h \
debug.h \
e1_config.h \
@@ -20,20 +27,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 \
- openbscdefines.h \
+ nm_common_fsm.h \
osmo_bsc.h \
osmo_bsc_grace.h \
osmo_bsc_rf.h \
@@ -43,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 \
@@ -53,4 +65,6 @@ noinst_HEADERS = \
penalty_timers.h \
osmo_bsc_lcls.h \
smscb.h \
+ power_control.h \
+ vgcs_fsm.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 83bc2f809..f55c29c9d 100644
--- a/include/osmocom/bsc/abis_nm.h
+++ b/include/osmocom/bsc/abis_nm.h
@@ -1,4 +1,4 @@
-/* GSM Network Management messages on the A-bis interface
+/* 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>
@@ -29,9 +29,6 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/signal.h>
-/* max number of attributes represented as 3GPP TS 52.021 §9.4.62 SW Description array */
-#define MAX_BTS_ATTR 5
-
/* The BCCH info from an ip.access test, in host byte order
* and already parsed... */
struct ipac_bcch_info {
@@ -89,7 +86,8 @@ int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len);
int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len);
int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb);
int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1,
- uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len);
+ uint8_t i2, uint8_t i3, int nack,
+ const uint8_t *attr, unsigned int attr_len);
int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *msg);
int abis_nm_event_reports(struct gsm_bts *bts, int on);
int abis_nm_reset_resource(struct gsm_bts *bts);
@@ -151,7 +149,7 @@ int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx);
int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class,
uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
uint8_t *attr, uint8_t attr_len);
-int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
+int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
uint32_t ip, uint16_t port, uint8_t stream);
void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts);
int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf);
@@ -178,4 +176,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/openbscdefines.h b/include/osmocom/bsc/abis_osmo.h
index c6ac153b8..97871ace0 100644
--- a/include/osmocom/bsc/openbscdefines.h
+++ b/include/osmocom/bsc/abis_osmo.h
@@ -1,6 +1,7 @@
-/*
- * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- *
+/* 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
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +19,15 @@
*
*/
-#ifndef OPENBSCDEFINES_H
-#define OPENBSCDEFINES_H
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
-#ifdef BUILDING_ON_WINDOWS
- #ifdef BUILDING_OPENBSC
- #define BSC_API __declspec(dllexport)
- #else
- #define BSC_API __declspec(dllimport)
- #endif
-#else
- #define BSC_API __attribute__((visibility("default")))
-#endif
+struct gsm_bts;
-#endif
+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 2611a3d00..023793ae5 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,17 +55,20 @@ 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_notification_cmd(struct gsm_bts *bts, struct gsm_lchan *lchan, struct gsm0808_group_callref *gc, uint8_t *drx);
+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);
int rsl_data_request(struct msgb *msg, uint8_t link_id);
+int rsl_unit_data_request(struct msgb *msg, uint8_t link_id);
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 msg_id, uint8_t len,
+ const uint8_t *val, uint8_t pag_grp, bool confirm);
/* Siemens vendor-specific RSL extensions */
int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci);
@@ -73,8 +85,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);
@@ -112,11 +122,19 @@ int rsl_tx_dyn_ts_pdch_act_deact(struct gsm_bts_trx_ts *ts, bool activate);
int rsl_forward_layer3_info(struct gsm_lchan *lchan, const uint8_t *l3_info, uint8_t l3_info_len);
+int ipacc_rtp_csd_fmt_transp(const struct channel_mode_and_rate *ch_mode_rate,
+ const enum rsl_ipac_rtp_csd_format_d format_d);
+int ipacc_rtp_csd_fmt_non_transp(const struct channel_mode_and_rate *ch_mode_rate,
+ const enum rsl_ipac_rtp_csd_format_d format_d);
+
int ipacc_speech_mode(enum gsm48_chan_mode tch_mode, enum gsm_chan_t type);
void ipacc_speech_mode_set_direction(uint8_t *speech_mode, bool send);
int ipacc_payload_type(enum gsm48_chan_mode tch_mode, enum gsm_chan_t type);
int rsl_tx_rf_chan_release(struct gsm_lchan *lchan);
+void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts);
+void abis_rsl_chan_rqd_queue_flush(struct gsm_bts *bts);
+
#endif /* RSL_MT_H */
diff --git a/include/osmocom/bsc/acc_ramp.h b/include/osmocom/bsc/acc.h
index 31fc74fbd..531a69acf 100644
--- a/include/osmocom/bsc/acc_ramp.h
+++ b/include/osmocom/bsc/acc.h
@@ -27,6 +27,40 @@
#include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#define ACC_MGR_QUANTUM_DEFAULT 20 /* 20 seconds */
+
+/* Manage rotating subset of allowed Access Class as per configuration */
+struct acc_mgr {
+ struct gsm_bts *bts; /*!< backpointer to BTS using this ACC manager */
+ /* Administrative Maximum Number of ACC 0-9 to be allowed at the same time.
+ Configurable through VTY cmd "access-control-class-roundrobin",
+ defaults to all allowed (10) */
+ uint8_t len_allowed_adm;
+ /* Further limiting the number of ACC to use. It may be lower due
+ to ramping, based for instance on channel or system load. */
+ uint8_t len_allowed_ramp;
+
+ /* Time until next subset is generated */
+ uint32_t rotation_time_sec;
+ struct osmo_timer_list rotate_timer;
+
+ /* Bitmask containing subset of allowed ACC 0-9 on current rotation iteration */
+ uint16_t allowed_subset_mask;
+ /* Number of bits (ACC) set in allowed_subset_mask: 0->min(len_allowed_ramp, len_allowed_adm) */
+ uint8_t allowed_subset_mask_count;
+ /* Number of ACC 0-9 allowed as per adminsitrative (permanent) config. */
+ uint8_t allowed_permanent_count;
+};
+
+void acc_mgr_init(struct acc_mgr *acc_mgr, struct gsm_bts *bts);
+uint8_t acc_mgr_get_len_allowed_adm(struct acc_mgr *acc_mgr);
+uint8_t acc_mgr_get_len_allowed_ramp(struct acc_mgr *acc_mgr);
+void acc_mgr_set_len_allowed_adm(struct acc_mgr *acc_mgr, uint8_t len_allowed_adm);
+void acc_mgr_set_len_allowed_ramp(struct acc_mgr *acc_mgr, uint8_t len_allowed_ramp);
+void acc_mgr_set_rotation_time(struct acc_mgr *acc_mgr, uint32_t rotation_time_sec);
+void acc_mgr_perm_subset_changed(struct acc_mgr *acc_mgr, struct gsm48_rach_control *rach_control);
+void acc_mgr_apply_acc(struct acc_mgr *acc_mgr, struct gsm48_rach_control *rach_control);
+
/*!
* Access control class (ACC) ramping is used to slowly make the cell available to
* an increasing number of MS. This avoids overload at startup time in cases where
@@ -37,9 +71,12 @@
#define ACC_RAMP_STEP_SIZE_DEFAULT ACC_RAMP_STEP_SIZE_MIN
#define ACC_RAMP_STEP_SIZE_MAX 10 /* allow all ACC in one step (effectively disables ramping) */
-#define ACC_RAMP_STEP_INTERVAL_MIN 30 /* 30 seconds */
+#define ACC_RAMP_STEP_INTERVAL_MIN 5 /* 5 seconds */
#define ACC_RAMP_STEP_INTERVAL_MAX 600 /* 10 minutes */
+#define ACC_RAMP_CHAN_LOAD_THRESHOLD_LOW 71
+#define ACC_RAMP_CHAN_LOAD_THRESHOLD_UP 89
+
/*!
* Data structure used to manage ACC ramping. Please avoid setting or reading fields
* in this structure directly. Use the accessor functions below instead.
@@ -50,17 +87,6 @@ struct acc_ramp {
bool acc_ramping_enabled; /*!< whether ACC ramping is enabled */
/*!
- * Bitmask which keeps track of access control classes that are currently denied
- * access. The function acc_ramp_apply() uses this mask to modulate bits from
- * octets 2 and 3 in RACH Control Parameters (see 3GPP 44.018 10.5.2.29).
- * Ramping is only concerned with ACCs 0-9. While any of the bits 0-9 is set,
- * the corresponding ACC is barred.
- * ACCs 11-15 should always be allowed, and ACC 10 denies emergency calls for
- * all ACCs from 0-9 inclusive; these ACCs are ignored in this implementation.
- */
- uint16_t barred_accs;
-
- /*!
* This controls the maximum number of ACCs to allow per ramping step (1 - 10).
* The compile-time default value is ACC_RAMP_STEP_SIZE_DEFAULT.
* This value can be changed by VTY configuration.
@@ -74,8 +100,17 @@ struct acc_ramp {
* it has been overridden by VTY configuration.
*/
unsigned int step_interval_sec;
- bool step_interval_is_fixed;
struct osmo_timer_list step_timer;
+
+ /*!
+ * Channel Load Upper/Lower Thresholds:
+ * They control how ramping subset size of allowed ACCs changes in
+ * relation to current channel load (%, 0-100): Under the lower
+ * threshold, subset size may be increased; above the upper threshold,
+ * subset size may be decreased.
+ */
+ unsigned int chan_load_lower_threshold;
+ unsigned int chan_load_upper_threshold;
};
/*!
@@ -118,44 +153,19 @@ static inline unsigned int acc_ramp_get_step_interval(struct acc_ramp *acc_ramp)
}
/*!
- * If the step interval is dynamic, return true, else return false.
- * \param[in] acc_ramp Pointer to acc_ramp structure.
- */
-static inline bool acc_ramp_step_interval_is_dynamic(struct acc_ramp *acc_ramp)
-{
- return !(acc_ramp->step_interval_is_fixed);
-}
-
-/*!
- * Return bitmasks which correspond to access control classes that are currently
- * denied access. Ramping is only concerned with those bits which control access
- * for ACCs 0-9, and any of the other bits will always be set to zero in these masks, i.e.
- * it is safe to OR these bitmasks with the corresponding fields in struct gsm48_rach_control.
+ * Return the current ACC ramp step interval (in seconds)
* \param[in] acc_ramp Pointer to acc_ramp structure.
*/
-static inline uint8_t acc_ramp_get_barred_t2(struct acc_ramp *acc_ramp)
-{
- return ((acc_ramp->barred_accs >> 8) & 0x03);
-};
-static inline uint8_t acc_ramp_get_barred_t3(struct acc_ramp *acc_ramp)
+static inline unsigned int acc_ramp_is_running(struct acc_ramp *acc_ramp)
{
- return (acc_ramp->barred_accs & 0xff);
+ return acc_ramp->step_interval_sec;
}
-/*!
- * Potentially mark certain Access Control Classes (ACCs) as barred in accordance to ACC ramping.
- * \param[in] rach_control RACH control parameters in which barred ACCs will be configured.
- * \param[in] acc_ramp Pointer to acc_ramp structure.
- */
-static inline void acc_ramp_apply(struct gsm48_rach_control *rach_control, struct acc_ramp *acc_ramp)
-{
- rach_control->t2 |= acc_ramp_get_barred_t2(acc_ramp);
- rach_control->t3 |= acc_ramp_get_barred_t3(acc_ramp);
-}
+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);
-void acc_ramp_set_step_interval_dynamic(struct acc_ramp *acc_ramp);
+int acc_ramp_set_chan_load_thresholds(struct acc_ramp *acc_ramp, unsigned int low_threshold, unsigned int up_threshold);
void acc_ramp_trigger(struct acc_ramp *acc_ramp);
void acc_ramp_abort(struct acc_ramp *acc_ramp);
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..b1adf82b7 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,14 @@ enum assignment_fsm_event {
ASSIGNMENT_EV_CONN_RELEASING,
};
-void assignment_fsm_init();
+void bssap_extend_osmux(struct msgb *msg, uint8_t cid);
+
+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 b1fe14d2f..ae29725af 100644
--- a/include/osmocom/bsc/bsc_msc_data.h
+++ b/include/osmocom/bsc/bsc_msc_data.h
@@ -59,14 +59,27 @@ 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,
+ MSC_CTR_BSSMAP_RX_DT1_COMMON_ID,
MSC_CTR_BSSMAP_RX_DT1_UNKNOWN,
MSC_CTR_BSSMAP_RX_DT1_DTAP,
MSC_CTR_BSSMAP_RX_DT1_DTAP_ERROR,
+ MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_REQUEST,
+ MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_ABORT,
+ MSC_CTR_BSSMAP_RX_DT1_VGCS_VBS_SETUP,
+ MSC_CTR_BSSMAP_RX_DT1_VGCS_VBS_ASSIGN_RQST,
+ MSC_CTR_BSSMAP_RX_DT1_UPLINK_RQST_ACKNOWLEDGE,
+ MSC_CTR_BSSMAP_RX_DT1_UPLINK_REJECT_CMD,
+ MSC_CTR_BSSMAP_RX_DT1_UPLINK_RELEASE_CMD,
+ MSC_CTR_BSSMAP_RX_DT1_UPLINK_SEIZED_CMD,
+ MSC_CTR_BSSMAP_RX_DT1_VGCS_ADDL_INFO,
+ MSC_CTR_BSSMAP_RX_DT1_VGCS_SMS,
+ MSC_CTR_BSSMAP_RX_DT1_NOTIFICATION_DATA,
/* Tx message counters (per connection type) */
MSC_CTR_BSSMAP_TX_BSS_MANAGEMENT,
@@ -82,8 +95,8 @@ enum {
MSC_CTR_BSSMAP_TX_UDT_RESET_ACK,
MSC_CTR_BSSMAP_TX_DT1_CLEAR_RQST,
MSC_CTR_BSSMAP_TX_DT1_CLEAR_COMPLETE,
- MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_FAILURE,
- MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_COMPLETE,
+ MSC_CTR_BSSMAP_TX_DT1_ASSIGNMENT_FAILURE,
+ MSC_CTR_BSSMAP_TX_DT1_ASSIGNMENT_COMPLETE,
MSC_CTR_BSSMAP_TX_DT1_SAPI_N_REJECT,
MSC_CTR_BSSMAP_TX_DT1_CIPHER_COMPLETE,
MSC_CTR_BSSMAP_TX_DT1_CIPHER_REJECT,
@@ -96,6 +109,19 @@ enum {
MSC_CTR_BSSMAP_TX_DT1_HANDOVER_COMPLETE,
MSC_CTR_BSSMAP_TX_DT1_HANDOVER_FAILURE,
MSC_CTR_BSSMAP_TX_DT1_DTAP,
+ MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS,
+ MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE,
+ MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_SETUP_ACK,
+ MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_SETUP_REFUSE,
+ MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_ASSIGN_RESULT,
+ MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_ASSIGN_FAIL,
+ MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_QUEUING_INDICATION,
+ MSC_CTR_BSSMAP_TX_DT1_UPLINK_RQST,
+ MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_ASSIGNMENT_STATUS,
+ MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_AREA_CELL_INFO,
+ MSC_CTR_BSSMAP_TX_DT1_UPLINK_RQST_CONFIRMATION,
+ MSC_CTR_BSSMAP_TX_DT1_UPLINK_RELEASE_INDICATION,
+ MSC_CTR_BSSMAP_TX_DT1_UPLINK_APP_DATA,
MSC_CTR_MSCPOOL_SUBSCR_NEW,
MSC_CTR_MSCPOOL_SUBSCR_REATTACH,
@@ -123,14 +149,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;
@@ -166,7 +195,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 5475272b0..766d56a49 100644
--- a/include/osmocom/bsc/bsc_subscr_conn_fsm.h
+++ b/include/osmocom/bsc/bsc_subscr_conn_fsm.h
@@ -1,17 +1,25 @@
#pragma once
+#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_A_CONN_REQ,
+ GSCON_EV_MO_COMPL_L3,
/* MSC confirms the SCCP connection */
GSCON_EV_A_CONN_CFM,
/* MSC has sent BSSMAP CLEAR CMD */
GSCON_EV_A_CLEAR_CMD,
/* MSC SCCP disconnect indication */
GSCON_EV_A_DISC_IND,
+ /* MSC has sent a BSSMAP COMMON ID */
+ GSCON_EV_A_COMMON_ID_IND,
GSCON_EV_ASSIGNMENT_START,
GSCON_EV_ASSIGNMENT_END,
@@ -38,6 +46,8 @@ enum gscon_fsm_event {
GSCON_EV_FORGET_LCHAN,
GSCON_EV_FORGET_MGW_ENDPOINT,
+
+ GSCON_EV_LCS_LOC_REQ_END,
};
struct gsm_subscriber_connection;
@@ -47,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);
@@ -56,9 +64,10 @@ 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);
+ uint16_t msc_assigned_cic, struct gsm_lchan *for_lchan);
bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn,
struct gsm_lchan *for_lchan,
const char *addr, uint16_t port,
@@ -71,7 +80,7 @@ void gscon_start_assignment(struct gsm_subscriber_connection *conn,
struct assignment_request *req);
void gscon_change_primary_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan *new_lchan);
-void gscon_release_lchans(struct gsm_subscriber_connection *conn, bool do_rr_release);
+void gscon_release_lchans(struct gsm_subscriber_connection *conn, bool do_rr_release, enum gsm48_rr_cause cause_rr);
void gscon_lchan_releasing(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan);
void gscon_forget_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan);
@@ -80,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 53fa7be5c..1fa00162d 100644
--- a/include/osmocom/bsc/bsc_subscriber.h
+++ b/include/osmocom/bsc/bsc_subscriber.h
@@ -5,43 +5,69 @@
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/linuxrbtree.h>
+#include <osmocom/core/use_count.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/gsm48.h>
struct log_target;
+struct gsm_bts;
+
+struct bsc_subscr_store {
+ struct llist_head bsub_list; /* list containing "struct bsc_subscr" */
+ /* rbtree root of 'struct bsc_subscr', ordered by tmsi */
+ struct rb_root bsub_tree_tmsi;
+};
+
+struct bsc_subscr_store *bsc_subscr_store_alloc(void *ctx);
struct bsc_subscr {
- struct llist_head entry;
- int use_count;
+ struct bsc_subscr_store *store; /* backpointer to "struct bsc_subscr_store" */
+ struct llist_head entry; /* entry in (struct bsc_subscr_store)->bsub_list */
+ /* entry in (struct bsc_subscr_store)->bsub_tree_tmsi. Inserted if tmsi != GSM_RESERVED_TMSI: */
+ struct rb_node node_tmsi;
+ 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;
- uint16_t lac;
+
+ /* 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);
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);
-struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list,
- uint32_t tmsi);
-struct bsc_subscr *bsc_subscr_find_or_create_by_mi(struct llist_head *list, const struct osmo_mobile_identity *mi);
+struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct bsc_subscr_store *bsubst,
+ const char *imsi,
+ const char *use_token);
+struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct bsc_subscr_store *bsubst,
+ uint32_t tmsi,
+ const char *use_token);
+struct bsc_subscr *bsc_subscr_find_or_create_by_mi(struct bsc_subscr_store *bsubst,
+ const struct osmo_mobile_identity *mi,
+ const char *use_token);
-struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list,
- const char *imsi);
-struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list,
- uint32_t tmsi);
-struct bsc_subscr *bsc_subscr_find_by_mi(struct llist_head *list, const struct osmo_mobile_identity *mi);
+struct bsc_subscr *bsc_subscr_find_by_imsi(struct bsc_subscr_store *bsubst,
+ const char *imsi,
+ const char *use_token);
+int bsc_subscr_set_tmsi(struct bsc_subscr *bsub, uint32_t tmsi);
void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi);
+void bsc_subscr_set_imei(struct bsc_subscr *bsub, const char *imei);
-struct bsc_subscr *_bsc_subscr_get(struct bsc_subscr *bsub,
- const char *file, int line);
-struct bsc_subscr *_bsc_subscr_put(struct bsc_subscr *bsub,
- const char *file, int line);
-#define bsc_subscr_get(bsub) _bsc_subscr_get(bsub, __FILE__, __LINE__)
-#define bsc_subscr_put(bsub) _bsc_subscr_put(bsub, __FILE__, __LINE__)
+#define bsc_subscr_get(bsc_subscr, use) \
+ OSMO_ASSERT(osmo_use_count_get_put(&(bsc_subscr)->use_count, use, 1) == 0)
+#define bsc_subscr_put(bsc_subscr, use) \
+ OSMO_ASSERT(osmo_use_count_get_put(&(bsc_subscr)->use_count, use, -1) == 0)
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..c0de13c4d
--- /dev/null
+++ b/include/osmocom/bsc/bssmap_reset.h
@@ -0,0 +1,32 @@
+/* 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_set_disconnected(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
new file mode 100644
index 000000000..e9634ee3e
--- /dev/null
+++ b/include/osmocom/bsc/bts.h
@@ -0,0 +1,862 @@
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdbool.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/bts_features.h>
+
+#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,
+ BTS_CTR_CODEC_AMR_F,
+ BTS_CTR_CODEC_AMR_H,
+ BTS_CTR_CODEC_EFR,
+ BTS_CTR_CODEC_V1_FR,
+ BTS_CTR_CODEC_V1_HR,
+ BTS_CTR_PAGING_ATTEMPTED,
+ BTS_CTR_PAGING_ALREADY,
+ BTS_CTR_PAGING_RESPONDED,
+ 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,
+ BTS_CTR_MODE_MODIFY_NACK,
+ BTS_CTR_LCHAN_BORKEN_FROM_UNUSED,
+ BTS_CTR_LCHAN_BORKEN_FROM_WAIT_ACTIV_ACK,
+ BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RF_RELEASE_ACK,
+ BTS_CTR_LCHAN_BORKEN_FROM_BORKEN,
+ BTS_CTR_LCHAN_BORKEN_FROM_UNKNOWN,
+ BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK,
+ BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK,
+ 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,
+ BTS_CTR_TS_BORKEN_FROM_UNUSED,
+ BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_ACT,
+ BTS_CTR_TS_BORKEN_FROM_PDCH,
+ BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_DEACT,
+ BTS_CTR_TS_BORKEN_FROM_IN_USE,
+ BTS_CTR_TS_BORKEN_FROM_BORKEN,
+ BTS_CTR_TS_BORKEN_FROM_UNKNOWN,
+ BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK,
+ 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,
+ BTS_CTR_HANDOVER_NO_CHANNEL,
+ BTS_CTR_HANDOVER_TIMEOUT,
+ BTS_CTR_HANDOVER_FAILED,
+ BTS_CTR_HANDOVER_ERROR,
+ BTS_CTR_INTRA_CELL_HO_ATTEMPTED,
+ BTS_CTR_INTRA_CELL_HO_COMPLETED,
+ BTS_CTR_INTRA_CELL_HO_STOPPED,
+ BTS_CTR_INTRA_CELL_HO_NO_CHANNEL,
+ BTS_CTR_INTRA_CELL_HO_TIMEOUT,
+ BTS_CTR_INTRA_CELL_HO_FAILED,
+ BTS_CTR_INTRA_CELL_HO_ERROR,
+ BTS_CTR_INTRA_BSC_HO_ATTEMPTED,
+ BTS_CTR_INTRA_BSC_HO_COMPLETED,
+ BTS_CTR_INTRA_BSC_HO_STOPPED,
+ BTS_CTR_INTRA_BSC_HO_NO_CHANNEL,
+ 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,
+ BTS_CTR_INTER_BSC_HO_OUT_TIMEOUT,
+ BTS_CTR_INTER_BSC_HO_OUT_FAILED,
+ BTS_CTR_INTER_BSC_HO_OUT_ERROR,
+ BTS_CTR_INTER_BSC_HO_IN_ATTEMPTED,
+ BTS_CTR_INTER_BSC_HO_IN_COMPLETED,
+ BTS_CTR_INTER_BSC_HO_IN_STOPPED,
+ BTS_CTR_INTER_BSC_HO_IN_NO_CHANNEL,
+ 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,
+};
+
+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,
+ BTS_STAT_CHAN_TCH_F_USED,
+ BTS_STAT_CHAN_TCH_F_TOTAL,
+ BTS_STAT_CHAN_TCH_H_USED,
+ BTS_STAT_CHAN_TCH_H_TOTAL,
+ BTS_STAT_CHAN_SDCCH8_USED,
+ BTS_STAT_CHAN_SDCCH8_TOTAL,
+ BTS_STAT_CHAN_TCH_F_PDCH_USED,
+ BTS_STAT_CHAN_TCH_F_PDCH_TOTAL,
+ BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED,
+ BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL,
+ BTS_STAT_CHAN_SDCCH8_CBCH_USED,
+ BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL,
+ BTS_STAT_CHAN_OSMO_DYN_USED,
+ BTS_STAT_CHAN_OSMO_DYN_TOTAL,
+ BTS_STAT_T3122,
+ BTS_STAT_RACH_BUSY,
+ BTS_STAT_RACH_ACCESS,
+ BTS_STAT_OML_CONNECTED,
+ 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,
+};
+
+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,
+ GSM_BTS_TYPE_BS11,
+ GSM_BTS_TYPE_NANOBTS,
+ GSM_BTS_TYPE_RBS2000,
+ GSM_BTS_TYPE_NOKIA_SITE,
+ GSM_BTS_TYPE_OSMOBTS,
+ _NUM_GSM_BTS_TYPE
+};
+extern const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1];
+extern const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1];
+
+enum gsm_bts_type_variant {
+ BTS_UNKNOWN,
+ BTS_OSMO_LITECELL15,
+ BTS_OSMO_OCTPHY,
+ BTS_OSMO_SYSMO,
+ BTS_OSMO_TRX,
+ _NUM_BTS_VARIANT
+};
+
+/* Used by OML layer for BTS Attribute reporting */
+enum bts_attribute {
+ BTS_TYPE_VARIANT,
+ BTS_SUB_MODEL,
+ 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 {
+ struct llist_head list;
+
+ enum gsm_bts_type type;
+ enum gsm_bts_type_variant variant;
+ 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_send_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);
+
+ /* Should SI2bis and SI2ter be disabled by default on this BTS model? */
+ bool force_combined_si;
+
+ struct tlv_definition nm_att_tlvdef;
+
+ /* 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 */
+struct gsm_bts {
+ /* list header in net->bts_list */
+ struct llist_head list;
+
+ /* Geographical location of the BTS */
+ struct llist_head loc_list;
+
+ /* number of this BTS in network */
+ uint8_t nr;
+ /* human readable name / description */
+ char *description;
+ /* Cell Identity */
+ uint16_t cell_identity;
+ /* location area code of this BTS */
+ uint16_t location_area_code;
+ /* Base Station Identification Code (BSIC), lower 3 bits is BCC,
+ * which is used as TSC for the CCCH */
+ uint8_t bsic;
+ /* type of BTS */
+ enum gsm_bts_type type;
+ enum gsm_bts_type_variant variant;
+ struct gsm_bts_model *model;
+ enum gsm_band band;
+ char version[MAX_VERSION_LENGTH];
+ char sub_model[MAX_VERSION_LENGTH];
+
+ /* 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;
+
+ /* how do we talk OML with this TRX? */
+ struct gsm_e1_subslot oml_e1_link;
+ uint8_t oml_tei;
+ struct e1inp_sign_link *oml_link;
+ /* Timer to use for deferred drop of OML link, see \ref ipaccess_drop_oml_deferred */
+ struct osmo_timer_list oml_drop_link_timer;
+ /* when OML link was established or lost */
+ time_t updowntime;
+
+ /* Abis network management O&M handle */
+ struct abis_nm_h *nmh;
+
+ struct gsm_abis_mo mo;
+
+ /* number of this BTS on given E1 link */
+ uint8_t bts_nr;
+
+ /* DTX features of this BTS */
+ enum gsm48_dtx_mode dtxu;
+ bool dtxd;
+
+ /* paging state and control */
+ struct gsm_bts_paging_state paging;
+
+ /* CCCH is on C0 */
+ struct gsm_bts_trx *c0;
+
+ struct gsm_bts_sm *site_mgr; /* backpointer */
+
+ /* bitmask of all SI that are present/valid in si_buf */
+ uint32_t si_valid;
+ /* 3GPP TS 44.018 Table 10.5.2.33b.1 INDEX and COUNT for SI2quater */
+ uint8_t si2q_index; /* distinguish individual SI2quater messages */
+ uint8_t si2q_count; /* si2q_index for the last (highest indexed) individual SI2quater message */
+ /* buffers where we put the pre-computed SI */
+ sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE][SI2Q_MAX_NUM];
+ /* offsets used while generating SI2quater */
+ size_t e_offset;
+ size_t u_offset;
+ /* 3GPP TS 08.58 §8.5.1 BCCH INFORMATION. Some nanoBTS fail upon
+ * receival of empty SI disabling unsupported SI. see OS#3707. */
+ bool si_unused_send_empty;
+
+ /* ip.access Unit ID's have Site/BTS/TRX layout */
+ union {
+ struct {
+ uint16_t site_id;
+ uint16_t bts_id;
+ uint32_t flags;
+ uint32_t rsl_ip;
+ } ip_access;
+ struct {
+ struct {
+ struct gsm_abis_mo mo;
+ } cclk;
+ struct {
+ struct gsm_abis_mo mo;
+ } rack;
+ struct gsm_envabtse envabtse[4];
+ } bs11;
+ struct {
+ struct osmo_fsm_inst *bts_fi;
+ struct {
+ struct om2k_mo om2k_mo;
+ struct gsm_abis_mo mo;
+ struct llist_head conn_groups;
+ } cf;
+ struct {
+ struct om2k_mo om2k_mo;
+ struct gsm_abis_mo mo;
+ struct llist_head conn_groups;
+ } is;
+ struct {
+ struct om2k_mo om2k_mo;
+ struct gsm_abis_mo mo;
+ struct llist_head conn_groups;
+ } con;
+ struct {
+ struct om2k_mo om2k_mo;
+ struct gsm_abis_mo mo;
+ } dp;
+ struct {
+ struct om2k_mo om2k_mo;
+ struct gsm_abis_mo mo;
+ } tf;
+ struct {
+ struct om2k_mo om2k_mo;
+ struct gsm_abis_mo mo;
+ } mctr;
+ uint32_t use_superchannel:1;
+ struct {
+ uint16_t limit;
+ uint16_t active;
+ } om2k_version[16];
+ enum om2k_sync_src sync_src;
+ } rbs2000;
+ struct {
+ uint8_t bts_type;
+ unsigned int configured:1, /* we sent the config data request */
+ skip_reset:1, /* skip reset at bootstrap */
+ no_loc_rel_cnf:1, /* don't wait for RSL REL CONF */
+ bts_reset_timer_cnf, /* timer for BTS RESET */
+ did_reset:1, /* we received a RESET ACK */
+ wait_reset:2; /* we are waiting for reset to complete */
+ struct osmo_timer_list reset_timer;
+ } nokia;
+ };
+
+ /* Not entirely sure how ip.access specific this is */
+ struct {
+ enum bts_gprs_mode mode;
+ 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;
+
+ /* 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;
+ int rach_ldavg_slots;
+
+ /* transceivers */
+ int num_trx;
+ struct llist_head trx_list;
+
+ /* SI related items */
+ int force_combined_si;
+ bool force_combined_si_set;
+ int bcch_change_mark;
+
+ /* Abis NM queue */
+ struct llist_head abis_queue;
+ int abis_nm_pend;
+
+ struct gsm_network *network;
+
+ /* should the channel allocator allocate channels from high TRX to TRX0,
+ * rather than starting from TRX0 and go upwards? */
+ bool chan_alloc_chan_req_reverse;
+ bool chan_alloc_assignment_reverse;
+ bool chan_alloc_handover_reverse;
+ bool chan_alloc_vgcs_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 */
+ struct {
+ struct gsm48_rach_control rach_control;
+ uint8_t ncc_permitted;
+ struct gsm48_cell_sel_par cell_sel_par;
+ 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;
+ struct bitvec cell_alloc;
+ 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];
+ uint8_t cell_alloc[1024/8];
+ /* If the user wants a different neighbor list in SI5 than in SI2 */
+ uint8_t si5_neigh_list[1024/8];
+ uint8_t meas_bw_list[MAX_EARFCN_LIST];
+ uint16_t earfcn_list[MAX_EARFCN_LIST];
+ uint16_t uarfcn_list[MAX_EARFCN_LIST];
+ uint16_t scramble_list[MAX_EARFCN_LIST];
+ } data;
+ } si_common;
+ /* NCH (Notification Channel) related parameters */
+ struct {
+ uint8_t num_blocks; /* NCH number of CCCH blocks (0 = no NCH) */
+ uint8_t first_block; /* NCH first block number */
+ } nch;
+ bool early_classmark_allowed;
+ bool early_classmark_allowed_3g;
+ /* for testing only: Have an infinitely long radio link timeout */
+ bool infinite_radio_link_timeout;
+
+ /* do we use static (user-defined) system information messages? (bitmask) */
+ uint32_t si_mode_static;
+
+ /* access control class ramping */
+ struct acc_mgr acc_mgr;
+ struct acc_ramp acc_ramp;
+
+ /* 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;
+
+ /* BTS dependencies bit field */
+ uint32_t depends_on[256/(8*4)];
+
+ /* full and half rate multirate config */
+ struct amr_multirate_conf mr_full;
+ struct amr_multirate_conf mr_half;
+
+ /* osmux config: */
+ enum osmux_usage use_osmux;
+
+ struct rate_ctr_group *bts_ctrs;
+ struct osmo_stat_item_group *bts_statg;
+
+ struct handover_cfg *ho;
+
+ /* 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 */
+ bool T3113_dynamic; /* Calculate T3113 timeout dynamically based on BTS channel config and load */
+
+ /* Periodic channel load measurements are used to maintain T3122. */
+ struct load_counter chan_load_samples[7];
+ int chan_load_samples_idx;
+ uint8_t chan_load_avg; /* current channel load average in percent (0 - 100). */
+
+ /* cell broadcast system */
+ struct osmo_timer_list cbch_timer;
+ struct bts_smscb_chan_state cbch_basic;
+ struct bts_smscb_chan_state cbch_extended;
+ 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;
+
+ /* We will ignore CHAN RQD sitting in the queue for period greater than rach_expiry_timeout */
+ uint8_t rach_expiry_timeout;
+
+ /* 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])
+#define GSM_BTS_HAS_SI(bts, i) ((bts)->si_valid & (1 << i))
+#define GSM_BTS_SI(bts, i) (void *)((bts)->si_buf[i][0])
+
+/* this actually refers to the IPA transport, not the BTS model */
+static inline bool is_ipa_abisip_bts(const struct gsm_bts *bts)
+{
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ case GSM_BTS_TYPE_OSMOBTS:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static inline bool is_nanobts(const struct gsm_bts *bts)
+{
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static inline bool is_osmobts(const struct gsm_bts *bts)
+{
+ switch (bts->type) {
+ case GSM_BTS_TYPE_OSMOBTS:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static inline bool is_siemens_bts(const struct gsm_bts *bts)
+{
+ switch (bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static inline bool is_nokia_bts(const struct gsm_bts *bts)
+{
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NOKIA_SITE:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static inline bool is_ericsson_bts(const struct gsm_bts *bts)
+{
+ switch (bts->type) {
+ case GSM_BTS_TYPE_RBS2000:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static inline bool is_e1_bts(const struct gsm_bts *bts)
+{
+ switch (bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ case GSM_BTS_TYPE_RBS2000:
+ case GSM_BTS_TYPE_NOKIA_SITE:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static inline bool bsc_co_located_pcu(const struct gsm_bts *bts)
+{
+ switch (bts->type) {
+ case GSM_BTS_TYPE_RBS2000:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static inline const struct osmo_location_area_id *bts_lai(struct gsm_bts *bts)
+{
+ static struct osmo_location_area_id lai;
+ lai = (struct osmo_location_area_id){
+ .plmn = bts->network->plmn,
+ .lac = bts->location_area_code,
+ };
+ return &lai;
+}
+
+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);
+
+/* 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_updowntime(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);
+int bts_depend_check(struct gsm_bts *bts);
+int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other);
+
+int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts);
+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 gsm_bts_set_system_infos(struct gsm_bts *bts);
+
+int gsm_bts_send_c0_power_red(const struct gsm_bts *bts, const uint8_t red);
+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);
+
+enum gsm_bts_type str2btstype(const char *arg);
+const char *btstype2str(enum gsm_bts_type type);
+
+enum bts_attribute str2btsattr(const char *s);
+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..9f51efd8b 100644
--- a/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h
+++ b/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h
@@ -23,10 +23,22 @@
#include <stdint.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/tlv.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;
+
+extern const struct tlv_definition ipacc_eie_tlv_def;
+
+int ipacc_parse_supp_features(const struct gsm_bts *bts,
+ const struct abis_om_fom_hdr *foh,
+ const uint8_t *data, uint16_t data_len);
+
+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..13be3d97e
--- /dev/null
+++ b/include/osmocom/bsc/bts_sm.h
@@ -0,0 +1,80 @@
+/* BTS Site Manager */
+
+/* (C) 2020 by 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 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;
+ bool enabled;
+ 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
new file mode 100644
index 000000000..8ded559a4
--- /dev/null
+++ b/include/osmocom/bsc/bts_trx.h
@@ -0,0 +1,102 @@
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdbool.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/gsm/tlv.h>
+
+#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 */
+ struct llist_head list;
+
+ struct gsm_bts *bts;
+ /* number of this TRX in the BTS */
+ uint8_t nr;
+ /* how do we talk RSL with this TRX? */
+ struct gsm_e1_subslot rsl_e1_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;
+
+ /* Some BTS (specifically Ericsson RBS) have a per-TRX OML Link */
+ struct e1inp_sign_link *oml_link;
+
+ struct gsm_abis_mo mo;
+ struct gsm_bts_bb_trx bb_transc;
+
+ uint16_t arfcn;
+ int nominal_power; /* in dBm */
+ unsigned int max_power_red; /* in actual dB */
+
+ union {
+ struct {
+ struct {
+ struct gsm_abis_mo mo;
+ } bbsig;
+ struct {
+ struct gsm_abis_mo mo;
+ } pa;
+ } bs11;
+ struct {
+ unsigned int test_state;
+ uint8_t test_nr;
+ struct rxlev_stats rxlev_stat;
+ } ipaccess;
+ struct {
+ struct osmo_fsm_inst *trx_fi;
+ struct {
+ struct om2k_mo om2k_mo;
+ } trxc;
+ struct {
+ struct om2k_mo om2k_mo;
+ } rx;
+ 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);
+
+struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
+ int *rc);
+
+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);
+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..5944a4a1d 100644
--- a/include/osmocom/bsc/codec_pref.h
+++ b/include/osmocom/bsc/codec_pref.h
@@ -17,6 +17,10 @@ enum rate_pref {
RATE_PREF_FR,
};
+int match_codec_pref_data(struct channel_mode_and_rate *ch_mode_rate,
+ const struct gsm0808_channel_type *ct,
+ const bool full_rate);
+
int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate,
const struct gsm0808_channel_type *ct,
const struct gsm0808_speech_codec_list *scl,
diff --git a/include/osmocom/bsc/ctrl.h b/include/osmocom/bsc/ctrl.h
index 04ca2cb5a..c61e01f85 100644
--- a/include/osmocom/bsc/ctrl.h
+++ b/include/osmocom/bsc/ctrl.h
@@ -1,9 +1,26 @@
#pragma once
#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/bsc/gsm_data.h>
+
+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);
+int bsc_bts_trx_ts_ctrl_cmds_install(void);
+int bsc_bts_trx_ts_lchan_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);
+char *lchan_dump_full_ctrl(const void *t, struct gsm_lchan *lchan);
+char *ts_lchan_dump_full_ctrl(const void *t, struct gsm_bts_trx_ts *ts);
+char *trx_lchan_dump_full_ctrl(const void *t, struct gsm_bts_trx *trx);
+char *bts_lchan_dump_full_ctrl(const void *t, struct gsm_bts *bts);
-struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net,
- const char *bind_addr, uint16_t port);
enum bsc_ctrl_node {
CTRL_NODE_MSC = _LAST_CTRL_NODE,
diff --git a/include/osmocom/bsc/data_rate_pref.h b/include/osmocom/bsc/data_rate_pref.h
new file mode 100644
index 000000000..da3f1fde2
--- /dev/null
+++ b/include/osmocom/bsc/data_rate_pref.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <stdbool.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+struct gsm0808_channel_type;
+struct channel_mode_and_rate;
+
+int match_data_rate_pref(struct channel_mode_and_rate *ch_mode_rate,
+ const struct gsm0808_channel_type *ct,
+ const bool full_rate);
diff --git a/include/osmocom/bsc/debug.h b/include/osmocom/bsc/debug.h
index 82c0703a8..a61753ddf 100644
--- a/include/osmocom/bsc/debug.h
+++ b/include/osmocom/bsc/debug.h
@@ -27,6 +27,10 @@ enum {
DTS,
DAS,
DCBS,
+ DLCS,
+ DASCI,
+ 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 8821251fc..75930baae 100644
--- a/include/osmocom/bsc/gsm_04_08_rr.h
+++ b/include/osmocom/bsc/gsm_04_08_rr.h
@@ -2,6 +2,9 @@
#include <stdint.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/bitvec.h>
+
+enum handover_scope;
struct amr_mode;
struct amr_multirate_conf;
@@ -16,27 +19,27 @@ struct gsm_subscriber_connection;
void gsm_net_update_ctype(struct gsm_network *network);
enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra);
int get_reason_by_chreq(uint8_t ra, int neci);
-int gsm48_send_rr_release(struct gsm_lchan *lchan);
+int gsm48_send_rr_release(struct gsm_lchan *lchan, bool ui);
int send_siemens_mrpci(struct gsm_lchan *lchan,
uint8_t *classmark2_lv);
-int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn,
- struct msgb *msg, struct bsc_subscr *bsub);
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_send_uplink_release(struct gsm_lchan *lchan, uint8_t cause);
+int gsm48_send_uplink_busy(struct gsm_lchan *lchan);
+int gsm48_send_uplink_free(struct gsm_lchan *lchan, uint8_t acc_bit, uint8_t *uic);
int gsm48_rx_rr_modif_ack(struct msgb *msg);
+int neigh_list_get_arfcn(struct gsm_bts *bts, const struct bitvec *nbv, unsigned int idx);
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 b46a8d306..eb2f4da15 100644
--- a/include/osmocom/bsc/gsm_08_08.h
+++ b/include/osmocom/bsc/gsm_08_08.h
@@ -6,10 +6,11 @@
struct gsm_subscriber_connection;
-void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci);
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr);
-int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel);
+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_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,
const uint8_t *cm2, uint8_t cm2_len,
const uint8_t *cm3, uint8_t cm3_len);
+bool bsc_chan_ind_requires_rtp_stream(enum gsm0808_chan_indicator ch_indctr);
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index ce18d1b7e..bd51a42a6 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -4,11 +4,11 @@
#include <stdint.h>
#include <sys/types.h>
#include <stdbool.h>
-#include <stdint.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/gsm/bts_features.h>
@@ -18,28 +18,50 @@
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/tdef.h>
+#include <osmocom/core/time_cc.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/linuxrbtree.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_ramp.h>
-#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/acc.h>
#include <osmocom/bsc/osmux.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))
+
+#define SCCP_CONN_ID_UNSET 0xFFFFFFFF
+#define SCCP_CONN_ID_MAX 0x00FFFFFE
+
struct mgcp_client_conf;
struct mgcp_client;
struct gsm0808_cell_id;
struct osmo_mgcpc_ep;
+struct gsm_bts;
+struct gsm_bts_trx;
/** annotations for msgb ownership */
#define __uses
@@ -49,9 +71,31 @@ struct osmo_mgcpc_ep;
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
@@ -65,22 +109,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;
@@ -96,42 +124,69 @@ 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;
+ bool vgcs;
uint16_t msc_assigned_cic;
- char msc_rtp_addr[INET_ADDRSTRLEN];
+ char msc_rtp_addr[INET6_ADDRSTRLEN];
uint16_t msc_rtp_port;
bool use_osmux;
uint8_t osmux_cid;
/* 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;
+
+ /* The "speech / data indicator" from 3GPP TS 48.008 § 3.2.2.11 Channel Type (speech/data/signalling). */
+ enum gsm0808_chan_indicator ch_indctr;
};
/* 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;
+
+ enum gsm0808_chan_indicator ch_indctr;
+ struct channel_mode_and_rate selected_ch_mode_rate;
struct osmo_fsm_inst *fi;
struct gsm_lchan *new_lchan;
@@ -170,10 +225,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 */
};
@@ -181,6 +242,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.
@@ -191,8 +256,10 @@ struct handover_in_req {
struct gsm0808_cell_id cell_id_target;
char cell_id_target_name[64];
uint16_t msc_assigned_cic;
- char msc_assigned_rtp_addr[INET_ADDRSTRLEN];
+ 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 {
@@ -201,7 +268,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;
@@ -211,6 +282,21 @@ struct handover {
struct osmo_mgcpc_ep_ci *created_ci_for_msc;
};
+struct gsm_subscriber_connection;
+
+struct bscp_sccp_conn_node {
+ /* entry in (struct bsc_sccp_inst)->connections */
+ struct rb_node node;
+ /* Sigtran connection ID:
+ * if set: Range (0..SCCP_CONN_ID_MAX) (24 bit)
+ * if unset: SCCP_CONN_ID_UNSET (-1) if unset */
+ uint32_t conn_id;
+ /* backpointer where this sccp conn belongs to: */
+ struct gsm_subscriber_connection *gscon;
+};
+
+void bscp_sccp_conn_node_init(struct bscp_sccp_conn_node *sccp_conn, struct gsm_subscriber_connection *gscon);
+
/* active radio connection of a mobile subscriber */
struct gsm_subscriber_connection {
/* global linked list of subscriber_connections */
@@ -244,7 +330,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
@@ -258,17 +344,12 @@ struct gsm_subscriber_connection {
/* flag to prevent multiple simultaneous ciphering commands */
int ciphering_handled;
- /* SCCP connection associatd with this subscriber_connection */
+ /* SCCP connection associated with this subscriber_connection */
struct {
- /* for advanced ping/pong */
- int send_ping;
-
/* SCCP connection related */
struct bsc_msc_data *msc;
-
- /* Sigtran connection ID */
- int conn_id;
enum subscr_sccp_state state;
+ struct bscp_sccp_conn_node conn;
} sccp;
/* for audio handling */
@@ -276,7 +357,7 @@ struct gsm_subscriber_connection {
uint16_t msc_assigned_cic;
/* RTP address where the MSC expects us to send the RTP stream coming from the BTS. */
- char msc_assigned_rtp_addr[INET_ADDRSTRLEN];
+ char msc_assigned_rtp_addr[INET6_ADDRSTRLEN];
uint16_t msc_assigned_rtp_port;
/* The endpoint at the MGW used to join both BTS and MSC side connections, e.g.
@@ -302,6 +383,90 @@ struct gsm_subscriber_connection {
/* MS Power Class, TS 05.05 sec 4.1.1 "Mobile station". 0 means unset. */
uint8_t ms_power_class:3;
+
+ bool rx_clear_command;
+
+ /* Location Services handling for this subscriber */
+ struct {
+ /* FSM to handle Perform Location Request coming in from the MSC via A interface,
+ * and receive BSSMAP-LE responses from the SMLC. */
+ struct lcs_loc_req *loc_req;
+
+ /* FSM to handle BSSLAP requests coming in from the SMLC via Lb interface.
+ * BSSLAP APDU are encapsulated in BSSMAP-LE Connection Oriented Information messages. */
+ struct lcs_bsslap *bsslap;
+
+ /* Lb interface to the SMLC: BSSMAP-LE/SCCP connection associated with this subscriber */
+ struct {
+ enum subscr_sccp_state state;
+ struct bscp_sccp_conn_node conn;
+ } 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;
+
+ /* VGCS/VBS "call controling" connection */
+ struct {
+ /* Features supported by MSC/BSC */
+ bool ff_present;
+ struct gsm0808_vgcs_feature_flags ff;
+ /* Group Call Reference IE */
+ struct gsm0808_group_callref gc_ie;
+ enum gsm0808_service_flag sf;
+ uint32_t call_ref;
+ /* Call (BSC) FSM */
+ struct osmo_fsm_inst *fi;
+ /* Current talker */
+ struct gsm_subscriber_connection *talker;
+ /* L3 info of link establihment (Talker established) */
+ struct llist_head l3_queue;
+ /* Flag and cause (Talker released) */
+ bool talker_rel;
+ uint8_t talker_cause;
+ /* Flag that states acknowledgement of the talker by MSC */
+ bool msc_ack;
+ /* List of VGCS/VBS "resource controling" connections */
+ struct llist_head chan_list;
+ } vgcs_call;
+
+ /* VGCS/VBS "resource controling" connection */
+ struct {
+ /* List entry of chan_list of "call controling" connection */
+ struct llist_head list;
+ /* Group Call Reference IE */
+ struct gsm0808_group_callref gc_ie;
+ enum gsm0808_service_flag sf;
+ uint32_t call_ref;
+ /* Channel type IE */
+ struct gsm0808_channel_type ct;
+ /* Channel mode and rate */
+ struct channel_mode_and_rate ch_mode_rate;
+ /* Cell Identifier IE */
+ struct gsm0808_cell_id ci;
+ char ci_str[16];
+ /* Assignment Requirements IE */
+ enum gsm0808_assignment_requirement ar;
+ /* Call Identifier IE */
+ uint32_t call_id;
+ /* Pointer to VGCS/VBS "call controling" gsconn */
+ struct gsm_subscriber_connection *call;
+ /* Cell (BTS) FSM */
+ struct osmo_fsm_inst *fi;
+ /* lchan to be assigned */
+ struct gsm_lchan *new_lchan;
+ /* MGW peer */
+ char msc_rtp_addr[INET6_ADDRSTRLEN];
+ uint16_t msc_rtp_port;
+ } vgcs_chan;
};
@@ -316,21 +481,10 @@ 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
-#define TRX_NR_TS 8
#define TS_MAX_LCHAN 8
#define HARDCODED_ARFCN 123
@@ -372,8 +526,24 @@ struct gsm_abis_mo {
struct abis_om_obj_inst obj_inst;
const char *name;
struct gsm_nm_state nm_state;
- struct tlv_parsed *nm_attr;
struct gsm_bts *bts;
+ struct osmo_fsm_inst *fi;
+ bool sw_act_rep_received;
+ 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;
+ /* vendor specific fields below */
+ union {
+ struct {
+ uint8_t obj_version;
+ } ipaccess;
+ };
};
/* Ericsson OM2000 Managed Object */
@@ -392,13 +562,29 @@ 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)
-#define MAX_EARFCN_LIST 32
-/* is the data link established? who established it? */
-#define LCHAN_SAPI_UNUSED 0
-#define LCHAN_SAPI_MS 1
-#define LCHAN_SAPI_NET 2
+/* 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)
/* BTS ONLY */
#define MAX_NUM_UL_MEAS 104
@@ -438,215 +624,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;
-
- 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;
-
- /* 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];
+#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 {
- 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 {
@@ -661,7 +655,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
@@ -680,7 +674,6 @@ struct gsm_bts_trx_ts {
bool is_rsl_ready;
struct gsm_abis_mo mo;
- struct tlv_parsed nm_attr;
uint8_t nm_chan_comb;
int tsc; /* -1 == use BTS TSC */
@@ -706,169 +699,24 @@ struct gsm_bts_trx_ts {
} rbs2000;
};
- struct gsm_lchan lchan[TS_MAX_LCHAN];
-};
-
-/* One TRX in a BTS */
-struct gsm_bts_trx {
- /* list header in bts->trx_list */
- struct llist_head list;
-
- 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;
-
- /* Timeout for initiating the RSL connection. */
- struct osmo_timer_list rsl_connect_timeout;
-
- /* Some BTS (specifically Ericsson RBS) have a per-TRX OML Link */
- struct e1inp_sign_link *oml_link;
-
- struct gsm_abis_mo mo;
- struct tlv_parsed nm_attr;
- struct {
- struct gsm_abis_mo mo;
- } bb_transc;
-
- uint16_t arfcn;
- int nominal_power; /* in dBm */
- unsigned int max_power_red; /* in actual dB */
-
- union {
- struct {
- struct {
- struct gsm_abis_mo mo;
- } bbsig;
- struct {
- struct gsm_abis_mo mo;
- } pa;
- } bs11;
- struct {
- unsigned int test_state;
- uint8_t test_nr;
- struct rxlev_stats rxlev_stat;
- } ipaccess;
- struct {
- struct {
- struct om2k_mo om2k_mo;
- } trxc;
- struct {
- struct om2k_mo om2k_mo;
- } rx;
- struct {
- struct om2k_mo om2k_mo;
- } tx;
- } rbs2000;
- };
- struct gsm_bts_trx_ts ts[TRX_NR_TS];
-};
-
-#define GSM_BTS_SI2Q(bts, i) (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i])
-#define GSM_BTS_HAS_SI(bts, i) ((bts)->si_valid & (1 << i))
-#define GSM_BTS_SI(bts, i) (void *)((bts)->si_buf[i][0])
-#define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0])
-
-enum gsm_bts_type {
- GSM_BTS_TYPE_UNKNOWN,
- GSM_BTS_TYPE_BS11,
- GSM_BTS_TYPE_NANOBTS,
- GSM_BTS_TYPE_RBS2000,
- GSM_BTS_TYPE_NOKIA_SITE,
- GSM_BTS_TYPE_OSMOBTS,
- _NUM_GSM_BTS_TYPE
-};
-
-enum gsm_bts_type_variant {
- BTS_UNKNOWN,
- BTS_OSMO_LITECELL15,
- BTS_OSMO_OCTPHY,
- BTS_OSMO_SYSMO,
- BTS_OSMO_TRX,
- _NUM_BTS_VARIANT
-};
-
-/* Used by OML layer for BTS Attribute reporting */
-enum bts_attribute {
- BTS_TYPE_VARIANT,
- BTS_SUB_MODEL,
- TRX_PHY_VERSION,
-};
-
-struct vty;
-
-struct gsm_bts_model {
- struct llist_head list;
-
- enum gsm_bts_type type;
- enum gsm_bts_type_variant variant;
- const char *name;
-
- bool started;
- int (*start)(struct gsm_network *net);
- int (*oml_rcvmsg)(struct msgb *msg);
- char * (*oml_status)(const struct gsm_bts *bts);
-
- void (*e1line_bind_ops)(struct e1inp_line *line);
-
- 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);
-
- /* Should SI2bis and SI2ter be disabled by default on this BTS model? */
- bool force_combined_si;
-
- struct tlv_definition nm_att_tlvdef;
-
- /* features of a given BTS model set via gsm_bts_model_register() locally */
- struct bitvec features;
- uint8_t _features_data[MAX_BTS_FEATURES/8];
-};
-
-
-
-/*
- * 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;
+ /* Maximum BCCH carrier power reduction */
+ uint8_t c0_max_power_red_db;
- /* 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 */
- uint16_t remote_port; /* on the SGSN */
- uint32_t remote_ip; /* on the SGSN */
-
- struct gsm_abis_mo mo;
-};
-
enum gprs_rlc_par {
RLC_T3142,
RLC_T3169,
@@ -942,12 +790,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 */
@@ -995,284 +837,31 @@ struct bts_smscb_chan_state {
uint8_t overflow;
};
-struct bts_oml_fail_rep {
- struct llist_head list;
- time_t time;
- struct msgb *mb;
-};
-
-/* One BTS */
-struct gsm_bts {
- /* list header in net->bts_list */
- struct llist_head list;
-
- /* Geographical location of the BTS */
- struct llist_head loc_list;
-
- /* number of this BTS in network */
- uint8_t nr;
- /* human readable name / description */
- char *description;
- /* Cell Identity */
- uint16_t cell_identity;
- /* location area code of this BTS */
- uint16_t location_area_code;
- /* Base Station Identification Code (BSIC), lower 3 bits is BCC,
- * which is used as TSC for the CCCH */
- uint8_t bsic;
- /* type of BTS */
- enum gsm_bts_type type;
- enum gsm_bts_type_variant variant;
- struct gsm_bts_model *model;
- enum gsm_band band;
- char version[MAX_VERSION_LENGTH];
- char sub_model[MAX_VERSION_LENGTH];
-
- /* features of a given BTS set/reported via OML */
- struct bitvec features;
- uint8_t _features_data[MAX_BTS_FEATURES/8];
-
- /* Connected PCU version (if any) */
- char pcu_version[MAX_VERSION_LENGTH];
-
- /* maximum Tx power that the MS is permitted to use in this cell */
- int ms_max_power;
-
- /* how do we talk OML with this TRX? */
- struct gsm_e1_subslot oml_e1_link;
- uint8_t oml_tei;
- struct e1inp_sign_link *oml_link;
- /* Timer to use for deferred drop of OML link, see \ref ipaccess_drop_oml_deferred */
- struct osmo_timer_list oml_drop_link_timer;
- /* when OML link was established */
- time_t uptime;
-
- /* Abis network management O&M handle */
- struct abis_nm_h *nmh;
-
- struct gsm_abis_mo mo;
-
- /* number of this BTS on given E1 link */
- uint8_t bts_nr;
-
- /* DTX features of this BTS */
- enum gsm48_dtx_mode dtxu;
- bool dtxd;
-
- /* paging state and control */
- struct gsm_bts_paging_state paging;
-
- /* CCCH is on C0 */
- struct gsm_bts_trx *c0;
+#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 {
- struct gsm_abis_mo mo;
- } site_mgr;
-
- /* bitmask of all SI that are present/valid in si_buf */
- uint32_t si_valid;
- /* 3GPP TS 44.018 Table 10.5.2.33b.1 INDEX and COUNT for SI2quater */
- uint8_t si2q_index; /* distinguish individual SI2quater messages */
- uint8_t si2q_count; /* si2q_index for the last (highest indexed) individual SI2quater message */
- /* buffers where we put the pre-computed SI */
- sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE][SI2Q_MAX_NUM];
- /* offsets used while generating SI2quater */
- size_t e_offset;
- size_t u_offset;
- /* 3GPP TS 08.58 §8.5.1 BCCH INFORMATION. Some nanoBTS fail upon
- * receival of empty SI disabling unsupported SI. see OS#3707. */
- bool si_unused_send_empty;
-
- /* ip.access Unit ID's have Site/BTS/TRX layout */
- union {
- struct {
- uint16_t site_id;
- uint16_t bts_id;
- uint32_t flags;
- uint32_t rsl_ip;
- } ip_access;
- struct {
- struct {
- struct gsm_abis_mo mo;
- } cclk;
- struct {
- struct gsm_abis_mo mo;
- } rack;
- struct gsm_envabtse envabtse[4];
- } bs11;
- struct {
- struct {
- struct om2k_mo om2k_mo;
- struct gsm_abis_mo mo;
- struct llist_head conn_groups;
- } cf;
- struct {
- struct om2k_mo om2k_mo;
- struct gsm_abis_mo mo;
- struct llist_head conn_groups;
- } is;
- struct {
- struct om2k_mo om2k_mo;
- struct gsm_abis_mo mo;
- struct llist_head conn_groups;
- } con;
- struct {
- struct om2k_mo om2k_mo;
- struct gsm_abis_mo mo;
- } dp;
- struct {
- struct om2k_mo om2k_mo;
- struct gsm_abis_mo mo;
- } tf;
- struct {
- struct om2k_mo om2k_mo;
- struct gsm_abis_mo mo;
- } mctr;
- uint32_t use_superchannel:1;
- struct {
- uint16_t limit;
- uint16_t active;
- } om2k_version[16];
- } rbs2000;
- struct {
- uint8_t bts_type;
- unsigned int configured:1, /* we sent the config data request */
- skip_reset:1, /* skip reset at bootstrap */
- no_loc_rel_cnf:1, /* don't wait for RSL REL CONF */
- bts_reset_timer_cnf, /* timer for BTS RESET */
- did_reset:1, /* we received a RESET ACK */
- wait_reset:2; /* we are waiting for reset to complete */
- struct osmo_timer_list reset_timer;
- } nokia;
- };
-
- /* 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];
- uint8_t rac;
- uint8_t net_ctrl_ord;
- bool ctrl_ack_type_use_block;
- bool egprs_pkt_chan_request;
- } gprs;
-
- /* threshold (in percent) when BTS shall send CCCH LOAD IND */
- int ccch_load_ind_thresh;
-
- /* RACH NM values */
- int rach_b_thresh;
- int rach_ldavg_slots;
-
- /* transceivers */
- int num_trx;
- struct llist_head trx_list;
-
- /* SI related items */
- int force_combined_si;
- bool force_combined_si_set;
- int bcch_change_mark;
-
- /* Abis NM queue */
- struct llist_head abis_queue;
- int abis_nm_pend;
-
- struct gsm_network *network;
-
- /* should the channel allocator allocate channels from high TRX to TRX0,
- * rather than starting from TRX0 and go upwards? */
- int chan_alloc_reverse;
-
- enum neigh_list_manual_mode neigh_list_manual_mode;
- /* parameters from which we build SYSTEM INFORMATION */
- struct {
- struct gsm48_rach_control rach_control;
- uint8_t ncc_permitted;
- struct gsm48_cell_sel_par cell_sel_par;
- struct gsm48_si_selection_params cell_ro_sel_par; /* rest octet */
- struct gsm48_cell_options cell_options;
- struct gsm48_control_channel_descr chan_desc;
- struct bitvec neigh_list;
- struct bitvec cell_alloc;
- struct bitvec si5_neigh_list;
- struct osmo_earfcn_si2q si2quater_neigh_list;
- size_t uarfcn_length; /* index for uarfcn and scramble lists */
- struct {
- /* bitmask large enough for all possible ARFCN's */
- uint8_t neigh_list[1024/8];
- uint8_t cell_alloc[1024/8];
- /* If the user wants a different neighbor list in SI5 than in SI2 */
- uint8_t si5_neigh_list[1024/8];
- uint8_t meas_bw_list[MAX_EARFCN_LIST];
- uint16_t earfcn_list[MAX_EARFCN_LIST];
- uint16_t uarfcn_list[MAX_EARFCN_LIST];
- uint16_t scramble_list[MAX_EARFCN_LIST];
- } data;
- } si_common;
- bool early_classmark_allowed;
- bool early_classmark_allowed_3g;
- /* for testing only: Have an infinitely long radio link timeout */
- bool infinite_radio_link_timeout;
-
- /* do we use static (user-defined) system information messages? (bitmask) */
- uint32_t si_mode_static;
-
- /* access control class ramping */
- struct acc_ramp acc_ramp;
-
- /* exclude the BTS from the global RF Lock handling */
- int excl_from_rf_lock;
-
- /* supported codecs beside FR */
- struct bts_codec_conf codec;
-
- /* BTS dependencies bit field */
- uint32_t depends_on[256/(8*4)];
-
- /* full and half rate multirate config */
- struct amr_multirate_conf mr_full;
- struct amr_multirate_conf mr_half;
-
- /* PCU socket state */
- char *pcu_sock_path;
- struct pcu_sock_state *pcu_state;
-
- struct rate_ctr_group *bts_ctrs;
- struct osmo_stat_item_group *bts_statg;
-
- 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;
-
- /* BTS-specific overrides for timer values from struct gsm_network. */
- uint8_t T3122; /* ASSIGNMENT REJECT wait indication */
- bool T3113_dynamic; /* Calculate T3113 timeout dynamically based on BTS channel config and load */
-
- /* Periodic channel load measurements are used to maintain T3122. */
- struct load_counter chan_load_samples[7];
- int chan_load_samples_idx;
- uint8_t chan_load_avg; /* current channel load average in percent (0 - 100). */
+ 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];
- /* cell broadcast system */
- 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 */
+ /* timer running for the duration of the ETWS Primary Notification (PN) */
+ struct osmo_timer_list timer;
+};
- struct llist_head oml_fail_rep;
+struct bts_oml_fail_rep {
+ struct llist_head list;
+ time_t time;
+ struct msgb *mb;
};
/* One rejected BTS */
@@ -1286,53 +875,30 @@ struct gsm_bts_rejected {
time_t time;
};
+extern struct osmo_tdef_group bsc_tdef_group[];
+
struct gsm_network *gsm_network_init(void *ctx);
-struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num);
struct gsm_bts *gsm_bts_num(const struct gsm_network *net, int num);
-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);
struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net,
const struct gsm0808_cell_id *cell_id,
int match_idx);
-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);
-
-struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts);
-struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num);
-
-enum gsm_bts_type str2btstype(const char *arg);
-const char *btstype2str(enum gsm_bts_type type);
-
-enum bts_attribute str2btsattr(const char *s);
-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);
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_bts_name(const struct gsm_bts *bts);
-char *gsm_trx_name(const struct gsm_bts_trx *trx);
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,
+ uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3);
+struct gsm_abis_mo *gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class,
+ const struct abis_om_obj_inst *obj_inst);
struct gsm_nm_state *
gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class,
const struct abis_om_obj_inst *obj_inst);
@@ -1340,300 +906,28 @@ void *
gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class,
const struct abis_om_obj_inst *obj_inst);
-/* reset the state of all MO in the BTS */
-void gsm_bts_mo_reset(struct gsm_bts *bts);
+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);
-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 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);
-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);
+uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts);
-/* return the gsm_lchan for the CBCH (if it exists at all) */
-struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts);
-
-static inline uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts)
-{
- if (ts->tsc != -1)
- return ts->tsc;
- else
- return ts->trx->bts->bsic & 7;
-}
-
-struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
- int *rc);
-
-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);
-
-static inline struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn) {
- if (!conn || !conn->lchan)
- return NULL;
- return conn->lchan->ts->trx->bts;
-}
+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 bts_counter_id {
- BTS_CTR_CHREQ_TOTAL,
- BTS_CTR_CHREQ_SUCCESSFUL,
- BTS_CTR_CHREQ_NO_CHANNEL,
- BTS_CTR_CHAN_RF_FAIL,
- BTS_CTR_CHAN_RLL_ERR,
- BTS_CTR_BTS_OML_FAIL,
- BTS_CTR_BTS_RSL_FAIL,
- BTS_CTR_CODEC_AMR_F,
- BTS_CTR_CODEC_AMR_H,
- BTS_CTR_CODEC_EFR,
- BTS_CTR_CODEC_V1_FR,
- BTS_CTR_CODEC_V1_HR,
- BTS_CTR_PAGING_ATTEMPTED,
- BTS_CTR_PAGING_ALREADY,
- BTS_CTR_PAGING_RESPONDED,
- BTS_CTR_PAGING_EXPIRED,
- BTS_CTR_PAGING_NO_ACTIVE_PAGING,
- BTS_CTR_PAGING_MSC_FLUSH,
- BTS_CTR_CHAN_ACT_TOTAL,
- BTS_CTR_CHAN_ACT_NACK,
- BTS_CTR_RSL_UNKNOWN,
- BTS_CTR_RSL_IPA_NACK,
- BTS_CTR_RSL_DELETE_IND,
- BTS_CTR_MODE_MODIFY_NACK,
- BTS_CTR_LCHAN_BORKEN_FROM_UNUSED,
- BTS_CTR_LCHAN_BORKEN_FROM_WAIT_ACTIV_ACK,
- BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RF_RELEASE_ACK,
- BTS_CTR_LCHAN_BORKEN_FROM_BORKEN,
- BTS_CTR_LCHAN_BORKEN_FROM_UNKNOWN,
- BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK,
- BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK,
- BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK,
- BTS_CTR_LCHAN_BORKEN_EV_VTY,
- BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN,
- BTS_CTR_TS_BORKEN_FROM_NOT_INITIALIZED,
- BTS_CTR_TS_BORKEN_FROM_UNUSED,
- BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_ACT,
- BTS_CTR_TS_BORKEN_FROM_PDCH,
- BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_DEACT,
- BTS_CTR_TS_BORKEN_FROM_IN_USE,
- BTS_CTR_TS_BORKEN_FROM_BORKEN,
- BTS_CTR_TS_BORKEN_FROM_UNKNOWN,
- BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK,
- BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK,
- BTS_CTR_TS_BORKEN_EV_TEARDOWN,
- BTS_CTR_ASSIGNMENT_ATTEMPTED,
- BTS_CTR_ASSIGNMENT_COMPLETED,
- BTS_CTR_ASSIGNMENT_STOPPED,
- BTS_CTR_ASSIGNMENT_NO_CHANNEL,
- BTS_CTR_ASSIGNMENT_TIMEOUT,
- BTS_CTR_ASSIGNMENT_FAILED,
- BTS_CTR_ASSIGNMENT_ERROR,
-};
-
-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_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"},
-
-};
-
-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,
-};
-
-enum {
- BTS_STAT_CHAN_LOAD_AVERAGE,
- BTS_STAT_CHAN_CCCH_SDCCH4_USED,
- BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL,
- BTS_STAT_CHAN_TCH_F_USED,
- BTS_STAT_CHAN_TCH_F_TOTAL,
- BTS_STAT_CHAN_TCH_H_USED,
- BTS_STAT_CHAN_TCH_H_TOTAL,
- BTS_STAT_CHAN_SDCCH8_USED,
- BTS_STAT_CHAN_SDCCH8_TOTAL,
- BTS_STAT_CHAN_TCH_F_PDCH_USED,
- BTS_STAT_CHAN_TCH_F_PDCH_TOTAL,
- BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED,
- 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_T3122,
- BTS_STAT_RACH_BUSY,
- BTS_STAT_RACH_ACCESS,
- BTS_STAT_OML_CONNECTED,
- BTS_STAT_RSL_CONNECTED,
- BTS_STAT_LCHAN_BORKEN,
- BTS_STAT_TS_BORKEN,
-};
-
-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_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_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_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_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,
-};
struct gsm_tz {
int override; /* if 0, use system's time zone instead. */
@@ -1642,13 +936,27 @@ 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 bsc_sccp_inst {
+ struct osmo_sccp_instance *sccp; /* backpointer */
+ /* rbtree root of 'struct bscp_sccp_conn_node' in this instance, ordered by conn_id */
+ struct rb_root connections;
+ uint32_t next_id; /* next id to allocate */
+};
+
+struct bsc_sccp_inst *bsc_sccp_inst_alloc(void *ctx);
+uint32_t bsc_sccp_inst_next_conn_id(struct bsc_sccp_inst *bsc_sccp);
+int bsc_sccp_inst_register_gscon(struct bsc_sccp_inst *bsc_sccp, struct bscp_sccp_conn_node *sccp_conn);
+void bsc_sccp_inst_unregister_gscon(struct bsc_sccp_inst *bsc_sccp, struct bscp_sccp_conn_node *sccp_conn);
+struct gsm_subscriber_connection *bsc_sccp_inst_get_gscon_by_conn_id(const struct bsc_sccp_inst *bsc_sccp, uint32_t conn_id);
+
+struct gsm_network {
struct osmo_plmn_id plmn;
/* bit-mask of permitted encryption algorithms. LSB=A5/0, MSB=A5/7 */
@@ -1669,6 +977,11 @@ 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;
+
/* see gsm_network_T_defs */
struct osmo_tdef *T_defs;
@@ -1679,10 +992,10 @@ struct gsm_network {
/* msc configuration */
struct llist_head mscs;
- uint8_t mscs_round_robin_next_nr;
+ unsigned int mscs_round_robin_next_nr;
/* Emergency calls potentially select a different set of MSCs, so to not mess up the normal round-robin
* behavior, emergency calls need a separate round-robin counter. */
- uint8_t mscs_round_robin_next_emerg_nr;
+ unsigned int mscs_round_robin_next_emerg_nr;
/* rf ctl related bits */
int mid_call_timeout;
@@ -1695,7 +1008,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. */
@@ -1708,31 +1021,55 @@ struct gsm_network {
* OsmoMSC, this should be tied to the location area code (LAC). */
struct gsm_tz tz;
- /* List of all struct bsc_subscr used in libbsc. This llist_head is
- * allocated so that the llist_head pointer itself can serve as a
- * talloc context (useful to not have to pass the entire gsm_network
- * struct to the bsc_subscr_* API, and for bsc_susbscr unit tests to
- * not require gsm_data.h). In an MSC-without-BSC environment, this
- * pointer is NULL to indicate absence of a bsc_subscribers list. */
- struct llist_head *bsc_subscribers;
+ /* Keeps track of struct bsc_subcr. */
+ struct bsc_subscr_store *bsc_subscribers;
/* 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 smlc_config *smlc;
+
+ struct chan_counts chan_counts;
+ struct all_allocated all_allocated;
+
+ /* PCU socket state */
+ char *pcu_sock_path;
+ unsigned int pcu_sock_wqueue_len_max;
+ struct pcu_sock_state *pcu_state;
};
struct gsm_audio_support {
@@ -1740,20 +1077,10 @@ struct gsm_audio_support {
ver : 7;
};
-static inline const struct osmo_location_area_id *bts_lai(struct gsm_bts *bts)
-{
- static struct osmo_location_area_id lai;
- lai = (struct osmo_location_area_id){
- .plmn = bts->network->plmn,
- .lac = bts->location_area_code,
- };
- return &lai;
-}
+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);
-int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type);
-
enum gsm_bts_type parse_btstype(const char *arg);
const char *btstype2str(enum gsm_bts_type type);
struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
@@ -1761,91 +1088,14 @@ struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
extern void *tall_bsc_ctx;
-/* this actually refers to the IPA transport, not the BTS model */
-static inline int is_ipaccess_bts(const struct gsm_bts *bts)
-{
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- return 1;
- default:
- break;
- }
- return 0;
-}
-
-static inline int is_sysmobts_v2(const struct gsm_bts *bts)
-{
- switch (bts->type) {
- case GSM_BTS_TYPE_OSMOBTS:
- return 1;
- default:
- break;
- }
- return 0;
-}
-
-static inline int is_siemens_bts(const struct gsm_bts *bts)
-{
- switch (bts->type) {
- case GSM_BTS_TYPE_BS11:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-static inline int is_nokia_bts(const struct gsm_bts *bts)
-{
- switch (bts->type) {
- case GSM_BTS_TYPE_NOKIA_SITE:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-static inline int is_ericsson_bts(const struct gsm_bts *bts)
-{
- switch (bts->type) {
- case GSM_BTS_TYPE_RBS2000:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-static inline int is_e1_bts(const struct gsm_bts *bts)
-{
- switch (bts->type) {
- case GSM_BTS_TYPE_BS11:
- case GSM_BTS_TYPE_RBS2000:
- case GSM_BTS_TYPE_NOKIA_SITE:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
extern struct gsm_network *bsc_gsmnet;
enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid);
const char *bts_gprs_mode_name(enum bts_gprs_mode mode);
-int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode);
void gsm48_ra_id_by_bts(struct gsm48_ra_id *buf, struct gsm_bts *bts);
void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
-int gsm_bts_model_register(struct gsm_bts_model *model);
-
struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *network);
struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, uint8_t bsic);
@@ -1854,44 +1104,29 @@ struct gsm_bts *bsc_bts_alloc_register(struct gsm_network *net, enum gsm_bts_typ
void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr,
uint8_t e1_ts, uint8_t e1_ts_ss);
-void gsm_trx_lock_rf(struct gsm_bts_trx *trx, bool locked, const char *reason);
-struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr);
-int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx);
-int gsm_bts_set_system_infos(struct gsm_bts *bts);
-
/* generic E1 line operations for all ISDN-based BTS. */
extern struct e1inp_line_ops bts_isdn_e1inp_line_ops;
-extern const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1];
-extern const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1];
-
-char *get_model_oml_status(const struct gsm_bts *bts);
-
-unsigned long long bts_uptime(const struct gsm_bts *bts);
-
-/* control interface handling */
-int bsc_base_ctrl_cmds_install(void);
-
-/* dependency handling */
-void bts_depend_mark(struct gsm_bts *bts, int dep);
-void bts_depend_clear(struct gsm_bts *bts, int dep);
-int bts_depend_check(struct gsm_bts *bts);
-int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other);
-
-int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts);
-void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value);
-
-bool trx_is_usable(const struct gsm_bts_trx *trx);
bool ts_is_usable(const struct gsm_bts_trx_ts *ts);
int gsm_lchan_type_by_pchan(enum gsm_phys_chan_config pchan);
enum gsm_phys_chan_config gsm_pchan_by_lchan_type(enum gsm_chan_t type);
-void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data);
-void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data);
+enum gsm48_rr_cause bsc_gsm48_rr_cause_from_gsm0808_cause(enum gsm0808_cause c);
+enum gsm48_rr_cause bsc_gsm48_rr_cause_from_rsl_cause(uint8_t c);
+
+/* 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;
-int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan);
+enum rsl_cmod_spd chan_mode_to_rsl_cmod_spd(enum gsm48_chan_mode chan_mode);
-bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx);
+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
new file mode 100644
index 000000000..08206d4d8
--- /dev/null
+++ b/include/osmocom/bsc/lb.h
@@ -0,0 +1,63 @@
+/* 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;
+
+enum {
+ SMLC_CTR_BSSMAP_LE_RX_UNKNOWN_PEER,
+ SMLC_CTR_BSSMAP_LE_RX_UDT_RESET,
+ SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK,
+ SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG,
+ SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG,
+ SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS,
+ SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE,
+ SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_REQUEST,
+
+ SMLC_CTR_BSSMAP_LE_TX_ERR_INVALID_MSG,
+ SMLC_CTR_BSSMAP_LE_TX_ERR_CONN_NOT_READY,
+ SMLC_CTR_BSSMAP_LE_TX_ERR_SEND,
+ SMLC_CTR_BSSMAP_LE_TX_SUCCESS,
+
+ SMLC_CTR_BSSMAP_LE_TX_UDT_RESET,
+ SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK,
+ SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_REQUEST,
+ SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_ABORT,
+ SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_RESPONSE,
+ SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_REJECT,
+ SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_RESET,
+ 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(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..c0c57615d
--- /dev/null
+++ b/include/osmocom/bsc/lchan.h
@@ -0,0 +1,393 @@
+#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/protocol/gsm_08_58.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);
+
+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_* */
+ bool data_transparent;
+ union {
+ enum rsl_cmod_csd_t t;
+ enum rsl_cmod_csd_nt nt;
+ } data_rate;
+};
+
+/* 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_VGCS_CHANNEL,
+ ACTIVATE_FOR_VTY,
+ ACTIVATE_FOR_MODE_MODIFY_RTP,
+};
+
+enum lchan_type_for {
+ LCHAN_TYPE_FOR_NORMAL = 0,
+ LCHAN_TYPE_FOR_VAMOS,
+ LCHAN_TYPE_FOR_VGCS,
+ LCHAN_TYPE_FOR_VBS,
+};
+
+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;
+ enum gsm0808_chan_indicator ch_indctr;
+ 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;
+
+ enum lchan_type_for type_for;
+
+ /* 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;
+ enum gsm0808_chan_indicator ch_indctr;
+ 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;
+
+ enum lchan_type_for type_for;
+};
+
+/* 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;
+ enum gsm0808_chan_indicator ch_indctr;
+ 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;
+ enum gsm0808_chan_indicator ch_indctr;
+ /* 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 rtp_csd_fmt;
+ 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;
+ enum gsm0808_chan_indicator current_ch_indctr;
+
+ /* 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 55ab02400..3c7bbc143 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,
@@ -18,12 +25,16 @@ enum lchan_fsm_state {
LCHAN_ST_WAIT_TS_READY,
LCHAN_ST_WAIT_ACTIV_ACK, /*< After RSL Chan Act Ack, lchan is active but RTP not configured. */
LCHAN_ST_WAIT_RLL_RTP_ESTABLISH,
+ LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK,
+ LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK,
LCHAN_ST_ESTABLISHED, /*< Active and RTP is fully configured. */
LCHAN_ST_WAIT_RLL_RTP_RELEASED,
LCHAN_ST_WAIT_BEFORE_RF_RELEASE,
LCHAN_ST_WAIT_RF_RELEASE_ACK,
LCHAN_ST_WAIT_AFTER_ERROR,
LCHAN_ST_BORKEN,
+ LCHAN_ST_RECOVER_WAIT_ACTIV_ACK, /*< Attempt to recover from BORKEN: first try to activate the lchan */
+ LCHAN_ST_RECOVER_WAIT_RF_RELEASE_ACK, /*< Attempt to recover from BORKEN: then try to release it */
};
enum lchan_fsm_event {
@@ -40,27 +51,28 @@ enum lchan_fsm_event {
LCHAN_EV_RLL_REL_CONF,
LCHAN_EV_RSL_RF_CHAN_REL_ACK,
LCHAN_EV_RLL_ERR_IND,
-
- /* FIXME: not yet implemented: Chan Mode Modify, see assignment_fsm_start(). */
- LCHAN_EV_CHAN_MODE_MODIF_ACK,
- LCHAN_EV_CHAN_MODE_MODIF_ERROR,
+ LCHAN_EV_RR_CHAN_MODE_MODIFY_ACK,
+ LCHAN_EV_RR_CHAN_MODE_MODIFY_ERROR,
+ LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK,
+ LCHAN_EV_RSL_CHAN_MODE_MODIFY_NACK,
+ 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);
@@ -68,6 +80,9 @@ static inline bool lchan_state_is(struct gsm_lchan *lchan, uint32_t state)
bool lchan_may_receive_data(struct gsm_lchan *lchan);
+bool lchan_is_asci(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 865181bf5..d6a1b2026 100644
--- a/include/osmocom/bsc/lchan_select.h
+++ b/include/osmocom/bsc/lchan_select.h
@@ -1,6 +1,29 @@
/* 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,
+ SELECT_FOR_VGCS,
+};
+
+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);
+ 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
new file mode 100644
index 000000000..78f938116
--- /dev/null
+++ b/include/osmocom/bsc/lcs_loc_req.h
@@ -0,0 +1,57 @@
+/* Location Services (LCS): BSSMAP and BSSMAP-LE Perform Location Request handling in OsmoBSC, API */
+#pragma once
+
+#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)
+
+struct lcs_ta_req;
+
+enum lcs_loc_req_fsm_event {
+ LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE,
+ LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT,
+ LCS_LOC_REQ_EV_TA_REQ_START,
+ LCS_LOC_REQ_EV_TA_REQ_END,
+ LCS_LOC_REQ_EV_HANDOVER_PERFORMED,
+ LCS_LOC_REQ_EV_CONN_CLEAR,
+};
+
+struct lcs_loc_req {
+ struct osmo_fsm_inst *fi;
+ struct gsm_subscriber_connection *conn;
+
+ struct {
+ struct bssmap_le_location_type location_type;
+
+ 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;
+
+ bool resp_present;
+ struct bssmap_le_perform_loc_resp resp;
+
+ struct lcs_cause_ie lcs_cause;
+
+ struct lcs_ta_req *ta_req;
+};
+
+void lcs_loc_req_start(struct gsm_subscriber_connection *conn, struct msgb *msg);
+int lcs_loc_req_rx_bssmap_le(struct gsm_subscriber_connection *conn, struct msgb *msg);
+void lcs_loc_req_reset(struct gsm_subscriber_connection *conn);
diff --git a/include/osmocom/bsc/lcs_ta_req.h b/include/osmocom/bsc/lcs_ta_req.h
new file mode 100644
index 000000000..bdfc14f60
--- /dev/null
+++ b/include/osmocom/bsc/lcs_ta_req.h
@@ -0,0 +1,29 @@
+/* Location Services (LCS): BSSLAP TA Request handling in OsmoBSC, API */
+#pragma once
+
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/gsm/bssmap_le.h>
+
+#define LOG_LCS_TA_REQ(TA_REQ, level, fmt, args...) do { \
+ if (TA_REQ) \
+ LOGPFSML((TA_REQ)->fi, level, fmt, ## args); \
+ else \
+ LOGP(DLCS, level, "LCS TA Req: " fmt, ## args); \
+ } while (0)
+
+enum lcs_ta_req_fsm_event {
+ LCS_TA_REQ_EV_GOT_TA,
+ LCS_TA_REQ_EV_ABORT,
+};
+
+struct lcs_ta_req {
+ struct osmo_fsm_inst *fi;
+ struct lcs_loc_req *loc_req;
+ enum lcs_cause failure_cause;
+ uint8_t failure_diagnostic_val;
+};
+int lcs_ta_req_start(struct lcs_loc_req *lcs_loc_req);
+
+void lcs_bsslap_rx(struct gsm_subscriber_connection *conn, struct msgb *msg);
diff --git a/include/osmocom/bsc/meas_feed.h b/include/osmocom/bsc/meas_feed.h
index 353278e71..447eab8c5 100644
--- a/include/osmocom/bsc/meas_feed.h
+++ b/include/osmocom/bsc/meas_feed.h
@@ -35,9 +35,12 @@ enum meas_feed_msgtype {
};
#define MEAS_FEED_VERSION 1
+#define MEAS_FEED_TXQUEUE_MAX_LEN_DEFAULT 100
int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port);
void meas_feed_scenario_set(const char *name);
+void meas_feed_txqueue_max_length_set(unsigned int max_length);
void meas_feed_cfg_get(char **host, uint16_t *port);
const char *meas_feed_scenario_get(void);
+unsigned int meas_feed_txqueue_max_length_get(void);
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..91be64411
--- /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.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 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 afc319b25..ee8cc1312 100644
--- a/include/osmocom/bsc/osmo_bsc.h
+++ b/include/osmocom/bsc/osmo_bsc.h
@@ -17,13 +17,7 @@ struct gsm_audio_support;
struct gsm_subscriber_connection;
struct gsm_bts;
-struct bsc_api *osmo_bsc_api();
-
-int bsc_queue_for_msc(struct gsm_subscriber_connection *conn, struct msgb *msg);
-int bsc_open_connection(struct gsm_subscriber_connection *sccp, struct msgb *msg);
-enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn,
- struct bsc_msc_data *msc, int send_ping);
-int bsc_delete_connection(struct gsm_subscriber_connection *sccp);
+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);
@@ -31,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, unsigned int len);
-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_grace.h b/include/osmocom/bsc/osmo_bsc_grace.h
index d78e41c82..07dce8cd5 100644
--- a/include/osmocom/bsc/osmo_bsc_grace.h
+++ b/include/osmocom/bsc/osmo_bsc_grace.h
@@ -27,10 +27,5 @@
struct bsc_msc_data;
int bsc_grace_allow_new_connection(struct gsm_network *net, struct gsm_bts *bts);
-int bsc_grace_paging_request(enum signal_rf rf_policy,
- struct bsc_subscr *subscr,
- int chan_needed,
- struct bsc_msc_data *msc,
- struct gsm_bts *bts);
#endif
diff --git a/include/osmocom/bsc/osmo_bsc_reset.h b/include/osmocom/bsc/osmo_bsc_reset.h
deleted file mode 100644
index fb66df03d..000000000
--- a/include/osmocom/bsc/osmo_bsc_reset.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* (C) 2017 by sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU 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/>.
- *
- */
-
-/* Create and start state machine which handles the reset/reset-ack procedure */
-void start_reset_fsm(struct bsc_msc_data *msc);
-
-/* Confirm that we successfully received a reset acknowledge message */
-void reset_ack_confirm(struct bsc_msc_data *msc);
-
-/* Report a failed connection */
-void report_conn_fail(struct bsc_msc_data *msc);
-
-/* Report a successful connection */
-void report_conn_success(struct bsc_msc_data *msc);
-
-/* Check if we have a connection to a specified msc */
-bool sccp_conn_ready(struct bsc_msc_data *msc);
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 dc86fade9..2ee35e53c 100644
--- a/include/osmocom/bsc/osmo_bsc_sigtran.h
+++ b/include/osmocom/bsc/osmo_bsc_sigtran.h
@@ -37,7 +37,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 2be71c39f..054e5c78d 100644
--- a/include/osmocom/bsc/paging.h
+++ b/include/osmocom/bsc/paging.h
@@ -28,52 +28,135 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bsc_subscriber.h>
+#include <osmocom/bsc/bsc_msc_data.h>
struct bsc_msc_data;
+#define LOG_PAGING(PARAMS, SUBSYS, LEVEL, fmt, args...) \
+ 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)
+
+#define BSUB_USE_PAGING_START "paging-start"
+#define BSUB_USE_PAGING_REQUEST "paging-req"
+
+/* Bitmask of reasons for Paging. Each individual Paging via bsc_paging_start() typically has only one of these reasons
+ * set, but when a subscriber responds, we need to aggregate all pending Paging reasons (by bitwise-OR). */
+enum bsc_paging_reason {
+ BSC_PAGING_NONE = 0,
+ BSC_PAGING_FROM_CN = 0x1,
+ 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;
+ struct bsc_subscr *bsub;
+ uint32_t tmsi;
+ struct osmo_mobile_identity imsi;
+ uint8_t chan_needed;
+ struct gsm0808_cell_id_list2 cil;
+};
+
/**
* A pending paging request
*/
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;
+
+ enum bsc_paging_reason reason;
};
-/* schedule paging request */
-int paging_request_bts(struct gsm_bts *bts, struct bsc_subscr *bsub, int type,
- struct bsc_msc_data *msc);
+/*
+ * 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];
-/* stop paging requests */
-void paging_request_stop(struct llist_head *bts_list,
- struct gsm_bts *_bts, struct bsc_subscr *bsub,
- struct gsm_subscriber_connection *conn,
- struct msgb *msg);
+ struct gsm_bts *bts;
-/* update paging load */
-void paging_update_buffer_space(struct gsm_bts *bts, uint16_t);
+ struct osmo_timer_list work_timer;
+ struct osmo_timer_list credit_timer;
-/* pending paging requests */
-unsigned int paging_pending_requests_nr(struct gsm_bts *bts);
+ /* 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);
-struct bsc_msc_data *paging_get_msc(struct gsm_bts *bts, struct bsc_subscr *bsub);
+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);
+
+void paging_request_stop(struct bsc_msc_data **msc_p, enum bsc_paging_reason *reasons_p,
+ struct gsm_bts *bts, struct bsc_subscr *bsub);
+void paging_request_cancel(struct bsc_subscr *bsub, enum bsc_paging_reason reasons);
+
+/* pending paging requests */
+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..f9aa9edbf 100644
--- a/include/osmocom/bsc/pcu_if.h
+++ b/include/osmocom/bsc/pcu_if.h
@@ -1,17 +1,24 @@
#ifndef _PCU_IF_H
#define _PCU_IF_H
+#include <osmocom/core/write_queue.h>
#include <osmocom/gsm/l1sap.h>
extern int pcu_direct;
+#define PCUIF_HDR_SIZE (sizeof(struct gsm_pcu_if) - sizeof(((struct gsm_pcu_if *)0)->u))
+
+#define BSC_PCU_SOCK_WQUEUE_LEN_DEFAULT 100
+
struct pcu_sock_state {
- struct gsm_network *net;
+ struct gsm_network *net; /* backpointer to GSM network */
struct osmo_fd listen_bfd; /* fd for listen socket */
- struct osmo_fd conn_bfd; /* fd for connection to lcr */
- struct llist_head upqueue; /* queue for sending messages */
+ struct osmo_wqueue upqueue; /* For sending messages; has fd for conn. to PCU */
};
+/* Check if BTS has a PCU connection */
+bool pcu_connected(const struct gsm_network *net);
+
/* PCU relevant information has changed; Inform PCU (if connected) */
void pcu_info_update(struct gsm_bts *bts);
@@ -19,17 +26,13 @@ void pcu_info_update(struct gsm_bts *bts);
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn,
uint8_t is_11bit, enum ph_burst_type burst_type);
-/* Confirm the sending of an immediate assignment to the pcu */
-int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli);
-
-
-/* Confirm the sending of an immediate assignment to the pcu */
-int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli);
+/* Confirm the sending of an AGCH or PCH MAC block to the pcu */
+int pcu_tx_data_cnf(struct gsm_bts *bts, uint32_t msg_id, uint8_t sapi);
/* Open connection to PCU */
-int pcu_sock_init(const char *path, struct gsm_bts *bts);
+int pcu_sock_init(struct gsm_network *net);
/* Close connection to PCU */
-void pcu_sock_exit(struct gsm_bts *bts);
+void pcu_sock_exit(struct gsm_network *net);
#endif /* _PCU_IF_H */
diff --git a/include/osmocom/bsc/pcuif_proto.h b/include/osmocom/bsc/pcuif_proto.h
index b9f61b6f1..33036c330 100644
--- a/include/osmocom/bsc/pcuif_proto.h
+++ b/include/osmocom/bsc/pcuif_proto.h
@@ -2,36 +2,48 @@
#define _PCUIF_PROTO_H
#include <osmocom/gsm/l1sap.h>
+#include <arpa/inet.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
-#define PCU_IF_VERSION 0x09
+#define PCU_SOCK_DEFAULT "/tmp/pcu_bts"
+
+#define PCU_IF_VERSION 0x0c
#define TXT_MAX_LEN 128
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
-#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
+#define PCU_IF_MSG_SUSP_REQ 0x03 /* BTS forwards GPRS SUSP REQ to PCU */
+#define PCU_IF_MSG_APP_INFO_REQ 0x04 /* BTS asks PCU to transmit APP INFO via PACCH */
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
-#define PCU_IF_MSG_DATA_CNF_DT 0x11 /* confirm (with direct tlli) */
+#define PCU_IF_MSG_DATA_CNF_2 0x11 /* confirm (using message id) */
#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 */
-#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
-#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */
#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */
#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_2 0x08 /* assignment on PCH (confirmed using message id) */
+#define PCU_IF_SAPI_AGCH_2 0x09 /* assignment on AGCH (confirmed using message id) */
/* 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_DIRECT_PHY (1 << 1)/* access PHY directly via dedicated hardware support */
#define PCU_IF_FLAG_CS1 (1 << 16)
#define PCU_IF_FLAG_CS2 (1 << 17)
#define PCU_IF_FLAG_CS3 (1 << 18)
@@ -46,6 +58,25 @@
#define PCU_IF_FLAG_MCS8 (1 << 27)
#define PCU_IF_FLAG_MCS9 (1 << 28)
+/* NSVC address type */
+#define PCU_IF_ADDR_TYPE_UNSPEC 0x00 /* No address - empty entry */
+#define PCU_IF_ADDR_TYPE_IPV4 0x04 /* IPv4 address */
+#define PCU_IF_ADDR_TYPE_IPV6 0x29 /* IPv6 address */
+
+/* BTS model */
+enum gsm_pcuif_bts_model {
+ PCU_IF_BTS_MODEL_UNSPEC,
+ PCU_IF_BTS_MODEL_LC15,
+ PCU_IF_BTS_MODEL_OC2G,
+ PCU_IF_BTS_MODEL_OCTPHY,
+ PCU_IF_BTS_MODEL_SYSMO,
+ PCU_IF_BTS_MODEL_TRX,
+ PCU_IF_BTS_MODEL_RBS,
+};
+
+#define PCU_IF_NUM_NSVC 2
+#define PCU_IF_NUM_TRX 8
+
enum gsm_pcu_if_text_type {
PCU_VERSION,
PCU_OML_ALERT,
@@ -71,19 +102,10 @@ struct gsm_pcu_if_data {
int16_t lqual_cb; /* !< \brief Link quality in centiBel */
} __attribute__ ((packed));
-/* data confirmation with direct tlli (instead of raw mac block with tlli) */
-struct gsm_pcu_if_data_cnf_dt {
+/* data confirmation with message id (instead of raw mac block) */
+struct gsm_pcu_if_data_cnf {
uint8_t sapi;
- uint32_t tlli;
- uint32_t fn;
- uint16_t arfcn;
- uint8_t trx_nr;
- uint8_t ts_nr;
- uint8_t block_nr;
- int8_t rssi;
- uint16_t ber10k; /* !< \brief BER in units of 0.01% */
- int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */
- int16_t lqual_cb; /* !< \brief Link quality in centiBel */
+ uint32_t msg_id;
} __attribute__ ((packed));
struct gsm_pcu_if_rts_req {
@@ -104,20 +126,31 @@ struct gsm_pcu_if_rach_ind {
uint16_t arfcn;
uint8_t is_11bit;
uint8_t burst_type;
+ uint8_t trx_nr;
+ uint8_t ts_nr;
+} __attribute__ ((packed));
+
+struct gsm_pcu_if_info_trx_ts {
+ uint8_t tsc;
+ uint8_t hopping;
+ uint8_t hsn;
+ uint8_t maio;
+ uint8_t ma_bit_len;
+ uint8_t ma[8];
} __attribute__ ((packed));
struct gsm_pcu_if_info_trx {
uint16_t arfcn;
- uint8_t pdch_mask; /* PDCH channels per TS */
+ uint8_t pdch_mask; /* PDCH timeslot mask */
uint8_t spare;
- uint8_t tsc[8]; /* TSC per channel */
uint32_t hlayer1;
+ struct gsm_pcu_if_info_trx_ts ts[8];
} __attribute__ ((packed));
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;
@@ -146,10 +179,26 @@ 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];
- uint32_t remote_ip[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[PCU_IF_NUM_NSVC];
+ uint8_t bts_model; /* enum gsm_pcuif_bts_model */
+} __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 {
@@ -169,6 +218,86 @@ struct gsm_pcu_if_pag_req {
uint8_t identity_lv[9];
} __attribute__ ((packed));
+/* BTS tells PCU to [once] send given application data via PACCH to all UE with active TBF */
+struct gsm_pcu_if_app_info_req {
+ uint8_t application_type; /* 4bit field, see TS 44.060 11.2.47 */
+ uint8_t len; /* length of data */
+ uint8_t data[162]; /* random size choice; ETWS needs 56 bytes */
+} __attribute__ ((packed));
+
+/* BTS tells PCU about a GPRS SUSPENSION REQUEST received on DCCH */
+struct gsm_pcu_if_susp_req {
+ uint32_t tlli;
+ uint8_t ra_id[6];
+ 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 to send a (confirmed) IMMEDIATE ASSIGNMENT message via PCH. The struct is sent as a data request
+ * (data_req) under SAPI PCU_IF_SAPI_PCH_2. */
+struct gsm_pcu_if_pch {
+ /* message id as reference for confirmation */
+ uint32_t msg_id;
+ /* IMSI (to derive paging group) */
+ char imsi[OSMO_IMSI_BUF_SIZE];
+ /* GSM mac-block (with immediate assignment message) */
+ uint8_t data[GSM_MACBLOCK_LEN];
+ /* Set to true in case the receiving end must send a confirmation
+ * when the MAC block (data) has been sent. */
+ bool confirm;
+} __attribute__((packed));
+
+/* Struct to send a (confirmed) IMMEDIATE ASSIGNMENT message via AGCH. The struct is sent as a data request
+ * (data_req) under SAPI PCU_IF_SAPI_AGCH_2. */
+struct gsm_pcu_if_agch {
+ /* message id as reference for confirmation */
+ uint32_t msg_id;
+ /* GSM mac-block (with immediate assignment message) */
+ uint8_t data[GSM_MACBLOCK_LEN];
+ /* Set to true in case the receiving end must send a confirmation
+ * when the MAC block (data) has been sent. */
+ bool confirm;
+} __attribute__((packed));
+
struct gsm_pcu_if {
/* context based information */
uint8_t msg_type; /* message type */
@@ -177,16 +306,20 @@ struct gsm_pcu_if {
union {
struct gsm_pcu_if_data data_req;
- struct gsm_pcu_if_data data_cnf;
- struct gsm_pcu_if_data_cnf_dt data_cnf_dt;
+ struct gsm_pcu_if_data_cnf data_cnf2;
struct gsm_pcu_if_data data_ind;
+ struct gsm_pcu_if_susp_req susp_req;
struct gsm_pcu_if_rts_req rts_req;
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 952e03ce1..4d5080da2 100644
--- a/include/osmocom/bsc/signal.h
+++ b/include/osmocom/bsc/signal.h
@@ -68,11 +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 */
@@ -116,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 22a258da9..b4f2e196f 100644
--- a/include/osmocom/bsc/smscb.h
+++ b/include/osmocom/bsc/smscb.h
@@ -2,12 +2,14 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/sockaddr_str.h>
#include <osmocom/netif/stream.h>
#include <osmocom/gsm/cbsp.h>
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);
@@ -17,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);
@@ -25,23 +26,29 @@ 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,
+ BSC_CBC_LINK_MODE_SERVER,
+ BSC_CBC_LINK_MODE_CLIENT,
+};
+
+extern const struct value_string bsc_cbc_link_mode_names[];
+static inline const char *bsc_cbc_link_mode_name(enum bsc_cbc_link_mode val)
+{ return get_value_string(bsc_cbc_link_mode_names, val); }
+
+extern const struct osmo_sockaddr_str bsc_cbc_default_server_local_addr;
/* cbsp_link.c */
struct bsc_cbc_link {
struct gsm_network *net;
- struct {
- /* hostname/IP of CBC */
- char *cbc_hostname;
- /* TCP port (Default: 48049) of CBC */
- int cbc_port;
- /* local listening port (0 for disabling local server) */
- int listen_port;
- /* local listening hostname/IP */
- char *listen_hostname;
- } config;
+ enum bsc_cbc_link_mode mode;
/* for handling inbound TCP connections */
struct {
+ struct osmo_sockaddr_str local_addr;
struct osmo_stream_srv *srv;
struct osmo_stream_srv_link *link;
char *sock_name;
@@ -49,11 +56,18 @@ struct bsc_cbc_link {
} server;
/* for handling outbound TCP connections */
struct {
+ struct osmo_sockaddr_str remote_addr;
+ struct osmo_sockaddr_str local_addr;
struct osmo_stream_cli *cli;
char *sock_name;
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..46213f619 100644
--- a/include/osmocom/bsc/system_information.h
+++ b/include/osmocom/bsc/system_information.h
@@ -3,18 +3,26 @@
#include <osmocom/gsm/sysinfo.h>
-#include <osmocom/bsc/arfcn_range_encode.h>
+#include <osmocom/gsm/gsm48_arfcn_range_encode.h>
+
+/* Complete length of SYSTEM INFORMATION 10 (SACCH) */
+#define SI10_LENGTH 21
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);
+int gsm_generate_si10(struct gsm48_system_information_type_10 *si10, size_t len,
+ const struct gsm_subscriber_connection *conn);
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_del(struct gsm_bts *bts, uint16_t earfcn);
int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio,
uint8_t qrx, uint8_t meas_bw);
int bts_uarfcn_del(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble);
diff --git a/include/osmocom/bsc/timeslot_fsm.h b/include/osmocom/bsc/timeslot_fsm.h
index d02e156df..c1b61b812 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,
@@ -36,18 +36,30 @@ enum ts_fsm_event {
TS_EV_RSL_DOWN,
TS_EV_LCHAN_REQUESTED,
TS_EV_LCHAN_UNUSED,
+
+ /* RSL responses received from the BTS: */
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,
+
+ /* BSC co-located PCU disconnects from PCU socket, deactivate PDCH */
+ TS_EV_PDCH_DEACT,
-void ts_fsm_init();
+ /* BSC co-located PCU (re)connects to PCU socket, activate PDCH */
+ TS_EV_PDCH_ACT,
+};
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);
+
+void ts_pdch_act(struct gsm_bts_trx_ts *ts);
+void ts_pdch_deact(struct gsm_bts_trx_ts *ts);
diff --git a/include/osmocom/bsc/vgcs_fsm.h b/include/osmocom/bsc/vgcs_fsm.h
new file mode 100644
index 000000000..80ea21fd0
--- /dev/null
+++ b/include/osmocom/bsc/vgcs_fsm.h
@@ -0,0 +1,121 @@
+/* Handle a call via VGCS/VBCS (Voice Group/Broadcast Call Service). */
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * Author: Andreas Eversberg
+ *
+ * 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
+
+/* Events for both VGCS/VBS state machines. */
+enum vgcs_fsm_event {
+ /* The BSC sets up a VGCS/VBS call. */
+ VGCS_EV_SETUP,
+ /* The BSC wants to assign a VGCS/VBS channel. */
+ VGCS_EV_ASSIGN_REQ,
+ /* The BTS detects a talker on a channel. */
+ VGCS_EV_TALKER_DET,
+ /* The BTS detects a listener on a channel. */
+ VGCS_EV_LISTENER_DET,
+ /* The MSC accepts a talker. */
+ VGCS_EV_MSC_ACK,
+ /* The MSC rejects a talker. */
+ VGCS_EV_MSC_REJECT,
+ /* The MSC seizes all channels. (blocking for calls) */
+ VGCS_EV_MSC_SEIZE,
+ /* The MSC releases all channels. (unblocking for calls) */
+ VGCS_EV_MSC_RELEASE,
+ /* The MSC sends message to talker. (E.g. CONNECT) */
+ VGCS_EV_MSC_DTAP,
+ /* Channel is now active. Waiting for Talker. */
+ VGCS_EV_LCHAN_ACTIVE,
+ /* Channel activation error. */
+ VGCS_EV_LCHAN_ERROR,
+ /* MGW connection is now active. Waiting for Talker. */
+ VGCS_EV_MGW_OK,
+ /* MGW connection error. */
+ VGCS_EV_MGW_FAIL,
+ /* Channel link established. (Talker establised.) */
+ VGCS_EV_TALKER_EST,
+ /* Channel link data. (Talker sends data.) */
+ VGCS_EV_TALKER_DATA,
+ /* Channel link released. (Talker released.) */
+ VGCS_EV_TALKER_REL,
+ /* Channel link failed. (Talker failed.) */
+ VGCS_EV_TALKER_FAIL,
+ /* Channel is blocked by BSC. */
+ VGCS_EV_BLOCK,
+ /* Channel is rejected by BSC. */
+ VGCS_EV_REJECT,
+ /* Channel is unblocked by BSC. */
+ VGCS_EV_UNBLOCK,
+ /* The connection will be destroyed. (free VGCS resources) */
+ VGCS_EV_CLEANUP,
+ /* The calling subscriber has been assigned to the group channel. */
+ VGCS_EV_CALLING_ASSIGNED,
+};
+
+
+/* States of the VGCS/VBS call state machine */
+enum vgcs_call_fsm_state {
+ /* Call is not setup. Initial state when instance is created. */
+ VGCS_CALL_ST_NULL = 0,
+ /* Call is idle. */
+ VGCS_CALL_ST_IDLE,
+ /* Call is busy, due to a talker in this BSC. */
+ VGCS_CALL_ST_BUSY,
+ /* Call is blocked, due to a talker in a different BSC. */
+ VGCS_CALL_ST_BLOCKED,
+};
+
+/* States of the VGCS/VBS channel state machine */
+enum vgcs_chan_fsm_state {
+ /* Channel not assigned. Initial state when instance is created. */
+ VGCS_CHAN_ST_NULL = 0,
+ /* Wait for establishment of VGCS/VBS channel at BTS. */
+ VGCS_CHAN_ST_WAIT_EST,
+ /* Channel active and idle. Channel is marked as uplink busy. */
+ VGCS_CHAN_ST_ACTIVE_BLOCKED,
+ /* Channel active and idle. Channel is marked as uplink free. */
+ VGCS_CHAN_ST_ACTIVE_FREE,
+ /* Channel active and talker was detected, L2 must be established. */
+ VGCS_CHAN_ST_ACTIVE_INIT,
+ /* Channel active and talker established L2. */
+ VGCS_CHAN_ST_ACTIVE_EST,
+ /* Channel active and wait for talker to release L2. */
+ VGCS_CHAN_ST_ACTIVE_REL,
+};
+
+int vgcs_vbs_chan_start(struct gsm_subscriber_connection *conn, struct msgb *msg);
+int vgcs_vbs_call_start(struct gsm_subscriber_connection *conn, struct msgb *msg);
+
+int bssmap_handle_ass_req_ct_speech(struct gsm_subscriber_connection *conn, struct gsm_bts *bts,
+ struct tlv_parsed *tp, struct gsm0808_channel_type *ct,
+ struct assignment_request *req, uint8_t *cause);
+void bsc_tx_setup_ack(struct gsm_subscriber_connection *conn, struct gsm0808_vgcs_feature_flags *ff);
+void bsc_tx_setup_refuse(struct gsm_subscriber_connection *conn, uint8_t cause);
+void bsc_tx_vgcs_vbs_assignment_result(struct gsm_subscriber_connection *conn, struct gsm0808_channel_type *ct,
+ struct gsm0808_cell_id *ci, uint32_t call_id);
+void bsc_tx_vgcs_vbs_assignment_fail(struct gsm_subscriber_connection *conn, uint8_t cause);
+void bsc_tx_uplink_req(struct gsm_subscriber_connection *conn);
+void bsc_tx_uplink_req_conf(struct gsm_subscriber_connection *conn, struct gsm0808_cell_id *ci, uint8_t *l3_info,
+ uint8_t length);
+void bsc_tx_uplink_app_data(struct gsm_subscriber_connection *conn, struct gsm0808_cell_id *ci, uint8_t *l3_info,
+ uint8_t length);
+void bsc_tx_uplink_release_ind(struct gsm_subscriber_connection *conn, uint8_t cause);
+struct gsm_lchan *vgcs_vbs_find_lchan(struct gsm_bts *bts, struct gsm0808_group_callref *gc);
diff --git a/include/osmocom/bsc/vty.h b/include/osmocom/bsc/vty.h
index 10ce16b2d..092bc20dd 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,23 +23,77 @@ 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,
BSC_NODE,
CBC_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 bts_dump_vty_oml_link_state(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/osmoappdesc.py b/osmoappdesc.py
index 08c3252c1..4565a22d6 100644
--- a/osmoappdesc.py
+++ b/osmoappdesc.py
@@ -15,7 +15,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>
app_configs = {
+ # TODO: doc/examples/osmo-bsc/{ericsson,nokia,siemens}
"osmo-bsc": ["doc/examples/osmo-bsc/osmo-bsc.cfg",
+ "doc/examples/osmo-bsc/osmo-bsc-minimal.cfg",
+ "doc/examples/osmo-bsc/osmo-bsc-4trx.cfg",
"doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg"]
}
diff --git a/src/Makefile.am b/src/Makefile.am
index 43b00b284..45d4df74b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,12 +13,6 @@ AM_CFLAGS = \
$(COVERAGE_CFLAGS) \
$(NULL)
-AM_LDFLAGS = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(COVERAGE_LDFLAGS) \
- $(NULL)
-
SUBDIRS = \
osmo-bsc \
utils \
diff --git a/src/ipaccess/Makefile.am b/src/ipaccess/Makefile.am
index 145ea3989..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,20 +48,16 @@ ipaccess_config_SOURCES = \
# FIXME: resolve the bogus dependencies patched around here:
ipaccess_config_LDADD = \
- $(top_builddir)/src/osmo-bsc/abis_nm.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)
ipaccess_proxy_SOURCES = \
ipaccess-proxy.c \
stubs.c \
- $(top_srcdir)/src/osmo-bsc/gsm_data.c \
$(NULL)
ipaccess_proxy_LDADD = \
+ $(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 21ed50e5a..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;
@@ -403,10 +403,10 @@ static int read_response(int fd)
static int bfd_cb(struct osmo_fd *bfd, unsigned int flags)
{
- if (flags & BSC_FD_READ)
+ if (flags & OSMO_FD_READ)
return read_response(bfd->fd);
- if (flags & BSC_FD_WRITE) {
- bfd->when &= ~BSC_FD_WRITE;
+ if (flags & 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 |= BSC_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 = BSC_FD_READ | BSC_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 306cc8786..3df9f61cb 100644
--- a/src/ipaccess/ipaccess-config.c
+++ b/src/ipaccess/ipaccess-config.c
@@ -56,8 +56,7 @@
#include <osmocom/abis/abis.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmocom/bsc/bss.h>
-
-struct gsm_network *bsc_gsmnet;
+#include <osmocom/bsc/bts.h>
static int net_listen_testnr;
static int restart;
@@ -92,7 +91,7 @@ static int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bfd->cb = ipaccess_fd_cb;
- bfd->when = BSC_FD_READ | BSC_FD_WRITE;
+ bfd->when = OSMO_FD_READ | OSMO_FD_WRITE;
bfd->data = line;
bfd->priv_nr = E1INP_SIGN_OML;
@@ -130,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)
@@ -141,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)
@@ -191,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);
@@ -268,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 */
@@ -281,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,
@@ -305,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");
@@ -323,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");
@@ -341,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;
@@ -501,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" },
@@ -516,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)
@@ -541,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);
@@ -631,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) {
@@ -640,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)
@@ -925,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,
},
};
@@ -1103,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();
@@ -1120,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)
@@ -1129,21 +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;
-}
-
-/* Stub */
-int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- return 0;
-}
-
-/* Stub */
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan)
-{
- return 0;
-}
diff --git a/src/ipaccess/ipaccess-proxy.c b/src/ipaccess/ipaccess-proxy.c
index f4d620b34..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 |= BSC_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 &= ~BSC_FD_WRITE;
+ osmo_fd_write_disable(bfd);
return -EIO;
}
@@ -303,9 +303,9 @@ static int udp_fd_cb(struct osmo_fd *bfd, unsigned int what)
{
int rc = 0;
- if (what & BSC_FD_READ)
+ if (what & OSMO_FD_READ)
rc = handle_udp_read(bfd);
- if (what & BSC_FD_WRITE)
+ if (what & OSMO_FD_WRITE)
rc = handle_udp_write(bfd);
return rc;
@@ -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 |= BSC_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 &= ~BSC_FD_WRITE;
+ osmo_fd_write_disable(bfd);
return 0;
}
lh = ipc->tx_queue.next;
@@ -897,12 +897,12 @@ static int proxy_ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what)
{
int rc = 0;
- if (what & BSC_FD_READ) {
+ if (what & OSMO_FD_READ) {
rc = handle_tcp_read(bfd);
if (rc < 0)
return rc;
}
- if (what & BSC_FD_WRITE)
+ if (what & OSMO_FD_WRITE)
rc = handle_tcp_write(bfd);
return rc;
@@ -917,7 +917,7 @@ static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
struct sockaddr_in sa;
socklen_t sa_len = sizeof(sa);
- if (!(what & BSC_FD_READ))
+ if (!(what & OSMO_FD_READ))
return 0;
ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
@@ -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 = BSC_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 = BSC_FD_READ | BSC_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,9 +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;
-}
diff --git a/src/ipaccess/network_listen.c b/src/ipaccess/network_listen.c
index bbaf79810..e9f5c93b0 100644
--- a/src/ipaccess/network_listen.c
+++ b/src/ipaccess/network_listen.c
@@ -34,6 +34,7 @@
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts_trx.h>
#include <osmocom/bsc/abis_nm.h>
#include <osmocom/bsc/signal.h>
#include <osmocom/bsc/debug.h>
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 0665af6f3..10265235e 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -21,77 +21,127 @@ 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_ramp.c \
- arfcn_range_encode.c \
+ acc.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_trx_ts_ctrl.c \
+ bts_trx_ts_lchan_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 \
+ data_rate_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 \
+ vgcs_fsm.c \
+ lb.c \
+ lchan.c \
lchan_fsm.c \
lchan_rtp_fsm.c \
lchan_select.c \
+ lcs_loc_req.c \
+ lcs_ta_req.c \
meas_feed.c \
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 713be86fb..c2c89e37b 100644
--- a/src/osmo-bsc/a_reset.c
+++ b/src/osmo-bsc/a_reset.c
@@ -18,177 +18,68 @@
*
*/
-#include <osmocom/core/logging.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/fsm.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
+#include <osmocom/core/signal.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/bssmap_reset.h>
+#include <osmocom/bsc/bsc_stats.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);
-}
-
-/* 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);
+ 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);
+ struct bsc_msc_data *msc = data;
+ LOGP(DMSC, LOGL_NOTICE, "(msc%d) BSSMAP association 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 association 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 */
@@ -197,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 */
@@ -209,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 */
@@ -221,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 */
@@ -233,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 e17c6f5e4..afb7abc6b 100644
--- a/src/osmo-bsc/abis_nm.c
+++ b/src/osmo-bsc/abis_nm.c
@@ -48,14 +48,24 @@
#include <osmocom/bsc/signal.h>
#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>
+#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.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)
+/* max number of SW Description IEs we can parse */
+#define SW_DESCR_MAX 5
+
+#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)
{
@@ -162,6 +172,7 @@ int _abis_nm_sendmsg(struct msgb *msg)
if (!msg->dst) {
LOGP(DNM, LOGL_ERROR, "%s: msg->dst == NULL\n", __func__);
+ msgb_free(msg);
return -EINVAL;
}
@@ -203,7 +214,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));
@@ -215,18 +226,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;
}
@@ -237,70 +248,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));
}
- DEBUGPC(DNM, "\n");
- if ((new_state.administrative != 0 && nm_state->administrative == 0) ||
- new_state.operational != nm_state->operational ||
- new_state.availability != nm_state->availability) {
- /* Update the operational state of a given object in our in-memory data
+ 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 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;
- }
-#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);
+ *nm_state = nsd.new_state;
+ osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd);
+ } else {
+ DEBUGPC(DNM, "(No State change detected)\n");
}
-#endif
return 0;
}
@@ -311,7 +319,7 @@ static inline void log_oml_fail_rep(const struct gsm_bts *bts, const char *type,
enum abis_nm_pcause_type pcause = p_val[0];
enum abis_mm_event_causes cause = osmo_load16be(p_val + 1);
- LOGP(DNM, LOGL_ERROR, "BTS %u: Failure Event Report: ", bts->nr);
+ LOGPMO(&bts->mo, DNM, LOGL_ERROR, "Failure Event Report: ");
if (type)
LOGPC(DNM, LOGL_ERROR, "Type=%s, ", type);
if (severity)
@@ -341,10 +349,10 @@ static inline void handle_manufact_report(struct gsm_bts *bts, const uint8_t *p_
switch (cause) {
case OSMO_EVT_PCU_VERS:
if (text) {
- LOGP(DNM, LOGL_NOTICE, "BTS %u reported connected PCU version %s\n", bts->nr, text);
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE, "Reported connected PCU version %s\n", text);
osmo_strlcpy(bts->pcu_version, text, sizeof(bts->pcu_version));
} else {
- LOGP(DNM, LOGL_ERROR, "BTS %u reported PCU disconnection.\n", bts->nr);
+ LOGPMO(&bts->mo, DNM, LOGL_ERROR, "Reported PCU disconnection.\n");
bts->pcu_version[0] = '\0';
}
break;
@@ -366,8 +374,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);
@@ -430,7 +440,7 @@ static int rx_fail_evt_rep(struct msgb *mb, struct gsm_bts *bts)
sd = abis_nm_fail_evt_rep_parse(mb, bts);
if (!sd) {
- LOGPFOH(DNM, LOGL_ERROR, foh, "BTS%u: failed to parse Failure Event Report\n", bts->nr);
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Failed to parse Failure Event Report\n");
return -EINVAL;
}
e_type = sd->parsed.event_type;
@@ -448,8 +458,7 @@ static int rx_fail_evt_rep(struct msgb *mb, struct gsm_bts *bts)
log_oml_fail_rep(bts, e_type, severity, p_val, p_text);
};
} else {
- LOGPFOH(DNM, LOGL_ERROR, foh, "BTS%u: Failure Event Report without "
- "Probable Cause?!\n", bts->nr);
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Failure Event Report without Probable Cause?!\n");
rc = -EINVAL;
}
@@ -522,10 +531,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;
@@ -534,17 +543,19 @@ static inline bool handle_attr(const struct gsm_bts *bts, enum bts_attribute id,
}
/* Parse Attribute Response Info - return pointer to the actual content */
-static inline const uint8_t *parse_attr_resp_info_unreported(uint8_t bts_nr, const uint8_t *ari, uint16_t ari_len, uint16_t *out_len)
+static inline const uint8_t *parse_attr_resp_info_unreported(const struct abis_om_fom_hdr *foh,
+ const uint8_t *ari, uint16_t ari_len,
+ uint16_t *out_len)
{
uint8_t num_unreported = ari[0], i;
- DEBUGP(DNM, "BTS%u Get Attributes Response Info: %u bytes total with %u unreported attributes\n",
- bts_nr, ari_len, num_unreported);
+ DEBUGPFOH(DNM, foh, "Get Attributes Response Info: %u bytes total "
+ "with %u unreported attributes\n", ari_len, num_unreported);
/* +1 because we have to account for number of unreported attributes, prefixing the list: */
for (i = 0; i < num_unreported; i++)
- LOGP(DNM, LOGL_ERROR, "BTS%u Attribute %s is unreported\n",
- bts_nr, get_value_string(abis_nm_att_names, ari[i + 1]));
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Attribute %s is unreported\n",
+ get_value_string(abis_nm_att_names, ari[i + 1]));
/* the data starts right after the list of unreported attributes + space for length of that list */
if (out_len)
@@ -553,6 +564,45 @@ static inline const uint8_t *parse_attr_resp_info_unreported(uint8_t bts_nr, con
return ari + num_unreported + 1; /* we have to account for 1st byte with number of unreported attributes */
}
+/* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.30 Manufacturer Id */
+static void parse_osmo_bts_features(struct gsm_bts *bts,
+ const uint8_t *data, uint16_t data_len)
+{
+ /* log potential BTS feature vector overflow */
+ if (data_len > sizeof(bts->_features_data)) {
+ LOGPMO(&bts->mo, DNM, LOGL_NOTICE,
+ "Get Attributes Response: feature vector is truncated "
+ "(from %u to %zu bytes)\n", data_len, sizeof(bts->_features_data));
+ data_len = sizeof(bts->_features_data);
+ }
+
+ /* check that max. expected BTS attribute is above given feature vector length */
+ if (data_len > OSMO_BYTES_FOR_BITS(_NUM_BTS_FEAT)) {
+ 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", data_len);
+ }
+
+ memcpy(bts->_features_data, data, data_len);
+ bts->features_known = true;
+
+ /* Log each BTS feature in the reported vector */
+ for (unsigned int i = 0; i < data_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));
+ }
+ }
+}
+
/* Handle 3GPP TS 52.021 §8.11.3 Get Attribute Response (with nanoBTS specific attribute formatting) */
static int parse_attr_resp_info_attr(struct gsm_bts *bts, const struct gsm_bts_trx *trx, struct abis_om_fom_hdr *foh, struct tlv_parsed *tp)
{
@@ -563,48 +613,34 @@ static int parse_attr_resp_info_attr(struct gsm_bts *bts, const struct gsm_bts_t
uint16_t port;
struct in_addr ia = {0};
char unit_id[40];
- struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR];
-
- /* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.30 Manufacturer Id */
- if (TLVP_PRES_LEN(tp, NM_ATT_MANUF_ID, 2)) {
- len = TLVP_LEN(tp, NM_ATT_MANUF_ID);
- /* 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));
- 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);
+ switch (bts->type) {
+ case GSM_BTS_TYPE_OSMOBTS:
+ if (TLVP_PRES_LEN(tp, NM_ATT_MANUF_ID, 2)) {
+ parse_osmo_bts_features(bts, TLVP_VAL(tp, NM_ATT_MANUF_ID),
+ TLVP_LEN(tp, NM_ATT_MANUF_ID));
}
-
- memcpy(bts->_features_data, TLVP_VAL(tp, NM_ATT_MANUF_ID), len);
-
- for (i = 0; i < _NUM_BTS_FEAT; i++) {
- if (osmo_bts_has_feature(&bts->features, i) != osmo_bts_has_feature(&bts->model->features, i)) {
- LOGP(DNM, LOGL_NOTICE, "BTS%u feature '%s' reported via OML does not match statically "
- "set feature: %u != %u. Please fix.\n", bts->nr,
- get_value_string(osmo_bts_features_descs, i),
- osmo_bts_has_feature(&bts->features, i), osmo_bts_has_feature(&bts->model->features, i));
- }
+ /* fall-through */
+ case GSM_BTS_TYPE_NANOBTS:
+ if (TLVP_PRESENT(tp, NM_ATT_IPACC_SUPP_FEATURES)) {
+ ipacc_parse_supp_features(bts, foh, TLVP_VAL(tp, NM_ATT_IPACC_SUPP_FEATURES),
+ TLVP_LEN(tp, NM_ATT_IPACC_SUPP_FEATURES));
}
+ break;
+ default:
+ break;
}
/* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.28 Manufacturer Dependent State */
/* this attribute does not make sense on BTS level, only on TRX level */
if (trx && TLVP_PRES_LEN(tp, NM_ATT_MANUF_STATE, 1)) {
data = TLVP_VAL(tp, NM_ATT_MANUF_STATE);
- LOGPFOH(DNM, LOGL_NOTICE, foh, "%s Get Attributes Response: nominal power is %u\n", gsm_trx_name(trx), *data);
+ LOGPFOH(DNM, LOGL_NOTICE, foh, "Get Attributes Response: nominal power is %u\n", *data);
}
/* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.61 SW Configuration */
if (TLVP_PRESENT(tp, NM_ATT_SW_CONFIG)) {
+ struct abis_nm_sw_desc sw_descr[SW_DESCR_MAX];
data = TLVP_VAL(tp, NM_ATT_SW_CONFIG);
len = TLVP_LEN(tp, NM_ATT_SW_CONFIG);
/* after parsing manufacturer-specific attributes there's list of replies in form of sw-conf structure: */
@@ -613,33 +649,31 @@ static int parse_attr_resp_info_attr(struct gsm_bts *bts, const struct gsm_bts_t
for (i = 0; i < rc; i++) {
if (!handle_attr(bts, str2btsattr((const char *)sw_descr[i].file_id),
sw_descr[i].file_version, sw_descr[i].file_version_len)) {
- LOGPFOH(DNM, LOGL_NOTICE, foh, "BTS%u: ARI reported sw[%d/%d]: %s "
- "is %s\n", bts->nr, i, rc, sw_descr[i].file_id,
- sw_descr[i].file_version);
+ LOGPFOH(DNM, LOGL_NOTICE, foh, "ARI reported sw[%d/%d]: %s is %s\n",
+ i, rc, sw_descr[i].file_id, sw_descr[i].file_version);
}
}
} else {
- LOGPFOH(DNM, LOGL_ERROR, foh, "BTS%u: failed to parse SW-Config part of "
- "Get Attribute Response Info: %s\n", bts->nr, strerror(-rc));
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Failed to parse SW-Config part of "
+ "Get Attribute Response Info: %s\n", strerror(-rc));
}
}
if (abis_nm_tlv_attr_primary_oml(tp, &ia, &port) == 0) {
LOGPFOH(DNM, LOGL_NOTICE, foh,
- "BTS%u Get Attributes Response: Primary OML IP is %s:%u\n",
- bts->nr, inet_ntoa(ia), port);
+ "Get Attributes Response: Primary OML IP is %s:%u\n",
+ inet_ntoa(ia), port);
}
if (abis_nm_tlv_attr_unit_id(tp, unit_id, sizeof(unit_id)) == 0) {
- LOGPFOH(DNM, LOGL_NOTICE, foh, "BTS%u Get Attributes Response: Unit ID is %s\n",
- bts->nr, unit_id);
+ LOGPFOH(DNM, LOGL_NOTICE, foh, "Get Attributes Response: Unit ID is %s\n", unit_id);
}
/* nanoBTS provides Get Attribute Response Info at random position and only the unreported part of it. */
if (TLVP_PRES_LEN(tp, NM_ATT_GET_ARI, 1)) {
data = TLVP_VAL(tp, NM_ATT_GET_ARI);
len = TLVP_LEN(tp, NM_ATT_GET_ARI);
- parse_attr_resp_info_unreported(bts->nr, data, len, NULL);
+ parse_attr_resp_info_unreported(foh, data, len, NULL);
}
return 0;
@@ -652,34 +686,45 @@ static int parse_attr_resp_info(struct gsm_bts *bts, const struct gsm_bts_trx *t
uint16_t data_len;
if (!TLVP_PRES_LEN(tp, NM_ATT_GET_ARI, 1)) {
- LOGPFOH(DNM, LOGL_ERROR, foh, "BTS%u: Get Attr Response without Response Info?!\n",
- bts->nr);
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Get Attr Response without Response Info?!\n");
return -EINVAL;
}
- data = parse_attr_resp_info_unreported(bts->nr, TLVP_VAL(tp, NM_ATT_GET_ARI), TLVP_LEN(tp, NM_ATT_GET_ARI),
+ data = parse_attr_resp_info_unreported(foh, TLVP_VAL(tp, NM_ATT_GET_ARI),
+ TLVP_LEN(tp, NM_ATT_GET_ARI),
&data_len);
/* 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;
- DEBUGPFOH(DNM, foh, "Get Attributes Response for BTS%u\n", bts->nr);
+ trx = foh->obj_class == NM_OC_BASEB_TRANSC ?
+ gsm_bts_trx_num(bts, foh->obj_inst.trx_nr) : NULL;
- abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
+ DEBUGPFOH(DNM, foh, "Get Attributes Response\n");
+
+ 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)
@@ -687,6 +732,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;
@@ -701,28 +752,37 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
struct tlv_parsed tp;
const uint8_t *sw_config;
int ret, sw_config_len, len;
- struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR];
+ struct abis_nm_sw_desc sw_descr[SW_DESCR_MAX];
DEBUGPFOH(DNM, foh, "Software Activate Request, ACKing and Activating\n");
+ if (oh->length < sizeof(*foh)) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Software Activate Request with length too small: %u\n", oh->length);
+ return -EINVAL;
+ }
+
ret = abis_nm_sw_act_req_ack(sign_link->trx->bts, foh->obj_class,
foh->obj_inst.bts_nr,
foh->obj_inst.trx_nr,
foh->obj_inst.ts_nr, 0,
- foh->data, oh->length-sizeof(*foh));
+ foh->data, oh->length - sizeof(*foh));
if (ret != 0) {
LOGPFOH(DNM, LOGL_ERROR, foh, "Sending SW ActReq ACK failed: %d\n", ret);
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)) {
LOGPFOH(DNM, LOGL_ERROR, foh, "SW config not found! Can't continue.\n");
return -EINVAL;
} else {
- DEBUGP(DNM, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len));
+ DEBUGPFOH(DNM, foh, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len));
}
/* Parse up to two sw descriptions from the data */
@@ -752,12 +812,19 @@ 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;
adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
+ DEBUGPFOH(DNM, foh, "Rx Change Administrative State ACK %s\n",
+ get_value_string(abis_nm_adm_state_names, adm_state));
+
return update_admstate(sign_link->trx->bts, foh->obj_class, &foh->obj_inst, adm_state);
}
@@ -768,8 +835,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);
@@ -799,16 +870,14 @@ struct gsm_bts_trx_ts *abis_nm_get_ts(const 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_trx *trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr);
+ struct gsm_bts_trx *trx = gsm_bts_trx_num(sign_link->trx->bts, foh->obj_inst.trx_nr);
uint8_t ts_nr = foh->obj_inst.ts_nr;
if (!trx) {
- LOGP(DNM, LOGL_ERROR, "%s Channel OPSTART ACK for sign_link without trx\n",
- abis_nm_dump_foh(foh));
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Channel OPSTART ACK for sign_link without trx\n");
return NULL;
}
if (ts_nr >= ARRAY_SIZE(trx->ts)) {
- LOGP(DNM, LOGL_ERROR, "bts%u-trx%u %s Channel OPSTART ACK for non-existent TS\n",
- trx->bts->nr, trx->nr, abis_nm_dump_foh(foh));
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Channel OPSTART ACK for non-existent TS\n");
return NULL;
}
return &trx->ts[ts_nr];
@@ -817,12 +886,43 @@ struct gsm_bts_trx_ts *abis_nm_get_ts(const struct msgb *oml_msg)
static int abis_nm_rx_opstart_ack(struct msgb *mb)
{
struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- DEBUGPFOH(DNM, foh, "bts=%u Opstart ACK\n", sign_link->trx->bts->nr);
+ DEBUGPFOH(DNM, foh, "Opstart ACK\n");
osmo_signal_dispatch(SS_NM, S_NM_OPSTART_ACK, 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);
+ DEBUGPFOH(DNM, foh, "Set Radio Carrier Attributes ACK\n");
+ osmo_signal_dispatch(SS_NM, S_NM_SET_RADIO_ATTR_ACK, 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;
@@ -834,16 +934,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))
@@ -853,14 +953,6 @@ bool all_trx_rsl_connected_unlocked(const struct gsm_bts *bts)
return true;
}
-char *get_model_oml_status(const struct gsm_bts *bts)
-{
- if (bts->model->oml_status)
- return bts->model->oml_status(bts);
-
- return "unknown";
-}
-
void abis_nm_queue_send_next(struct gsm_bts *bts)
{
int wait = 0;
@@ -900,9 +992,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)));
@@ -944,13 +1039,16 @@ 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:
- DEBUGPFOH(DNM, foh, "Set Radio Carrier Attributes ACK\n");
+ abis_nm_rx_set_radio_attr_ack(mb);
break;
case NM_MT_CONN_MDROP_LINK_ACK:
DEBUGPFOH(DNM, foh, "CONN MDROP LINK ACK\n");
@@ -964,10 +1062,22 @@ 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:
+ case NM_MT_DISC_TERR_SIGN_ACK:
+ case NM_MT_CONN_TERR_TRAF_ACK:
+ case NM_MT_DISC_MDROP_LINK_ACK:
+ case NM_MT_STOP_EVENT_REP_ACK:
+ case NM_MT_REST_EVENT_REP_ACK:
+ case NM_MT_BS11_BEGIN_DB_TX_ACK:
+ case NM_MT_BS11_END_DB_TX_ACK:
+ case NM_MT_BS11_SET_ATTR_ACK:
+ DEBUGPFOH(DNM, foh, "%s\n", get_value_string(abis_nm_msgtype_names, mt));
break;
default:
LOGPFOH(DNM, LOGL_ERROR, foh, "Unhandled message %s\n",
@@ -984,17 +1094,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 (%u)\n", 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;
}
@@ -1139,7 +1249,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");
}
}
@@ -1237,7 +1347,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;
}
@@ -1285,56 +1395,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;
}
@@ -1495,7 +1600,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
switch (foh->msg_type) {
case NM_MT_LOAD_END_ACK:
sw_close_file(sw);
- DEBUGPFOH(DNM, foh, "Software Load End (BTS %u)\n", sw->bts->nr);
+ DEBUGPFOH(DNM, foh, "Software Load End\n");
sw->state = SW_STATE_NONE;
if (sw->cbfn)
sw->cbfn(GSM_HOOK_NM_SWLOAD,
@@ -1576,7 +1681,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;
@@ -1593,13 +1698,13 @@ int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname,
break;
case GSM_BTS_TYPE_NANOBTS:
sw->obj_class = NM_OC_BASEB_TRANSC;
- sw->obj_instance[0] = sw->bts->nr;
+ sw->obj_instance[0] = sw->bts->bts_nr;
sw->obj_instance[1] = sw->trx_nr;
sw->obj_instance[2] = 0xff;
break;
case GSM_BTS_TYPE_UNKNOWN:
default:
- LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n");
+ LOGPMO(&bts->mo, DNM, LOGL_ERROR, "Software Load not properly implemented.\n");
return -1;
break;
}
@@ -1644,7 +1749,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;
@@ -1741,9 +1846,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);
}
@@ -1760,23 +1864,24 @@ int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst,
int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
const uint8_t *attr, uint8_t attr_len)
{
+ const struct abis_om_fom_hdr *foh;
struct abis_om_hdr *oh;
struct msgb *msg;
if (bts->type != GSM_BTS_TYPE_OSMOBTS && bts->type != GSM_BTS_TYPE_NANOBTS) {
- LOGPC(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;
}
- LOG_BTS(bts, DNM, LOGL_DEBUG, "Get Attr (trx=%u)\n", trx_nr);
-
msg = nm_msgb_alloc();
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, TL16V_GROSS_LEN(attr_len), NM_MT_GET_ATTR, obj_class,
- bts_nr, trx_nr, ts_nr);
+ foh = fill_om_fom_hdr(oh, TL16V_GROSS_LEN(attr_len), NM_MT_GET_ATTR,
+ obj_class, bts_nr, trx_nr, ts_nr);
msgb_tl16v_put(msg, NM_ATT_LIST_REQ_ATTR, attr_len, attr);
+ DEBUGPFOH(DNM, foh, "Tx Get Attributes (Request)\n");
+
return abis_nm_sendmsg(bts, msg);
}
@@ -1787,7 +1892,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);
@@ -1835,7 +1940,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;
@@ -1933,7 +2038,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.";
@@ -1965,7 +2070,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 {
@@ -2002,7 +2107,7 @@ int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb)
foh = fill_om_fom_hdr(oh, 0, NM_MT_SET_CHAN_ATTR, NM_OC_CHANNEL, bts->bts_nr,
ts->trx->nr, ts->nr);
- DEBUGPFOH(DNM, foh, "Set Chan Attr %s\n", gsm_ts_name(ts));
+ DEBUGPFOH(DNM, foh, "Set Chan Attr\n");
if (verify_chan_comb(ts, chan_comb, &reason) < 0) {
LOGPFOH(DNM, LOGL_ERROR, foh, "Invalid Channel Combination %d on %s. Reason: %s\n",
chan_comb, gsm_ts_name(ts), reason);
@@ -2039,8 +2144,8 @@ int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb)
/* BS-11 cannot handle more than 255 ARFCNs, because L is 8 bit.
* This is unlikely to happen, but better check than sorry... */
if (bts->type == GSM_BTS_TYPE_BS11 && n > 0xff) {
- LOGPFOH(DNM, LOGL_ERROR, foh, "%s cannot handle %u (more than 255) "
- "hopping channels\n", gsm_bts_name(bts), n);
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Cannot handle %u (more than 255) "
+ "hopping channels\n", n);
msgb_free(msg);
return -EINVAL;
}
@@ -2064,12 +2169,13 @@ int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb)
}
int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1,
- uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len)
+ uint8_t i2, uint8_t i3, int nack,
+ const uint8_t *attr, unsigned int attr_len)
{
struct abis_om_hdr *oh;
struct msgb *msg = nm_msgb_alloc();
uint8_t msgtype = NM_MT_SW_ACT_REQ_ACK;
- uint8_t len = att_len;
+ uint8_t len = attr_len;
if (nack) {
len += 2;
@@ -2077,12 +2183,10 @@ int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1,
}
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3);
+ fill_om_fom_hdr(oh, len, msgtype, obj_class, i1, i2, i3);
- if (attr) {
- uint8_t *ptr = msgb_put(msg, att_len);
- memcpy(ptr, attr, att_len);
- }
+ if (attr != NULL && attr_len > 0)
+ memcpy(msgb_put(msg, attr_len), attr, attr_len);
if (nack)
msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP);
@@ -2181,8 +2285,11 @@ int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class,
DEBUGP(DNM, "PERFORM TEST %s\n", abis_nm_test_name(test_nr));
- if (!msg)
+ if (!msg) {
msg = nm_msgb_alloc();
+ if (!msg)
+ return -ENOMEM;
+ }
msgb_tv_push(msg, NM_ATT_AUTON_REPORT, auton_report);
msgb_tv_push(msg, NM_ATT_TEST_NO, test_nr);
@@ -2553,8 +2660,8 @@ struct file_list_entry *fl_dequeue(struct llist_head *queue)
static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw)
{
+ struct file_list_entry *fle;
char linebuf[255];
- struct llist_head *lh, *lh2;
FILE *swl;
int rc = 0;
@@ -2563,10 +2670,8 @@ static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw)
return -ENODEV;
/* zero the stale file list, if any */
- llist_for_each_safe(lh, lh2, &bs11_sw->file_list) {
- llist_del(lh);
- talloc_free(lh);
- }
+ while ((fle = fl_dequeue(&bs11_sw->file_list)))
+ talloc_free(fle);
while (fgets(linebuf, sizeof(linebuf), swl)) {
char file_id[12+1];
@@ -2773,6 +2878,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);
@@ -2782,11 +2888,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_by_nr(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)));
@@ -2866,17 +2975,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:
@@ -2943,40 +3050,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);
@@ -2991,7 +3106,7 @@ int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx)
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC,
- trx->bts->nr, trx->nr, 0xff);
+ trx->bts->bts_nr, trx->nr, 0xff);
return abis_nm_sendmsg_direct(trx->bts, msg);
}
@@ -3015,26 +3130,6 @@ void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts)
memcpy(&_buf->rac, &ci, sizeof(ci));
}
-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;
-
-
- if (!trx->bts || !trx->bts->oml_link) {
- /* Set initial state which will be sent when BTS connects. */
- trx->mo.nm_state.administrative = new_state;
- return;
- }
-
- LOG_TRX(trx, DNM, LOGL_NOTICE, "Requesting administrative state change %s -> %s [%s]\n",
- 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);
-}
-
static const struct value_string ipacc_testres_names[] = {
{ NM_IPACC_TESTRES_SUCCESS, "SUCCESS" },
{ NM_IPACC_TESTRES_TIMEOUT, "TIMEOUT" },
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 0aea684c0..69a86b54d 100644
--- a/src/osmo-bsc/abis_om2000.c
+++ b/src/osmo-bsc/abis_om2000.c
@@ -44,8 +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,
@@ -54,20 +68,11 @@ struct osmo_fsm_inst *osmo_fsm_inst_alloc_child_id(struct osmo_fsm *fsm,
{
struct osmo_fsm_inst *fi;
- fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level,
- id ? id : parent->id);
- if (!fi) {
- /* indicate immediate termination to caller */
- osmo_fsm_inst_dispatch(parent, parent_term_event, NULL);
+ fi = osmo_fsm_inst_alloc_child(fsm, parent, parent_term_event);
+ if (!fi)
return NULL;
- }
-
- LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent));
-
- fi->proc.parent = parent;
- fi->proc.parent_term_event = parent_term_event;
- llist_add(&fi->proc.child, &parent->proc.children);
-
+ if (id)
+ osmo_fsm_inst_update_id_f_sanitize(fi, '-', id);
return fi;
}
@@ -309,6 +314,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 {
@@ -387,6 +393,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 },
},
};
@@ -775,6 +782,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;
@@ -848,7 +858,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];
@@ -860,8 +870,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;
@@ -916,7 +925,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;
@@ -944,17 +953,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:
@@ -965,61 +968,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));
- nm_state->operational = new_state.operational;
+ 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;
+
+ 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)
@@ -1038,20 +1112,20 @@ static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg)
case OM2K_MO_CLS_TX:
case OM2K_MO_CLS_RX:
/* Route through per-TRX OML Link to the appropriate TRX */
- trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst);
+ 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;
break;
case OM2K_MO_CLS_TS:
/* Route through per-TRX OML Link to the appropriate TRX */
- trx = gsm_bts_trx_by_nr(bts, o2h->mo.assoc_so);
+ 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;
@@ -1065,8 +1139,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;
@@ -1084,8 +1157,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);
@@ -1101,8 +1173,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;
@@ -1110,8 +1181,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);
}
@@ -1156,8 +1226,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;
@@ -1167,7 +1236,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 */
@@ -1227,19 +1296,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);
@@ -1285,11 +1351,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);
@@ -1315,17 +1379,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;
@@ -1334,8 +1395,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;
@@ -1357,7 +1417,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);
}
@@ -1399,16 +1459,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);
@@ -1427,7 +1484,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;
@@ -1437,11 +1494,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: */
@@ -1536,7 +1591,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 */
@@ -1605,7 +1660,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);
@@ -1619,7 +1674,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,
@@ -1632,7 +1689,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" },
@@ -1664,6 +1723,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)
@@ -1675,20 +1735,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;
}
@@ -1702,14 +1759,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;
}
@@ -1719,8 +1774,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);
}
@@ -1730,8 +1784,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);
@@ -1748,21 +1801,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
@@ -1825,8 +1875,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);
}
@@ -1840,11 +1889,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);
}
}
@@ -1854,8 +1901,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);
}
@@ -1867,8 +1913,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)
@@ -1879,11 +1926,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) |
@@ -1894,6 +1954,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),
@@ -1903,6 +1964,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,
@@ -1911,6 +1973,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,
@@ -1919,15 +1982,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,
@@ -1936,6 +2002,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,
@@ -1944,6 +2011,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,
@@ -1952,6 +2020,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,
@@ -1960,19 +2029,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,
},
@@ -1989,13 +2059,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;
@@ -2005,8 +2076,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;
@@ -2014,38 +2084,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:
@@ -2055,8 +2125,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:
@@ -2066,24 +2135,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;
@@ -2097,7 +2162,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,
@@ -2106,7 +2173,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" },
@@ -2121,6 +2190,7 @@ enum om2k_trx_state {
OM2K_TRX_S_WAIT_TX,
OM2K_TRX_S_WAIT_RX,
OM2K_TRX_S_WAIT_TS,
+ OM2K_TRX_S_SEND_SI,
OM2K_TRX_S_DONE,
OM2K_TRX_S_ERROR
};
@@ -2128,6 +2198,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)
@@ -2135,10 +2206,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)
@@ -2146,10 +2215,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)
@@ -2157,10 +2224,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)
@@ -2169,12 +2234,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)
@@ -2182,69 +2245,140 @@ 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_DONE, 0, 0);
+ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_SEND_SI, 0, 0);
}
}
+static void om2k_trx_s_send_si(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct om2k_trx_fsm_priv *otfp = fi->priv;
+
+ if (gsm_bts_trx_set_system_infos(otfp->trx) == 0)
+ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_DONE, 0, 0);
+ else
+ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_ERROR, 0, 0);
+}
+
static void om2k_trx_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct om2k_trx_fsm_priv *otfp = fi->priv;
- gsm_bts_trx_set_system_infos(otfp->trx);
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ 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_DONE),
+ 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_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",
},
};
@@ -2260,41 +2394,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,
@@ -2302,11 +2463,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" },
@@ -2318,19 +2482,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;
@@ -2342,10 +2493,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)
@@ -2356,8 +2505,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)
@@ -2367,10 +2515,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)
@@ -2380,10 +2532,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)
@@ -2395,13 +2545,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);
}
}
@@ -2409,8 +2556,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)
@@ -2420,11 +2566,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)
@@ -2436,7 +2581,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);
}
@@ -2444,34 +2589,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,
},
@@ -2479,35 +2640,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",
},
};
@@ -2530,31 +2697,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
@@ -2571,7 +2751,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);
@@ -2693,18 +2873,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;
@@ -2718,8 +2896,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));
@@ -2727,10 +2904,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);
@@ -2770,7 +2945,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;
}
@@ -2800,8 +2975,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;
}
@@ -2814,22 +2988,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;
}
@@ -2856,8 +3027,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));
}
}
@@ -2874,28 +3044,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);
@@ -2904,11 +3070,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;
@@ -2980,26 +3148,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;
@@ -3011,21 +3177,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);
}
}
@@ -3033,20 +3206,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 222546f87..76048071a 100644
--- a/src/osmo-bsc/abis_om2000_vty.c
+++ b/src/osmo-bsc/abis_om2000_vty.c
@@ -33,6 +33,7 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/signal.h>
#include <osmocom/bsc/abis_om2000.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/vty.h>
#include <osmocom/vty/vty.h>
@@ -40,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)# ",
@@ -60,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" \
@@ -78,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
@@ -342,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;
@@ -370,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;
@@ -401,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]);
@@ -423,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]);
@@ -445,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;
@@ -467,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;
@@ -501,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]);
@@ -542,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",
@@ -560,7 +614,7 @@ DEFUN(om2k_conf_req, om2k_conf_req_cmd,
abis_om2k_tx_con_conf_req(bts);
break;
case OM2K_MO_CLS_TS:
- trx = gsm_bts_trx_by_nr(bts, oms->mo.assoc_so);
+ trx = gsm_bts_trx_num(bts, oms->mo.assoc_so);
if (!trx) {
vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr,
oms->mo.assoc_so, VTY_NEWLINE);
@@ -577,7 +631,7 @@ DEFUN(om2k_conf_req, om2k_conf_req_cmd,
case OM2K_MO_CLS_RX:
case OM2K_MO_CLS_TX:
case OM2K_MO_CLS_TRXC:
- trx = gsm_bts_trx_by_nr(bts, oms->mo.inst);
+ trx = gsm_bts_trx_num(bts, oms->mo.inst);
if (!trx) {
vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr,
oms->mo.inst, VTY_NEWLINE);
@@ -621,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;
@@ -646,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);
@@ -673,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 f6e564b28..49e8b52c3 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -44,6 +44,7 @@
#include <osmocom/gsm/rsl.h>
#include <osmocom/core/talloc.h>
#include <osmocom/bsc/pcu_if.h>
+#include <osmocom/bsc/pcuif_proto.h>
#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/core/tdef.h>
@@ -54,9 +55,11 @@
#include <osmocom/bsc/lchan_rtp_fsm.h>
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/smscb.h>
-
-#define RSL_ALLOC_SIZE 1024
-#define RSL_ALLOC_HEADROOM 128
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/power_control.h>
+#include <osmocom/bsc/chan_counts.h>
+#include <osmocom/bsc/lchan.h>
+#include <osmocom/bsc/vgcs_fsm.h>
static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,
struct gsm_meas_rep *resp)
@@ -72,26 +75,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 +149,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 +157,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 +221,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 +295,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 +315,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 +324,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 +339,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 +348,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 +359,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,24 +395,23 @@ 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,
+ enum lchan_type_for type_for)
{
+ int rc;
memset(cm, 0, sizeof(*cm));
- /* FIXME: what to do with data calls ? */
cm->dtx_dtu = 0;
if (lchan->ts->trx->bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
cm->dtx_dtu |= RSL_CMOD_DTXu;
@@ -337,33 +419,57 @@ 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;
+ switch (type_for) {
+ case LCHAN_TYPE_FOR_VAMOS:
+ cm->chan_rt = RSL_CMOD_CRT_OSMO_TCH_VAMOS_Bm;
+ break;
+ case LCHAN_TYPE_FOR_VGCS:
+ cm->chan_rt = RSL_CMOD_CRT_TCH_GROUP_Bm;
+ break;
+ case LCHAN_TYPE_FOR_VBS:
+ cm->chan_rt = RSL_CMOD_CRT_TCH_BCAST_Bm;
+ break;
+ default:
+ cm->chan_rt = RSL_CMOD_CRT_TCH_Bm;
+ }
break;
case GSM_LCHAN_TCH_H:
- cm->chan_rt = RSL_CMOD_CRT_TCH_Lm;
+ switch (type_for) {
+ case LCHAN_TYPE_FOR_VAMOS:
+ cm->chan_rt = RSL_CMOD_CRT_OSMO_TCH_VAMOS_Lm;
+ break;
+ case LCHAN_TYPE_FOR_VGCS:
+ cm->chan_rt = RSL_CMOD_CRT_TCH_GROUP_Lm;
+ break;
+ case LCHAN_TYPE_FOR_VBS:
+ cm->chan_rt = RSL_CMOD_CRT_TCH_BCAST_Lm;
+ break;
+ default:
+ cm->chan_rt = 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,82 +485,96 @@ 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) {
- case LCHAN_CSD_M_NT:
- /* non-transparent CSD with RLP */
- switch (lchan->tch_mode) {
- case GSM48_CMODE_DATA_14k5:
- cm->chan_rate = RSL_CMOD_SP_NT_14k5;
- break;
- case GSM48_CMODE_DATA_12k0:
- cm->chan_rate = RSL_CMOD_SP_NT_12k0;
- break;
- case GSM48_CMODE_DATA_6k0:
- cm->chan_rate = RSL_CMOD_SP_NT_6k0;
- break;
- default:
- LOGP(DRSL, LOGL_ERROR,
- "unsupported lchan->tch_mode %u\n",
- lchan->tch_mode);
- return -EINVAL;
- }
- break;
- /* transparent data services below */
- case LCHAN_CSD_M_T_1200_75:
- cm->chan_rate = RSL_CMOD_CSD_T_1200_75;
- break;
- case LCHAN_CSD_M_T_600:
- cm->chan_rate = RSL_CMOD_CSD_T_600;
- break;
- case LCHAN_CSD_M_T_1200:
- cm->chan_rate = RSL_CMOD_CSD_T_1200;
- break;
- case LCHAN_CSD_M_T_2400:
- cm->chan_rate = RSL_CMOD_CSD_T_2400;
- break;
- case LCHAN_CSD_M_T_9600:
- cm->chan_rate = RSL_CMOD_CSD_T_9600;
- break;
- case LCHAN_CSD_M_T_14400:
- cm->chan_rate = RSL_CMOD_CSD_T_14400;
- break;
- case LCHAN_CSD_M_T_29000:
- cm->chan_rate = RSL_CMOD_CSD_T_29000;
- break;
- case LCHAN_CSD_M_T_32000:
- cm->chan_rate = RSL_CMOD_CSD_T_32000;
- break;
- default:
- LOGP(DRSL, LOGL_ERROR,
- "unsupported lchan->csd_mode %u\n",
- lchan->csd_mode);
- return -EINVAL;
+ case GSM48_CMODE_DATA_3k6:
+ /* 3GPP TS 48.058 § 9.3.6 Channel Mode octet 6 */
+ if (ch_mode_rate->data_transparent) {
+ cm->chan_rate = ch_mode_rate->data_rate.t;
+ } else {
+ cm->chan_rate = ch_mode_rate->data_rate.nt;
+ cm->chan_rate |= 0x40;
}
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);
+}
- if (lchan->tch_mode != GSM48_CMODE_SPEECH_AMR)
+/* 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;
+
+ /* 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 +586,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 +600,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.type_for);
if (rc < 0) {
LOGP(DRSL, LOGL_ERROR,
"%s Cannot find channel mode from lchan type\n",
@@ -486,20 +608,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 +645,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 +665,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;
+ }
+ }
+
+ put_rep_acch_cap_ie(lchan, msg);
+ put_top_acch_cap_ie(lchan, &cm, msg);
- mr_config_for_bts(lchan, msg);
+ /* Selecting a specific TSC Set is only applicable to VAMOS mode */
+ if (lchan->activate.info.type_for == LCHAN_TYPE_FOR_VAMOS && lchan->activate.tsc_set >= 1)
+ put_osmo_training_sequence_ie(msg, lchan->activate.tsc_set, lchan->activate.tsc);
- msg->dst = trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHAN_ACT_TOTAL]);
+ 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 +730,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;
- rc = channel_mode_from_lchan(&cm, lchan);
+ int chan_nr = gsm_lchan2chan_nr(lchan, true);
+ if (chan_nr < 0)
+ return chan_nr;
+
+ rc = channel_mode_from_lchan(&cm, lchan, &lchan->modify.ch_mode_rate, lchan->modify.info.type_for);
if (rc < 0)
return rc;
@@ -582,16 +749,37 @@ 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;
+ }
}
- mr_config_for_bts(lchan, msg);
+ 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;
+ }
+ }
- msg->dst = lchan->ts->trx->rsl_link;
+ put_rep_acch_cap_ie(lchan, msg);
+ put_top_acch_cap_ie(lchan, &cm, msg);
+
+ /* Selecting a specific TSC Set is only applicable to VAMOS mode. Send this Osmocom specific IE only to OsmoBTS
+ * types. */
+ if (lchan->modify.info.type_for == LCHAN_TYPE_FOR_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 +789,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 +814,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 +825,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 +847,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 +866,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,8 +892,47 @@ 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);
+}
+
+/* Chapter 8.5.10: NOTIFICATION COMMAND */
+int rsl_notification_cmd(struct gsm_bts *bts, struct gsm_lchan *lchan, struct gsm0808_group_callref *gc, uint8_t *drx)
+{
+ struct abis_rsl_cchan_hdr *cch;
+ struct msgb *msg = rsl_msgb_alloc();
+ struct gsm48_chan_desc cd;
+ uint8_t sti = (lchan) ? RSL_CMD_INDICATOR_START : RSL_CMD_INDICATOR_STOP;
+ uint8_t *t;
+ int rc;
+
+ cch = (struct abis_rsl_cchan_hdr *) msgb_put(msg, sizeof(*cch));
+ rsl_init_cchan_hdr(cch, RSL_MT_NOT_CMD);
+ cch->chan_nr = RSL_CHAN_PCH_AGCH;
+
+ msgb_tlv_put(msg, RSL_IE_CMD_INDICATOR, 1, &sti);
+
+ /* Use TLV encoding from TS 08.08. Change different IE type. */
+ t = msg->tail;
+ gsm0808_enc_group_callref(msg, gc);
+ *t = RSL_IE_GROUP_CALL_REF;
+ if (lchan) {
+ memset(&cd, 0, sizeof(cd));
+ rc = gsm48_lchan2chan_desc(&cd, lchan, lchan->activate.tsc, true);
+ if (rc) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "Error encoding Channel Number\n");
+ msgb_free(msg);
+ return rc;
+ }
+ msgb_tlv_put(msg, RSL_IE_CHAN_DESC, sizeof(cd), (const uint8_t *)&cd);
+
+ if (drx)
+ msgb_tlv_put(msg, RSL_IE_NCH_DRX_INFO, 1, drx);
+ }
+
+ msg->dst = bts->c0->rsl_link_primary;
return abis_rsl_sendmsg(msg);
}
@@ -713,30 +951,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 +982,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,17 +995,24 @@ 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 msg_id, uint8_t len,
+ const uint8_t *val, uint8_t pag_grp, bool confirm)
{
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);
- msgb_put_u32(msg, tlli);
+ if (confirm) {
+ msgb_put_u8(msg, RSL_IE_ERIC_MOBILE_ID);
+ msgb_put_u32(msg, msg_id);
+ }
return abis_rsl_sendmsg(msg);
}
@@ -784,37 +1020,178 @@ 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;
+ }
+
+ count_unsucc_reqs_for_service(msg);
+
+ chan_nr = gsm_lchan2chan_nr(msg->lchan, true);
+ if (chan_nr < 0) {
+ msgb_free(msg);
+ return chan_nr;
+ }
+
+ rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, chan_nr, link_id, 1);
+
+ msg->dst = rsl_chan_link(msg->lchan);
+
+ return abis_rsl_sendmsg(msg);
+}
+
+/* Send "UNIT DATA REQUEST" message with given L3 Info payload */
+/* Chapter 8.3.11 */
+int rsl_unit_data_request(struct msgb *msg, uint8_t link_id)
+{
+ int chan_nr;
+
+ if (msg->lchan == NULL) {
+ LOGP(DRSL, LOGL_ERROR, "cannot send UNIT 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);
+ 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_UNIT_DATA_REQ, chan_nr, link_id, 1);
- msg->dst = msg->lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(msg->lchan);
return abis_rsl_sendmsg(msg);
}
@@ -824,10 +1201,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 +1224,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 +1245,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 +1261,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 +1269,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 +1290,131 @@ 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;
+ }
+
+ /* Report to VGCS FSM */
+ if (lchan_is_asci(lchan)) {
+ if (lchan->conn && lchan->conn->vgcs_chan.fi) {
+ uint8_t cause = GSM0808_CAUSE_RADIO_INTERFACE_FAILURE;
+ osmo_fsm_inst_dispatch(msg->lchan->conn->vgcs_chan.fi, VGCS_EV_TALKER_FAIL, &cause);
+ }
+ return 0;
+ }
/* 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 +1442,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 +1452,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 +1480,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 +1499,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 +1521,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 +1537,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);
@@ -1113,6 +1557,66 @@ static int rsl_rx_hando_det(struct msgb *msg)
return 0;
}
+/* Chapter 8.4.21: TALKER DETECTION */
+static int rsl_rx_talker_det(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tp;
+ /* We use this struct, because it has same data. */
+ struct handover_rr_detect_data d = {
+ .msg = msg,
+ };
+
+ 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);
+
+ if (!msg->lchan->conn || !msg->lchan->conn->vgcs_chan.fi) {
+ LOGP(DRSL, LOGL_ERROR, "%s TALKER DETECTION but no VGCS channel\n",
+ gsm_lchan_name(msg->lchan));
+ return 0;
+ }
+
+ osmo_fsm_inst_dispatch(msg->lchan->conn->vgcs_chan.fi, VGCS_EV_TALKER_DET, &d);
+
+ return 0;
+}
+
+/* Chapter 8.4.22: LISTENER DETECTION */
+static int rsl_rx_listener_det(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tp;
+ /* We use this struct, because it has same data. */
+ struct handover_rr_detect_data d = {
+ .msg = msg,
+ };
+
+ 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);
+
+ if (!msg->lchan->conn || !msg->lchan->conn->vgcs_chan.fi) {
+ LOGP(DRSL, LOGL_ERROR, "%s LISTENER DETECTION but no VGCS channel\n",
+ gsm_lchan_name(msg->lchan));
+ return 0;
+ }
+
+ osmo_fsm_inst_dispatch(msg->lchan->conn->vgcs_chan.fi, VGCS_EV_LISTENER_DET, &d);
+
+ return 0;
+}
+
static int rsl_rx_ipacc_pdch(struct msgb *msg, char *name, uint32_t ts_ev)
{
struct gsm_bts_trx_ts *ts = msg->lchan->ts;
@@ -1133,6 +1637,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",
@@ -1158,6 +1665,10 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
switch (rslh->c.msg_type) {
case RSL_MT_CHAN_ACTIV_ACK:
+ /* Ignore acknowlegement of channel reactivation, if a VGCS/VBS channel was reactivated to assign
+ * the calling subscriber to it. */
+ if (msg->lchan->conn && msg->lchan->conn->assignment.req.vgcs)
+ break;
if (msg_for_osmocom_dyn_ts(msg))
osmo_fsm_inst_dispatch(msg->lchan->ts->fi, TS_EV_PDCH_ACT_ACK, NULL);
else {
@@ -1177,6 +1688,12 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
case RSL_MT_HANDO_DET:
rc = rsl_rx_hando_det(msg);
break;
+ case RSL_MT_TALKER_DET:
+ rc = rsl_rx_talker_det(msg);
+ break;
+ case RSL_MT_LISTENER_DET:
+ rc = rsl_rx_listener_det(msg);
+ break;
case RSL_MT_RF_CHAN_REL_ACK:
if (msg_for_osmocom_dyn_ts(msg))
osmo_fsm_inst_dispatch(msg->lchan->ts->fi, TS_EV_PDCH_DEACT_ACK, NULL);
@@ -1186,10 +1703,12 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
case RSL_MT_MODE_MODIFY_ACK:
LOG_LCHAN(msg->lchan, LOGL_DEBUG, "CHANNEL MODE MODIFY ACK\n");
count_codecs(sign_link->trx->bts, msg->lchan);
+ osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK, NULL);
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:
rc = rsl_rx_ipacc_pdch(msg, "ACT ACK", TS_EV_PDCH_ACT_ACK);
@@ -1205,20 +1724,18 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
break;
case RSL_MT_PHY_CONTEXT_CONF:
case RSL_MT_PREPROC_MEAS_RES:
- case RSL_MT_TALKER_DET:
- case RSL_MT_LISTENER_DET:
case RSL_MT_REMOTE_CODEC_CONF_REP:
case RSL_MT_MR_CODEC_MOD_ACK:
case RSL_MT_MR_CODEC_MOD_NACK:
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;
}
@@ -1231,7 +1748,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));
@@ -1239,6 +1763,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);
@@ -1251,7 +1850,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 */
@@ -1267,7 +1866,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;
@@ -1305,7 +1904,15 @@ static int rsl_send_imm_ass_rej(struct gsm_bts *bts,
/* we need to subtract 1 byte from sizeof(*iar) since ia includes the l2_plen field */
iar->l2_plen = GSM48_LEN2PLEN((sizeof(*iar)-1));
- return rsl_imm_assign_cmd(bts, sizeof(*iar), (uint8_t *) iar);
+ /* IAR Rest Octets:
+ * 0... .... = Extended RA: Not Present
+ * .0.. .... = Extended RA: Not Present
+ * ..0. .... = Extended RA: Not Present
+ * ...0 .... = Extended RA: Not Present
+ * .... L... = Additions in Rel-13: Not Present */
+ iar->rest[0] = GSM_MACBLOCK_PADDING & 0x0f;
+
+ return rsl_imm_assign_cmd(bts, sizeof(*iar) + 1, buf);
}
int rsl_tx_imm_ass_rej(struct gsm_bts *bts, struct gsm48_req_ref *rqd_ref)
@@ -1320,120 +1927,493 @@ int rsl_tx_imm_ass_rej(struct gsm_bts *bts, struct gsm48_req_ref *rqd_ref)
return rsl_send_imm_ass_rej(bts, rqd_ref, wait_ind);
}
+struct chan_rqd {
+ struct llist_head entry;
+ struct gsm_bts *bts;
+ struct gsm48_req_ref ref;
+ enum gsm_chreq_reason_t reason;
+ uint8_t ta;
+ /* set to true to mark that the release of the release_lchan is in progress */
+ struct gsm_lchan *release_lchan;
+ time_t timestamp;
+};
+
/* Handle packet channel rach requests */
-static int rsl_rx_pchan_rqd(struct msgb *msg, struct gsm_bts *bts)
+static int rsl_rx_pchan_rqd(struct chan_rqd *rqd)
{
- struct gsm48_req_ref *rqd_ref;
- struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg);
- rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1];
- uint8_t ra = rqd_ref->ra;
- 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 (ra == 0x7F) {
+ 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",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);
- rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2];
+ 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(bts, rqd_ta, ra, fn, is_11bit,
+ return pcu_tx_rach_ind(rqd->bts, rqd_ta, rqd->ref.ra, fn, is_11bit,
GSM_L1_BURST_TYPE_ACCESS_0);
}
+/* Protect against RACH DoS attack: If an excessive amount of RACH requests queues up it is likely that the current BTS
+ * is under RACH DoS attack. To prevent excessive memory usage, remove all expired or at least one of the oldest channel
+ * requests from the queue to prevent the queue from growing indefinetly. */
+static void reduce_rach_dos(struct gsm_bts *bts)
+{
+ time_t timestamp_current = time(NULL);
+ struct chan_rqd *rqd;
+ struct chan_rqd *rqd_tmp;
+ unsigned int rqd_count = 0;
+
+ /* Drop all expired channel requests in the list */
+ llist_for_each_entry_safe(rqd, rqd_tmp, &bts->chan_rqd_queue, entry) {
+ /* If the channel request is older than the rach expiry timeout we drop it. This also means that the
+ * queue is under its overflow limit again. */
+ if (timestamp_current - rqd->timestamp > bts->rach_expiry_timeout) {
+ LOG_BTS(bts, DRSL, LOGL_INFO, "CHAN RQD: tossing expired channel request"
+ "(ra=0x%02x, neci=0x%02x, chreq_reason=0x%02x)\n",
+ rqd->ref.ra, bts->network->neci, rqd->reason);
+ llist_del(&rqd->entry);
+ talloc_free(rqd);
+ } else {
+ rqd_count++;
+ }
+ }
+
+ /* If we find more than 255 (256) unexpired channel requests in the queue it is very likely that there is a
+ * problem with RACH dos on this BTS. We drop the first entry in the list to clip the growth of the list. */
+ if (rqd_count > 255) {
+ LOG_BTS(bts, DRSL, LOGL_INFO, "CHAN RQD: more than 255 queued RACH requests -- RACH DoS attack?\n");
+ rqd = llist_first_entry(&bts->chan_rqd_queue, struct chan_rqd, entry);
+ llist_del(&rqd->entry);
+ talloc_free(rqd);
+ }
+}
+
+/* Flush all channel requests pending on this BTS */
+void abis_rsl_chan_rqd_queue_flush(struct gsm_bts *bts)
+{
+ struct chan_rqd *rqd;
+ struct chan_rqd *rqd_tmp;
+
+ llist_for_each_entry_safe(rqd, rqd_tmp, &bts->chan_rqd_queue, entry) {
+ llist_del(&rqd->entry);
+ talloc_free(rqd);
+ }
+}
+
/* MS has requested a channel on the RACH */
static int rsl_rx_chan_rqd(struct msgb *msg)
{
- struct lchan_activate_info info;
struct e1inp_sign_link *sign_link = msg->dst;
struct gsm_bts *bts = sign_link->trx->bts;
struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg);
- struct gsm48_req_ref *rqd_ref;
- enum gsm_chan_t lctype;
- enum gsm_chreq_reason_t chreq_reason;
- struct gsm_lchan *lchan;
- uint8_t rqd_ta;
+ struct chan_rqd *rqd;
+
+ reduce_rach_dos(bts);
+
+ rqd = talloc_zero(bts, struct chan_rqd);
+ OSMO_ASSERT(rqd);
+
+ rqd->bts = bts;
+ rqd->timestamp = time(NULL);
/* parse request reference to be used in immediate assign */
- if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE)
+ if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) {
+ talloc_free(rqd);
return -EINVAL;
-
- rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1];
+ }
+ memcpy(&rqd->ref, &rqd_hdr->data[1], sizeof(rqd->ref));
/* parse access delay and use as TA */
- if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY)
+ if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) {
+ talloc_free(rqd);
+ 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;
- rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2];
+ }
/* Determine channel request cause code */
- chreq_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, chreq_reason), rqd_ref->ra, bts->network->neci, chreq_reason);
+ 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, 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;
+ }
+
+ /* 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);
+
+ /* Forward the request directly. Most request will be finished with one attempt so no queuing will be
+ * necessary. */
+ abis_rsl_chan_rqd_queue_poll(bts);
+
+ return 0;
+}
+
+/* Find any busy TCH/H or TCH/F lchan */
+static struct gsm_lchan *get_any_lchan(struct gsm_bts *bts)
+{
+ int trx_nr;
+ int ts_nr;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+ struct gsm_lchan *lchan_est = NULL;
+ struct gsm_lchan *lchan_any = NULL;
+ struct gsm_lchan *lchan;
+
+ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
+ 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_n_lchans(lchan, ts, ts->max_primary_lchans) {
+ if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) {
+ if (lchan->fi->state == LCHAN_ST_ESTABLISHED) {
+ if (!lchan_est || bts->chan_alloc_chan_req_reverse)
+ lchan_est = lchan;
+ } else {
+ if (!lchan_any || bts->chan_alloc_chan_req_reverse)
+ lchan_any = lchan;
+ }
+ }
+ }
+ }
+ }
+
+ if (lchan_est)
+ return lchan_est;
+ else if (lchan_any)
+ return lchan_any;
+ return NULL;
+}
+
+/* Ensure that an incoming emergency call gets priority, if all voice channels are busy, terminate one regular call.
+ * Return true if freeing of a busy lchan is in progress, but not done yet, return false when done (either successfully
+ * or unsuccessfully). */
+static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)
+{
+ /* If the request is not about an emergency call, we may exit early, without doing anything. */
+ if (rqd->reason != GSM_CHREQ_REASON_EMERG)
+ return false;
+
+ /* 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, 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, 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;
+ }
+
+ /* No free TCH/F or TCH/H was found, we now select one of the busy lchans and initiate a release on that lchan.
+ * This will take a short amount of time. We need to come back and check regularly 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 initiate a channel
+ * release to make room for the incoming emergency call */
+ 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
+ * this case, the BTS probably does not have any
+ * voice channels configured? */
+ LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,
+ "CHAN RQD/EMERGENCY-PRIORITY: no TCH/H or TCH/F available - check VTY config!\n");
+ return false;
+ }
+
+ 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(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 {
+ /* if BTS has shut down, give up... */
+ if (rqd->release_lchan->ts->fi->state == TS_ST_NOT_INITIALIZED)
+ return false;
+
+ OSMO_ASSERT(rqd->release_lchan->fi);
+
+ LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,
+ "CHAN RQD/EMERGENCY-PRIORITY: still terminating 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));
+
+ /* If the channel was released in error (not established), the
+ * lchan FSM automatically blocks the LCHAN for a short time.
+ * This is not acceptable in an emergency situation, so we skip
+ * this waiting period. */
+ if (rqd->release_lchan->fi->state == LCHAN_ST_WAIT_AFTER_ERROR)
+ lchan_fsm_skip_error(rqd->release_lchan);
+ }
+
+ /* We are still in the process of releasing a busy lchan in favvor of the incoming emergency call. */
+ 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;
- /* Handle PDCH related rach requests (in case of BSC-co-located-PCU */
- if (chreq_reason == GSM_CHREQ_REASON_PDCH)
- return rsl_rx_pchan_rqd(msg, bts);
+ 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;
+ enum gsm_chan_t lctype;
+ struct gsm_lchan *lchan = NULL;
+ struct chan_rqd *rqd;
+
+ rqd = llist_first_entry_or_null(&bts->chan_rqd_queue, struct chan_rqd, entry);
+ if (!rqd)
+ return;
+
+ /* Handle PDCH related rach requests (in case of BSC-co-located-PCU) */
+ if (rqd->reason == GSM_CHREQ_REASON_PDCH) {
+ if (rsl_rx_pchan_rqd(rqd) == 0)
+ goto leave;
+ }
+
+ /* Ensure that emergency calls will get priority over regular calls, however releasing
+ * lchan in favor of an emergency call may take some time, so we exit here. The lchan_fsm
+ * will poll again when an lchan becomes available. */
+ if (force_free_lchan_for_emergency(rqd))
+ return;
/* determine channel type (SDCCH/TCH_F/TCH_H) based on
* request reference RA */
- lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra);
-
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL]);
+ lctype = get_ctype_by_chreq(bts->network, rqd->ref.ra);
/* check availability / allocate channel
*
- * - First try to allocate SDCCH.
+ * - First check for EMERGENCY call attempts,
+ * - then try to allocate SDCCH.
* - If SDCCH is not available, try a TCH/H (less bandwidth).
* - If there is still no channel available, try a TCH/F.
*
*/
- 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]);
- rsl_tx_imm_ass_rej(bts, rqd_ref);
- return 0;
+ 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);
+ return;
}
/* save the RACH data as we need it after the CHAN ACT ACK */
lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref);
OSMO_ASSERT(lchan->rqd_ref);
- *(lchan->rqd_ref) = *rqd_ref;
- lchan->rqd_ta = rqd_ta;
+ *(lchan->rqd_ref) = rqd->ref;
LOG_LCHAN(lchan, LOGL_DEBUG, "MS: Channel Request: reason=%s ra=0x%02x ta=%d\n",
- gsm_chreq_name(chreq_reason), rqd_ref->ra, rqd_ta);
+ 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);
- return 0;
+
+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)
@@ -1442,6 +2422,7 @@ int rsl_tx_imm_assignment(struct gsm_lchan *lchan)
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));
@@ -1449,11 +2430,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 {
@@ -1467,12 +2460,12 @@ 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;
}
-/* current load on the CCCH */
+/* 5.4 and 8.5.2 Rx CCCH Load Ind */
static int rsl_rx_ccch_load(struct msgb *msg)
{
struct e1inp_sign_link *sign_link = msg->dst;
@@ -1480,32 +2473,36 @@ 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_ipa_abisip_bts(sd.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];
sd.rach_busy_count = rslh->data[4] << 8 | rslh->data[5];
sd.rach_access_count = rslh->data[6] << 8 | rslh->data[7];
/* update stats group */
- busy_percent = (int32_t) sd.rach_busy_count * 100 / (int32_t) sd.rach_slot_count;
- access_percent = (int32_t) sd.rach_access_count * 100 / (int32_t) sd.rach_slot_count;
- 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);
+ if (sd.rach_slot_count) {
+ access_percent = (int32_t) sd.rach_access_count * 100 / (int32_t) sd.rach_slot_count;
+ busy_percent = (int32_t) sd.rach_busy_count * 100 / (int32_t) sd.rach_slot_count;
+ } else {
+ access_percent = 0;
+ busy_percent = 100;
+ }
+
+ 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);
}
@@ -1529,7 +2526,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;
@@ -1552,7 +2554,7 @@ static int rsl_rx_ericsson_imm_assign_sent(struct msgb *msg)
{
struct e1inp_sign_link *sign_link = msg->dst;
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
- uint32_t tlli;
+ uint32_t msg_id;
LOGP(DRSL, LOGL_INFO, "IMM.ass sent\n");
msgb_pull(msg, sizeof(*dh));
@@ -1564,8 +2566,8 @@ static int rsl_rx_ericsson_imm_assign_sent(struct msgb *msg)
LOGP(DRSL, LOGL_ERROR, "unsupported IMM.ass message format! (please fix)\n");
else {
msgb_pull(msg, 1); /* drop previous data to use msg_pull_u32 */
- tlli = msgb_pull_u32(msg);
- pcu_tx_imm_ass_sent(sign_link->trx->bts, tlli);
+ msg_id = msgb_pull_u32(msg);
+ pcu_tx_data_cnf(sign_link->trx->bts, msg_id, PCU_IF_SAPI_PCH_2);
}
return 0;
}
@@ -1574,8 +2576,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: ");
@@ -1591,7 +2597,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 */
@@ -1603,7 +2609,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;
}
@@ -1616,9 +2622,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;
}
@@ -1627,10 +2638,17 @@ 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);
+ /* Report to VGCS FSM */
+ if (lchan_is_asci(msg->lchan)) {
+ if (msg->lchan->conn && msg->lchan->conn->vgcs_chan.fi) {
+ uint8_t cause = GSM0808_CAUSE_RADIO_INTERFACE_FAILURE;
+ osmo_fsm_inst_dispatch(msg->lchan->conn->vgcs_chan.fi, VGCS_EV_TALKER_FAIL, &cause);
+ }
+ }
return 0;
}
@@ -1645,17 +2663,30 @@ 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:
LOG_LCHAN(msg->lchan, LOGL_DEBUG, "SAPI=%u DATA INDICATION\n", sapi);
- if (msgb_l2len(msg) >
- sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
+
+ if (msgb_l2len(msg) > (sizeof(*rllh) + 3) &&
rllh->data[0] == RSL_IE_L3_INFO) {
msg->l3h = &rllh->data[3];
+ /* Data message on a VGCS channel is handled by VGCS FSM only. */
+ if (lchan_is_asci(msg->lchan)) {
+ if (msg->lchan->conn && msg->lchan->conn->vgcs_chan.fi)
+ osmo_fsm_inst_dispatch(msg->lchan->conn->vgcs_chan.fi, VGCS_EV_TALKER_DATA,
+ msg);
+ return 0;
+ }
return gsm0408_rcvmsg(msg, rllh->link_id);
}
break;
@@ -1695,8 +2726,14 @@ static int abis_rsl_rx_rll(struct msgb *msg)
msg->lchan->sapis[sapi] = LCHAN_SAPI_MS;
osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_RLL_ESTABLISH_IND, msg);
- if (msgb_l2len(msg) >
- sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
+ /* Establishment message on a VGCS channel is handled by VGCS FSM only. */
+ if (lchan_is_asci(msg->lchan)) {
+ if (msg->lchan->conn && msg->lchan->conn->vgcs_chan.fi)
+ osmo_fsm_inst_dispatch(msg->lchan->conn->vgcs_chan.fi, VGCS_EV_TALKER_EST, msg);
+ break;
+ }
+
+ if (msgb_l2len(msg) > (sizeof(*rllh) + 3) &&
rllh->data[0] == RSL_IE_L3_INFO) {
msg->l3h = &rllh->data[3];
return gsm0408_rcvmsg(msg, rllh->link_id);
@@ -1711,6 +2748,14 @@ static int abis_rsl_rx_rll(struct msgb *msg)
case RSL_MT_REL_IND:
/* BTS informs us of having received DISC from MS */
osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_RLL_REL_IND, &rllh->link_id);
+
+ /* Report to VGCS FSM */
+ if (lchan_is_asci(msg->lchan)) {
+ if (msg->lchan->conn && msg->lchan->conn->vgcs_chan.fi) {
+ uint8_t cause = GSM0808_CAUSE_CALL_CONTROL;
+ osmo_fsm_inst_dispatch(msg->lchan->conn->vgcs_chan.fi, VGCS_EV_TALKER_REL, &cause);
+ }
+ }
break;
case RSL_MT_REL_CONF:
/* BTS informs us of having received UA from MS,
@@ -1728,15 +2773,77 @@ 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;
}
+/* Return an ip.access RTP CSD FMT value (uint8_t) or negative on error. */
+int ipacc_rtp_csd_fmt_transp(const struct channel_mode_and_rate *ch_mode_rate,
+ const enum rsl_ipac_rtp_csd_format_d format_d)
+{
+ uint8_t ret = format_d;
+
+ switch (ch_mode_rate->data_rate.t) {
+ case RSL_CMOD_CSD_T_32k0:
+ case RSL_CMOD_CSD_T_29k0:
+ ret |= RSL_IPAC_RTP_CSD_IR_32k << 4;
+ break;
+ case RSL_CMOD_CSD_T_14k4:
+ case RSL_CMOD_CSD_T_9k6:
+ ret |= RSL_IPAC_RTP_CSD_IR_16k << 4;
+ break;
+ case RSL_CMOD_CSD_T_4k8:
+ case RSL_CMOD_CSD_T_2k4:
+ case RSL_CMOD_CSD_T_1k2:
+ case RSL_CMOD_CSD_T_600:
+ case RSL_CMOD_CSD_T_1200_75:
+ ret |= RSL_IPAC_RTP_CSD_IR_8k << 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/* Return an ip.access RTP CSD FMT value (uint8_t) or negative on error. */
+int ipacc_rtp_csd_fmt_non_transp(const struct channel_mode_and_rate *ch_mode_rate,
+ const enum rsl_ipac_rtp_csd_format_d format_d)
+{
+ uint8_t ret = format_d;
+
+ switch (ch_mode_rate->data_rate.nt) {
+ case RSL_CMOD_CSD_NTA_43k5_14k5:
+ case RSL_CMOD_CSD_NTA_43k5_29k0:
+ case RSL_CMOD_CSD_NTA_14k5_43k5:
+ case RSL_CMOD_CSD_NTA_29k0_43k5:
+ case RSL_CMOD_CSD_NT_43k5:
+ ret |= RSL_IPAC_RTP_CSD_IR_64k << 4;
+ break;
+ case RSL_CMOD_CSD_NTA_29k0_14k5:
+ case RSL_CMOD_CSD_NTA_14k5_29k0:
+ case RSL_CMOD_CSD_NT_28k8:
+ ret |= RSL_IPAC_RTP_CSD_IR_32k << 4;
+ break;
+ case RSL_CMOD_CSD_NT_14k5:
+ case RSL_CMOD_CSD_NT_12k0:
+ ret |= RSL_IPAC_RTP_CSD_IR_16k << 4;
+ break;
+ case RSL_CMOD_CSD_NT_6k0:
+ ret |= RSL_IPAC_RTP_CSD_IR_8k << 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
/* 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:
@@ -1784,7 +2891,12 @@ 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_DATA_14k5:
+ case GSM48_CMODE_DATA_12k0:
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ return RTP_PT_CSDATA;
case GSM48_CMODE_SPEECH_V1:
switch (type) {
case GSM_LCHAN_TCH_F:
@@ -1869,11 +2981,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.
@@ -1881,22 +2998,43 @@ 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;
+
+ if (lchan->current_ch_indctr == GSM0808_CHAN_DATA) {
+ msgb_tv_put(msg, RSL_IE_IPAC_RTP_CSD_FMT, lchan->abis_ip.rtp_csd_fmt);
+
+ LOG_LCHAN(lchan, LOGL_DEBUG,
+ "Sending IPACC CRCX to BTS: rtp_csd_fmt=0x%02x RTP_PAYLOAD=%d (CSD) osmux_use=%d osmux_loc_cid=%d\n",
+ lchan->abis_ip.rtp_csd_fmt, lchan->abis_ip.rtp_payload,
+ lchan->abis_ip.osmux.use, lchan->abis_ip.osmux.local_cid);
+ } else {
+ /* 0x1- == receive-only, 0x-1 == EFR codec */
+ msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
+
+ 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);
+ }
- /* 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);
- msg->dst = lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(lchan);
return abis_rsl_sendmsg(msg);
}
@@ -1908,26 +3046,39 @@ 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);
att_ip = (uint32_t *)msgb_put(msg, sizeof(uint32_t));
*att_ip = htonl(dest_ip);
msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, dest_port);
- msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
+
+ if (lchan->current_ch_indctr == GSM0808_CHAN_DATA)
+ msgb_tv_put(msg, RSL_IE_IPAC_RTP_CSD_FMT, lchan->abis_ip.rtp_csd_fmt);
+ else
+ 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.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;
}
@@ -1940,14 +3091,27 @@ 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);
- 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),
- lchan->abis_ip.connect_port,
- lchan->abis_ip.rtp_payload,
- lchan->abis_ip.rtp_payload2,
- lchan->abis_ip.conn_id,
- lchan->abis_ip.speech_mode);
+ if (!msg)
+ return -EINVAL;
+
+ if (lchan->current_ch_indctr == GSM0808_CHAN_DATA)
+ LOG_LCHAN(lchan, LOGL_DEBUG, "Sending IPACC MDCX to BTS:"
+ " %s:%u rtp_payload=%u (CSD) rtp_payload2=%u conn_id=%u rtp_csd_fmt=0x%02x\n",
+ ip_to_a(lchan->abis_ip.connect_ip),
+ lchan->abis_ip.connect_port,
+ lchan->abis_ip.rtp_payload,
+ lchan->abis_ip.rtp_payload2,
+ lchan->abis_ip.conn_id,
+ lchan->abis_ip.rtp_csd_fmt);
+ else
+ 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),
+ lchan->abis_ip.connect_port,
+ lchan->abis_ip.rtp_payload,
+ lchan->abis_ip.rtp_payload2,
+ lchan->abis_ip.conn_id,
+ lchan->abis_ip.speech_mode);
return abis_rsl_sendmsg(msg);
}
@@ -1967,7 +3131,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)) {
@@ -1975,6 +3144,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);
@@ -1987,7 +3165,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");
@@ -2012,7 +3190,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);
@@ -2025,7 +3208,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");
@@ -2040,7 +3223,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));
@@ -2053,6 +3241,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: ");
@@ -2094,7 +3285,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;
}
@@ -2124,22 +3315,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);
}
@@ -2150,7 +3347,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";
@@ -2223,7 +3420,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);
@@ -2244,7 +3441,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);
}
@@ -2270,7 +3467,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);
}
@@ -2284,7 +3481,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);
}
@@ -2300,7 +3497,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);
}
@@ -2317,7 +3514,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
new file mode 100644
index 000000000..80a35766c
--- /dev/null
+++ b/src/osmo-bsc/acc.c
@@ -0,0 +1,575 @@
+/* (C) 2018-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Stefan Sperling <ssperling@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 <strings.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/acc.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts.h>
+
+/*
+ * Check if an ACC has been permanently barred for a BTS,
+ * e.g. with the 'rach access-control-class' VTY command.
+ */
+static bool acc_is_permanently_barred(struct gsm_bts *bts, unsigned int acc)
+{
+ OSMO_ASSERT(acc <= 9);
+ if (acc == 8 || acc == 9)
+ return (bts->si_common.rach_control.t2 & (1 << (acc - 8)));
+ return (bts->si_common.rach_control.t3 & (1 << (acc)));
+}
+
+/*!
+ * Return bitmasks which correspond to access control classes that are currently
+ * denied access. Ramping is only concerned with those bits which control access
+ * for ACCs 0-9, and any of the other bits will always be set to zero in these masks, i.e.
+ * it is safe to OR these bitmasks with the corresponding fields in struct gsm48_rach_control.
+ * \param[in] acc_mgr Pointer to acc_mgr structure.
+ */
+static inline uint8_t acc_mgr_get_barred_t2(struct acc_mgr *acc_mgr)
+{
+ return ((~acc_mgr->allowed_subset_mask) >> 8) & 0x03;
+};
+static inline uint8_t acc_mgr_get_barred_t3(struct acc_mgr *acc_mgr)
+{
+ return (~acc_mgr->allowed_subset_mask) & 0xff;
+}
+
+static uint8_t acc_mgr_subset_len(struct acc_mgr *acc_mgr)
+{
+ return OSMO_MIN(acc_mgr->len_allowed_ramp, acc_mgr->len_allowed_adm);
+}
+
+static void acc_mgr_enable_rotation_cond(struct acc_mgr *acc_mgr)
+{
+ if (acc_mgr->allowed_permanent_count && acc_mgr->allowed_subset_mask_count &&
+ acc_mgr->allowed_permanent_count != acc_mgr->allowed_subset_mask_count) {
+ 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 pending) */
+ osmo_timer_del(&acc_mgr->rotate_timer);
+ }
+}
+
+static void acc_mgr_gen_subset(struct acc_mgr *acc_mgr, bool update_si)
+{
+ uint8_t acc;
+
+ acc_mgr->allowed_subset_mask = 0; /* clean mask */
+ acc_mgr->allowed_subset_mask_count = 0;
+ acc_mgr->allowed_permanent_count = 0;
+
+ for (acc = 0; acc < 10; acc++) {
+ if (acc_is_permanently_barred(acc_mgr->bts, acc))
+ continue;
+ acc_mgr->allowed_permanent_count++;
+ if (acc_mgr->allowed_subset_mask_count < acc_mgr_subset_len(acc_mgr)) {
+ acc_mgr->allowed_subset_mask |= (1 << acc);
+ acc_mgr->allowed_subset_mask_count++;
+ }
+ }
+
+ acc_mgr_enable_rotation_cond(acc_mgr);
+
+ LOG_BTS(acc_mgr->bts, DRSL, LOGL_INFO,
+ "ACC: New ACC allowed subset 0x%03" PRIx16 " (active_len=%" PRIu8
+ ", ramp_len=%" PRIu8 ", adm_len=%" PRIu8 ", perm_len=%" PRIu8 ", rotation=%s)\n",
+ acc_mgr->allowed_subset_mask, acc_mgr->allowed_subset_mask_count,
+ acc_mgr->len_allowed_ramp, acc_mgr->len_allowed_adm,
+ acc_mgr->allowed_permanent_count,
+ osmo_timer_pending(&(acc_mgr)->rotate_timer) ? "on" : "off");
+
+ /* Trigger SI data update, acc_mgr_apply_acc will bew called */
+ if (update_si)
+ gsm_bts_set_system_infos(acc_mgr->bts);
+}
+
+static uint8_t get_highest_allowed_acc(uint16_t mask)
+{
+ int i;
+
+ for (i = 9; i >= 0; i--) {
+ if (mask & (1 << i))
+ return i;
+ }
+ OSMO_ASSERT(0);
+ return 0;
+}
+
+static uint8_t get_lowest_allowed_acc(uint16_t mask)
+{
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ if (mask & (1 << i))
+ return i;
+ }
+ OSMO_ASSERT(0);
+ return 0;
+}
+
+#define LOG_ACC_CHG(acc_mgr, level, old_mask, verb_str) \
+ LOG_BTS((acc_mgr)->bts, DRSL, level, \
+ "ACC: %s ACC allowed active subset 0x%03" PRIx16 " -> 0x%03" PRIx16 \
+ " (active_len=%" PRIu8 ", ramp_len=%" PRIu8 ", adm_len=%" PRIu8 \
+ ", perm_len=%" PRIu8 ", rotation=%s)\n", \
+ verb_str, old_mask, (acc_mgr)->allowed_subset_mask, \
+ (acc_mgr)->allowed_subset_mask_count, \
+ (acc_mgr)->len_allowed_ramp, (acc_mgr)->len_allowed_adm, \
+ (acc_mgr)->allowed_permanent_count, \
+ osmo_timer_pending(&(acc_mgr)->rotate_timer) ? "on" : "off")
+
+/* Call when either adm_len or ramp_len changed (and values have been updated) */
+static void acc_mgr_subset_length_changed(struct acc_mgr *acc_mgr)
+{
+ uint16_t old_mask = acc_mgr->allowed_subset_mask;
+ uint8_t curr_len = acc_mgr->allowed_subset_mask_count;
+ uint8_t new_len = acc_mgr_subset_len(acc_mgr);
+ int8_t diff = new_len - curr_len;
+ uint8_t i;
+
+ if (curr_len == new_len)
+ return;
+
+ if (new_len == 0) {
+ acc_mgr->allowed_subset_mask = 0;
+ acc_mgr->allowed_subset_mask_count = 0;
+ acc_mgr_enable_rotation_cond(acc_mgr);
+ LOG_ACC_CHG(acc_mgr, LOGL_INFO, old_mask, "update");
+ gsm_bts_set_system_infos(acc_mgr->bts);
+ return;
+ }
+
+ if (curr_len == 0) {
+ acc_mgr_gen_subset(acc_mgr, true);
+ return;
+ }
+
+ /* Try to add new ACCs to the set starting from highest one (since we rotate rolling up) */
+ if (diff > 0) { /* curr_len < new_len */
+ uint8_t highest = get_highest_allowed_acc(acc_mgr->allowed_subset_mask);
+ /* It's fine skipping highest in the loop since it's known to be already set: */
+ for (i = (highest + 1) % 10; i != highest; i = (i + 1) % 10) {
+ if (acc_is_permanently_barred(acc_mgr->bts, i))
+ continue;
+ if (acc_mgr->allowed_subset_mask & (1 << i))
+ continue; /* already in set */
+ acc_mgr->allowed_subset_mask |= (1 << i);
+ acc_mgr->allowed_subset_mask_count++;
+ diff--;
+ if (diff == 0)
+ break;
+ }
+ } else { /* curr_len > new_len, try removing from lowest one. */
+ uint8_t lowest = get_lowest_allowed_acc(acc_mgr->allowed_subset_mask);
+ i = lowest;
+ do {
+ if ((acc_mgr->allowed_subset_mask & (1 << i))) {
+ acc_mgr->allowed_subset_mask &= ~(1 << i);
+ acc_mgr->allowed_subset_mask_count--;
+ diff++;
+ if (diff == 0)
+ break;
+ }
+ i = (i + 1) % 10;
+ } while(i != lowest);
+ }
+
+ acc_mgr_enable_rotation_cond(acc_mgr);
+ LOG_ACC_CHG(acc_mgr, LOGL_INFO, old_mask, "update");
+
+ /* if we updated the set, notify about it */
+ if (curr_len != acc_mgr->allowed_subset_mask_count)
+ gsm_bts_set_system_infos(acc_mgr->bts);
+
+}
+
+/* Eg: (2,3,4) -> first=2; last=4. (3,7,8) -> first=3, last=8; (8,9,2) -> first=8, last=2 */
+void get_subset_limits(struct acc_mgr *acc_mgr, uint8_t *first, uint8_t *last)
+{
+ uint8_t lowest = get_lowest_allowed_acc(acc_mgr->allowed_subset_mask);
+ uint8_t highest = get_highest_allowed_acc(acc_mgr->allowed_subset_mask);
+ /* check if there's unselected ACCs between lowest and highest, that
+ * means subset is wrapping around, eg: (8,9,1)
+ * Assumption: The permanent set is bigger than the current selected subset */
+ bool is_wrapped = false;
+ uint8_t i = (lowest + 1) % 10;
+ if (lowest != highest) { /* len(allowed_subset_mask) > 1 */
+ i = (lowest + 1) % 10;
+ do {
+ if (!acc_is_permanently_barred(acc_mgr->bts, i) &&
+ !(acc_mgr->allowed_subset_mask & (1 << i))) {
+ is_wrapped = true;
+ break;
+ }
+ i = (i + 1) % 10;
+ } while (i != (highest + 1) % 10);
+ }
+
+ if (is_wrapped) {
+ /* Assumption: "i" is pointing to the lowest dynamically barred ACC.
+ Example: 11 1000 00>0<1. */
+ *last = i - 1;
+ while (acc_is_permanently_barred(acc_mgr->bts, *last))
+ *last -= 1;
+ *first = i + 1;
+ while (acc_is_permanently_barred(acc_mgr->bts, *first) ||
+ !(acc_mgr->allowed_subset_mask & (1 << (*first))))
+ *first += 1;
+ } else {
+ *first = lowest;
+ *last = highest;
+ }
+}
+static void do_acc_rotate_step(void *data)
+{
+ struct acc_mgr *acc_mgr = data;
+ uint8_t i;
+ uint8_t first, last;
+ uint16_t old_mask = acc_mgr->allowed_subset_mask;
+
+ /* Assumption: The size of the subset didn't change, that's handled by
+ * acc_mgr_subset_length_changed()
+ */
+
+ /* Assumption: Rotation timer has been disabled if no ACC is allowed */
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask_count != 0);
+
+ /* One ACC is rotated at a time: Drop first ACC and add next from last ACC */
+ get_subset_limits(acc_mgr, &first, &last);
+
+ acc_mgr->allowed_subset_mask &= ~(1 << first);
+ i = (last + 1) % 10;
+ do {
+ if (!acc_is_permanently_barred(acc_mgr->bts, i) &&
+ !(acc_mgr->allowed_subset_mask & (1 << i))) {
+ /* found first one which can be allowed, do it and be done */
+ acc_mgr->allowed_subset_mask |= (1 << i);
+ break;
+ }
+ i = (i + 1 ) % 10;
+ } while (i != (last + 1) % 10);
+
+ osmo_timer_schedule(&acc_mgr->rotate_timer, acc_mgr->rotation_time_sec, 0);
+
+ if (old_mask != acc_mgr->allowed_subset_mask) {
+ LOG_ACC_CHG(acc_mgr, LOGL_INFO, old_mask, "rotate");
+ gsm_bts_set_system_infos(acc_mgr->bts);
+ }
+}
+
+void acc_mgr_init(struct acc_mgr *acc_mgr, struct gsm_bts *bts)
+{
+ acc_mgr->bts = bts;
+ acc_mgr->len_allowed_adm = 10; /* Allow all by default */
+ acc_mgr->len_allowed_ramp = 10;
+ acc_mgr->rotation_time_sec = ACC_MGR_QUANTUM_DEFAULT;
+ osmo_timer_setup(&acc_mgr->rotate_timer, do_acc_rotate_step, acc_mgr);
+ /* FIXME: Don't update SI yet, avoid crash due to bts->model being NULL */
+ acc_mgr_gen_subset(acc_mgr, false);
+}
+
+uint8_t acc_mgr_get_len_allowed_adm(struct acc_mgr *acc_mgr)
+{
+ return acc_mgr->len_allowed_adm;
+}
+
+uint8_t acc_mgr_get_len_allowed_ramp(struct acc_mgr *acc_mgr)
+{
+ return acc_mgr->len_allowed_ramp;
+}
+
+void acc_mgr_set_len_allowed_adm(struct acc_mgr *acc_mgr, uint8_t len_allowed_adm)
+{
+ uint8_t old_len;
+
+ OSMO_ASSERT(len_allowed_adm <= 10);
+
+ if (acc_mgr->len_allowed_adm == len_allowed_adm)
+ return;
+
+ LOG_BTS(acc_mgr->bts, DRSL, LOGL_DEBUG,
+ "ACC: administrative rotate subset size set to %" PRIu8 "\n", len_allowed_adm);
+
+ old_len = acc_mgr_subset_len(acc_mgr);
+ acc_mgr->len_allowed_adm = len_allowed_adm;
+ if (old_len != acc_mgr_subset_len(acc_mgr))
+ acc_mgr_subset_length_changed(acc_mgr);
+}
+void acc_mgr_set_len_allowed_ramp(struct acc_mgr *acc_mgr, uint8_t len_allowed_ramp)
+{
+ uint8_t old_len;
+
+ OSMO_ASSERT(len_allowed_ramp <= 10);
+
+ if (acc_mgr->len_allowed_ramp == len_allowed_ramp)
+ return;
+
+ LOG_BTS(acc_mgr->bts, DRSL, LOGL_DEBUG,
+ "ACC: ramping rotate subset size set to %" PRIu8 "\n", len_allowed_ramp);
+
+ old_len = acc_mgr_subset_len(acc_mgr);
+ acc_mgr->len_allowed_ramp = len_allowed_ramp;
+ if (old_len != acc_mgr_subset_len(acc_mgr))
+ acc_mgr_subset_length_changed(acc_mgr);
+}
+
+void acc_mgr_set_rotation_time(struct acc_mgr *acc_mgr, uint32_t rotation_time_sec)
+{
+ LOG_BTS(acc_mgr->bts, DRSL, LOGL_DEBUG,
+ "ACC: rotate subset time set to %" PRIu32 " seconds\n", rotation_time_sec);
+ acc_mgr->rotation_time_sec = rotation_time_sec;
+}
+
+void acc_mgr_perm_subset_changed(struct acc_mgr *acc_mgr, struct gsm48_rach_control *rach_control)
+{
+ /* Even if amount is the same, the allowed/barred ones may have changed,
+ * so let's retrigger generation of an entire subset rather than
+ * rotating it */
+ acc_mgr_gen_subset(acc_mgr, true);
+}
+
+/*!
+ * Potentially mark certain Access Control Classes (ACCs) as barred in accordance to ACC policy.
+ * \param[in] acc_mgr Pointer to acc_mgr structure.
+ * \param[in] rach_control RACH control parameters in which barred ACCs will be configured.
+ */
+void acc_mgr_apply_acc(struct acc_mgr *acc_mgr, struct gsm48_rach_control *rach_control)
+{
+ rach_control->t2 |= acc_mgr_get_barred_t2(acc_mgr);
+ rach_control->t3 |= acc_mgr_get_barred_t3(acc_mgr);
+}
+
+
+//////////////////////////
+// acc_ramp
+//////////////////////////
+static void do_acc_ramping_step(void *data)
+{
+ struct acc_ramp *acc_ramp = data;
+ struct gsm_bts *bts = acc_ramp->bts;
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+
+ uint8_t old_len = acc_mgr_get_len_allowed_ramp(acc_mgr);
+ uint8_t new_len = old_len;
+
+ /* Remark dec: Never decrease back to 0, it is desirable to always allow at
+ * least 1 ACC at ramping lvl to allow subscribers to eventually use the
+ * network. If total barring is desired, it can be controlled by the
+ * adminsitrative subset length through VTY.
+ * Remark inc: Never try going over the admin subset size, since it
+ * wouldn't change final subset size anyway and it would create a fake
+ * sense of safe load handling capacity. If then load became high, being
+ * on upper size would mean the BTS requires more time to effectively
+ * drop down the final subset size, hence delaying recovery.
+ */
+ if (bts->chan_load_avg > acc_ramp->chan_load_upper_threshold)
+ new_len = (uint8_t)OSMO_MAX(1, (int)(old_len - acc_ramp->step_size));
+ else if (bts->chan_load_avg < acc_ramp->chan_load_lower_threshold)
+ new_len = OSMO_MIN(acc_mgr_get_len_allowed_adm(acc_mgr),
+ old_len + acc_ramp->step_size);
+ else
+ new_len = old_len;
+
+ if (new_len != old_len) {
+ LOG_BTS(bts, DRSL, LOGL_DEBUG,
+ "ACC RAMP: changing ramping subset size %" PRIu8
+ " -> %" PRIu8 ", chan_load_avg=%" PRIu8 "%%\n",
+ old_len, new_len, bts->chan_load_avg);
+ acc_mgr_set_len_allowed_ramp(acc_mgr, new_len);
+ }
+
+ osmo_timer_schedule(&acc_ramp->step_timer, acc_ramp->step_interval_sec, 0);
+}
+
+/* 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)
+{
+ 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;
+ 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 the first TRX. */
+ if (trx != trx->bts->c0)
+ return 0;
+
+ 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);
+ }
+ return 0;
+}
+
+/*!
+ * Initialize an acc_ramp data structure.
+ * Storage for this structure must be provided by the caller.
+ *
+ * By default, ACC ramping is disabled and all ACCs are allowed.
+ *
+ * \param[in] acc_ramp Pointer to acc_ramp structure to be initialized.
+ * \param[in] bts BTS which uses this ACC ramp data structure.
+ */
+void acc_ramp_init(struct acc_ramp *acc_ramp, struct gsm_bts *bts)
+{
+ acc_ramp->bts = bts;
+ acc_ramp_set_enabled(acc_ramp, false);
+ acc_ramp->step_size = ACC_RAMP_STEP_SIZE_DEFAULT;
+ acc_ramp->step_interval_sec = ACC_RAMP_STEP_INTERVAL_MIN;
+ 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);
+}
+
+/*!
+ * Change the ramping step size which controls how many ACCs will be allowed per ramping step.
+ * Returns negative on error (step_size out of range), else zero.
+ * \param[in] acc_ramp Pointer to acc_ramp structure.
+ * \param[in] step_size The new step size value.
+ */
+int acc_ramp_set_step_size(struct acc_ramp *acc_ramp, unsigned int step_size)
+{
+ if (step_size < ACC_RAMP_STEP_SIZE_MIN || step_size > ACC_RAMP_STEP_SIZE_MAX)
+ return -ERANGE;
+
+ acc_ramp->step_size = step_size;
+ LOG_BTS(acc_ramp->bts, DRSL, LOGL_DEBUG, "ACC RAMP: ramping step size set to %u\n", step_size);
+ return 0;
+}
+
+/*!
+ * Change the ramping step interval to a fixed value. Unless this function is called,
+ * the interval is automatically scaled to the BTS channel load average.
+ * \param[in] acc_ramp Pointer to acc_ramp structure.
+ * \param[in] step_interval The new fixed step interval in seconds.
+ */
+int acc_ramp_set_step_interval(struct acc_ramp *acc_ramp, unsigned int step_interval)
+{
+ if (step_interval < ACC_RAMP_STEP_INTERVAL_MIN || step_interval > ACC_RAMP_STEP_INTERVAL_MAX)
+ return -ERANGE;
+
+ acc_ramp->step_interval_sec = step_interval;
+ LOG_BTS(acc_ramp->bts, DRSL, LOGL_DEBUG, "ACC RAMP: ramping step interval set to %u seconds\n",
+ step_interval);
+ return 0;
+}
+
+/*!
+ * Change the ramping channel load thresholds. They control how ramping subset
+ * size of allowed ACCs changes in relation to current channel load (%, 0-100):
+ * Under the lower threshold, subset size may be increased; above the upper
+ * threshold, subset size may be decreased.
+ * \param[in] acc_ramp Pointer to acc_ramp structure.
+ * \param[in] low_threshold The new minimum threshold: values under it allow for increasing the ramping subset size.
+ * \param[in] up_threshold The new maximum threshold: values under it allow for increasing the ramping subset size.
+ */
+int acc_ramp_set_chan_load_thresholds(struct acc_ramp *acc_ramp, unsigned int low_threshold, unsigned int up_threshold)
+{
+ /* for instance, high=49 and lower=50 makes sense:
+ [50-100] -> decrease, [0-49] -> increase */
+ if ((int)up_threshold - (int)low_threshold < -1)
+ return -ERANGE;
+
+ acc_ramp->chan_load_lower_threshold = low_threshold;
+ acc_ramp->chan_load_upper_threshold = up_threshold;
+ return 0;
+}
+
+/*!
+ * Determine if ACC ramping should be started according to configuration, and
+ * begin the ramping process if the necessary conditions are present.
+ * Perform at least one ramping step to allow 'step_size' ACCs.
+ * If 'step_size' is ACC_RAMP_STEP_SIZE_MAX, or if ACC ramping is disabled,
+ * all ACCs will be allowed immediately.
+ * \param[in] acc_ramp Pointer to acc_ramp structure.
+ */
+void acc_ramp_trigger(struct acc_ramp *acc_ramp)
+{
+ if (acc_ramp_is_enabled(acc_ramp)) {
+ if (osmo_timer_pending(&acc_ramp->step_timer))
+ return; /* Already started, nothing to do */
+
+ /* Set all available ACCs to barred and start ramping up. */
+ acc_mgr_set_len_allowed_ramp(&acc_ramp->bts->acc_mgr, 0);
+ if (acc_ramp->chan_load_lower_threshold == 0 &&
+ acc_ramp->chan_load_upper_threshold == 100) {
+ LOG_BTS(acc_ramp->bts, DRSL, LOGL_ERROR,
+ "ACC RAMP: starting ramp up with 0 ACCs and "
+ "no possibility to grow the allowed subset size! "
+ "Check VTY cmd access-control-class-ramping-chan-load\n");
+ }
+ do_acc_ramping_step(acc_ramp);
+ } else {
+ /* Abort any previously running ramping process and allow all available ACCs. */
+ acc_ramp_abort(acc_ramp);
+ }
+}
+
+/*!
+ * Abort the ramping process and allow all available ACCs immediately.
+ * \param[in] acc_ramp Pointer to acc_ramp structure.
+ */
+void acc_ramp_abort(struct acc_ramp *acc_ramp)
+{
+ 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/acc_ramp.c b/src/osmo-bsc/acc_ramp.c
deleted file mode 100644
index b79c0c2ac..000000000
--- a/src/osmo-bsc/acc_ramp.c
+++ /dev/null
@@ -1,361 +0,0 @@
-/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- *
- * Author: Stefan Sperling <ssperling@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 <strings.h>
-#include <errno.h>
-#include <stdbool.h>
-
-#include <osmocom/bsc/debug.h>
-#include <osmocom/bsc/acc_ramp.h>
-#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/bsc/chan_alloc.h>
-#include <osmocom/bsc/signal.h>
-#include <osmocom/bsc/abis_nm.h>
-
-/*
- * Check if an ACC has been permanently barred for a BTS,
- * e.g. with the 'rach access-control-class' VTY command.
- */
-static bool acc_is_permanently_barred(struct gsm_bts *bts, unsigned int acc)
-{
- OSMO_ASSERT(acc <= 9);
- if (acc == 8 || acc == 9)
- return (bts->si_common.rach_control.t2 & (1 << (acc - 8)));
- return (bts->si_common.rach_control.t3 & (1 << (acc)));
-}
-
-static void allow_one_acc(struct acc_ramp *acc_ramp, unsigned int acc)
-{
- OSMO_ASSERT(acc <= 9);
- if (acc_ramp->barred_accs & (1 << acc))
- LOG_BTS(acc_ramp->bts, DRSL, LOGL_NOTICE,
- "ACC RAMP: allowing Access Control Class %u\n", acc);
- acc_ramp->barred_accs &= ~(1 << acc);
-}
-
-static void barr_one_acc(struct acc_ramp *acc_ramp, unsigned int acc)
-{
- OSMO_ASSERT(acc <= 9);
- if ((acc_ramp->barred_accs & (1 << acc)) == 0)
- LOG_BTS(acc_ramp->bts, DRSL, LOGL_NOTICE,
- "ACC RAMP: barring Access Control Class %u\n", acc);
- acc_ramp->barred_accs |= (1 << acc);
-}
-
-static void barr_all_accs(struct acc_ramp *acc_ramp)
-{
- unsigned int acc;
- for (acc = 0; acc < 10; acc++) {
- if (!acc_is_permanently_barred(acc_ramp->bts, acc))
- barr_one_acc(acc_ramp, acc);
- }
-}
-
-static void allow_all_accs(struct acc_ramp *acc_ramp)
-{
- unsigned int acc;
- for (acc = 0; acc < 10; acc++) {
- if (!acc_is_permanently_barred(acc_ramp->bts, acc))
- allow_one_acc(acc_ramp, acc);
- }
-}
-
-static unsigned int get_next_step_interval(struct acc_ramp *acc_ramp)
-{
- struct gsm_bts *bts = acc_ramp->bts;
- uint64_t load;
-
- if (acc_ramp->step_interval_is_fixed)
- return acc_ramp->step_interval_sec;
-
- /* Scale the step interval to current channel load average. */
- load = (bts->chan_load_avg << 8); /* convert to fixed-point */
- acc_ramp->step_interval_sec = ((load * ACC_RAMP_STEP_INTERVAL_MAX) / 100) >> 8;
- if (acc_ramp->step_interval_sec < ACC_RAMP_STEP_SIZE_MIN)
- acc_ramp->step_interval_sec = ACC_RAMP_STEP_INTERVAL_MIN;
- else if (acc_ramp->step_interval_sec > ACC_RAMP_STEP_INTERVAL_MAX)
- acc_ramp->step_interval_sec = ACC_RAMP_STEP_INTERVAL_MAX;
-
- LOG_BTS(bts, DRSL, LOGL_DEBUG,
- "ACC RAMP: step interval set to %u seconds based on %u%% channel load average\n",
- acc_ramp->step_interval_sec, bts->chan_load_avg);
- return acc_ramp->step_interval_sec;
-}
-
-static void do_acc_ramping_step(void *data)
-{
- struct acc_ramp *acc_ramp = data;
- int i;
-
- /* Shortcut in case we only do one ramping step. */
- if (acc_ramp->step_size == ACC_RAMP_STEP_SIZE_MAX) {
- allow_all_accs(acc_ramp);
- gsm_bts_set_system_infos(acc_ramp->bts);
- return;
- }
-
- /* Allow 'step_size' ACCs, starting from ACC0. ACC9 will be allowed last. */
- for (i = 0; i < acc_ramp->step_size; i++) {
- int idx = ffs(acc_ramp_get_barred_t3(acc_ramp));
- if (idx > 0) {
- /* One of ACC0-ACC7 is still bared. */
- unsigned int acc = idx - 1;
- if (!acc_is_permanently_barred(acc_ramp->bts, acc))
- allow_one_acc(acc_ramp, acc);
- } else {
- idx = ffs(acc_ramp_get_barred_t2(acc_ramp));
- if (idx == 1 || idx == 2) {
- /* ACC8 or ACC9 is still barred. */
- unsigned int acc = idx - 1 + 8;
- if (!acc_is_permanently_barred(acc_ramp->bts, acc))
- allow_one_acc(acc_ramp, acc);
- } else {
- /* All ACCs are now allowed. */
- break;
- }
- }
- }
-
- gsm_bts_set_system_infos(acc_ramp->bts);
-
- /* If we have not allowed all ACCs yet, schedule another ramping step. */
- if (acc_ramp_get_barred_t2(acc_ramp) != 0x00 ||
- acc_ramp_get_barred_t3(acc_ramp) != 0x00)
- osmo_timer_schedule(&acc_ramp->step_timer, get_next_step_interval(acc_ramp), 0);
-}
-
-/* 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)
-{
- 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)
- return 0;
-
- if (nsd->obj_class != NM_OC_RADIO_CARRIER)
- 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)
- 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;
- }
- }
-
- if (trigger_ramping)
- acc_ramp_trigger(acc_ramp);
- else if (abort_ramping)
- acc_ramp_abort(acc_ramp);
-
- return 0;
-}
-
-/*!
- * Initialize an acc_ramp data structure.
- * Storage for this structure must be provided by the caller.
- *
- * By default, ACC ramping is disabled and all ACCs are allowed.
- *
- * \param[in] acc_ramp Pointer to acc_ramp structure to be initialized.
- * \param[in] bts BTS which uses this ACC ramp data structure.
- */
-void acc_ramp_init(struct acc_ramp *acc_ramp, struct gsm_bts *bts)
-{
- acc_ramp->bts = bts;
- acc_ramp_set_enabled(acc_ramp, false);
- acc_ramp->step_size = ACC_RAMP_STEP_SIZE_DEFAULT;
- acc_ramp->step_interval_sec = ACC_RAMP_STEP_INTERVAL_MIN;
- acc_ramp->step_interval_is_fixed = false;
- allow_all_accs(acc_ramp);
- 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);
-}
-
-/*!
- * Change the ramping step size which controls how many ACCs will be allowed per ramping step.
- * Returns negative on error (step_size out of range), else zero.
- * \param[in] acc_ramp Pointer to acc_ramp structure.
- * \param[in] step_size The new step size value.
- */
-int acc_ramp_set_step_size(struct acc_ramp *acc_ramp, unsigned int step_size)
-{
- if (step_size < ACC_RAMP_STEP_SIZE_MIN || step_size > ACC_RAMP_STEP_SIZE_MAX)
- return -ERANGE;
-
- acc_ramp->step_size = step_size;
- LOG_BTS(acc_ramp->bts, DRSL, LOGL_DEBUG, "ACC RAMP: ramping step size set to %u\n", step_size);
- return 0;
-}
-
-/*!
- * Change the ramping step interval to a fixed value. Unless this function is called,
- * the interval is automatically scaled to the BTS channel load average.
- * \param[in] acc_ramp Pointer to acc_ramp structure.
- * \param[in] step_interval The new fixed step interval in seconds.
- */
-int acc_ramp_set_step_interval(struct acc_ramp *acc_ramp, unsigned int step_interval)
-{
- if (step_interval < ACC_RAMP_STEP_INTERVAL_MIN || step_interval > ACC_RAMP_STEP_INTERVAL_MAX)
- return -ERANGE;
-
- acc_ramp->step_interval_sec = step_interval;
- acc_ramp->step_interval_is_fixed = true;
- LOG_BTS(acc_ramp->bts, DRSL, LOGL_DEBUG, "ACC RAMP: ramping step interval set to %u seconds\n",
- step_interval);
- return 0;
-}
-
-/*!
- * Clear a previously set fixed ramping step interval, so that the interval
- * is again automatically scaled to the BTS channel load average.
- * \param[in] acc_ramp Pointer to acc_ramp structure.
- */
-void acc_ramp_set_step_interval_dynamic(struct acc_ramp *acc_ramp)
-{
- acc_ramp->step_interval_is_fixed = false;
- LOG_BTS(acc_ramp->bts, DRSL, LOGL_DEBUG, "ACC RAMP: ramping step interval set to 'dynamic'\n");
-}
-
-/*!
- * Determine if ACC ramping should be started according to configuration, and
- * begin the ramping process if the necessary conditions are present.
- * Perform at least one ramping step to allow 'step_size' ACCs.
- * If 'step_size' is ACC_RAMP_STEP_SIZE_MAX, or if ACC ramping is disabled,
- * all ACCs will be allowed immediately.
- * \param[in] acc_ramp Pointer to acc_ramp structure.
- */
-void acc_ramp_trigger(struct acc_ramp *acc_ramp)
-{
- /* Abort any previously running ramping process and allow all available ACCs. */
- acc_ramp_abort(acc_ramp);
-
- if (acc_ramp_is_enabled(acc_ramp)) {
- /* Set all available ACCs to barred and start ramping up. */
- barr_all_accs(acc_ramp);
- do_acc_ramping_step(acc_ramp);
- }
-}
-
-/*!
- * Abort the ramping process and allow all available ACCs immediately.
- * \param[in] acc_ramp Pointer to acc_ramp structure.
- */
-void acc_ramp_abort(struct acc_ramp *acc_ramp)
-{
- if (osmo_timer_pending(&acc_ramp->step_timer))
- osmo_timer_del(&acc_ramp->step_timer);
-
- allow_all_accs(acc_ramp);
-}
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 cd5abc857..5e98a28e8 100644
--- a/src/osmo-bsc/assignment_fsm.c
+++ b/src/osmo-bsc/assignment_fsm.c
@@ -32,14 +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);
@@ -48,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.
@@ -71,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 { \
@@ -79,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) { \
@@ -93,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){
@@ -117,13 +144,18 @@ 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_ASSIGNMENT_FAILURE));
+ gscon_sigtran_send(conn, resp);
+ }
}
/* If assignment failed as early as in assignment_fsm_start(), there may not be an fi yet. */
@@ -133,7 +165,7 @@ static void on_assignment_failure(struct gsm_subscriber_connection *conn)
}
}
-static void _gsm0808_ass_compl_extend_osmux(struct msgb *msg, uint8_t cid)
+void bssap_extend_osmux(struct msgb *msg, uint8_t cid)
{
OSMO_ASSERT(msg->l3h[1] == msgb_l3len(msg) - 2); /*TL not in len */
msgb_tv_put(msg, GSM0808_IE_OSMO_OSMUX_CID, cid);
@@ -154,30 +186,40 @@ 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);
+ if (!lchan) {
+ assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE, "Assignment interrupted: primary lchan lost");
+ return;
+ }
+
+ 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);
-
- if (gscon_is_aoip(conn)) {
- if (!osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(conn->user_plane.mgw_endpoint_ci_msc,
- &addr_local)) {
- assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE,
- "Unable to compose RTP address of MGW -> MSC");
- return;
- }
- addr_local_p = &addr_local;
+ if (gscon_is_aoip(conn) && bsc_chan_ind_requires_rtp_stream(conn->assignment.ch_indctr)) {
+ if (!osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(conn->user_plane.mgw_endpoint_ci_msc,
+ &addr_local)) {
+ assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE,
+ "Unable to compose RTP address of MGW -> MSC");
+ return;
}
+ addr_local_p = &addr_local;
+ }
- if (gscon_is_aoip(conn) && conn->assignment.req.use_osmux) {
+ /* Generate rtp related fields */
+ switch (conn->assignment.ch_indctr) {
+ case GSM0808_CHAN_SPEECH:
+ perm_spch = gsm0808_permitted_speech(lchan->type, lchan->current_ch_mode_rate.chan_mode);
+
+ /* below is AoIP specific logic */
+ if (!gscon_is_aoip(conn))
+ break;
+
+ if (conn->assignment.req.use_osmux) {
if (!osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(conn->user_plane.mgw_endpoint_ci_msc,
&osmux_cid)) {
assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE,
@@ -186,19 +228,33 @@ static void send_assignment_complete(struct gsm_subscriber_connection *conn)
}
}
- /* Only AoIP networks include a speech codec (choosen) in the
- * assignment complete message. */
- 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_ptr = &sc;
- }
+ /* Extrapolate speech codec from speech mode */
+ gsm0808_speech_codec_from_chan_type(&sc, perm_spch);
+ sc.cfg = conn->lchan->current_ch_mode_rate.s15_s0;
+ sc_ptr = &sc;
+ break;
+ case GSM0808_CHAN_DATA:
+ /* below is AoIP specific logic */
+ if (!gscon_is_aoip(conn))
+ break;
+
+ /* The coding of Speech Codec Element for the CSData Codec Type
+ * is defined in 3GPP TS 48.008 section 3.2.2.103 */
+ sc = (struct gsm0808_speech_codec) {
+ .pi = true, /* PI indicates CSDoIP support */
+ .pt = false, /* PT indicates CSDoTDM support */
+ .type = GSM0808_SCT_CSD,
+ .cfg = 0, /* R2/R3 not set (redundancy not supported) */
+ };
+ sc_ptr = &sc;
+ break;
+ default:
+ break;
}
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) {
@@ -207,11 +263,11 @@ static void send_assignment_complete(struct gsm_subscriber_connection *conn)
return;
}
- if (gscon_is_aoip(conn) && conn->assignment.requires_voice_stream &&
+ if (gscon_is_aoip(conn) && bsc_chan_ind_requires_rtp_stream(conn->assignment.ch_indctr) &&
conn->assignment.req.use_osmux)
- _gsm0808_ass_compl_extend_osmux(resp, osmux_cid);
+ bssap_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_ASSIGNMENT_COMPLETE));
rc = gscon_sigtran_send(conn, resp);
if (rc) {
assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE,
@@ -223,55 +279,77 @@ 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 && !conn->assignment.req.vgcs);
+
+ /* Take on the new lchan. If there only was a Channel Mode Modify, then there is no new lchan to take on.
+ * In case of VGCS/VBS channel, the assignment is handled by its state machine. This subscriber connection will
+ * be released by MSC. */
+ 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)
@@ -279,7 +357,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;
@@ -314,73 +392,63 @@ 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);
}
-static int check_requires_voice(bool *requires_voice, enum gsm48_chan_mode chan_mode)
+static int chan_mode_to_ch_indctr(enum gsm48_chan_mode chan_mode)
{
- *requires_voice = false;
-
- switch (chan_mode) {
+ switch (gsm48_chan_mode_to_non_vamos(chan_mode)) {
+ case GSM48_CMODE_DATA_14k5:
+ case GSM48_CMODE_DATA_12k0:
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ return GSM0808_CHAN_DATA;
case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_EFR:
case GSM48_CMODE_SPEECH_AMR:
- *requires_voice = true;
- break;
+ return GSM0808_CHAN_SPEECH;
case GSM48_CMODE_SIGN:
- *requires_voice = false;
- break;
+ return GSM0808_CHAN_SIGN;
default:
return -EINVAL;
}
-
- return 0;
}
-/* Check if the incoming assignment requests 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)
+/* Check if the incoming assignment request has a channel mode that is
+ * inconsistent with ch_indctr, e.g. GSM48_CMODE_DATA_14k5 and
+ * GSM0808_CHAN_SPEECH */
+static int check_chan_mode_rate_against_ch_indctr(struct gsm_subscriber_connection *conn)
{
- bool requires_voice_pref = false, requires_voice_alt;
struct assignment_request *req = &conn->assignment.req;
struct osmo_fsm_inst *fi = conn->fi;
- int i, rc;
-
- /* When the assignment request indicates that there is an alternate
- * rate available (e.g. "Full or Half rate channel, Half rate
- * preferred..."), then both must be either voice or either signalling,
- * a mismatch is not permitted */
+ int i;
+ int rc;
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 = chan_mode_to_ch_indctr(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;
}
- if (i==0)
- requires_voice_pref = requires_voice_alt;
- else if (requires_voice_alt != requires_voice_pref) {
+ if (rc != req->ch_indctr) {
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));
+ "Channel mode %s has ch_indctr %d, channel type has ch_indctr %d",
+ gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode),
+ rc, req->ch_indctr);
return -EINVAL;
}
}
- conn->assignment.requires_voice_stream = requires_voice_pref;
return 0;
}
-/* Check if the conn is already associated with an lchan. If yes, we will check
- * if that lchan is compatible with the preferred rate/codec. If the lchan
- * turns out to be incompatible we try with the alternate rate/codec. */
+/* Decide if we should re-use an existing lchan. For this we check if the
+ * current lchan is compatible with one of the requested modes. */
static bool reuse_existing_lchan(struct gsm_subscriber_connection *conn)
{
struct assignment_request *req = &conn->assignment.req;
@@ -391,25 +459,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];
- break;
- }
+ 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;
+ }
- if (i == req->n_ch_mode_rate)
- return false;
+ return false;
+}
- if (conn->lchan->tch_mode != conn->lchan->ch_mode_rate.chan_mode) {
- /* FIXME: send Channel Mode Modify to put the current lchan in the right mode, and kick
- * off its RTP stream setup code path. See gsm48_lchan_modify() and
- * gsm48_rx_rr_modif_ack(), and see lchan_fsm.h LCHAN_EV_CHAN_MODE_MODIF_* */
- LOG_ASSIGNMENT(conn, LOGL_DEBUG,
- "Current lchan would be compatible, but Channel Mode Modify is not implemented\n");
- 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,
+ },
+
+ .ch_indctr = chan_mode_to_ch_indctr(lchan->current_ch_mode_rate.chan_mode),
+ };
+
+ 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 true;
+ 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,
@@ -421,7 +531,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);
@@ -429,8 +538,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;
@@ -440,38 +547,99 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
conn->assignment.req = *req;
req = &conn->assignment.req;
- /* Check if we need a voice stream. If yes, set the appropriate struct
- * members in conn */
- if (check_requires_voice_stream(conn) < 0)
+ assignment_count(CTR_ASSIGNMENT_ATTEMPTED);
+
+ if (check_chan_mode_rate_against_ch_indctr(conn) < 0)
return;
+ conn->assignment.ch_indctr = req->ch_indctr;
+
+ 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->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->assignment.selected_ch_mode_rate.chan_mode),
+ gsm_lchan_name(conn->lchan));
+
+ 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) {
+ assignment_count_result(CTR_ASSIGNMENT_COMPLETED);
+ osmo_fsm_inst_term(conn->assignment.fi, OSMO_FSM_TERM_REGULAR, 0);
+ }
+ return;
+ }
- /* There may be an already existing lchan, if yes, try to work with
- * the existing lchan */
- if (reuse_existing_lchan(conn)) {
+ /* The requested mode does not match the current TCH mode but the lchan is
+ * compatible. We will initiate a mode modify procedure. */
LOG_ASSIGNMENT(conn, LOGL_DEBUG,
- "Current lchan 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),
+ "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->assignment.selected_ch_mode_rate.chan_mode),
gsm_lchan_name(conn->lchan));
- 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) {
- assignment_count_result(CTR_ASSIGNMENT_COMPLETED);
- osmo_fsm_inst_term(conn->assignment.fi, OSMO_FSM_TERM_REGULAR, 0);
- }
+ 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->vgcs) {
+ /* When assigning to a VGCS/VBS, the target lchan is already defined. */
+ conn->assignment.new_lchan = req->target_lchan;
+ } else 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
@@ -480,13 +648,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;
}
@@ -494,33 +662,49 @@ 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,
+ /* Wait for lchan to become active before send assignment. In case of VGCS/VBS directly send assignment,
+ * because the channel is already active. */
+ assignment_fsm_state_chg(req->vgcs ? ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE : ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE);
+}
+
+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,
+ .ch_indctr = conn->assignment.ch_indctr,
.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,
};
- lchan_activate(conn->assignment.new_lchan, &info);
+ if (conn->assignment.new_lchan->vamos.is_secondary)
+ activ_info.type_for = LCHAN_TYPE_FOR_VAMOS;
+ 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);
@@ -538,6 +722,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);
@@ -605,7 +798,7 @@ static void assignment_fsm_wait_lchan_established(struct osmo_fsm_inst *fi, uint
static void assignment_fsm_post_lchan_established(struct osmo_fsm_inst *fi)
{
struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
- if (conn->assignment.requires_voice_stream)
+ if (bsc_chan_ind_requires_rtp_stream(conn->assignment.ch_indctr))
assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_MGW_ENDPOINT_TO_MSC);
else
assignment_success(conn);
@@ -615,15 +808,17 @@ static void assignment_fsm_wait_mgw_endpoint_to_msc_onenter(struct osmo_fsm_inst
{
struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
- OSMO_ASSERT(conn->assignment.requires_voice_stream);
+ OSMO_ASSERT(bsc_chan_ind_requires_rtp_stream(conn->assignment.ch_indctr));
LOG_ASSIGNMENT(conn, LOGL_DEBUG,
"Connecting MGW endpoint to the MSC's RTP port: %s:%u\n",
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,
@@ -669,18 +864,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,
+ .ch_indctr = conn->assignment.ch_indctr,
+ .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_MODIFIED)
,
},
[ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE] = {
@@ -716,11 +950,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),
@@ -730,9 +976,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:
@@ -741,11 +992,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:
@@ -753,7 +1005,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);
@@ -761,7 +1013,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 b94a749bf..aff1d83e6 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,229 +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/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_ipa_abisip_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 msc_connection_status = 0; /* XXX unused */
-
-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;
+ 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 && msc_connection_status == 1) {
- LOGP(DCTRL, LOGL_DEBUG, "MSC connection lost, sending TRAP.\n");
- msc_connection_status = 0;
- } else if (signal == S_MSC_CONNECTED && msc_connection_status == 0) {
- LOGP(DCTRL, LOGL_DEBUG, "MSC connection (re)established, sending TRAP.\n");
- msc_connection_status = 1;
- } else {
- return 0;
+ tmp = talloc_strdup(cmd, cmd->value);
+ if (!tmp)
+ goto oom;
+
+ 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 = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n");
+ tz->override = override;
+
+
+ talloc_free(tmp);
+ tmp = NULL;
+
+ 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;
}
- cmd->id = "0";
- cmd->variable = "msc_connection_status";
+ if (minstr == NULL || dststr == NULL)
+ goto err;
- get_msc0_connection_status(cmd, NULL);
+ tz_hours = atol(hourstr);
+ tz_mins = atol(minstr);
+ tz_dst = atol(dststr);
- ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
+ talloc_free(tmp);
+ tmp = NULL;
- talloc_free(cmd);
+ 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");
@@ -301,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 (osmo_ss7_asp_get_proto(asp) != 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");
@@ -663,63 +820,87 @@ 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)
+/* Return full information about all logical channels.
+ * format: show-lchan.full
+ * result format: New line delimited list of <bts>,<trx>,<ts>,<lchan>,<type>,<connection>,<state>,<last error>,<bs power>,
+ * <ms power>,<interference dbm>, <interference band>,<channel mode>,<imsi>,<tmsi>,<ipa bound ip>,<ipa bound port>,
+ * <ipa bound conn id>,<ipa conn ip>,<ipa conn port>,<ipa conn speech mode>
+ */
+static int get_net_show_lchan_full(struct ctrl_cmd *cmd, void *data)
{
- struct msc_signal_data *msc;
- struct gsm_network *net;
- struct gsm_bts *bts;
+ struct gsm_network *net = cmd->node;
+ int bts_nr;
+ bool first_bts = true;
+ char *bts_dump;
- if (subsys != SS_MSC)
- return 0;
- if (signal != S_MSC_AUTHENTICATED)
- return 0;
+ cmd->reply = talloc_strdup(cmd, "");
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
- msc = signal_data;
+ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
+ bts_dump = bts_lchan_dump_full_ctrl(cmd, gsm_bts_num(net, bts_nr));
+ if (!bts_dump) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ if (!strlen(bts_dump))
+ continue;
+ cmd->reply = talloc_asprintf_append(cmd->reply, first_bts ? "%s" : "\n%s", bts_dump);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ first_bts = false;
+ }
- net = msc->data->network;
- llist_for_each_entry(bts, &net->bts_list, list)
- generate_location_state_trap(bts, msc->data);
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(net_show_lchan_full, "show-lchan full");
- return 0;
+static int bsc_base_ctrl_cmds_install(struct gsm_network *net)
+{
+ 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);
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_show_lchan_full);
+
+ rc |= ctrl_cmd_install(CTRL_NODE_MSC, &cmd_msc_connection_status);
+
+ 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);
+
+ 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 774ded21b..000000000
--- a/src/osmo-bsc/bsc_ctrl_commands.c
+++ /dev/null
@@ -1,500 +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>
-
-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 38d1ba4ea..63d0c866c 100644
--- a/src/osmo-bsc/bsc_ctrl_lookup.c
+++ b/src/osmo-bsc/bsc_ctrl_lookup.c
@@ -6,18 +6,14 @@
* All Rights Reserved
*
* 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 2 of the License, or
+ * 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 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.
+ * GNU Affero General Public License for more details.
*
*/
@@ -29,6 +25,7 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bts.h>
extern vector ctrl_node_vec;
@@ -46,6 +43,7 @@ static int bsc_ctrl_node_lookup(void *data, vector vline, int *node_type,
struct gsm_bts *bts = NULL;
struct gsm_bts_trx *trx = NULL;
struct gsm_bts_trx_ts *ts = NULL;
+ struct gsm_lchan *lchan = NULL;
struct bsc_msc_data *msc = NULL;
char *token = vector_slot(vline, *i);
long num;
@@ -92,6 +90,20 @@ static int bsc_ctrl_node_lookup(void *data, vector vline, int *node_type,
goto err_missing;
*node_data = ts;
*node_type = CTRL_NODE_TS;
+ } else if (!strcmp(token, "lchan")) {
+ if (*node_type != CTRL_NODE_TS || !*node_data)
+ goto err_missing;
+ ts = *node_data;
+ (*i)++;
+ if (!ctrl_parse_get_num(vline, *i, &num))
+ goto err_index;
+
+ if ((num >= 0) && (num < TS_MAX_LCHAN))
+ lchan = &ts->lchan[num];
+ if (!lchan)
+ goto err_missing;
+ *node_data = lchan;
+ *node_type = CTRL_NODE_LCHAN;
} else if (!strcmp(token, "msc")) {
if (*node_type != CTRL_NODE_ROOT || !net)
goto err_missing;
@@ -114,10 +126,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 733353352..c6c3e79a5 100644
--- a/src/osmo-bsc/bsc_init.c
+++ b/src/osmo-bsc/bsc_init.c
@@ -36,6 +36,9 @@
#include <osmocom/bsc/handover_cfg.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
#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>
@@ -44,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)
{
@@ -68,186 +61,43 @@ int bsc_shutdown_net(struct gsm_network *net)
return 0;
}
-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);
- return 0;
- }
-
- if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) {
- LOGP(DNM, LOGL_ERROR, "BTS %u uptime computation failure: %s\n", bts->nr, strerror(errno));
- return 0;
- }
-
- /* monotonic clock helps to ensure that the conversion is valid */
- return difftime(tp.tv_sec, bts->uptime);
-}
-
-static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len)
-{
- struct gsm_bts *bts = trx->bts;
- int rc, j;
-
- if (si_len) {
- DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i),
- osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN));
- } else
- DEBUGP(DRR, "SI%s: OFF\n", get_value_string(osmo_sitype_strs, i));
-
- switch (i) {
- case SYSINFO_TYPE_5:
- case SYSINFO_TYPE_5bis:
- case SYSINFO_TYPE_5ter:
- case SYSINFO_TYPE_6:
- rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i),
- si_len ? GSM_BTS_SI(bts, i) : NULL, si_len);
- break;
- case SYSINFO_TYPE_2quater:
- if (si_len == 0) {
- rc = rsl_bcch_info(trx, i, NULL, 0);
- break;
- }
- rc = 0;
- for (j = 0; j <= bts->si2q_count; j++)
- rc = rsl_bcch_info(trx, i, (const uint8_t *)GSM_BTS_SI2Q(bts, j), GSM_MACBLOCK_LEN);
- break;
- default:
- rc = rsl_bcch_info(trx, i, si_len ? GSM_BTS_SI(bts, i) : NULL, si_len);
- break;
- }
-
- return rc;
-}
+/* XXX hard-coded for now */
+#define T3122_CHAN_LOAD_SAMPLE_INTERVAL 1 /* in seconds */
-/* set all system information types for a TRX */
-int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx)
+static void update_t3122_chan_load_timer(void *data)
{
- int i, rc;
- struct gsm_bts *bts = trx->bts;
- uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n;
- int si_len[_MAX_SYSINFO_TYPE];
-
- bts->si_common.cell_sel_par.ms_txpwr_max_ccch =
- ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
- bts->si_common.cell_sel_par.neci = bts->network->neci;
-
- /* Zero/forget the state of the dynamically computed SIs, leeping the static ones */
- bts->si_valid = bts->si_mode_static;
-
- /* First, we determine which of the SI messages we actually need */
-
- if (trx == bts->c0) {
- /* 1...4 are always present on a C0 TRX */
- gen_si[n_si++] = SYSINFO_TYPE_1;
- gen_si[n_si++] = SYSINFO_TYPE_2;
- gen_si[n_si++] = SYSINFO_TYPE_2bis;
- gen_si[n_si++] = SYSINFO_TYPE_2ter;
- gen_si[n_si++] = SYSINFO_TYPE_2quater;
- gen_si[n_si++] = SYSINFO_TYPE_3;
- gen_si[n_si++] = SYSINFO_TYPE_4;
-
- /* 13 is always present on a C0 TRX of a GPRS BTS */
- if (bts->gprs.mode != BTS_GPRS_NONE)
- gen_si[n_si++] = SYSINFO_TYPE_13;
- }
-
- /* 5 and 6 are always present on every TRX */
- gen_si[n_si++] = SYSINFO_TYPE_5;
- gen_si[n_si++] = SYSINFO_TYPE_5bis;
- gen_si[n_si++] = SYSINFO_TYPE_5ter;
- gen_si[n_si++] = SYSINFO_TYPE_6;
-
- /* Second, we generate the selected SI via RSL */
-
- for (n = 0; n < n_si; n++) {
- i = gen_si[n];
- /* Only generate SI if this SI is not in "static" (user-defined) mode */
- if (!(bts->si_mode_static & (1 << i))) {
- /* 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);
- if (rc < 0)
- goto err_out;
- si_len[i] = 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;
- }
- }
-
- /* Third, we send the selected SI via RSL */
-
- for (n = 0; n < n_si; n++) {
- i = 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 (bts->si_unused_send_empty)
- rc = rsl_si(trx, i, 0);
- else
- rc = 0; /* some nanoBTS fw don't like receiving empty unsupported SI */
- } else
- rc = rsl_si(trx, i, si_len[i]);
- if (rc < 0)
- return rc;
- }
+ struct gsm_network *net = data;
+ struct gsm_bts *bts;
- /* Make sure the PCU is aware (in case anything GPRS related has
- * changed in SI */
- pcu_info_update(bts);
+ llist_for_each_entry(bts, &net->bts_list, list)
+ bts_update_t3122_chan_load(bts);
- return 0;
-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));
- return rc;
+ /* Keep this timer ticking. */
+ osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0);
}
-/* set all system information types for a BTS */
-int gsm_bts_set_system_infos(struct gsm_bts *bts)
+static void bsc_store_bts_uptime(void *data)
{
- struct gsm_bts_trx *trx;
-
- /* Generate a new ID */
- bts->bcch_change_mark += 1;
- bts->bcch_change_mark %= 0x7;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- int rc;
+ struct gsm_network *net = data;
+ struct gsm_bts *bts;
- rc = gsm_bts_trx_set_system_infos(trx);
- if (rc != 0)
- return rc;
- }
+ llist_for_each_entry(bts, &net->bts_list, list)
+ bts_store_uptime(bts);
- return 0;
+ /* Keep this timer ticking. */
+ osmo_timer_schedule(&net->bts_store_uptime_timer, BTS_STORE_UPTIME_INTERVAL, 0);
}
-/* XXX hard-coded for now */
-#define T3122_CHAN_LOAD_SAMPLE_INTERVAL 1 /* in seconds */
-
-static void update_t3122_chan_load_timer(void *data)
+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_update_t3122_chan_load(bts);
+ bts_store_lchan_durations(bts);
/* Keep this timer ticking. */
- osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0);
+ osmo_timer_schedule(&net->bts_store_lchan_durations_timer, BTS_STORE_LCHAN_DURATIONS_INTERVAL, 0);
}
static struct gsm_network *bsc_network_init(void *ctx)
@@ -256,8 +106,7 @@ static struct gsm_network *bsc_network_init(void *ctx)
net->cbc = talloc_zero(net, struct bsc_cbc_link);
if (!net->cbc) {
- talloc_free(net);
- return NULL;
+ goto err_out;
}
/* Init back pointer */
@@ -266,20 +115,67 @@ 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);
- if (!net->bsc_ctrs) {
- talloc_free(net);
- return NULL;
- }
+ if (!net->bsc_ctrs)
+ goto err_out;
net->bsc_statg = osmo_stat_item_group_alloc(net, &bsc_statg_desc, 0);
- if (!net->bsc_statg) {
- rate_ctr_group_free(net->bsc_ctrs);
- talloc_free(net);
- return NULL;
- }
+ if (!net->bsc_statg)
+ goto err_free_bsc_ctr;
+
+ /* init statistics */
+ net->bts_unknown_ctrs = rate_ctr_group_alloc(net, &bts_ctrg_desc, BTS_STAT_IDX_UNKNOWN);
+ if (!net->bts_unknown_ctrs)
+ goto err_free_bsc_ctr_stat;
+ net->bts_unknown_statg = osmo_stat_item_group_alloc(net, &bts_statg_desc, BTS_STAT_IDX_UNKNOWN);
+ 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);
@@ -292,14 +188,34 @@ 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);
- net->cbc->net = net;
- /* no cbc_hostname: client not started by default */
- net->cbc->config.cbc_port = CBSP_TCP_PORT;
- /* listen_port == -1: server not started by default */
- net->cbc->config.listen_port = -1;
- net->cbc->config.listen_hostname = talloc_strdup(net->cbc, "127.0.0.1");
+ /* 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;
+ /* For CBSP client mode: default remote CBSP server port is CBSP_TCP_PORT == 48049. Leave the IP address unset.
+ * Also leave the local bind for the CBSP client disabled (unconfigured). */
+ net->cbc->client.remote_addr = (struct osmo_sockaddr_str){ .port = CBSP_TCP_PORT, };
+ net->cbc->client.local_addr = (struct osmo_sockaddr_str){};
+
+ net->pcu_sock_wqueue_len_max = BSC_PCU_SOCK_WQUEUE_LEN_DEFAULT;
return net;
+
+err_free_all:
+ rate_ctr_group_free(net->bts_unknown_ctrs);
+err_free_bsc_ctr_stat:
+ osmo_stat_item_group_free(net->bsc_statg);
+err_free_bsc_ctr:
+ rate_ctr_group_free(net->bsc_ctrs);
+err_out:
+ talloc_free(net);
+ return NULL;
}
int bsc_network_alloc(void)
diff --git a/src/osmo-bsc/bsc_rf_ctrl.c b/src/osmo-bsc/bsc_rf_ctrl.c
index 11cd22404..108f7e9ce 100644
--- a/src/osmo-bsc/bsc_rf_ctrl.c
+++ b/src/osmo-bsc/bsc_rf_ctrl.c
@@ -26,6 +26,7 @@
#include <osmocom/bsc/signal.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/ipaccess.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
@@ -123,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;
@@ -212,7 +303,7 @@ static void rf_check_cb(void *_data)
struct gsm_bts_trx *trx;
/* don't bother to check a booting or missing BTS */
- if (!bts->oml_link || !is_ipaccess_bts(bts))
+ if (!bts->oml_link || !is_ipa_abisip_bts(bts))
continue;
/* Exclude the BTS from the global lock */
@@ -371,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 = BSC_FD_READ | BSC_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;
@@ -483,9 +572,7 @@ static int rf_create_socket(struct osmo_bsc_rf *rf, const char *path)
return -1;
}
- bfd->when = BSC_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
new file mode 100644
index 000000000..323d7fd4f
--- /dev/null
+++ b/src/osmo-bsc/bsc_sccp.c
@@ -0,0 +1,140 @@
+/* Generic SCCP handling across all OsmoBSC users */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.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/core/utils.h>
+
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/lb.h>
+
+void bscp_sccp_conn_node_init(struct bscp_sccp_conn_node *sccp_conn, struct gsm_subscriber_connection *gscon)
+{
+ sccp_conn->conn_id = SCCP_CONN_ID_UNSET;
+ sccp_conn->gscon = gscon;
+}
+
+struct bsc_sccp_inst *bsc_sccp_inst_alloc(void *ctx)
+{
+ struct bsc_sccp_inst *bsc_sccp;
+
+ bsc_sccp = talloc_zero(ctx, struct bsc_sccp_inst);
+ OSMO_ASSERT(bsc_sccp);
+ bsc_sccp->next_id = 1;
+
+ return bsc_sccp;
+}
+
+int bsc_sccp_inst_register_gscon(struct bsc_sccp_inst *bsc_sccp, struct bscp_sccp_conn_node *sccp_conn)
+{
+ struct rb_node **n = &(bsc_sccp->connections.rb_node);
+ struct rb_node *parent = NULL;
+ uint32_t conn_id = sccp_conn->conn_id;
+
+ OSMO_ASSERT(conn_id != SCCP_CONN_ID_UNSET);
+
+ while (*n) {
+ struct bscp_sccp_conn_node *it = container_of(*n, struct bscp_sccp_conn_node, node);
+
+ parent = *n;
+ if (conn_id < it->conn_id) {
+ n = &((*n)->rb_left);
+ } else if (conn_id > it->conn_id) {
+ n = &((*n)->rb_right);
+ } else {
+ LOGP(DMSC, LOGL_ERROR,
+ "Trying to reserve already reserved conn_id %u\n", conn_id);
+ return -EEXIST;
+ }
+ }
+
+ rb_link_node(&sccp_conn->node, parent, n);
+ rb_insert_color(&sccp_conn->node, &bsc_sccp->connections);
+ return 0;
+}
+
+void bsc_sccp_inst_unregister_gscon(struct bsc_sccp_inst *bsc_sccp, struct bscp_sccp_conn_node *sccp_conn)
+{
+ OSMO_ASSERT(sccp_conn->conn_id != SCCP_CONN_ID_UNSET);
+ rb_erase(&sccp_conn->node, &bsc_sccp->connections);
+}
+
+/* Helper function to Check if the given connection id is already assigned */
+struct gsm_subscriber_connection *bsc_sccp_inst_get_gscon_by_conn_id(const struct bsc_sccp_inst *bsc_sccp, uint32_t conn_id)
+{
+ const struct rb_node *node = bsc_sccp->connections.rb_node;
+
+ OSMO_ASSERT(conn_id != SCCP_CONN_ID_UNSET);
+ /* Range (0..SCCP_CONN_ID_MAX) expected, see bsc_sccp_inst_next_conn_id() */
+ OSMO_ASSERT(conn_id <= SCCP_CONN_ID_MAX);
+
+ while (node) {
+ struct bscp_sccp_conn_node *sccp_conn = container_of(node, struct bscp_sccp_conn_node, node);
+ if (conn_id < sccp_conn->conn_id)
+ node = node->rb_left;
+ else if (conn_id > sccp_conn->conn_id)
+ node = node->rb_right;
+ else
+ return sccp_conn->gscon;
+ }
+
+ return NULL;
+}
+
+/* We need an unused SCCP conn_id across all SCCP users. */
+uint32_t bsc_sccp_inst_next_conn_id(struct bsc_sccp_inst *bsc_sccp)
+{
+ uint32_t first_id, test_id;
+
+ first_id = test_id = bsc_sccp->next_id;
+
+ /* SUA: RFC3868 sec 3.10.4:
+ * The source reference number is a 4 octet long integer.
+ * This is allocated by the source SUA instance.
+ * M3UA/SCCP: ITU-T Q.713 sec 3.3:
+ * The "source local reference" parameter field is a three-octet field containing a
+ * reference number which is generated and used by the local node to identify the
+ * connection section after the connection section is set up.
+ * The coding "all ones" is reserved for future use.
+ *Hence, as we currently use the connection ID also as local reference,
+ *let's simply use 24 bit ids to fit all link types (excluding 0x00ffffff).
+ */
+
+ while (bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, test_id)) {
+ /* Optimized modulo operation (% SCCP_CONN_ID_MAX) using bitwise AND plus CMP: */
+ test_id = (test_id + 1) & 0x00FFFFFF;
+ if (OSMO_UNLIKELY(test_id == 0x00FFFFFF))
+ test_id = 0;
+
+ /* Did a whole loop, all used, fail */
+ if (OSMO_UNLIKELY(test_id == first_id))
+ return SCCP_CONN_ID_UNSET;
+ }
+
+ bsc_sccp->next_id = test_id;
+ /* Optimized modulo operation (% SCCP_CONN_ID_MAX) using bitwise AND plus CMP: */
+ bsc_sccp->next_id = (bsc_sccp->next_id + 1) & 0x00FFFFFF;
+ if (OSMO_UNLIKELY(bsc_sccp->next_id == 0x00FFFFFF))
+ bsc_sccp->next_id = 0;
+
+ return test_id;
+}
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 1d3024610..f1f48bc35 100644
--- a/src/osmo-bsc/bsc_subscr_conn_fsm.c
+++ b/src/osmo-bsc/bsc_subscr_conn_fsm.c
@@ -28,9 +28,11 @@
#include <osmocom/bsc/a_reset.h>
#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
#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>
@@ -44,7 +46,11 @@
#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>
+#include <osmocom/bsc/vgcs_fsm.h>
#define S(x) (1 << (x))
@@ -56,6 +62,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 */
@@ -63,15 +71,18 @@ 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_CONN_REQ, "MO-CONNECT.req"},
+ {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"},
- {GSCON_EV_A_DISC_IND, "DISCONNET.ind"},
+ {GSCON_EV_A_DISC_IND, "DISCONNECT.ind"},
+ {GSCON_EV_A_COMMON_ID_IND, "COMMON_ID.ind"},
{GSCON_EV_ASSIGNMENT_START, "ASSIGNMENT_START"},
{GSCON_EV_ASSIGNMENT_END, "ASSIGNMENT_END"},
{GSCON_EV_HANDOVER_START, "HANDOVER_START"},
@@ -84,12 +95,15 @@ static const struct value_string gscon_fsm_event_names[] = {
{GSCON_EV_LCLS_FAIL, "LCLS_FAIL"},
{GSCON_EV_FORGET_LCHAN, "FORGET_LCHAN"},
{GSCON_EV_FORGET_MGW_ENDPOINT, "FORGET_MGW_ENDPOINT"},
+ {GSCON_EV_LCS_LOC_REQ_END, "LCS_LOC_REQ_END"},
{}
};
struct osmo_tdef_state_timeout conn_fsm_timeouts[32] = {
- [ST_WAIT_CC] = { .T = 993210 },
- [ST_CLEARING] = { .T = 999 },
+ [ST_WAIT_INITIAL_USER_DATA] = { .T = -25 },
+ [ST_WAIT_CC] = { .T = -3210 },
+ [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.
@@ -132,23 +146,85 @@ 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;
+ struct gsm_subscriber_connection *conn = fi->priv;
+ enum gsm0808_cause cause = conn->clear_cause;
- LOGPFSML(conn->fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST(%s) to MSC\n", gsm0808_cause_name(cause));
+ if (!conn->sccp.msc) {
+ LOGPFSML(fi, LOGL_ERROR, "Unable to deliver BSSMAP Clear Request message, no MSC for this conn\n");
+ goto nothing_sent;
+ }
+
+ LOGPFSML(fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST(%s) to MSC\n", gsm0808_cause_name(cause));
resp = gsm0808_create_clear_rqst(cause);
if (!resp) {
- LOGPFSML(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);
+
+ if (conn->vgcs_call.fi)
+ osmo_fsm_inst_dispatch(conn->vgcs_call.fi, VGCS_EV_CLEANUP, NULL);
+
+ if (conn->vgcs_chan.fi)
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.fi, VGCS_EV_CLEANUP, 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 */
@@ -160,7 +236,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);
}
@@ -177,85 +253,167 @@ static void gscon_release_lchan(struct gsm_subscriber_connection *conn, struct g
conn->lchan = NULL;
if (conn->ho.fi && conn->ho.new_lchan == lchan)
conn->ho.new_lchan = NULL;
+ if (conn->vgcs_chan.new_lchan == lchan)
+ conn->vgcs_chan.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)
+void gscon_release_lchans(struct gsm_subscriber_connection *conn, bool do_rr_release, enum gsm48_rr_cause cause_rr)
{
if (conn->ho.fi)
handover_end(conn, HO_RESULT_CONN_RELEASE);
assignment_reset(conn);
- gscon_release_lchan(conn, conn->lchan, do_rr_release, false, 0);
+ 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)];
+
+ /* 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:
+ case BSS_MAP_MSG_VGCS_VBS_SETUP:
+ case BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST:
+ return 0;
+
+ default:
+ LOGPFSML(fi, LOGL_ERROR, "No support for initial BSSMAP: %s: %s\n",
+ gsm0808_bssap_name(bs->type), gsm0808_bssmap_name(bssmap_type));
+ return -EINVAL;
+ }
+}
+
+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;
+
+ /* validate_initial_user_data() must be called before this */
+ OSMO_ASSERT(msgb_l4(msg));
+
+ bs = msgb_l3(msg);
bssmap_type = msg->l4h[0];
- LOGPFSML(fi, LOGL_DEBUG, "Rx N-CONNECT: %s: %s\n", gsm0808_bssap_name(bs->type),
+ /* 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:
- /* First off, accept the new conn. */
- 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);
+ 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;
- /* Make sure the conn FSM will osmo_sccp_tx_disconn() on term */
- conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED;
+ 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;
- /* Inter-BSC MT Handover Request, another BSS is handovering to us. */
- handover_start_inter_bsc_in(conn, msg);
+ case BSS_MAP_MSG_VGCS_VBS_SETUP:
+ rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_RX_DT1_VGCS_VBS_SETUP]);
+ /* VGCS: MSC asks vor voice group/bcast call. */
+ conn_fsm_state_chg(ST_ACTIVE);
+ vgcs_vbs_call_start(conn, msg);
return;
+
+ case BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST:
+ rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_RX_DT1_VGCS_VBS_ASSIGN_RQST]);
+ /* VGCS: MSC asks vor resource (channel) for voice group/bcast call. */
+ conn_fsm_state_chg(ST_ACTIVE);
+ vgcs_vbs_chan_start(conn, msg);
+ return;
+
default:
- break;
+ 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;
+ }
+}
+
+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;
+ }
}
- LOGPFSML(fi, LOGL_NOTICE, "No support for N-CONNECT: %s: %s\n",
- gsm0808_bssap_name(bs->type), gsm0808_bssmap_name(bssmap_type));
-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);
+ /* 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)
@@ -264,17 +422,11 @@ 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_A_CONN_REQ:
+ case GSCON_EV_MO_COMPL_L3:
/* RLL ESTABLISH IND with initial L3 Message */
msg = data;
- /* FIXME: Extract Mobile ID and update FSM using osmo_fsm_inst_set_id()
- * i.e. we will probably extract the mobile identity earlier, where the
- * imsi filter code is. Then we could just use it here.
- * related: OS#2969 */
-
rc = osmo_bsc_sigtran_open_conn(conn, msg);
if (rc < 0) {
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
@@ -297,11 +449,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);
+ }
+}
- handle_bssap_n_connect(fi, scu_prim);
+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;
+
+ 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)
@@ -318,12 +487,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, 999);
return;
default:
OSMO_ASSERT(false);
@@ -343,7 +510,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, 999);
gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
break;
}
@@ -380,7 +546,6 @@ static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *dat
return;
case GSCON_EV_HANDOVER_START:
- rate_ctr_inc(&conn->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]);
/* Rely on handover_fsm timeout */
if (osmo_fsm_inst_state_chg(fi, ST_HANDOVER, 0, 0))
LOGPFSML(fi, LOGL_ERROR, "Cannot transition to HANDOVER state, discarding\n");
@@ -397,6 +562,21 @@ static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *dat
case GSCON_EV_TX_SCCP:
gscon_sigtran_send(conn, (struct msgb *)data);
break;
+
+ case GSCON_EV_LCS_LOC_REQ_END:
+ /* On the A-interface, there is nothing to do. If there still is an lchan, the conn should stay open. If
+ * not, it is up to the MSC to send a Clear Command.
+ * On the Lb-interface, tear down the SCCP connection. */
+ lb_close_conn(conn);
+ break;
+
+ case GSCON_EV_MO_COMPL_L3:
+ /* It is possible to have an A-interface conn already established without an lchan being active, during
+ * a Perform Location Request (LCS). */
+ /* RLL ESTABLISH IND with initial L3 Message */
+ gscon_sigtran_send(conn, (struct msgb*)data);
+ break;
+
default:
OSMO_ASSERT(false);
}
@@ -466,35 +646,120 @@ 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. */
struct osmo_mgcpc_ep *gscon_ensure_mgw_endpoint(struct gsm_subscriber_connection *conn,
- uint16_t msc_assigned_cic)
+ 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)) {
- /* use dynamic RTPBRIDGE endpoint allocation in MGW */
+ if (is_ipa_abisip_bts(for_lchan->ts->trx->bts))
+ /* use dynamic RTPBRIDGE endpoint allocation in MGW */
+ epname = mgcp_client_rtpbridge_wildcard(mgcp_client);
+ else {
+ 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,
+ 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", mgcp_client_rtpbridge_wildcard(conn->network->mgw.client));
+ 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;
@@ -529,7 +794,7 @@ bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn,
mgw_info = (struct mgcp_conn_peer){
.port = port,
- .call_id = conn->sccp.conn_id,
+ .call_id = conn->sccp.conn.conn_id,
.ptime = 20,
.x_osmo_osmux_use = conn->assignment.req.use_osmux,
.x_osmo_osmux_cid = conn->assignment.req.osmux_cid,
@@ -544,7 +809,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");
@@ -562,6 +827,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;
}
@@ -569,7 +836,7 @@ bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn,
} else
verb = MGCP_VERB_CRCX;
- gscon_ensure_mgw_endpoint(conn, for_lchan->activate.info.msc_assigned_cic);
+ gscon_ensure_mgw_endpoint(conn, for_lchan->activate.info.msc_assigned_cic, for_lchan);
if (!conn->user_plane.mgw_endpoint) {
LOGPFSML(conn->fi, LOGL_ERROR, "Unable to allocate endpoint info\n");
@@ -596,41 +863,60 @@ 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_A_CONN_REQ) | 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] = {
.name = "ACTIVE",
.in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_ASSIGNMENT_START) |
- S(GSCON_EV_HANDOVER_START),
- .out_state_mask = S(ST_CLEARING) | S(ST_ASSIGNMENT) |
+ S(GSCON_EV_HANDOVER_START)
+ | S(GSCON_EV_LCS_LOC_REQ_END)
+ | S(GSCON_EV_MO_COMPL_L3)
+ ,
+ .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)
@@ -638,6 +924,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;
@@ -645,7 +944,7 @@ void gscon_change_primary_lchan(struct gsm_subscriber_connection *conn, struct g
osmo_fsm_inst_dispatch(conn->lchan->fi_rtp, LCHAN_RTP_EV_ESTABLISHED, 0);
if (old_lchan && (old_lchan != new_lchan))
- gscon_release_lchan(conn, old_lchan, false, false, 0);
+ gscon_release_lchan(conn, old_lchan, false, false, GSM48_RR_CAUSE_NORMAL);
}
void gscon_lchan_releasing(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan)
@@ -662,23 +961,23 @@ void gscon_lchan_releasing(struct gsm_subscriber_connection *conn, struct gsm_lc
if (conn->ho.fi)
osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_LCHAN_ERROR, lchan);
}
+ if (conn->vgcs_chan.new_lchan == lchan) {
+ if (conn->vgcs_chan.fi)
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.fi, VGCS_EV_LCHAN_ERROR, lchan);
+ }
if (conn->lchan == lchan) {
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, 999);
- /* 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;
}
@@ -702,6 +1001,10 @@ void gscon_forget_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan
conn->ho.new_lchan = NULL;
detach_label = "ho.new_lchan";
}
+ if (conn->vgcs_chan.new_lchan == lchan) {
+ conn->vgcs_chan.new_lchan = NULL;
+ detach_label = "vgcs.new_lchan";
+ }
if (conn->lchan == lchan) {
conn->lchan = NULL;
detach_label = "primary lchan";
@@ -717,20 +1020,32 @@ 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->assignment.new_lchan
+ && !conn->vgcs_chan.new_lchan
+ && !conn->lcs.loc_req)
gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
}
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;
lchan_forget_mgw_endpoint(conn->lchan);
lchan_forget_mgw_endpoint(conn->assignment.new_lchan);
+ lchan_forget_mgw_endpoint(conn->vgcs_chan.new_lchan);
lchan_forget_mgw_endpoint(conn->ho.new_lchan);
}
@@ -741,36 +1056,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 tlv_parsed *tp;
+ struct osmo_mobile_identity mi_imsi;
/* Regular allstate event processing */
switch (event) {
case GSCON_EV_A_CLEAR_CMD:
- if (conn->lchan)
- conn->lchan->release.is_csfb = *(bool *)data;
- /* MSC tells us to cleanly shut down */
- if (conn->fi->state != ST_CLEARING)
- osmo_fsm_inst_state_chg(fi, ST_CLEARING, 60, 999);
- LOGPFSML(fi, LOGL_DEBUG, "Releasing all lchans (if any) after BSSMAP Clear Command\n");
- gscon_release_lchans(conn, true);
- /* 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());
+ OSMO_ASSERT(data);
+ 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:
@@ -780,14 +1086,43 @@ static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *d
if (conn->lchan) {
conn->lchan->release.in_error = true;
conn->lchan->release.rsl_error_cause = data ? *(uint8_t*)data : RSL_ERR_IE_ERROR;
+ 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");
break;
case GSCON_EV_LCLS_FAIL:
break;
+ case GSCON_EV_A_COMMON_ID_IND:
+ OSMO_ASSERT(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,
+ 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);
+ }
+ 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;
default:
OSMO_ASSERT(false);
break;
@@ -800,19 +1135,27 @@ static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau
lchan_forget_conn(conn->lchan);
lchan_forget_conn(conn->assignment.new_lchan);
+ lchan_forget_conn(conn->vgcs_chan.new_lchan);
lchan_forget_conn(conn->ho.new_lchan);
+ lb_close_conn(conn);
+
if (conn->sccp.state != SUBSCR_SCCP_ST_NONE) {
LOGPFSML(fi, LOGL_DEBUG, "Disconnecting SCCP\n");
struct bsc_msc_data *msc = conn->sccp.msc;
/* FIXME: include a proper cause value / error message? */
- osmo_sccp_tx_disconn(msc->a.sccp_user, conn->sccp.conn_id, &msc->a.bsc_addr, 0);
+ osmo_sccp_tx_disconn(msc->a.sccp_user, conn->sccp.conn.conn_id, &msc->a.bsc_addr, 0);
conn->sccp.state = SUBSCR_SCCP_ST_NONE;
}
+ if (conn->sccp.conn.conn_id != SCCP_CONN_ID_UNSET && conn->sccp.msc) {
+ struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(conn->sccp.msc->a.sccp);
+ bsc_sccp_inst_unregister_gscon(bsc_sccp, &conn->sccp.conn);
+ conn->sccp.conn.conn_id = SCCP_CONN_ID_UNSET;
+ }
if (conn->bsub) {
LOGPFSML(fi, LOGL_DEBUG, "Putting bsc_subscr\n");
- bsc_subscr_put(conn->bsub);
+ bsc_subscr_put(conn->bsub, BSUB_USE_CONN);
conn->bsub = NULL;
}
@@ -823,8 +1166,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 */
@@ -832,13 +1188,19 @@ static void gscon_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
conn->lcls.fi = NULL;
}
+ if (conn->vgcs_call.fi)
+ osmo_fsm_inst_dispatch(conn->vgcs_call.fi, VGCS_EV_CLEANUP, NULL);
+
+ if (conn->vgcs_chan.fi)
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.fi, VGCS_EV_CLEANUP, NULL);
+
LOGPFSML(fi, LOGL_DEBUG, "Releasing all lchans (if any) because this conn is terminating\n");
- gscon_release_lchans(conn, true);
+ 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)
@@ -846,8 +1208,8 @@ static int gscon_timer_cb(struct osmo_fsm_inst *fi)
struct gsm_subscriber_connection *conn = fi->priv;
switch (fi->T) {
- case 993210:
- gscon_release_lchan(conn, conn->lchan, true, true, RSL_ERR_INTERWORKING);
+ case -3210:
+ gscon_release_lchan(conn, conn->lchan, true, true, GSM48_RR_CAUSE_ABNORMAL_TIMER);
/* MSC has not responded/confirmed connection with CC, this
* could indicate a bad SCCP connection. We now inform the the
@@ -860,11 +1222,12 @@ static int gscon_timer_cb(struct osmo_fsm_inst *fi)
* gscon_cleanup() above) */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
break;
- case 999:
+ 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;
@@ -879,7 +1242,8 @@ static struct osmo_fsm gscon_fsm = {
.name = "SUBSCR_CONN",
.states = gscon_fsm_states,
.num_states = ARRAY_SIZE(gscon_fsm_states),
- .allstate_event_mask = S(GSCON_EV_A_DISC_IND) | S(GSCON_EV_A_CLEAR_CMD) | S(GSCON_EV_RSL_CONN_FAIL) |
+ .allstate_event_mask = S(GSCON_EV_A_DISC_IND) | S(GSCON_EV_A_CLEAR_CMD) | S(GSCON_EV_A_COMMON_ID_IND) |
+ S(GSCON_EV_RSL_CONN_FAIL) |
S(GSCON_EV_LCLS_FAIL) |
S(GSCON_EV_FORGET_LCHAN) |
S(GSCON_EV_FORGET_MGW_ENDPOINT),
@@ -891,7 +1255,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);
@@ -908,8 +1272,11 @@ 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. */
- conn->sccp.conn_id = -1;
+ INIT_LLIST_HEAD(&conn->hodec2.penalty_timers);
+ bscp_sccp_conn_node_init(&conn->sccp.conn, conn);
+ bscp_sccp_conn_node_init(&conn->lcs.lb.conn, conn);
+ /* 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 */
@@ -986,17 +1353,28 @@ static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, en
* fire after a lchan_release call and before the S_CHALLOC_FREED
* is called. Check if a conn is set before proceeding.
*/
- if (!lchan->conn)
+ if (!lchan->conn) {
+ msgb_free(msg);
return;
+ }
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, 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;
}
@@ -1020,7 +1398,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) {
@@ -1036,7 +1413,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);
+ bsc_sapi_n_reject(conn, RSL_LINK_ID2DLCI(link_id), GSM0808_CAUSE_BSS_NOT_EQUIPPED);
goto failed_to_send;
}
return;
@@ -1051,7 +1428,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,
@@ -1071,7 +1447,7 @@ void gscon_update_id(struct gsm_subscriber_connection *conn)
{
osmo_fsm_inst_update_id_f(conn->fi, "msc%u-conn%u%s%s",
conn->sccp.msc ? conn->sccp.msc->nr : UINT_MAX,
- conn->sccp.conn_id,
+ conn->sccp.conn.conn_id,
conn->bsub? "_" : "",
conn->bsub? bsc_subscr_id(conn->bsub) : "");
}
diff --git a/src/osmo-bsc/bsc_subscriber.c b/src/osmo-bsc/bsc_subscriber.c
index 9ddfcaa43..1f69d442d 100644
--- a/src/osmo-bsc/bsc_subscriber.c
+++ b/src/osmo-bsc/bsc_subscriber.c
@@ -30,63 +30,187 @@
#include <osmocom/core/logging.h>
#include <osmocom/bsc/bsc_subscriber.h>
+#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/debug.h>
-static struct bsc_subscr *bsc_subscr_alloc(struct llist_head *list)
+static void bsc_subscr_free(struct bsc_subscr *bsub);
+
+static int bsub_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
+{
+ struct bsc_subscr *bsub = e->use_count->talloc_object;
+ int32_t total;
+ int level;
+
+ if (!e->use)
+ return -EINVAL;
+
+ total = osmo_use_count_total(&bsub->use_count);
+
+ if (total == 0
+ || (total == 1 && old_use_count == 0 && e->count == 1))
+ level = LOGL_INFO;
+ else
+ level = LOGL_DEBUG;
+
+ LOGPSRC(DREF, level, file, line, "%s: %s %s: now used by %s\n",
+ bsc_subscr_name(bsub),
+ (e->count - old_use_count) > 0? "+" : "-", e->use,
+ osmo_use_count_to_str_c(OTC_SELECT, &bsub->use_count));
+
+ if (e->count < 0)
+ return -ERANGE;
+
+ if (total == 0)
+ bsc_subscr_free(bsub);
+ return 0;
+}
+
+struct bsc_subscr_store *bsc_subscr_store_alloc(void *ctx)
+{
+ struct bsc_subscr_store *bsubst;
+
+ bsubst = talloc_zero(ctx, struct bsc_subscr_store);
+ if (!bsubst)
+ return NULL;
+
+ INIT_LLIST_HEAD(&bsubst->bsub_list);
+ return bsubst;
+}
+
+static struct bsc_subscr *bsc_subscr_alloc(struct bsc_subscr_store *bsubst)
{
struct bsc_subscr *bsub;
- bsub = talloc_zero(list, struct bsc_subscr);
+ bsub = talloc_zero(bsubst, struct bsc_subscr);
if (!bsub)
return NULL;
- llist_add_tail(&bsub->entry, list);
+ bsub->store = bsubst;
+ bsub->tmsi = GSM_RESERVED_TMSI;
+ bsub->use_count = (struct osmo_use_count){
+ .talloc_object = bsub,
+ .use_cb = bsub_use_cb,
+ };
+ INIT_LLIST_HEAD(&bsub->active_paging_requests);
+
+ llist_add_tail(&bsub->entry, &bsubst->bsub_list);
return bsub;
}
-struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list,
- const char *imsi)
+struct bsc_subscr *bsc_subscr_find_by_imsi(struct bsc_subscr_store *bsubst,
+ const char *imsi,
+ const char *use_token)
{
struct bsc_subscr *bsub;
if (!imsi || !*imsi)
return NULL;
- llist_for_each_entry(bsub, list, entry) {
- if (!strcmp(bsub->imsi, imsi))
- return bsc_subscr_get(bsub);
+ llist_for_each_entry(bsub, &bsubst->bsub_list, entry) {
+ if (!strcmp(bsub->imsi, imsi)) {
+ 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)
+static struct bsc_subscr *bsc_subscr_find_by_imei(struct bsc_subscr_store *bsubst,
+ const char *imei,
+ const char *use_token)
{
struct bsc_subscr *bsub;
- if (tmsi == GSM_RESERVED_TMSI)
+ if (!imei || !*imei)
return NULL;
- llist_for_each_entry(bsub, list, entry) {
- if (bsub->tmsi == tmsi)
- return bsc_subscr_get(bsub);
+ llist_for_each_entry(bsub, &bsubst->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_mi(struct llist_head *list, const struct osmo_mobile_identity *mi)
+static struct bsc_subscr *bsc_subscr_find_by_tmsi(struct bsc_subscr_store *bsubst,
+ uint32_t tmsi,
+ const char *use_token)
{
- if (!mi)
- return NULL;
- switch (mi->type) {
- case GSM_MI_TYPE_IMSI:
- return bsc_subscr_find_by_imsi(list, mi->imsi);
- case GSM_MI_TYPE_TMSI:
- return bsc_subscr_find_by_tmsi(list, mi->tmsi);
- default:
+ const struct rb_node *node = bsubst->bsub_tree_tmsi.rb_node;
+ struct bsc_subscr *bsub;
+
+ if (tmsi == GSM_RESERVED_TMSI)
return NULL;
+
+ while (node) {
+ bsub = container_of(node, struct bsc_subscr, node_tmsi);
+ if (tmsi < bsub->tmsi)
+ node = node->rb_left;
+ else if (tmsi > bsub->tmsi)
+ node = node->rb_right;
+ else {
+ bsc_subscr_get(bsub, use_token);
+ return bsub;
+ }
+ }
+
+ return NULL;
+}
+
+static int bsc_subscr_store_insert_bsub_tmsi(struct bsc_subscr *bsub)
+{
+ struct bsc_subscr_store *bsubst = bsub->store;
+ struct rb_node **n = &(bsubst->bsub_tree_tmsi.rb_node);
+ struct rb_node *parent = NULL;
+
+ OSMO_ASSERT(bsub->tmsi != GSM_RESERVED_TMSI);
+
+ while (*n) {
+ struct bsc_subscr *it;
+
+ it = container_of(*n, struct bsc_subscr, node_tmsi);
+
+ parent = *n;
+ if (bsub->tmsi < it->tmsi) {
+ n = &((*n)->rb_left);
+ } else if (bsub->tmsi > it->tmsi) {
+ n = &((*n)->rb_right);
+ } else {
+ LOGP(DMSC, LOGL_ERROR, "Trying to reserve already reserved tmsi %u\n", bsub->tmsi);
+ return -EEXIST;
+ }
+ }
+
+ rb_link_node(&bsub->node_tmsi, parent, n);
+ rb_insert_color(&bsub->node_tmsi, &bsubst->bsub_tree_tmsi);
+ return 0;
+}
+
+int bsc_subscr_set_tmsi(struct bsc_subscr *bsub, uint32_t tmsi)
+{
+ int rc = 0;
+
+ if (!bsub)
+ return -EINVAL;
+
+ if (bsub->tmsi == tmsi)
+ return 0;
+
+ /* bsub was already inserted, remove and re-insert with new tmsi */
+ if (bsub->tmsi != GSM_RESERVED_TMSI)
+ rb_erase(&bsub->node_tmsi, &bsub->store->bsub_tree_tmsi);
+
+ bsub->tmsi = tmsi;
+
+ /* If new tmsi is set, insert bsub into rbtree: */
+ if (bsub->tmsi != GSM_RESERVED_TMSI) {
+ if ((rc = bsc_subscr_store_insert_bsub_tmsi(bsub)) < 0)
+ bsub->tmsi = GSM_RESERVED_TMSI;
}
+
+ return rc;
}
void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi)
@@ -96,103 +220,127 @@ void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi)
osmo_strlcpy(bsub->imsi, imsi, sizeof(bsub->imsi));
}
-struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list,
- const char *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 bsc_subscr_store *bsubst,
+ const char *imsi,
+ const char *use_token)
{
struct bsc_subscr *bsub;
- bsub = bsc_subscr_find_by_imsi(list, imsi);
+ bsub = bsc_subscr_find_by_imsi(bsubst, imsi, use_token);
if (bsub)
return bsub;
- bsub = bsc_subscr_alloc(list);
+ bsub = bsc_subscr_alloc(bsubst);
if (!bsub)
return NULL;
bsc_subscr_set_imsi(bsub, imsi);
- return bsc_subscr_get(bsub);
+ 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)
+static struct bsc_subscr *bsc_subscr_find_or_create_by_imei(struct bsc_subscr_store *bsubst,
+ const char *imei,
+ const char *use_token)
{
struct bsc_subscr *bsub;
- bsub = bsc_subscr_find_by_tmsi(list, tmsi);
+ bsub = bsc_subscr_find_by_imei(bsubst, imei, use_token);
if (bsub)
return bsub;
- bsub = bsc_subscr_alloc(list);
+ bsub = bsc_subscr_alloc(bsubst);
if (!bsub)
return NULL;
- bsub->tmsi = tmsi;
- return bsc_subscr_get(bsub);
+ 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 bsc_subscr_store *bsubst,
+ uint32_t tmsi,
+ const char *use_token)
+{
+ struct bsc_subscr *bsub;
+ bsub = bsc_subscr_find_by_tmsi(bsubst, tmsi, use_token);
+ if (bsub)
+ return bsub;
+ bsub = bsc_subscr_alloc(bsubst);
+ if (!bsub)
+ return NULL;
+ if (bsc_subscr_set_tmsi(bsub, tmsi) < 0) {
+ bsc_subscr_free(bsub);
+ return NULL;
+ }
+ bsc_subscr_get(bsub, use_token);
+ return bsub;
}
-struct bsc_subscr *bsc_subscr_find_or_create_by_mi(struct llist_head *list, const struct osmo_mobile_identity *mi)
+struct bsc_subscr *bsc_subscr_find_or_create_by_mi(struct bsc_subscr_store *bsubst, const struct osmo_mobile_identity *mi,
+ const char *use_token)
{
if (!mi)
return NULL;
switch (mi->type) {
case GSM_MI_TYPE_IMSI:
- return bsc_subscr_find_or_create_by_imsi(list, mi->imsi);
+ return bsc_subscr_find_or_create_by_imsi(bsubst, mi->imsi, use_token);
+ case GSM_MI_TYPE_IMEI:
+ return bsc_subscr_find_or_create_by_imei(bsubst, mi->imei, use_token);
case GSM_MI_TYPE_TMSI:
- return bsc_subscr_find_or_create_by_tmsi(list, mi->tmsi);
+ return bsc_subscr_find_or_create_by_tmsi(bsubst, mi->tmsi, use_token);
default:
return NULL;
}
}
-const char *bsc_subscr_name(struct bsc_subscr *bsub)
+static int bsc_subscr_name_buf(char *buf, size_t buflen, struct bsc_subscr *bsub)
{
- static char buf[32];
- if (!bsub)
- return "unknown";
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ OSMO_STRBUF_PRINTF(sb, "subscr");
+ if (!bsub) {
+ OSMO_STRBUF_PRINTF(sb, "-null");
+ return sb.chars_needed;
+ }
if (bsub->imsi[0])
- snprintf(buf, sizeof(buf), "IMSI:%s", bsub->imsi);
- else
- snprintf(buf, sizeof(buf), "TMSI:0x%08x", bsub->tmsi);
- return buf;
+ 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;
+}
+
+static char *bsc_subscr_name_c(void *ctx, struct bsc_subscr *bsub)
+{
+ OSMO_NAME_C_IMPL(ctx, 64, "ERROR", bsc_subscr_name_buf, bsub)
+}
+
+const char *bsc_subscr_name(struct bsc_subscr *bsub)
+{
+ return bsc_subscr_name_c(OTC_SELECT, bsub);
}
/* Like bsc_subscr_name() but returns only characters approved by osmo_identifier_valid(), useful for
* osmo_fsm_inst IDs. */
const char *bsc_subscr_id(struct bsc_subscr *bsub)
{
- static char buf[32];
- if (!bsub)
- return "unknown";
- if (bsub->imsi[0])
- snprintf(buf, sizeof(buf), "IMSI%s", bsub->imsi);
- else
- snprintf(buf, sizeof(buf), "TMSI%08x", bsub->tmsi);
- return buf;
+ return bsc_subscr_name(bsub);
}
static void bsc_subscr_free(struct bsc_subscr *bsub)
{
+ OSMO_ASSERT(llist_empty(&bsub->active_paging_requests));
+
+ if (bsub->tmsi != GSM_RESERVED_TMSI)
+ rb_erase(&bsub->node_tmsi, &bsub->store->bsub_tree_tmsi);
+
llist_del(&bsub->entry);
talloc_free(bsub);
}
-struct bsc_subscr *_bsc_subscr_get(struct bsc_subscr *bsub,
- const char *file, int line)
-{
- OSMO_ASSERT(bsub->use_count < INT_MAX);
- bsub->use_count++;
- LOGPSRC(DREF, LOGL_DEBUG, file, line,
- "BSC subscr %s usage increases to: %d\n",
- bsc_subscr_name(bsub), bsub->use_count);
- return bsub;
-}
-
-struct bsc_subscr *_bsc_subscr_put(struct bsc_subscr *bsub,
- const char *file, int line)
-{
- bsub->use_count--;
- LOGPSRC(DREF, bsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR,
- file, line,
- "BSC subscr %s usage decreases to: %d\n",
- bsc_subscr_name(bsub), bsub->use_count);
- if (bsub->use_count <= 0)
- bsc_subscr_free(bsub);
- return NULL;
-}
+#define BSUB_USE_LOG_FILTER "log_filter"
void log_set_filter_bsc_subscr(struct log_target *target,
struct bsc_subscr *bsc_subscr)
@@ -201,13 +349,52 @@ void log_set_filter_bsc_subscr(struct log_target *target,
/* free the old data */
if (*fsub) {
- bsc_subscr_put(*fsub);
+ bsc_subscr_put(*fsub, BSUB_USE_LOG_FILTER);
*fsub = NULL;
}
if (bsc_subscr) {
target->filter_map |= (1 << LOG_FLT_BSC_SUBSCR);
- *fsub = bsc_subscr_get(bsc_subscr);
+ *fsub = bsc_subscr;
+ bsc_subscr_get(*fsub, BSUB_USE_LOG_FILTER);
} 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 36b16a21e..8690a47c3 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,104 +32,48 @@
#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/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/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_ramp.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/mgcp_client/mgcp_client_endpoint_fsm.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
+#include <osmocom/bsc/assignment_fsm.h>
+#include <osmocom/bsc/bssmap_reset.h>
+#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/lchan.h>
+#include <osmocom/bsc/pcu_if.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" },
@@ -136,30 +82,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)
@@ -174,12 +102,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),
@@ -187,7 +143,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;
@@ -263,6 +219,9 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
vty_out(vty, " Last RF Lock Command: %s%s",
net->rf_ctrl->last_rf_lock_ctrl_command,
VTY_NEWLINE);
+
+ if (net->pcu_sock_path)
+ vty_out(vty, " PCU Socket Path: %s%s", net->pcu_sock_path, VTY_NEWLINE);
}
DEFUN(bsc_show_net, bsc_show_net_cmd, "show network",
@@ -273,269 +232,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 ramping: %senabled%s",
- acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "not ", VTY_NEWLINE);
- if (acc_ramp_is_enabled(&bts->acc_ramp)) {
- if (!acc_ramp_step_interval_is_dynamic(&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);
- else
- vty_out(vty, " Access Control Class ramping step interval: dynamic%s", 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")
@@ -561,6 +257,22 @@ DEFUN(show_bts, show_bts_cmd, "show bts [<0-255>]",
return CMD_SUCCESS;
}
+DEFUN(show_bts_brief, show_bts_brief_cmd, "show bts brief",
+ SHOW_STR "Display information about a BTS\n"
+ "Display availability status of all BTS\n")
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ struct gsm_bts *bts;
+
+ /* Print OML state of BTSs. */
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ vty_out(vty, "BTS %d:", bts->nr);
+ bts_dump_vty_oml_link_state(vty, bts);
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN(show_bts_fail_rep, show_bts_fail_rep_cmd, "show bts <0-255> fail-rep [reset]",
SHOW_STR "Display information about a BTS\n"
"BTS number\n" "OML failure reports\n"
@@ -649,465 +361,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 in_addr ia;
-
- ia.s_addr = htonl(nsvc->remote_ip);
- 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);
- vty_out(vty, " gprs nsvc %u remote udp port %u%s", i,
- nsvc->remote_port, VTY_NEWLINE);
- vty_out(vty, " gprs nsvc %u remote ip %s%s", i,
- inet_ntoa(ia), 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);
- vty_out(vty, " %saccess-control-class-ramping%s", acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "no ", VTY_NEWLINE);
- if (!acc_ramp_step_interval_is_dynamic(&bts->acc_ramp)) {
- vty_out(vty, " access-control-class-ramping-step-interval %u%s",
- acc_ramp_get_step_interval(&bts->acc_ramp), VTY_NEWLINE);
- } else {
- vty_out(vty, " access-control-class-ramping-step-interval dynamic%s", VTY_NEWLINE);
- }
- vty_out(vty, " access-control-class-ramping-step-size %u%s", acc_ramp_get_step_size(&bts->acc_ramp),
- 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);
@@ -1141,12 +394,14 @@ static int config_write_net(struct vty *vty)
gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE);
}
- osmo_tdef_vty_write(vty, gsmnet->T_defs, " timer ");
+ /* Timer introspection commands (generic osmo_tdef API) */
+ osmo_tdef_vty_groups_write(vty, " ");
{
uint16_t meas_port;
char *meas_host;
const char *meas_scenario;
+ unsigned int max_len = meas_feed_txqueue_max_length_get();
meas_feed_cfg_get(&meas_host, &meas_port);
meas_scenario = meas_feed_scenario_get();
@@ -1157,6 +412,9 @@ static int config_write_net(struct vty *vty)
if (strlen(meas_scenario) > 0)
vty_out(vty, " meas-feed scenario %s%s",
meas_scenario, VTY_NEWLINE);
+ if (max_len != MEAS_FEED_TXQUEUE_MAX_LEN_DEFAULT)
+ vty_out(vty, " meas-feed write-queue-max-length %u%s",
+ max_len, VTY_NEWLINE);
}
if (gsmnet->allow_unusable_timeslots)
@@ -1172,38 +430,16 @@ static int config_write_net(struct vty *vty)
vty_out(vty, "%s", VTY_NEWLINE);
}
- return CMD_SUCCESS;
-}
+ if (gsmnet->pcu_sock_path)
+ vty_out(vty, " pcu-socket %s%s", gsmnet->pcu_sock_path, VTY_NEWLINE);
+ if (gsmnet->pcu_sock_wqueue_len_max != BSC_PCU_SOCK_WQUEUE_LEN_DEFAULT)
+ vty_out(vty, " pcu-socket-wqueue-length %u%s", gsmnet->pcu_sock_wqueue_len_max,
+ VTY_NEWLINE);
-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, " 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);
- }
+ neighbor_ident_vty_write_network(vty, " ");
+ mgcp_client_pool_config_write(vty, " ");
+
+ return CMD_SUCCESS;
}
static void trx_dump_vty_all(struct vty *vty, struct gsm_bts_trx *trx)
@@ -1262,56 +498,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>]",
@@ -1388,47 +574,14 @@ 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);
if (bsub->tmsi != GSM_RESERVED_TMSI)
vty_out(vty, " TMSI: 0x%08x%s", bsub->tmsi,
VTY_NEWLINE);
- vty_out(vty, " Use count: %d%s", 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");
+ vty_out(vty, " Use count: %s%s", osmo_use_count_to_str_c(OTC_SELECT, &bsub->use_count), VTY_NEWLINE);
}
static inline void print_all_trx_ext(struct vty *vty, bool show_connected)
@@ -1459,88 +612,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);
@@ -1679,7 +757,7 @@ DEFUN(show_lchan_summary_all,
static void dump_one_subscr_conn(struct vty *vty, const struct gsm_subscriber_connection *conn)
{
vty_out(vty, "conn ID=%u, MSC=%u, hodec2_fail=%d, mgw_ep=%s%s",
- conn->sccp.conn_id, conn->sccp.msc->nr, conn->hodec2.failures,
+ conn->sccp.conn.conn_id, conn->sccp.msc->nr, conn->hodec2.failures,
osmo_mgcpc_ep_name(conn->user_plane.mgw_endpoint), VTY_NEWLINE);
if (conn->lcls.global_call_ref_len) {
vty_out(vty, " LCLS GCR: %s%s",
@@ -1705,14 +783,12 @@ DEFUN(show_subscr_conn,
struct gsm_subscriber_connection *conn;
struct gsm_network *net = gsmnet_from_vty(vty);
bool no_conns = true;
- unsigned int count = 0;
vty_out(vty, "Active subscriber connections: %s", VTY_NEWLINE);
llist_for_each_entry(conn, &net->subscr_conns, entry) {
dump_one_subscr_conn(vty, conn);
no_conns = false;
- count++;
}
if (no_conns)
@@ -1721,23 +797,35 @@ 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;
}
@@ -1751,11 +839,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) {
@@ -1766,7 +853,7 @@ static int ho_or_as(struct vty *vty, const char *argv[], int argc)
}
if (!new_bts) {
- vty_out(vty, "Unable to trigger handover, specified bts #%u does not exist %s",
+ vty_out(vty, "%% Unable to trigger handover, specified bts #%u does not exist %s",
bts_nr_new, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1784,16 +871,42 @@ 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);
}
}
- vty_out(vty, "Unable to trigger %s, specified connection (bts=%u,trx=%u,ts=%u,ss=%u) does not exist%s",
+ vty_out(vty, "%% Unable to trigger %s, specified connection (bts=%u,trx=%u,ts=%u,ss=%u) does not exist%s",
action, bts_nr, trx_nr, ts_nr, ss_nr, VTY_NEWLINE);
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,
+ .ch_indctr = lchan->current_ch_indctr,
+ .tsc_set = {
+ .present = (tsc_set >= 0),
+ .val = tsc_set,
+ },
+ .tsc = {
+ .present = (tsc >= 0),
+ .val = tsc,
+ },
+ };
+ if (vamos)
+ info.type_for = LCHAN_TYPE_FOR_VAMOS;
+
+ 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"
@@ -1835,7 +948,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)) {
@@ -1860,7 +973,7 @@ static struct gsm_lchan *find_used_voice_lchan(struct vty *vty, int random_idx)
random_idx %= count;
}
- vty_out(vty, "Cannot find any ongoing voice calls%s", VTY_NEWLINE);
+ vty_out(vty, "%% Cannot find any ongoing voice calls%s", VTY_NEWLINE);
return NULL;
}
@@ -1877,18 +990,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);
+ 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_chan_t_name(free_type), VTY_NEWLINE);
return NULL;
}
@@ -1909,7 +1024,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,
@@ -1924,18 +1039,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;
@@ -1948,12 +1064,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;
@@ -1961,7 +1073,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);
}
@@ -1969,10 +1081,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);
}
@@ -2031,17 +1142,18 @@ DEFUN(show_paging_group,
page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc,
str_to_imsi(argv[1]));
- vty_out(vty, "%%Paging group for IMSI %" PRIu64 " on BTS #%d is %u%s",
+ vty_out(vty, "%% Paging group for IMSI %" PRIu64 " on BTS #%d is %u%s",
str_to_imsi(argv[1]), bts->nr,
page_group, VTY_NEWLINE);
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);
@@ -2050,13 +1162,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]);
@@ -2074,7 +1187,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" \
@@ -2086,22 +1199,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;
@@ -2115,10 +1231,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;
@@ -2132,2932 +1250,777 @@ 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);
- /*
- * Initialize bts->acc_ramp here. Else we could segfault while
- * processing a configuration file with ACC ramping settings.
- */
- acc_ramp_init(&bts->acc_ramp, bts);
- } 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")
+int print_counter(struct rate_ctr_group *bsc_ctrs, struct rate_ctr *ctr, const struct rate_ctr_desc *desc, void *data)
{
- 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;
+ struct vty *vty = data;
+ vty_out(vty, "%25s: %10"PRIu64" %s%s", desc->name, ctr->current, desc->description, VTY_NEWLINE);
+ return 0;
}
-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")
+void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net)
{
- struct gsm_bts *bts = vty->index;
-
- bts->dtxd = false;
-
- return CMD_SUCCESS;
+ rate_ctr_for_each_counter(net->bsc_ctrs, print_counter, vty);
}
-DEFUN(cfg_bts_ci,
- cfg_bts_ci_cmd,
- "cell_identity <0-65535>",
- "Set the Cell identity of this BTS\n" "Cell Identity\n")
+DEFUN(drop_bts,
+ drop_bts_cmd,
+ "drop bts connection <0-65535> (oml|rsl)",
+ "Debug/Simulation command to drop Abis/IP BTS\n"
+ "Debug/Simulation command to drop Abis/IP BTS\n"
+ "Debug/Simulation command to drop Abis/IP BTS\n"
+ "BTS NR\n" "Drop OML Connection\n" "Drop RSL Connection\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;
-}
+ struct gsm_network *gsmnet;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts *bts;
+ unsigned int bts_nr;
-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]);
+ gsmnet = gsmnet_from_vty(vty);
- if (lac < 0 || lac > 0xffff) {
- vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s",
- lac, VTY_NEWLINE);
+ 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;
}
- 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);
+ 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;
}
- 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);
+ if (!is_ipa_abisip_bts(bts)) {
+ vty_out(vty, "%% This command only works for IPA Abis/IP.%s", 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);
+ /* close all connections */
+ if (strcmp(argv[1], "oml") == 0) {
+ ipaccess_drop_oml(bts, "vty");
+ } else if (strcmp(argv[1], "rsl") == 0) {
+ /* close all rsl connections */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ ipaccess_drop_rsl(trx, "vty");
+ }
+ } else {
+ vty_out(vty, "%% Argument must be 'oml' or 'rsl'.%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")
+DEFUN(restart_bts, restart_bts_cmd,
+ "restart-bts <0-65535>",
+ "Restart ip.access nanoBTS through OML\n"
+ BTS_NR_STR)
{
- 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"
+ struct gsm_network *gsmnet;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts *bts;
+ unsigned int bts_nr;
-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;
+ gsmnet = gsmnet_from_vty(vty);
- if (bts->type != GSM_BTS_TYPE_NOKIA_SITE) {
- vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE);
+ 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->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);
+ 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;
}
- 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",
+ if (!is_ipa_abisip_bts(bts)) {
+ vty_out(vty, "%% This command only works for IPA Abis/IP.%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));
-
- 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"
-
-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;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- 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;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- 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]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- 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]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.nsvc[idx].local_port = atoi(argv[1]);
+ /* go from last TRX to c0 */
+ llist_for_each_entry_reverse(trx, &bts->trx_list, list)
+ abis_nm_ipaccess_restart(trx);
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")
+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")
{
- struct gsm_bts *bts = vty->index;
- int idx = atoi(argv[0]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.nsvc[idx].remote_port = atoi(argv[1]);
-
- return CMD_SUCCESS;
-}
+ struct gsm_network *gsmnet;
+ struct gsm_bts *bts;
+ unsigned int bts_nr;
-DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd,
- "gprs nsvc <0-1> remote ip A.B.C.D",
- GPRS_TEXT NSVC_TEXT
- "GPRS NS Remote IP Address\n"
- "GPRS NS Remote IP Address\n"
- "GPRS NS Remote IP Address\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = atoi(argv[0]);
- struct in_addr ia;
+ gsmnet = gsmnet_from_vty(vty);
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
+ 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;
}
- inet_aton(argv[1], &ia);
- bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr);
-
- 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]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
+ 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 (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]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
+ 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;
}
- 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")
+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")
{
- struct gsm_bts *bts = vty->index;
+ const struct gsm_bts_trx *trx;
+ const struct gsm_bts *bts;
+ int bts_nr = atoi(argv[0]);
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
+ 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;
}
- 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;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
+ 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;
}
- 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 "
- "four access bursts format instead of default RLC/MAC control block\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%s", bts->nr, VTY_NEWLINE);
+ 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;
}
- 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;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on BTS %u%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;
+ }
}
- 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")
+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")
{
- struct gsm_bts *bts = vty->index;
- enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0], NULL);
+ int bts_nr = atoi(argv[0]);
+ int red = atoi(argv[1]);
+ struct gsm_bts *bts;
+ int rc;
- if (!bts_gprs_mode_is_compat(bts, mode)) {
- vty_out(vty, "This BTS type does not support %s%s", argv[0],
- VTY_NEWLINE);
+ 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;
}
- 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);
+ if (red % 2 != 0) {
+ vty_out(vty, "%% Incorrect BCCH power reduction value, "
+ "an even number is expected%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);
+ rc = gsm_bts_set_c0_power_red(bts, red);
+ switch (rc) {
+ case 0: /* success */
+ return CMD_SUCCESS;
+ case -ENOTCONN:
+ vty_out(vty, "%% BTS%u is offline%s", bts_nr, 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);
+ case -ENOTSUP:
+ vty_out(vty, "%% BCCH carrier power reduction operation mode "
+ "is not supported for BTS%u%s", bts_nr, 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);
+ default:
+ 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;
}
-
- 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")
+/* 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"
+ "Normal (one-shot) SMSCB Message; sent once over Abis+Um\n"
+ "Schedule (one-shot) SMSCB Message; sent once over Abis+Um\n"
+ "Default (repeating) SMSCB Message; sent once over Abis, unlimited ovrer Um\n"
+ "Last Valid Block\n"
+ "Hex Encoded SMSCB message (up to 88 octets)\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);
+ struct gsm_bts *bts;
+ int bts_nr = atoi(argv[0]);
+ const char *type_str = argv[1];
+ int last_block = atoi(argv[2]);
+ struct rsl_ie_cb_cmd_type cb_cmd;
+ uint8_t buf[88];
+ int rc;
- /* 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);
+ 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;
}
-
- /* 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);
+ if (!gsm_bts_get_cbch(bts)) {
+ vty_out(vty, "%% BTS %d doesn't have a CBCH%s", bts_nr, 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);
+ rc = osmo_hexparse(argv[3], buf, sizeof(buf));
+ if (rc < 0 || rc > sizeof(buf)) {
+ vty_out(vty, "%% Error parsing HEXSTRING%s", VTY_NEWLINE);
return CMD_WARNING;
}
- if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
- vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
+ cb_cmd.spare = 0;
+ cb_cmd.def_bcast = 0;
+ if (!strcmp(type_str, "normal"))
+ cb_cmd.command = RSL_CB_CMD_TYPE_NORMAL;
+ else if (!strcmp(type_str, "schedule"))
+ cb_cmd.command = RSL_CB_CMD_TYPE_SCHEDULE;
+ else if (!strcmp(type_str, "default"))
+ cb_cmd.command = RSL_CB_CMD_TYPE_DEFAULT;
+ else {
+ vty_out(vty, "%% Error parsing type%s", 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) {
+ switch (last_block) {
case 1:
- vty_out(vty, "Warning: multiple threshold-high are not supported, overriding with %u%s",
- thresh_hi, VTY_NEWLINE);
+ cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_1;
break;
- case EARFCN_THRESH_LOW_INVALID:
- vty_out(vty, "Warning: multiple threshold-low are not supported, overriding with %u%s",
- thresh_lo, VTY_NEWLINE);
+ case 2:
+ cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_2;
break;
- case EARFCN_QRXLV_INVALID + 1:
- vty_out(vty, "Warning: multiple QRXLEVMIN are not supported, overriding with %u%s",
- qrx, VTY_NEWLINE);
+ case 3:
+ cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_3;
break;
- case EARFCN_PRIO_INVALID:
- vty_out(vty, "Warning: multiple priorities are not supported, overriding with %u%s",
- prio, VTY_NEWLINE);
+ case 4:
+ cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_4;
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);
+ vty_out(vty, "%% Error parsing LASTBLOCK%s", 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;
- }
+ /* 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;
}
-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")
+DEFUN(pdch_act, pdch_act_cmd,
+ "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)",
+ BTS_NR_TRX_TS_STR2
+ "Packet Data Channel\n"
+ "Activate Dynamic PDCH/TCH (-> PDCH mode)\n"
+ "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n")
{
- struct gsm_bts *bts = vty->index;
+ struct gsm_bts_trx_ts *ts;
+ int activate;
- 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);
+ ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
+ if (!ts || !ts->fi || ts->fi->state == TS_ST_NOT_INITIALIZED || ts->fi->state == TS_ST_BORKEN) {
+ vty_out(vty, "%% Timeslot is not usable%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);
+ if (!is_ipa_abisip_bts(ts->trx->bts)) {
+ vty_out(vty, "%% This command only works for IPA Abis/IP BTS%s",
+ VTY_NEWLINE);
return CMD_WARNING;
}
- if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
- vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
+ 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_SDCCH8_PDCH or TCH/F_PDCH%s",
+ ts->nr, VTY_NEWLINE);
return CMD_WARNING;
}
- if (!strcmp(argv[0], "add"))
- bitvec_set_bit_pos(bv, arfcn, 1);
+ if (!strcmp(argv[3], "activate"))
+ activate = 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_ramping,
- cfg_bts_acc_ramping_cmd,
- "access-control-class-ramping",
- "Enable Access Control Class ramping\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_set_enabled(&bts->acc_ramp, true);
-
- /*
- * 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);
- gsm_bts_set_system_infos(bts);
- }
-
- 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\n")
-{
- struct gsm_bts *bts = vty->index;
- bool dynamic = (strcmp(argv[0], "dynamic") == 0);
- int error;
-
- if (dynamic) {
- acc_ramp_set_step_interval_dynamic(&bts->acc_ramp);
- 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;
-}
-
-#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]);
-
+ activate = 0;
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "This feature is only available for IP systems.%s",
- VTY_NEWLINE);
+ if (activate && ts->fi->state != TS_ST_UNUSED) {
+ vty_out(vty, "%% Timeslot %u is still in use%s",
+ ts->nr, 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);
+ } else if (!activate && ts->fi->state != TS_ST_PDCH) {
+ vty_out(vty, "%% Timeslot %u is not in PDCH mode%s",
+ ts->nr, 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;
- }
+ LOG_TS(ts, LOGL_NOTICE, "telnet VTY user asks to %s\n", activate ? "PDCH ACT" : "PDCH DEACT");
+ ts->pdch_act_allowed = activate;
+ osmo_fsm_inst_state_chg(ts->fi, activate ? TS_ST_WAIT_PDCH_ACT : TS_ST_WAIT_PDCH_DEACT, 4, 0);
- 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)
+/* 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 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;
+ 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 };
- /* 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 (activate) {
+ if (!codec_str) {
+ vty_out(vty, "%% Error: need a channel type argument to activate%s", VTY_NEWLINE);
+ return CMD_WARNING;
}
- if (mode_prev == mode) {
- vty_out(vty, "Modes must be unique %s", VTY_NEWLINE);
- return -2;
+ 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;
}
- 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++;
+ int lchan_t;
+ if (lchan->fi->state != LCHAN_ST_UNUSED) {
+ vty_out(vty, "%% Cannot activate: Channel busy!%s", VTY_NEWLINE);
+ return CMD_WARNING;
}
- }
-
- 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;
+ /* pick a suitable lchan type */
+ lchan_t = gsm_lchan_type_by_pchan(lchan->ts->pchan_is);
+ 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_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_OSMO_DYN)
+ && !strcmp(codec_str, "fr"))
+ lchan_t = GSM_LCHAN_TCH_F;
+ else {
+ vty_out(vty, "%% Cannot activate: Invalid lchan type (%s)!%s",
+ gsm_pchan_name(lchan->ts->pchan_on_init), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
- 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;
- }
+ /* configure the lchan */
+ lchan_select_set_type(lchan, lchan_t);
+ if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr")) {
+ info.ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1;
+ } else if (!strcmp(codec_str, "efr")) {
+ 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.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.ch_mode_rate.chan_mode = GSM48_CMODE_SIGN;
+ } else {
+ vty_out(vty, "%% Invalid channel mode specified!%s", VTY_NEWLINE);
+ return 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;
- }
+ info.activ_for = ACTIVATE_FOR_VTY;
+ info.ch_indctr = GSM0808_CHAN_SIGN;
+ info.ch_mode_rate.chan_rate = chan_t_to_chan_rate(lchan_t);
- 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;
- }
+ if (activate == 2 || lchan->vamos.is_secondary) {
+ info.type_for = LCHAN_TYPE_FOR_VAMOS;
+ 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);
+ }
- 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_out(vty, "%% activating lchan %s as %s%s", gsm_lchan_name(lchan), gsm_chan_t_name(lchan->type),
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;
+ lchan_activate(lchan, &info);
+ } else {
+ LOG_LCHAN(lchan, LOGL_NOTICE, "attempt from VTY to release lchan %s\n", gsm_lchan_name(lchan));
+ if (!lchan->fi) {
+ vty_out(vty, "%% Cannot release: Channel not initialized%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ 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,
+ gscon_last_eutran_plmn(lchan->conn));
}
- /* 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")
+/* Activate / Deactivate a single lchan with a specific codec mode */
+static int lchan_act_trx(struct vty *vty, struct gsm_bts_trx *trx, int activate)
{
- struct gsm_bts_trx *trx = vty->index;
-
- trx->nominal_power = atoi(argv[0]);
+ int ts_nr;
+ struct gsm_bts_trx_ts *ts;
+ struct gsm_lchan *lchan;
+ char *codec_str;
+ bool skip_next = false;
- return CMD_SUCCESS;
-}
+ for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
+ ts = &trx->ts[ts_nr];
+ 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:
+ case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
+ case GSM_PCHAN_CCCH:
+ case GSM_PCHAN_CCCH_SDCCH4:
+ codec_str = "sig";
+ break;
+ case GSM_PCHAN_TCH_F:
+ case GSM_PCHAN_TCH_F_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
+ codec_str = "fr";
+ break;
+ case GSM_PCHAN_TCH_H:
+ codec_str = "hr";
+ break;
+ default:
+ codec_str = NULL;
+ }
-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. */
+ if (codec_str && skip_next == false) {
+ lchan_act_single(vty, lchan, codec_str, -1, activate);
- /* 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;
+ /* 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_OSMO_DYN)
+ skip_next = true;
+ }
+ else {
+ vty_out(vty, "%% omitting lchan %s%s", gsm_lchan_name(lchan), VTY_NEWLINE);
+ skip_next = false;
+ }
+ }
}
- 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")
+static int lchan_act_deact(struct vty *vty, const char **argv, int argc)
{
- int ts_nr = atoi(argv[0]);
- struct gsm_bts_trx *trx = vty->index;
struct gsm_bts_trx_ts *ts;
+ struct gsm_bts *bts;
+ struct gsm_lchan *lchan;
+ 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 (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 (argc > 5)
+ act_str = argv[5];
+ if (argc > 6)
+ codec_str = argv[6];
+ if (argc > 7)
+ amr_mode = atoi(argv[7]);
- 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;
+ ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
+ if (!ts)
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 support hopping%s",
- VTY_NEWLINE);
+ 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;
}
- 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);
+ 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;
}
- bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1);
-
- return CMD_SUCCESS;
-}
+ if (vamos)
+ ss_nr += ts->max_primary_lchans;
-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]);
+ lchan = &ts->lchan[ss_nr];
- if (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
- vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE);
+ if (!act_str)
+ activate = 0;
+ else if (!strcmp(act_str, "activate"))
+ activate = 1;
+ else if (!strcmp(act_str, "activate-vamos"))
+ activate = 2;
+ else
return CMD_WARNING;
- }
-
- bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0);
- return CMD_SUCCESS;
+ return lchan_act_single(vty, lchan, codec_str, amr_mode, activate);
}
-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")
+/* 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")
{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]);
-
- return CMD_SUCCESS;
+ return lchan_act_deact(vty, argv, argc);
}
-int print_counter(struct rate_ctr_group *bsc_ctrs, struct rate_ctr *ctr, const struct rate_ctr_desc *desc, void *data)
+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")
{
- struct vty *vty = data;
- vty_out(vty, "%25s: %10"PRIu64" %s%s", desc->name, ctr->current, desc->description, VTY_NEWLINE);
- return 0;
+ return lchan_act_deact(vty, argv, argc);
}
-void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net)
-{
- rate_ctr_for_each_counter(net->bsc_ctrs, print_counter, vty);
-}
+#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"
-DEFUN(drop_bts,
- drop_bts_cmd,
- "drop bts connection <0-65535> (oml|rsl)",
- "Debug/Simulation command to drop Abis/IP BTS\n"
- "Debug/Simulation command to drop Abis/IP BTS\n"
- "Debug/Simulation command to drop Abis/IP BTS\n"
- "BTS NR\n" "Drop OML Connection\n" "Drop RSL Connection\n")
+/* Similar to lchan_act, but activates all lchans on the network at once,
+ * this is intended to perform lab tests / measurements. */
+DEFUN_HIDDEN(lchan_act_bts, lchan_act_all_cmd,
+ "(activate-all-lchan|deactivate-all-lchan)",
+ ACTIVATE_ALL_LCHANS_STR
+ DEACTIVATE_ALL_LCHANS_STR)
{
- struct gsm_network *gsmnet;
- struct gsm_bts_trx *trx;
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ const char *act_str = argv[0];
+ int activate;
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;
- }
+ struct gsm_bts_trx *trx;
- 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 (!strcmp(act_str, "activate-all-lchan"))
+ activate = 1;
+ else
+ activate = 0;
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "This command only works for ipaccess.%s", VTY_NEWLINE);
- return CMD_WARNING;
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ lchan_act_trx(vty, trx, activate);
}
-
- /* close all connections */
- if (strcmp(argv[1], "oml") == 0) {
- ipaccess_drop_oml(bts, "vty");
- } else if (strcmp(argv[1], "rsl") == 0) {
- /* close all rsl connections */
- llist_for_each_entry(trx, &bts->trx_list, list) {
- ipaccess_drop_rsl(trx, "vty");
- }
- } else {
- vty_out(vty, "Argument must be 'oml# or 'rsl'.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ 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;
}
-DEFUN(restart_bts, restart_bts_cmd,
- "restart-bts <0-65535>",
- "Restart ip.access nanoBTS through OML\n"
- BTS_NR_STR)
+/* Similar to lchan_act, but activates all lchans on the specified BTS at once,
+ * this is intended to perform lab tests / measurements. */
+DEFUN_HIDDEN(lchan_act_all_bts, lchan_act_all_bts_cmd,
+ "bts <0-255> (activate-all-lchan|deactivate-all-lchan)",
+ "BTS Specific Commands\n" BTS_NR_STR
+ ACTIVATE_ALL_LCHANS_STR
+ DEACTIVATE_ALL_LCHANS_STR)
{
- struct gsm_network *gsmnet;
- struct gsm_bts_trx *trx;
+ int bts_nr = atoi(argv[0]);
+ const char *act_str = argv[1];
+ int activate;
struct gsm_bts *bts;
- unsigned int bts_nr;
-
- gsmnet = gsmnet_from_vty(vty);
+ int trx_nr;
+ struct gsm_bts_trx *trx;
- 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;
- }
+ if (!strcmp(act_str, "activate-all-lchan"))
+ activate = 1;
+ else
+ activate = 0;
- bts = gsm_bts_num(gsmnet, bts_nr);
+ bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
if (!bts) {
- vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE);
+ vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
return CMD_WARNING;
}
- if (!is_ipaccess_bts(bts) || is_sysmobts_v2(bts)) {
- vty_out(vty, "This command only works for ipaccess nanoBTS.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
+ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
+ trx = gsm_bts_trx_num(bts, trx_nr);
+ lchan_act_trx(vty, trx, activate);
}
- /* go from last TRX to c0 */
- llist_for_each_entry_reverse(trx, &bts->trx_list, list)
- abis_nm_ipaccess_restart(trx);
+ 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;
}
-DEFUN(bts_resend, bts_resend_cmd,
- "bts <0-255> resend-system-information",
- "BTS Specific Commands\n" BTS_NR_STR
- "Re-generate + re-send BCCH SYSTEM INFORMATION\n")
+/* Similar to lchan_act, but activates all lchans on the specified BTS at once,
+ * this is intended to perform lab tests / measurements. */
+DEFUN_HIDDEN(lchan_act_all_trx, lchan_act_all_trx_cmd,
+ "bts <0-255> trx <0-255> (activate-all-lchan|deactivate-all-lchan)",
+ "BTS for manual command\n" BTS_NR_STR
+ "TRX for manual command\n" TRX_NR_STR
+ ACTIVATE_ALL_LCHANS_STR
+ DEACTIVATE_ALL_LCHANS_STR)
{
- struct gsm_network *gsmnet;
+ int bts_nr = atoi(argv[0]);
+ int trx_nr = atoi(argv[1]);
+ const char *act_str = argv[2];
+ int activate;
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;
- }
-
- gsm_bts_set_system_infos(bts);
-
- return CMD_SUCCESS;
-}
-
+ struct gsm_bts_trx *trx;
-DEFUN(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"
- "Normal (one-shot) SMSCB Message; sent once over Abis+Um\n"
- "Schedule (one-shot) SMSCB Message; sent once over Abis+Um\n"
- "Default (repeating) SMSCB Message; sent once over Abis, unlimited ovrer Um\n"
- "Last Valid Block\n"
- "Hex Encoded SMSCB message (up to 88 octets)\n")
-{
- struct gsm_bts *bts;
- int bts_nr = atoi(argv[0]);
- const char *type_str = argv[1];
- int last_block = atoi(argv[2]);
- struct rsl_ie_cb_cmd_type cb_cmd;
- uint8_t buf[88];
- int rc;
+ if (!strcmp(act_str, "activate-all-lchan"))
+ activate = 1;
+ else
+ activate = 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 (!gsm_bts_get_cbch(bts)) {
- vty_out(vty, "%% BTS %d doesn't have a CBCH%s", bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
- rc = osmo_hexparse(argv[3], buf, sizeof(buf));
- if (rc < 0 || rc > sizeof(buf)) {
- vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- cb_cmd.spare = 0;
- cb_cmd.def_bcast = 0;
- if (!strcmp(type_str, "normal"))
- cb_cmd.command = RSL_CB_CMD_TYPE_NORMAL;
- else if (!strcmp(type_str, "schedule"))
- cb_cmd.command = RSL_CB_CMD_TYPE_SCHEDULE;
- else if (!strcmp(type_str, "default"))
- cb_cmd.command = RSL_CB_CMD_TYPE_DEFAULT;
- else {
- vty_out(vty, "Error parsing type%s", VTY_NEWLINE);
+ 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;
}
- switch (last_block) {
- case 1:
- cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_1;
- break;
- case 2:
- cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_2;
- break;
- case 3:
- cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_3;
- break;
- case 4:
- cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_4;
- break;
- default:
- vty_out(vty, "Error parsing LASTBLOCK%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ lchan_act_trx(vty, trx, activate);
- rsl_sms_cb_command(bts, RSL_CHAN_SDCCH4_ACCH, cb_cmd, false, buf, rc);
+ 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;
}
-/* 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)
+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")
{
- 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;
+ 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 NULL;
+ 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 NULL;
+ return CMD_WARNING;
}
- 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
- "Packet Data Channel\n"
- "Activate Dynamic PDCH/TCH (-> PDCH mode)\n"
- "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n")
-{
- struct gsm_bts_trx_ts *ts;
- int activate;
-
ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
- if (!ts || !ts->fi || ts->fi->state == TS_ST_NOT_INITIALIZED || ts->fi->state == TS_ST_BORKEN) {
- vty_out(vty, "%% Timeslot is not usable%s", VTY_NEWLINE);
+ if (!ts) {
+ vty_out(vty, "%% No such TS (%d)%s", atoi(argv[2]), VTY_NEWLINE);
return CMD_WARNING;
}
-
- if (!is_ipaccess_bts(ts->trx->bts)) {
- vty_out(vty, "%% This command only works for ipaccess BTS%s",
- VTY_NEWLINE);
+ if (ss_nr >= ts->max_primary_lchans) {
+ vty_out(vty, "%% Invalid sub-slot number for this timeslot type%s", VTY_NEWLINE);
return CMD_WARNING;
}
- if (ts->pchan_on_init != GSM_PCHAN_TCH_F_TCH_H_PDCH
- && 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",
- ts->nr, VTY_NEWLINE);
+ lchan = &ts->lchan[ss_nr];
+ if (!lchan->fi)
return CMD_WARNING;
- }
-
- if (!strcmp(argv[3], "activate"))
- activate = 1;
- else
- activate = 0;
- if (activate && ts->fi->state != TS_ST_UNUSED) {
- vty_out(vty, "%% Timeslot %u is still in use%s",
- ts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- } else if (!activate && ts->fi->state != TS_ST_PDCH) {
- vty_out(vty, "%% Timeslot %u is not in PDCH mode%s",
- ts->nr, VTY_NEWLINE);
- return CMD_WARNING;
+ if (verify) {
+ lchan_update_ms_power_ctrl_level(lchan, atoi(argv[4]));
+ return CMD_SUCCESS;
}
-
- LOG_TS(ts, LOGL_NOTICE, "telnet VTY user asks to %s\n", activate ? "PDCH ACT" : "PDCH DEACT");
- ts->pdch_act_allowed = activate;
- osmo_fsm_inst_state_chg(ts->fi, activate ? TS_ST_WAIT_PDCH_ACT : TS_ST_WAIT_PDCH_DEACT, 4, 0);
-
+ lchan->ms_power = ms_pwr_ctl_lvl(ts->trx->bts->band, atoi(argv[4]));
+ rsl_chan_ms_power_ctrl(lchan);
return CMD_SUCCESS;
-
-}
-
-/* configure the lchan for a single AMR mode (as specified) */
-static int lchan_set_single_amr_mode(struct vty *vty, struct gsm_lchan *lchan, uint8_t amr_mode)
-{
- struct amr_multirate_conf mr;
- struct gsm48_multi_rate_conf *mr_conf;
- int rc, vty_rc = CMD_SUCCESS;
- mr_conf = (struct gsm48_multi_rate_conf *) &mr.gsm48_ie;
-
- if (amr_mode > 7)
- return -1;
-
- memset(&mr, 0, sizeof(mr));
- mr_conf->ver = 1;
- /* bit-mask of supported modes, only one bit is set. Reflects
- * Figure 10.5.2.47a where there are no thershold and only a
- * single mode */
- mr.gsm48_ie[1] = 1 << amr_mode;
-
- mr.ms_mode[0].mode = amr_mode;
- mr.bts_mode[0].mode = amr_mode;
- mr.num_modes = 1;
-
- /* encode this configuration into the lchan for both uplink and
- * downlink direction */
- rc = gsm48_multirate_config(lchan->mr_ms_lv, mr_conf, mr.ms_mode, mr.num_modes);
- if (rc != 0) {
- vty_out(vty,
- "Invalid AMR multirate configuration (%s, amr mode %d, ms) - check parameters%s",
- gsm_lchant_name(lchan->type), amr_mode, VTY_NEWLINE);
- vty_rc = CMD_WARNING;
- }
- rc = gsm48_multirate_config(lchan->mr_bts_lv, mr_conf, mr.bts_mode, mr.num_modes);
- if (rc != 0) {
- vty_out(vty,
- "Invalid AMR multirate configuration (%s, amr mode %d, bts) - check parameters%s",
- gsm_lchant_name(lchan->type), amr_mode, VTY_NEWLINE);
- vty_rc = CMD_WARNING;
- }
-
- return vty_rc;
}
-/* 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) [<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" "AMR Mode\n")
+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 *act_str = argv[4];
- const char *codec_str = argv[5];
- int activate;
+ 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;
- lchan = &ts->lchan[ss_nr];
-
- if (!strcmp(act_str, "activate"))
- activate = 1;
- else
- activate = 0;
-
- /* FIXME: allow dynamic channels with switchover, lchan_activate(lchan, FOR_VTY) */
- if (ss_nr >= pchan_subslots(ts->pchan_is)) {
- 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),
- VTY_NEWLINE);
+ if (ss_nr >= ts->max_primary_lchans) {
+ vty_out(vty, "%% Invalid sub-slot number for this timeslot type%s", VTY_NEWLINE);
return CMD_WARNING;
}
- if (activate) {
- int lchan_t;
- if (lchan->fi->state != LCHAN_ST_UNUSED) {
- vty_out(vty, "%% Cannot activate: Channel busy!%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- lchan_t = gsm_lchan_type_by_pchan(ts->pchan_is);
- if (lchan_t < 0)
- return CMD_WARNING;
- /* configure the lchan */
- lchan->type = lchan_t;
- lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
- if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr"))
- lchan->tch_mode = GSM48_CMODE_SPEECH_V1;
- else if (!strcmp(codec_str, "efr"))
- lchan->tch_mode = GSM48_CMODE_SPEECH_EFR;
- else if (!strcmp(codec_str, "amr")) {
- int amr_mode, vty_rc;
- if (argc < 7) {
- vty_out(vty, "%% AMR requires specification of AMR mode%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- amr_mode = atoi(argv[6]);
- lchan->tch_mode = GSM48_CMODE_SPEECH_AMR;
- vty_rc = lchan_set_single_amr_mode(vty, lchan, amr_mode);
- if (vty_rc != CMD_SUCCESS)
- return vty_rc;
- }
- vty_out(vty, "%% activating lchan %s%s", gsm_lchan_name(lchan), VTY_NEWLINE);
- rsl_tx_chan_activ(lchan, RSL_ACT_TYPE_INITIAL, 0);
- if (is_ipaccess_bts(lchan->ts->trx->bts))
- rsl_tx_ipacc_crcx(lchan);
- } else {
- if (!lchan->fi) {
- vty_out(vty, "%% Cannot release: Channel not initialized%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- 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);
+ 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;
}
- return CMD_SUCCESS;
+ 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
@@ -5092,7 +2055,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,
@@ -5125,14 +2088,14 @@ DEFUN(lchan_mdcx, lchan_mdcx_cmd,
lchan = &ts->lchan[ss_nr];
- if (!is_ipaccess_bts(lchan->ts->trx->bts)) {
- vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
+ if (!is_ipa_abisip_bts(lchan->ts->trx->bts)) {
+ vty_out(vty, "%% BTS is not of IPA Abis/IP type%s", VTY_NEWLINE);
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;
}
@@ -5145,6 +2108,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"
@@ -5163,9 +2218,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;
@@ -5173,13 +2229,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;
@@ -5194,13 +2251,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;
@@ -5217,15 +2275,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;
@@ -5251,18 +2311,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]);
@@ -5274,19 +2334,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]);
@@ -5299,27 +2359,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);
@@ -5328,14 +2391,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);
@@ -5346,9 +2411,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];
@@ -5361,35 +2427,69 @@ 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]);
return CMD_SUCCESS;
}
-DEFUN(show_timer, show_timer_cmd,
- "show timer " OSMO_TDEF_VTY_ARG_T_OPTIONAL,
+DEFUN_ATTR(cfg_net_meas_feed_wqueue_max_len, cfg_net_meas_feed_wqueue_max_len_cmd,
+ "meas-feed write-queue-max-length <1-65535>",
+ MEAS_FEED_STR "Set the maximum length of the message write queue towards the UDP socket\n"
+ "Maximum number of messages to be queued waiting for transmission\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ meas_feed_txqueue_max_length_set(atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+static void legacy_timers(struct vty *vty, const char **T_arg)
+{
+ if (!strcmp((*T_arg), "T993111") || !strcmp((*T_arg), "t993111")) {
+ vty_out(vty, "%% Legacy: timer T993111 is now X3111%s", VTY_NEWLINE);
+ (*T_arg) = "X3111";
+ } else if (!strcmp((*T_arg), "T993210") || !strcmp((*T_arg), "t993210")) {
+ vty_out(vty, "%% Legacy: timer T993210 is now X3210%s", VTY_NEWLINE);
+ (*T_arg) = "X3210";
+ } else if (!strcmp((*T_arg), "T999") || !strcmp((*T_arg), "t999")) {
+ vty_out(vty, "%% Legacy: timer T999 is now X4%s", VTY_NEWLINE);
+ (*T_arg) = "X4";
+ }
+}
+
+/* LEGACY TIMER COMMAND. The proper commands are added by osmo_tdef_vty_groups_init(), using explicit timer group
+ * naming. The old groupless timer command accesses the 'net' group only, but is still available. */
+DEFUN_HIDDEN(show_timer, show_timer_cmd,
+ "show timer " OSMO_TDEF_VTY_ARG_T,
SHOW_STR "Show timers\n"
OSMO_TDEF_VTY_DOC_T)
{
struct gsm_network *net = gsmnet_from_vty(vty);
- const char *T_arg = argc > 0 ? argv[0] : NULL;
+ const char *T_arg = argv[0];
+ if (T_arg)
+ legacy_timers(vty, &T_arg);
return osmo_tdef_vty_show_cmd(vty, net->T_defs, T_arg, NULL);
}
-DEFUN(cfg_net_timer, cfg_net_timer_cmd,
- "timer " OSMO_TDEF_VTY_ARG_SET_OPTIONAL,
+/* LEGACY TIMER COMMAND. The proper commands are added by osmo_tdef_vty_groups_init(), using explicit timer group
+ * naming. The old groupless timer command accesses the 'net' group only, but is still available. */
+DEFUN_HIDDEN(cfg_net_timer, cfg_net_timer_cmd,
+ "timer " OSMO_TDEF_VTY_ARG_T " " OSMO_TDEF_VTY_ARG_VAL_OPTIONAL,
"Configure or show timers\n"
OSMO_TDEF_VTY_DOC_SET)
{
struct gsm_network *net = gsmnet_from_vty(vty);
+ const char *mod_argv[argc];
+ memcpy(mod_argv, argv, sizeof(mod_argv));
+ legacy_timers(vty, &mod_argv[0]);
/* If any arguments are missing, redirect to 'show' */
if (argc < 2)
- return show_timer(self, vty, argc, argv);
- return osmo_tdef_vty_set_cmd(vty, net->T_defs, argv);
+ return show_timer(self, vty, argc, mod_argv);
+ return osmo_tdef_vty_set_cmd(vty, net->T_defs, mod_argv);
}
DEFUN(cfg_net_allow_unusable_timeslots, cfg_net_allow_unusable_timeslots_cmd,
@@ -5403,6 +2503,63 @@ DEFUN(cfg_net_allow_unusable_timeslots, cfg_net_allow_unusable_timeslots_cmd,
return CMD_SUCCESS;
}
+DEFUN_ATTR(cfg_bts_pcu_sock_wqueue_len, cfg_bts_pcu_sock_wqueue_len_cmd,
+ "pcu-socket-wqueue-length <1-2147483646>",
+ "Configure the PCU socket queue length\n"
+ "Queue length\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ size_t dropped_msgs = 0;
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ size_t old = net->pcu_sock_wqueue_len_max;
+ net->pcu_sock_wqueue_len_max = atoi(argv[0]);
+ if (net->pcu_state)
+ dropped_msgs = osmo_wqueue_set_maxlen(&net->pcu_state->upqueue, net->pcu_sock_wqueue_len_max);
+ if (dropped_msgs) {
+ LOGP(DPCU, LOGL_INFO, "Have dropped %zu messages due to shortened max. message queue size (from: %zu to %u)\n",
+ dropped_msgs, old, net->pcu_sock_wqueue_len_max);
+ vty_out(vty, "Have dropped %zu messages due to shortened max. message queue size (from: %zu to %u)%s",
+ dropped_msgs, old, net->pcu_sock_wqueue_len_max, VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_net_pcu_sock_path,
+ cfg_net_pcu_sock_path_cmd,
+ "pcu-socket PATH",
+ "PCU Socket Path for using OsmoPCU co-located with BSC\n"
+ "Path in the file system for the unix-domain PCU socket\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+ int rc;
+
+ osmo_talloc_replace_string(net, &net->pcu_sock_path, argv[0]);
+ pcu_sock_exit(net);
+ rc = pcu_sock_init(net);
+ if (rc < 0) {
+ vty_out(vty, "%% Error creating PCU socket `%s'%s",
+ net->pcu_sock_path, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_net_no_pcu_sock,
+ cfg_net_no_pcu_sock_cmd,
+ "no pcu-socket",
+ NO_STR "Disable BSC co-located PCU\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_network *net = gsmnet_from_vty(vty);
+
+ pcu_sock_exit(net);
+ talloc_free(net->pcu_sock_path);
+ net->pcu_sock_path = NULL;
+ return CMD_SUCCESS;
+}
+
static struct bsc_msc_data *bsc_msc_data(struct vty *vty)
{
return vty->index;
@@ -5422,15 +2579,17 @@ 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;
msc = osmo_msc_data_alloc(bsc_gsmnet, index);
if (!msc) {
- vty_out(vty, "%%Failed to allocate MSC data.%s", VTY_NEWLINE);
+ vty_out(vty, "%% Failed to allocate MSC data.%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -5439,8 +2598,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;
@@ -5466,7 +2627,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);
@@ -5480,12 +2641,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;
@@ -5495,10 +2650,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);
@@ -5571,13 +2726,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;
@@ -5592,10 +2759,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);
@@ -5607,28 +2775,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")
{
@@ -5636,52 +2802,64 @@ 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)];
+ const char *arg = NULL;
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;
+ int ver;
+ arg = argv[i];
- 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;
+ if (strncmp("hr", arg, 2) == 0)
+ tmp[i].hr = 1;
+ else if (strncmp("fr", arg, 2) == 0)
+ tmp[i].hr = 0;
+ else
+ goto invalid_arg;
+
+ ver = atoi(arg + 2);
+ if (ver < 1 || ver > 7)
+ goto invalid_arg;
+ tmp[i].ver = ver;
- data->audio_support[i] = talloc_zero(data->audio_support,
- struct gsm_audio_support);
- data->audio_support[i]->ver = atoi(argv[i] + 2);
+ if (tmp[i].hr == 1 && ver == 2)
+ goto invalid_arg;
- if (strncmp("hr", argv[i], 2) == 0)
- data->audio_support[i]->hr = 1;
- else if (strncmp("fr", argv[i], 2) == 0)
- data->audio_support[i]->hr = 0;
+ /* 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-list': %s%s", argv[i], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
}
+ memcpy(data->audio_support, tmp, sizeof(data->audio_support));
+ data->audio_length = argc;
+
return CMD_SUCCESS;
-error:
- vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s",
- argv[i], VTY_NEWLINE);
- return CMD_ERR_INCOMPLETE;
+invalid_arg:
+ vty_out(vty, "%s is not a valid codec version%s", osmo_quote_cstr_c(OTC_SELECT, arg, -1), VTY_NEWLINE);
+ return CMD_WARNING;
}
#define LEGACY_STR "This command has no effect, it is kept to support legacy config files\n"
@@ -5738,11 +2916,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;
@@ -5751,8 +2930,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") \
{ \
@@ -5864,25 +3043,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);
@@ -5894,15 +3075,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;
@@ -5910,12 +3092,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;
@@ -5924,10 +3107,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)
@@ -5945,10 +3129,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;
@@ -5963,24 +3148,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",
@@ -5997,13 +3266,19 @@ DEFUN(show_mscs,
{
struct bsc_msc_data *msc;
llist_for_each_entry(msc, &bsc_gsmnet->mscs, entry) {
- vty_out(vty, "%d %s %s ",
+ vty_out(vty, "MSC %d: %s <-> ",
msc->a.cs7_instance,
- osmo_ss7_asp_protocol_name(msc->a.asp_proto),
osmo_sccp_inst_addr_name(msc->a.sccp, &msc->a.bsc_addr));
vty_out(vty, "%s%s",
osmo_sccp_inst_addr_name(msc->a.sccp, &msc->a.msc_addr),
VTY_NEWLINE);
+ vty_out(vty, " ASP protocol: %s%s",
+ osmo_ss7_asp_protocol_name(msc->a.asp_proto),
+ VTY_NEWLINE);
+ vty_out(vty, " BSSMAP state: %s%s",
+ msc->a.bssmap_reset ? osmo_fsm_inst_state_name(msc->a.bssmap_reset->fi) : "",
+ VTY_NEWLINE);
+
}
return CMD_SUCCESS;
@@ -6083,24 +3358,24 @@ DEFUN(logging_fltr_imsi,
if (!tgt)
return CMD_WARNING;
- bsc_subscr = bsc_subscr_find_or_create_by_imsi(bsc_gsmnet->bsc_subscribers, imsi);
+ bsc_subscr = bsc_subscr_find_or_create_by_imsi(bsc_gsmnet->bsc_subscribers, imsi, __func__);
if (!bsc_subscr) {
- vty_out(vty, "%%failed to enable logging for subscriber with IMSI(%s)%s",
+ vty_out(vty, "%% failed to enable logging for subscriber with IMSI(%s)%s",
imsi, VTY_NEWLINE);
return CMD_WARNING;
}
log_set_filter_bsc_subscr(tgt, bsc_subscr);
/* log_set_filter has grabbed its own reference */
- bsc_subscr_put(bsc_subscr);
+ bsc_subscr_put(bsc_subscr, __func__);
return CMD_SUCCESS;
}
static void dump_one_sub(struct vty *vty, struct bsc_subscr *bsub)
{
- vty_out(vty, " %15s %08x %5u %d%s", bsub->imsi, bsub->tmsi, bsub->lac, bsub->use_count,
+ vty_out(vty, " %15s %08x %s%s", bsub->imsi, bsub->tmsi, osmo_use_count_to_str_c(OTC_SELECT, &bsub->use_count),
VTY_NEWLINE);
}
@@ -6111,10 +3386,10 @@ DEFUN(show_subscr_all,
{
struct bsc_subscr *bsc_subscr;
- vty_out(vty, " IMSI TMSI LAC Use%s", VTY_NEWLINE);
- /* " 001010123456789 ffffffff 65534 1" */
+ vty_out(vty, " IMSI TMSI Use%s", VTY_NEWLINE);
+ /* " 001010123456789 ffffffff 1" */
- llist_for_each_entry(bsc_subscr, bsc_gsmnet->bsc_subscribers, entry)
+ llist_for_each_entry(bsc_subscr, &bsc_gsmnet->bsc_subscribers->bsub_list, entry)
dump_one_sub(vty, bsc_subscr);
return CMD_SUCCESS;
@@ -6144,27 +3419,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;
@@ -6199,10 +3487,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;
@@ -6217,22 +3506,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;
@@ -6305,30 +3596,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;
@@ -6347,11 +3638,19 @@ int bsc_vty_init(struct gsm_network *network)
install_element(GSMNET_NODE, &cfg_net_dyn_ts_allow_tch_f_cmd);
install_element(GSMNET_NODE, &cfg_net_meas_feed_dest_cmd);
install_element(GSMNET_NODE, &cfg_net_meas_feed_scenario_cmd);
+ install_element(GSMNET_NODE, &cfg_net_meas_feed_wqueue_max_len_cmd);
install_element(GSMNET_NODE, &cfg_net_timer_cmd);
install_element(GSMNET_NODE, &cfg_net_allow_unusable_timeslots_cmd);
+ install_element(GSMNET_NODE, &cfg_net_pcu_sock_path_cmd);
+ install_element(GSMNET_NODE, &cfg_bts_pcu_sock_wqueue_len_cmd);
+ install_element(GSMNET_NODE, &cfg_net_no_pcu_sock_cmd);
+
+ /* Timer configuration commands (generic osmo_tdef API) */
+ osmo_tdef_vty_groups_init(GSMNET_NODE, bsc_tdef_group);
install_element_ve(&bsc_show_net_cmd);
install_element_ve(&show_bts_cmd);
+ install_element_ve(&show_bts_brief_cmd);
install_element_ve(&show_bts_fail_rep_cmd);
install_element_ve(&show_rejected_bts_cmd);
install_element_ve(&show_trx_cmd);
@@ -6382,160 +3681,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_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_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_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);
@@ -6562,14 +3727,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);
@@ -6589,6 +3758,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);
@@ -6599,7 +3769,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);
@@ -6614,13 +3788,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..018ecbac1
--- /dev/null
+++ b/src/osmo-bsc/bssmap_reset.c
@@ -0,0 +1,263 @@
+/* (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 = "DISCONNECTED",
+ .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 = "CONNECTED",
+ .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);
+}
+
+void bssmap_reset_set_disconnected(struct bssmap_reset *bssmap_reset)
+{
+ /* Go to disconnected state, with the normal RESET timeout to re-send RESET. */
+ bssmap_reset_fsm_state_chg(bssmap_reset->fi, BSSMAP_RESET_ST_DISC);
+}
+
+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
new file mode 100644
index 000000000..8cc9e9af0
--- /dev/null
+++ b/src/osmo-bsc/bts.c
@@ -0,0 +1,1764 @@
+/* (C) 2008-2018 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 <osmocom/gsm/abis_nm.h>
+
+#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),
+ OSMO_VALUE_STRING(BTS_SUB_MODEL),
+ OSMO_VALUE_STRING(TRX_PHY_VERSION),
+ { 0, NULL }
+};
+
+enum bts_attribute str2btsattr(const char *s)
+{
+ return get_string_value(bts_attribute_names, s);
+}
+
+const char *btsatttr2str(enum bts_attribute v)
+{
+ return get_value_string(bts_attribute_names, v);
+}
+
+const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = {
+ { BTS_UNKNOWN, "unknown" },
+ { BTS_OSMO_LITECELL15, "osmo-bts-lc15" },
+ { BTS_OSMO_OCTPHY, "osmo-bts-octphy" },
+ { BTS_OSMO_SYSMO, "osmo-bts-sysmo" },
+ { BTS_OSMO_TRX, "osmo-bts-trx" },
+ { 0, NULL }
+};
+
+enum gsm_bts_type_variant str2btsvariant(const char *arg)
+{
+ return get_string_value(osmo_bts_variant_names, arg);
+}
+
+const char *btsvariant2str(enum gsm_bts_type_variant v)
+{
+ return get_value_string(osmo_bts_variant_names, v);
+}
+
+const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = {
+ { GSM_BTS_TYPE_UNKNOWN, "unknown" },
+ { GSM_BTS_TYPE_BS11, "bs11" },
+ { GSM_BTS_TYPE_NANOBTS, "nanobts" },
+ { GSM_BTS_TYPE_RBS2000, "rbs2000" },
+ { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" },
+ { GSM_BTS_TYPE_OSMOBTS, "osmo-bts" },
+ { 0, NULL }
+};
+
+const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = {
+ { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" },
+ { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" },
+ { 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, "Osmocom Base Transceiver Station" },
+ { 0, NULL }
+};
+
+enum gsm_bts_type str2btstype(const char *arg)
+{
+ return get_string_value(bts_type_names, arg);
+}
+
+const char *btstype2str(enum gsm_bts_type type)
+{
+ return get_value_string(bts_type_names, type);
+}
+
+static LLIST_HEAD(bts_models);
+
+struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
+{
+ struct gsm_bts_model *model;
+
+ llist_for_each_entry(model, &bts_models, list) {
+ if (model->type == type)
+ return model;
+ }
+
+ return NULL;
+}
+
+int gsm_bts_model_register(struct gsm_bts_model *model)
+{
+ if (bts_model_find(model->type))
+ return -EEXIST;
+
+ tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef);
+ llist_add_tail(&model->list, &bts_models);
+ return 0;
+}
+
+static const uint8_t bts_cell_timer_default[11] = {
+ 3, /* blocking timer (T1) */
+ 3, /* blocking retries */
+ 3, /* unblocking retries */
+ 3, /* reset timer (T2) */
+ 3, /* reset retries */
+ 10, /* suspend timer (T3) in 100ms */
+ 3, /* suspend retries */
+ 10, /* resume timer (T4) in 100ms */
+ 3, /* resume retries */
+ 10, /* capability update timer (T5) */
+ 3, /* capability update retries */
+};
+
+static const struct gprs_rlc_cfg rlc_cfg_default = {
+ .parameter = {
+ [RLC_T3142] = 20,
+ [RLC_T3169] = 5,
+ [RLC_T3191] = 5,
+ [RLC_T3193] = 160, /* 10ms */
+ [RLC_T3195] = 5,
+ [RLC_N3101] = 10,
+ [RLC_N3103] = 4,
+ [RLC_N3105] = 8,
+ [CV_COUNTDOWN] = 15,
+ [T_DL_TBF_EXT] = 250 * 10, /* ms */
+ [T_UL_TBF_EXT] = 250 * 10, /* ms */
+ },
+ .paging = {
+ .repeat_time = 5 * 50, /* ms */
+ .repeat_count = 3,
+ },
+ .cs_mask = 0x1fff,
+ .initial_cs = 2,
+ .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, struct gsm_bts_sm *bts_sm, uint8_t bts_num)
+{
+ 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);
+ bts->network = net;
+
+ bts->ms_max_power = 15; /* dBm */
+
+ 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));
+ memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default,
+ sizeof(bts->gprs.cell.rlc_cfg));
+ 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);
+ if (!bts->bts_ctrs) {
+ talloc_free(bts);
+ return NULL;
+ }
+ 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) {
+ rate_ctr_group_free(bts->bts_ctrs);
+ osmo_stat_item_group_free(bts->bts_statg);
+ talloc_free(bts);
+ return NULL;
+ }
+ 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;
+
+ paging_init(bts);
+
+ bts->features.data = &bts->_features_data[0];
+ bts->features.data_len = sizeof(bts->_features_data);
+
+ /* si handling */
+ bts->bcch_change_mark = 1;
+ bts->chan_load_avg = 0;
+
+ /* timer overrides */
+ bts->T3122 = 0; /* not overridden by default */
+ bts->T3113_dynamic = true; /* dynamic by default */
+
+ bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED;
+ bts->dtxd = false;
+ bts->gprs.ctrl_ack_type_use_block = true; /* use RLC/MAC control block */
+ 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;
+ bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list;
+ bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST;
+ bts->si_common.si2quater_neigh_list.thresh_hi = 0;
+ osmo_earfcn_init(&bts->si_common.si2quater_neigh_list);
+ bts->si_common.neigh_list.data = bts->si_common.data.neigh_list;
+ bts->si_common.neigh_list.data_len =
+ sizeof(bts->si_common.data.neigh_list);
+ bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list;
+ bts->si_common.si5_neigh_list.data_len =
+ sizeof(bts->si_common.data.si5_neigh_list);
+ bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc;
+ bts->si_common.cell_alloc.data_len =
+ sizeof(bts->si_common.data.cell_alloc);
+ bts->si_common.rach_control.re = 1; /* no re-establishment */
+ 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->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){
+ .hr = 1,
+ .efr = 1,
+ .amr = 1,
+ };
+
+ /* Set reasonable defaults for AMR-FR and AMR-HR rate configuration.
+ * 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_90 = 1,
+ .m7_95 = 1,
+ .m12_2 = 1
+ };
+ static const struct gsm48_multi_rate_conf amr_hr_mr_cfg = {
+ .m4_75 = 1,
+ .m5_90 = 1,
+ .m6_70 = 1,
+ .m7_95 = 1,
+ };
+
+ 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;
+ bts->rach_expiry_timeout = 32;
+
+ /* 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)
+{
+ if (!bts)
+ snprintf(ts2str, sizeof(ts2str), "(bts=NULL)");
+ else
+ snprintf(ts2str, sizeof(ts2str), "(bts=%d)", bts->nr);
+
+ return ts2str;
+}
+
+bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai)
+{
+ return osmo_plmn_cmp(&lai->plmn, &bts->network->plmn) == 0
+ && lai->lac == bts->location_area_code;
+}
+
+bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id)
+{
+ const union gsm0808_cell_id_u *id = &cell_id->id;
+ if (!bts || !cell_id)
+ return false;
+
+ switch (cell_id->id_discr) {
+ 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;
+ case CELL_IDENT_CI:
+ return id->ci == bts->cell_identity;
+ case CELL_IDENT_NO_CELL:
+ return false;
+ case CELL_IDENT_LAI_AND_LAC:
+ return gsm_bts_matches_lai(bts, &id->lai_and_lac);
+ case CELL_IDENT_LAC:
+ return id->lac == bts->location_area_code;
+ case CELL_IDENT_BSS:
+ return true;
+ 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);
+ }
+}
+
+/* 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)
+{
+ *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,
+ },
+ };
+}
+
+/* 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 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_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)
+ 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 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;
+ gsm_set_bts_model(bts, model);
+
+ if (model->start && !model->started) {
+ int ret = model->start(bts->network);
+ if (ret < 0)
+ return ret;
+
+ 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_OSMOBTS:
+ case GSM_BTS_TYPE_NANOBTS:
+ /* Set the default OML Stream ID to 0xff */
+ bts->oml_tei = 0xff;
+ bts->c0->nominal_power = 23;
+ break;
+ case GSM_BTS_TYPE_RBS2000:
+ INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups);
+ INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups);
+ break;
+ case GSM_BTS_TYPE_BS11:
+ case GSM_BTS_TYPE_UNKNOWN:
+ case GSM_BTS_TYPE_NOKIA_SITE:
+ /* Set default BTS reset timer */
+ bts->nokia.bts_reset_timer_cnf = 15;
+ case _NUM_GSM_BTS_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->features, BTS_FEAT_GPRS)) {
+ return 0;
+ }
+ if (mode == BTS_GPRS_EGPRS &&
+ !osmo_bts_has_feature(&bts->features, BTS_FEAT_EGPRS)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num)
+{
+ struct gsm_bts_trx *trx;
+
+ if (num >= bts->num_trx)
+ return NULL;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx->nr == num)
+ return trx;
+ }
+
+ 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->oml_link ? bts_updowntime(bts) : 0);
+}
+
+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_updowntime(const struct gsm_bts *bts)
+{
+ struct timespec tp;
+
+ if (!bts->updowntime)
+ return 0;
+
+ if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp) != 0) {
+ LOGP(DNM, LOGL_ERROR, "BTS %u uptime/downtime computation failure: %s\n", bts->nr, strerror(errno));
+ return 0;
+ }
+
+ /* monotonic clock helps to ensure that the conversion is valid */
+ return difftime(tp.tv_sec, bts->updowntime);
+}
+
+char *get_model_oml_status(const struct gsm_bts *bts)
+{
+ if (bts->model->oml_status)
+ return bts->model->oml_status(bts);
+
+ return "unknown";
+}
+
+/* reset the state of all MO in the BTS */
+void gsm_bts_mo_reset(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+ unsigned int i;
+
+ gsm_abis_mo_reset(&bts->mo);
+ gsm_abis_mo_reset(&bts->gprs.cell.mo);
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ gsm_abis_mo_reset(&trx->mo);
+ gsm_abis_mo_reset(&trx->bb_transc.mo);
+
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ gsm_abis_mo_reset(&ts->mo);
+ }
+ }
+}
+
+/* Assume there are only 256 possible bts */
+osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256);
+static void depends_calc_index_bit(int bts_nr, int *idx, int *bit)
+{
+ *idx = bts_nr / (8 * 4);
+ *bit = bts_nr % (8 * 4);
+}
+
+void bts_depend_mark(struct gsm_bts *bts, int dep)
+{
+ int idx, bit;
+ depends_calc_index_bit(dep, &idx, &bit);
+
+ bts->depends_on[idx] |= 1U << bit;
+}
+
+void bts_depend_clear(struct gsm_bts *bts, int dep)
+{
+ int idx, bit;
+ depends_calc_index_bit(dep, &idx, &bit);
+
+ bts->depends_on[idx] &= ~(1U << bit);
+}
+
+int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other)
+{
+ int idx, bit;
+ depends_calc_index_bit(other->nr, &idx, &bit);
+
+ /* Check if there is a depends bit */
+ return (base->depends_on[idx] & (1U << bit)) > 0;
+}
+
+static bool bts_is_online(const struct gsm_bts *bts)
+{
+ /* TODO: support E1 BTS too */
+ if (!is_ipa_abisip_bts(bts))
+ return true;
+
+ if (!bts->oml_link)
+ return false;
+
+ return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED;
+}
+
+int bts_depend_check(struct gsm_bts *bts)
+{
+ struct gsm_bts *other_bts;
+
+ llist_for_each_entry(other_bts, &bts->network->bts_list, list) {
+ if (!bts_depend_is_depedency(bts, other_bts))
+ continue;
+ if (bts_is_online(other_bts))
+ continue;
+ return 0;
+ }
+ return 1;
+}
+
+/* get the radio link timeout (based on SACCH decode errors, according
+ * to algorithm specified in TS 05.08 section 5.2. A value of -1
+ * indicates we should use an infinitely long timeout, which only works
+ * with OsmoBTS as the BTS implementation */
+int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts)
+{
+ const struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
+
+ if (bts->infinite_radio_link_timeout)
+ return -1;
+ else {
+ /* Encoding as per Table 10.5.21 of TS 04.08 */
+ return (cell_options->radio_link_timeout + 1) << 2;
+ }
+}
+
+/* set the radio link timeout (based on SACCH decode errors, according
+ * to algorithm specified in TS 05.08 Section 5.2. A value of -1
+ * indicates we should use an infinitely long timeout, which only works
+ * with OsmoBTS as the BTS implementation */
+void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value)
+{
+ struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
+
+ if (value < 0)
+ bts->infinite_radio_link_timeout = true;
+ else {
+ bts->infinite_radio_link_timeout = false;
+ /* Encoding as per Table 10.5.21 of TS 04.08 */
+ if (value < 4)
+ value = 4;
+ if (value > 64)
+ value = 64;
+ cell_options->radio_link_timeout = (value >> 2) - 1;
+ }
+}
+
+void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data)
+{
+ struct gsm_bts_trx *trx;
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ gsm_trx_all_ts_dispatch(trx, ts_ev, data);
+}
+
+/* set all system information types for a BTS */
+int gsm_bts_set_system_infos(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+
+ /* Generate a new ID */
+ bts->bcch_change_mark += 1;
+ bts->bcch_change_mark %= 0x7;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ int rc;
+
+ rc = gsm_bts_trx_set_system_infos(trx);
+ if (rc != 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+/* Send the given C0 power reduction value to the BTS */
+int gsm_bts_send_c0_power_red(const struct gsm_bts *bts, const uint8_t red)
+{
+ if (!bts_is_online(bts))
+ return -ENOTCONN;
+ if (!osmo_bts_has_feature(&bts->features, BTS_FEAT_BCCH_POWER_RED))
+ return -ENOTSUP;
+ if (bts->model->power_ctrl_send_c0_power_red == NULL)
+ return -ENOTSUP;
+ return bts->model->power_ctrl_send_c0_power_red(bts, red);
+}
+
+/* Send the given C0 power reduction value to the BTS and update the internal state */
+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;
+
+ rc = gsm_bts_send_c0_power_red(bts, red);
+ if (rc != 0)
+ return rc;
+
+ LOG_BTS(bts, DRSL, LOGL_NOTICE, "%sabling BCCH carrier power reduction "
+ "operation mode (maximum %u dB)\n", red ? "En" : "Dis", red);
+
+ /* 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:
+ /* Preceding 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..814a17dca
--- /dev/null
+++ b/src/osmo-bsc/bts_ctrl.c
@@ -0,0 +1,1580 @@
+/*
+ * (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/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>
+
+#include <osmocom/gsm/sysinfo.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);
+CTRL_CMD_DEFINE_RANGE(bts_bsic, "bsic", struct gsm_bts, bsic, 0, 63);
+CTRL_CMD_DEFINE_RANGE(bts_rach_max_delay, "rach-max-delay", struct gsm_bts, rach_max_delay, 1, 127);
+CTRL_CMD_DEFINE_RANGE(bts_rach_expiry_timeout, "rach-expiry-timeout", struct gsm_bts, rach_expiry_timeout, 4, 64);
+CTRL_CMD_DEFINE_RANGE(bts_ms_max_power, "ms-max-power", struct gsm_bts, ms_max_power, 0, 40);
+
+static int set_bts_apply_config(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ if (!is_ipa_abisip_bts(bts)) {
+ cmd->reply = "BTS is not IPA Abis/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->oml_link ? bts_updowntime(bts) : 0);
+ 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);
+ switch (rc) {
+ case 0: /* success */
+ return get_bts_c0_power_red(cmd, data);
+ case -ENOTCONN:
+ cmd->reply = "BTS is offline";
+ return CTRL_CMD_ERROR;
+ case -ENOTSUP:
+ cmd->reply = "BCCH carrier power reduction is not supported";
+ return CTRL_CMD_ERROR;
+ default:
+ cmd->reply = "Failed to enable BCCH carrier power reduction";
+ return CTRL_CMD_ERROR;
+ }
+}
+
+CTRL_CMD_DEFINE(bts_c0_power_red, "c0-power-reduction");
+
+static int get_bts_neighbor_list(struct ctrl_cmd *cmd, const struct bitvec *neigh_list)
+{
+ int i;
+ char *pos;
+
+ /* The length of "1 2 3 ... 1023" is 4009, so 4096 is enough */
+ cmd->reply = talloc_size(cmd, 4096);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply[0] = '\0';
+
+ pos = cmd->reply;
+
+ for (i = 0; i < neigh_list->data_len * 8; i++) {
+ if (!bitvec_get_bit_pos(neigh_list, i))
+ continue;
+
+ pos += sprintf(pos, i == 0 ? "%u" : " %u", i);
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int get_bts_neighbor_list_si2(struct ctrl_cmd *cmd, void *data)
+{
+ const struct gsm_bts *bts = cmd->node;
+ return get_bts_neighbor_list(cmd, &bts->si_common.neigh_list);
+}
+
+CTRL_CMD_DEFINE_RO(bts_neighbor_list_si2, "neighbor-list si2");
+
+static int get_bts_neighbor_list_si5(struct ctrl_cmd *cmd, void *data)
+{
+ const struct gsm_bts *bts = cmd->node;
+ return get_bts_neighbor_list(cmd, &bts->si_common.si5_neigh_list);
+}
+
+CTRL_CMD_DEFINE_RO(bts_neighbor_list_si5, "neighbor-list si5");
+
+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 bitvec *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 (gsm_arfcn2band_rc(arfcn, &unused) < 0) {
+ cmd->reply = "Invalid arfcn detected";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (add)
+ bitvec_set_bit_pos(neigh_list, arfcn, 1);
+ else
+ bitvec_set_bit_pos(neigh_list, 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)
+{
+ struct gsm_bts *bts = cmd->node;
+ if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) {
+ cmd->reply = "Neighbor list not in manual mode";
+ return CTRL_CMD_ERROR;
+ }
+ return set_bts_neighbor_list_add_del(cmd, data, true, &bts->si_common.neigh_list);
+}
+
+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)
+{
+ struct gsm_bts *bts = cmd->node;
+ if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) {
+ cmd->reply = "Neighbor list not in manual mode";
+ return CTRL_CMD_ERROR;
+ }
+ return set_bts_neighbor_list_add_del(cmd, data, false, &bts->si_common.neigh_list);
+}
+
+CTRL_CMD_DEFINE_WO(bts_neighbor_list_del, "neighbor-list del");
+
+static int verify_bts_neighbor_list_si5_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_si5_add(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ if (bts->neigh_list_manual_mode != NL_MODE_MANUAL_SI5SEP) {
+ cmd->reply = "Neighbor list not in manual mode with separate SI5";
+ return CTRL_CMD_ERROR;
+ }
+ return set_bts_neighbor_list_add_del(cmd, data, true, &bts->si_common.si5_neigh_list);
+}
+
+CTRL_CMD_DEFINE_WO(bts_neighbor_list_si5_add, "neighbor-list si5-add");
+
+static int verify_bts_neighbor_list_si5_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_si5_del(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ if (bts->neigh_list_manual_mode != NL_MODE_MANUAL_SI5SEP) {
+ cmd->reply = "Neighbor list not in manual mode with separate SI5";
+ return CTRL_CMD_ERROR;
+ }
+ return set_bts_neighbor_list_add_del(cmd, data, false, &bts->si_common.si5_neigh_list);
+}
+
+CTRL_CMD_DEFINE_WO(bts_neighbor_list_si5_del, "neighbor-list si5-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");
+
+/* si2quater neighbor management: delete an EARFCN.
+ * Format: bts.<0-255>.si2quater-neighbor-list.del.earfcn EARFCN
+ * EARFCN is in range 0..65535 */
+static int set_bts_si2quater_neighbor_list_del_earfcn(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)cmd->node;
+ int earfcn;
+
+ if (osmo_str_to_int(&earfcn, cmd->value, 10, 0, 65535) < 0) {
+ cmd->reply = "Failed to parse neighbor EARFCN value";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (bts_earfcn_del(bts, earfcn) < 0) {
+ cmd->reply = "Failed to delete a (not existent?) neighbor EARFCN";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(bts_si2quater_neighbor_list_del_earfcn,
+ "si2quater-neighbor-list del earfcn");
+
+/* si2quater neighbor management: delete an UARFCN
+ * Format: bts.<0-255>.si2quater-neighbor-list.del.uarfcn UARFCN,SCRAMBLE
+ * UARFCN is in range 0..16383, SCRAMBLE is in range 0..511 */
+static int set_bts_si2quater_neighbor_list_del_uarfcn(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)cmd->node;
+ char *uarfcn_str, *scramble_str;
+ char *tmp, *saveptr;
+ int uarfcn, scramble;
+
+ tmp = talloc_strdup(OTC_SELECT, cmd->value);
+ if (!tmp) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ uarfcn_str = strtok_r(tmp, ",", &saveptr);
+ scramble_str = strtok_r(NULL, ",", &saveptr);
+
+ if (!uarfcn_str || osmo_str_to_int(&uarfcn, uarfcn_str, 10, 0, 16383) < 0) {
+ cmd->reply = "Failed to parse neighbor UARFCN value";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!scramble_str || osmo_str_to_int(&scramble, scramble_str, 10, 0, 511) < 0) {
+ cmd->reply = "Failed to parse neighbor scrambling code";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (bts_uarfcn_del(bts, uarfcn, scramble) < 0) {
+ cmd->reply = "Failed to delete a (not existent?) neighbor UARFCN";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(bts_si2quater_neighbor_list_del_uarfcn,
+ "si2quater-neighbor-list del uarfcn");
+
+static int verify_bts_si2quater_neighbor_list_add_earfcn(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ char *earfcn_str, *thresh_hi_str, *thresh_lo_str, *prio_str, *qrxlv_str, *meas_str, *saveptr, *tmp;
+ int earfcn, thresh_hi, thresh_lo, prio, qrxlv, meas;
+
+ tmp = talloc_strdup(cmd, value);
+ if (!tmp)
+ return 1;
+
+ earfcn_str = strtok_r(tmp, ",", &saveptr);
+ thresh_hi_str = strtok_r(NULL, ",", &saveptr);
+ thresh_lo_str = strtok_r(NULL, ",", &saveptr);
+ prio_str = strtok_r(NULL, ",", &saveptr);
+ qrxlv_str = strtok_r(NULL, ",", &saveptr);
+ meas_str = strtok_r(NULL, "\0", &saveptr);
+
+
+ if (!earfcn_str || osmo_str_to_int(&earfcn, earfcn_str, 10, 0, 65535) < 0) {
+ cmd->reply = "Failed to parse neighbor EARFCN value";
+ return 1;
+ }
+
+ if (!thresh_hi_str || osmo_str_to_int(&thresh_hi, thresh_hi_str, 10, 0, 31) < 0) {
+ cmd->reply = "Failed to parse neighbor threshold high bits value";
+ return 1;
+ }
+
+ if (!thresh_lo_str || osmo_str_to_int(&thresh_lo, thresh_lo_str, 10, 0, 32) < 0) {
+ cmd->reply = "Failed to parse neighbor threshold low bits value";
+ return 1;
+ }
+
+ if (!prio_str || osmo_str_to_int(&prio, prio_str, 10, 0, 8) < 0) {
+ cmd->reply = "Failed to parse neighbor priority value";
+ return 1;
+ }
+
+ if (!qrxlv_str || osmo_str_to_int(&qrxlv, qrxlv_str, 10, 0, 32) < 0) {
+ cmd->reply = "Failed to parse neighbor QRXLEVMIN value";
+ return 1;
+ }
+
+ if (!meas_str || osmo_str_to_int(&meas, meas_str, 10, 0, 8) < 0) {
+ cmd->reply = "Failed to parse neighbor measurement bandwidth";
+ return 1;
+ }
+
+ return 0;
+}
+
+/* si2quater neighbor management: add an EARFCN
+ * Format: bts.<0-255>.si2quater-neighbor-list.add.earfcn <EARFCN>,<thresh-hi>,<thresh-lo>,<priority>,<QRXLEVMIN>,<measurement bandwidth>
+ * EARFCN is in range 0..65535, thresh-hi is in range 0..31, thresh-hi is in range 0..32,
+ * priority is in range 0..8, QRXLEVMIN is in range 0..32, measurement bandwidth is in range 0..8 */
+static int set_bts_si2quater_neighbor_list_add_earfcn(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)cmd->node;
+ char *earfcn_str, *thresh_hi_str, *thresh_lo_str, *prio_str, *qrxlv_str, *meas_str, *saveptr, *tmp;
+ int earfcn, thresh_hi, thresh_lo, prio, qrxlv, meas, result;
+
+ tmp = talloc_strdup(cmd, cmd->value);
+ if (!tmp) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ earfcn_str = strtok_r(tmp, ",", &saveptr);
+ thresh_hi_str = strtok_r(NULL, ",", &saveptr);
+ thresh_lo_str = strtok_r(NULL, ",", &saveptr);
+ prio_str = strtok_r(NULL, ",", &saveptr);
+ qrxlv_str = strtok_r(NULL, ",", &saveptr);
+ meas_str = strtok_r(NULL, "\0", &saveptr);
+
+
+ if (!earfcn_str || osmo_str_to_int(&earfcn, earfcn_str, 10, 0, 65535) < 0) {
+ cmd->reply = "Failed to parse neighbor EARFCN value";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!thresh_hi_str || osmo_str_to_int(&thresh_hi, thresh_hi_str, 10, 0, 31) < 0) {
+ cmd->reply = "Failed to parse neighbor threshold high bits value";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!thresh_lo_str || osmo_str_to_int(&thresh_lo, thresh_lo_str, 10, 0, 32) < 0) {
+ cmd->reply = "Failed to parse neighbor threshold low bits value";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!prio_str || osmo_str_to_int(&prio, prio_str, 10, 0, 8) < 0) {
+ cmd->reply = "Failed to parse neighbor priority value";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!qrxlv_str || osmo_str_to_int(&qrxlv, qrxlv_str, 10, 0, 32) < 0) {
+ cmd->reply = "Failed to parse neighbor QRXLEVMIN value";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!meas_str || osmo_str_to_int(&meas, meas_str, 10, 0, 8) < 0) {
+ cmd->reply = "Failed to parse neighbor measurement bandwidth";
+ return CTRL_CMD_ERROR;
+ }
+
+ result = bts_earfcn_add(bts, earfcn, thresh_hi, thresh_lo, prio, qrxlv, meas);
+
+ if ((result == 0) && (si2q_num(bts) <= SI2Q_MAX_NUM)) {
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+ }
+
+ switch (result) {
+ case 0:
+ cmd->reply = talloc_asprintf(cmd, "Not enough space in SI2quater (%u/%u used)", bts->si2q_count, SI2Q_MAX_NUM);
+ if (!cmd->reply)
+ cmd->reply = "OOM";
+ break;
+ case 1:
+ cmd->reply = "Multiple threshold-high are not supported";
+ break;
+ case EARFCN_THRESH_LOW_INVALID:
+ cmd->reply = "Multiple threshold-low are not supported";
+ break;
+ case EARFCN_QRXLV_INVALID + 1:
+ cmd->reply = "Multiple QRXLEVMIN are not supported";
+ break;
+ case EARFCN_PRIO_INVALID:
+ cmd->reply = "Multiple priorities are not supported";
+ break;
+ default:
+ cmd->reply = talloc_asprintf(cmd, "Unable to add EARFCN: %s", strerror(-result));
+ if (!cmd->reply)
+ cmd->reply = "OOM";
+ }
+
+ if (bts_earfcn_del(bts, earfcn) != 0)
+ cmd->reply = "Failed to roll-back adding EARFCN";
+
+ return CTRL_CMD_ERROR;
+}
+
+CTRL_CMD_DEFINE_WO(bts_si2quater_neighbor_list_add_earfcn,
+ "si2quater-neighbor-list add earfcn");
+
+static int verify_bts_si2quater_neighbor_list_add_uarfcn(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ char *uarfcn_str, *scramble_str, *diversity_str, *saveptr, *tmp;
+ int uarfcn, scramble;
+
+ tmp = talloc_strdup(cmd, value);
+ if (!tmp)
+ return 1;
+
+ uarfcn_str = strtok_r(tmp, ",", &saveptr);
+ scramble_str = strtok_r(NULL, ",", &saveptr);
+ diversity_str = strtok_r(NULL, "\0", &saveptr);
+
+ if (!uarfcn_str || osmo_str_to_int(&uarfcn, uarfcn_str, 10, 0, 16383) < 0) {
+ cmd->reply = "Failed to parse neighbor UARFCN value";
+ return 1;
+ }
+
+ if (!scramble_str || osmo_str_to_int(&scramble, scramble_str, 10, 0, 511) < 0) {
+ cmd->reply = "Failed to parse neighbor scrambling code";
+ return 1;
+ }
+
+ if (!diversity_str || ((strcmp(diversity_str, "1") != 0) && (strcmp(diversity_str, "0") != 0))) {
+ cmd->reply = "Failed to parse neighbor diversity bit";
+ return 1;
+ }
+
+ return 0;
+}
+
+/* si2quater neighbor management: add an UARFCN
+ * Format: bts.<0-255>.si2quater-neighbor-list.add.uarfcn <UARFCN>,<scrambling code>,<diversity bit>
+ * UARFCN is in range 0..16383, scrambling code is in range 0..511 */
+static int set_bts_si2quater_neighbor_list_add_uarfcn(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)cmd->node;
+ char *uarfcn_str, *scramble_str, *diversity_str, *saveptr, *tmp;
+ int uarfcn, scramble;
+ bool diversity;
+
+ tmp = talloc_strdup(cmd, cmd->value);
+ if (!tmp) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ uarfcn_str = strtok_r(tmp, ",", &saveptr);
+ scramble_str = strtok_r(NULL, ",", &saveptr);
+ diversity_str = strtok_r(NULL, "\0", &saveptr);
+
+
+ if (!uarfcn_str || osmo_str_to_int(&uarfcn, uarfcn_str, 10, 0, 16383) < 0) {
+ cmd->reply = "Failed to parse neighbor UARFCN value";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!scramble_str || osmo_str_to_int(&scramble, scramble_str, 10, 0, 511) < 0) {
+ cmd->reply = "Failed to parse neighbor scrambling code";
+ return CTRL_CMD_ERROR;
+ }
+
+ diversity = strcmp(diversity_str, "1") == 0;
+
+ switch (bts_uarfcn_add(bts, uarfcn, scramble, diversity)) {
+ case -ENOMEM:
+ cmd->reply = "max number of UARFCNs reached";
+ return CTRL_CMD_ERROR;
+ case -ENOSPC:
+ cmd->reply = "not enough space in SI2quater";
+ return CTRL_CMD_ERROR;
+ }
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO(bts_si2quater_neighbor_list_add_uarfcn,
+ "si2quater-neighbor-list add uarfcn");
+
+static int verify_bts_cell_reselection_offset(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ const int cell_reselection_offset = atoi(value);
+
+ if (cell_reselection_offset < 0 || cell_reselection_offset > 126) {
+ cmd->reply = "Value is out of range";
+ return 1;
+ } else if (cell_reselection_offset % 2 != 0) {
+ cmd->reply = "Value must be even";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_bts_cell_reselection_offset(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ if (!bts->si_common.cell_ro_sel_par.present) {
+ cmd->reply = "0";
+ return CTRL_CMD_REPLY;
+ }
+
+ cmd->reply = talloc_asprintf(cmd, "%u", bts->si_common.cell_ro_sel_par.cell_resel_off * 2);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_cell_reselection_offset(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ bts->si_common.cell_ro_sel_par.present = 1;
+ bts->si_common.cell_ro_sel_par.cell_resel_off = atoi(cmd->value) / 2;
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE(bts_cell_reselection_offset, "cell-reselection-offset");
+
+static int verify_bts_cell_reselection_penalty_time(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int penalty_time;
+
+ if (strcmp(value, "reserved") == 0)
+ return 0;
+
+ penalty_time = atoi(value);
+
+ if (penalty_time < 20 || penalty_time > 620) {
+ cmd->reply = "Value is out of range";
+ return 1;
+ } else if (penalty_time % 20 != 0) {
+ cmd->reply = "Value must be a multiple of 20";
+ return 1;
+ }
+
+ return 0;
+}
+
+/* According to 3GPP TS 45.008, PENALTY_TIME in the Control parameters section */
+static int get_bts_cell_reselection_penalty_time(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ if (!bts->si_common.cell_ro_sel_par.present) {
+ cmd->reply = "0";
+ return CTRL_CMD_REPLY;
+ }
+
+ if (bts->si_common.cell_ro_sel_par.penalty_time == 31) {
+ cmd->reply = "reserved";
+ return CTRL_CMD_REPLY;
+ }
+
+ /* Calculate the penalty time in seconds */
+ cmd->reply = talloc_asprintf(cmd, "%u", (bts->si_common.cell_ro_sel_par.penalty_time * 20) + 20);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_cell_reselection_penalty_time(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ bts->si_common.cell_ro_sel_par.present = 1;
+
+ if (strcmp(cmd->value, "reserved") == 0)
+ bts->si_common.cell_ro_sel_par.penalty_time = 31;
+ else
+ bts->si_common.cell_ro_sel_par.penalty_time = (atoi(cmd->value) - 20) / 20;
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE(bts_cell_reselection_penalty_time, "cell-reselection-penalty-time");
+
+static int verify_bts_cell_reselection_hysteresis(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ const int cell_reselection_hysteresis = atoi(value);
+
+ if (cell_reselection_hysteresis < 0 || cell_reselection_hysteresis > 14) {
+ cmd->reply = "Value is out of range";
+ return 1;
+ } else if (cell_reselection_hysteresis % 2 != 0) {
+ cmd->reply = "Value must be even";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_bts_cell_reselection_hysteresis(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_asprintf(cmd, "%u", bts->si_common.cell_sel_par.cell_resel_hyst * 2);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_cell_reselection_hysteresis(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ bts->si_common.cell_sel_par.cell_resel_hyst = atoi(cmd->value) / 2;
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE(bts_cell_reselection_hysteresis, "cell-reselection-hysteresis");
+
+
+static int verify_bts_radio_link_timeout(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int radio_link_timeout;
+ struct gsm_bts *bts = cmd->node;
+
+ if (strcmp(value, "infinite") == 0) {
+ if (bts->type != GSM_BTS_TYPE_OSMOBTS) {
+ cmd->reply = "Infinite radio link timeout not supported by BTS";
+ return 1;
+ }
+ return 0;
+ }
+
+ radio_link_timeout = atoi(cmd->value);
+
+ if (radio_link_timeout < 0 || radio_link_timeout > 64) {
+ cmd->reply = "Value is out of range";
+ return 1;
+ } else if (radio_link_timeout % 4 != 0) {
+ cmd->reply = "Value must be a multiple of 4";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_bts_radio_link_timeout(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_asprintf(cmd, "%u", gsm_bts_get_radio_link_timeout(bts));
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_radio_link_timeout(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ gsm_bts_set_radio_link_timeout(bts, atoi(cmd->value));
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE(bts_radio_link_timeout, "radio-link-timeout");
+
+static int verify_bts_rxlev_access_min(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int rxlev_access_min = atoi(cmd->value);
+
+ if (rxlev_access_min < 0 || rxlev_access_min > 63) {
+ cmd->reply = "Value is out of range";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_bts_rxlev_access_min(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_asprintf(cmd, "%u", bts->si_common.cell_sel_par.rxlev_acc_min);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_rxlev_access_min(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ bts->si_common.cell_sel_par.rxlev_acc_min = atoi(cmd->value);
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE(bts_rxlev_access_min, "rach-rxlev-access-min");
+
+/* Return space concatenated set of pairs <class>,<barred/allowed> */
+static int get_bts_rach_access_control_class(struct ctrl_cmd *cmd, void *data)
+{
+ int i;
+ const struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_strdup(cmd, "");
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ for (i = 0; i < 8; i++) {
+ cmd->reply = talloc_asprintf_append(cmd->reply,
+ i == 0 ? "%u,%s" : " %u,%s",
+ i, bts->si_common.rach_control.t3 & (0x1 << i) ? "barred" : "allowed");
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (i != 2)
+ cmd->reply = talloc_asprintf_append(cmd->reply,
+ " %u,%s",
+ i + 8, bts->si_common.rach_control.t2 & (0x1 << i) ? "barred" : "allowed");
+ else
+ cmd->reply = talloc_asprintf_append(cmd->reply,
+ " emergency,%s",
+ bts->si_common.rach_control.t2 & (0x1 << i) ? "barred" : "allowed");
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_RO(bts_rach_access_control_class, "rach-access-control-classes");
+
+static int verify_access_control_class(struct ctrl_cmd *cmd, const char *value)
+{
+ int acc;
+
+ if (strcmp(value, "emergency") == 0)
+ return 0;
+
+ acc = atoi(value);
+
+ if (acc < 0 || acc > 15) {
+ cmd->reply = "Value is out of range";
+ return 1;
+ } else if (acc == 10) {
+ cmd->reply = "Access control class 10 does not exist, consider using \"emergency\" instead";
+ return 1;
+ }
+
+ return 0;
+}
+
+static int set_access_control_class(struct ctrl_cmd *cmd, bool allow)
+{
+ int acc;
+ struct gsm_bts *bts = cmd->node;
+
+ if (strcmp(cmd->value, "emergency") == 0) {
+ if (allow)
+ bts->si_common.rach_control.t2 &= ~0x4;
+ else
+ bts->si_common.rach_control.t2 |= 0x4;
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+ }
+
+ acc = atoi(cmd->value);
+ if (acc < 8)
+ if (allow)
+ bts->si_common.rach_control.t3 &= ~(0x1 << acc);
+ else
+ bts->si_common.rach_control.t3 |= (0x1 << acc);
+ else
+ if (allow)
+ bts->si_common.rach_control.t2 &= ~(0x1 << (acc - 8));
+ else
+ bts->si_common.rach_control.t2 |= (0x1 << (acc - 8));
+
+ if (acc < 10)
+ acc_mgr_perm_subset_changed(&bts->acc_mgr, &bts->si_common.rach_control);
+
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+static int verify_bts_rach_access_control_class_bar(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return verify_access_control_class(cmd, value);
+}
+
+static int set_bts_rach_access_control_class_bar(struct ctrl_cmd *cmd, void *data)
+{
+ return set_access_control_class(cmd, false);
+}
+
+CTRL_CMD_DEFINE_WO(bts_rach_access_control_class_bar, "rach-access-control-class bar");
+
+static int verify_bts_rach_access_control_class_allow(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ return verify_access_control_class(cmd, value);
+}
+
+static int set_bts_rach_access_control_class_allow(struct ctrl_cmd *cmd, void *data)
+{
+ return set_access_control_class(cmd, true);
+}
+
+CTRL_CMD_DEFINE_WO(bts_rach_access_control_class_allow, "rach-access-control-class allow");
+
+static int verify_bts_rach_cell_barred(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int bar = atoi(cmd->value);
+
+ if ((bar != 0) && (bar != 1))
+ return 1;
+
+ return 0;
+}
+
+static int get_bts_rach_cell_barred(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_asprintf(cmd, "%u", bts->si_common.rach_control.cell_bar);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_rach_cell_barred(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ bts->si_common.rach_control.cell_bar = atoi(cmd->value);
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE(bts_rach_cell_barred, "rach-cell-barred");
+
+static int verify_bts_rach_max_trans(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int max_trans = atoi(cmd->value);
+
+ if ((max_trans != 1) && (max_trans != 2) && (max_trans != 4) && (max_trans != 7))
+ return 1;
+
+ return 0;
+}
+
+static int get_bts_rach_max_trans(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_asprintf(cmd, "%u", rach_max_trans_raw2val(bts->si_common.rach_control.max_trans));
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+static int set_bts_rach_max_trans(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+ bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(cmd->value));
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE(bts_rach_max_trans, "rach-max-transmission");
+
+/* Return space concatenated set of tuples <UARFCN>,<scrambling code>,<diversity bit> */
+static int get_bts_neighbor_list_si2quater_uarfcn(struct ctrl_cmd *cmd, void *data)
+{
+ int i;
+ const struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = talloc_strdup(cmd, "");
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ for (i = 0; i < bts->si_common.uarfcn_length; i++) {
+ cmd->reply = talloc_asprintf_append(cmd->reply,
+ i == 0 ? "%u,%u,%u" : " %u,%u,%u",
+ 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);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_RO(bts_neighbor_list_si2quater_uarfcn, "neighbor-list si2quater uarfcns");
+
+/* Return space concatenated set of tuples <EARFCN>,<thresh-hi>,<thresh-lo>,<prio>,<qrxlv>,<meas> */
+static int get_bts_neighbor_list_si2quater_earfcn(struct ctrl_cmd *cmd, void *data)
+{
+ int i;
+ bool first_earfcn = true;
+ const struct gsm_bts *bts = cmd->node;
+ const struct osmo_earfcn_si2q *neighbors = &bts->si_common.si2quater_neigh_list;
+
+ cmd->reply = talloc_strdup(cmd, "");
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ for (i = 0; i < MAX_EARFCN_LIST; i++) {
+ if (neighbors->arfcn[i] == OSMO_EARFCN_INVALID)
+ continue;
+ cmd->reply = talloc_asprintf_append(cmd->reply,
+ first_earfcn ? "%u,%u,%u,%u,%u,%u" : " %u,%u,%u,%u,%u,%u",
+ neighbors->arfcn[i],
+ neighbors->thresh_hi,
+ neighbors->thresh_lo_valid ? neighbors->thresh_lo : 32,
+ neighbors->prio_valid ? neighbors->prio : 8,
+ neighbors->qrxlm_valid ? neighbors->qrxlm : 32,
+ (neighbors->meas_bw[i] != OSMO_EARFCN_MEAS_INVALID) ? neighbors->meas_bw[i] : 8);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+ first_earfcn = false;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_RO(bts_neighbor_list_si2quater_earfcn, "neighbor-list si2quater earfcns");
+
+char *bts_lchan_dump_full_ctrl(const void *t, struct gsm_bts *bts)
+{
+ int trx_nr;
+ bool first_trx = true;
+ char *trx_dump, *dump;
+ struct gsm_bts_trx *trx;
+
+ dump = talloc_strdup(t, "");
+ if (!dump)
+ return NULL;
+
+ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
+ trx = gsm_bts_trx_num(bts, trx_nr);
+ trx_dump = trx_lchan_dump_full_ctrl(t, trx);
+ if (!trx_dump)
+ return NULL;
+ if (!strlen(trx_dump))
+ continue;
+ dump = talloc_asprintf_append(dump, first_trx ? "%s" : "\n%s", trx_dump);
+ if (!dump)
+ return NULL;
+ first_trx = false;
+ }
+
+ return dump;
+}
+
+/* Return full information about all logical channels in a BTS.
+ * format: bts.<0-255>.show-lchan.full
+ * result format: New line delimited list of <bts>,<trx>,<ts>,<lchan>,<type>,<connection>,<state>,<last error>,<bs power>,
+ * <ms power>,<interference dbm>, <interference band>,<channel mode>,<imsi>,<tmsi>,<ipa bound ip>,<ipa bound port>,
+ * <ipa bound conn id>,<ipa conn ip>,<ipa conn port>,<ipa conn speech mode>
+ */
+static int get_bts_show_lchan_full(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts *bts = cmd->node;
+
+ cmd->reply = bts_lchan_dump_full_ctrl(cmd, bts);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(bts_show_lchan_full, "show-lchan full");
+
+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_bsic);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rach_max_delay);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rach_expiry_timeout);
+ 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_si2);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_si5);
+ 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_si5_add);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_si5_del);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_mode);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si2quater_neighbor_list_del_earfcn);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si2quater_neighbor_list_del_uarfcn);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si2quater_neighbor_list_add_earfcn);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si2quater_neighbor_list_add_uarfcn);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_cell_reselection_offset);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_cell_reselection_penalty_time);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_cell_reselection_hysteresis);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ms_max_power);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_radio_link_timeout);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rxlev_access_min);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rach_access_control_class);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rach_access_control_class_bar);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rach_access_control_class_allow);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rach_cell_barred);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rach_max_trans);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_si2quater_uarfcn);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_neighbor_list_si2quater_earfcn);
+ rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_show_lchan_full);
+
+ 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 02ef46387..36b378318 100644
--- a/src/osmo-bsc/bts_ericsson_rbs2000.c
+++ b/src/osmo-bsc/bts_ericsson_rbs2000.c
@@ -29,21 +29,14 @@
#include <osmocom/abis/e1_input.h>
#include <osmocom/bsc/signal.h>
#include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/abis/lapd.h>
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);
}
@@ -52,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;
@@ -144,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);
@@ -176,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)
@@ -183,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);
@@ -203,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 f004c1598..b0532e5d9 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>
@@ -45,10 +48,17 @@
#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
#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)
@@ -64,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.
@@ -116,6 +131,7 @@ struct gsm_bts_model bts_model_nanobts = {
[NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V },
},
},
+ .features_get_reported = false,
};
@@ -124,125 +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;
-
- if (!is_ipaccess_bts(nsd->bts))
- return 0;
-
- /* This event-driven BTS setup is currently only required on nanoBTS */
+ struct gsm_gprs_nsvc *nsvc;
+ struct gsm_gprs_nse *nse;
+ struct gsm_gprs_cell *cell;
- /* S_NM_STATECHG_ADM is called after we call chg_adm_state() and would create
- * endless loop */
- if (evt != S_NM_STATECHG_OPER)
+ if (!is_ipa_abisip_bts(nsd->bts))
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:
trx = obj;
- if (new_state->operational == NM_OPSTATE_DISABLED &&
- new_state->availability == NM_AVSTATE_OK)
- abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
- trx->nr, 0xff);
+ 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_opstart(bts, obj_class, bts->bts_nr,
- 0, 0xff);
- 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);
- }
+ 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 ((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_opstart(bts, obj_class, bts->bts_nr,
- nsvc->id, 0xff);
- abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
- nsvc->id, 0xff,
- NM_STATE_UNLOCKED);
- }
+ osmo_fsm_inst_dispatch(nsvc->mo.fi, NM_EV_STATE_CHG_REP, nsd);
+ break;
default:
break;
}
@@ -252,75 +195,229 @@ static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd)
/* Callback function to be called every time we receive a 12.21 SW activated report */
static int sw_activ_rep(struct msgb *mb)
{
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ const 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);
+ struct gsm_abis_mo *mo;
+ struct tlv_parsed tp;
- if (!trx)
+ if (!is_ipa_abisip_bts(bts))
+ return 0;
+
+ mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ if (mo == NULL) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Rx SW activated report for non-existent MO\n");
+ return -ENOENT;
+ }
+
+ if (abis_nm_tlv_parse(&tp, bts, &foh->data[0], msgb_l3len(mb) - sizeof(*foh)) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
return -EINVAL;
+ }
- if (!is_ipaccess_bts(trx->bts))
- return 0;
+ mo->ipaccess.obj_version = 0; /* implicit default */
+ if (TLVP_PRES_LEN(&tp, NM_ATT_IPACC_OBJ_VERSION, 1)) {
+ const uint8_t *versions = TLVP_VAL(&tp, NM_ATT_IPACC_OBJ_VERSION);
+ char buf[256];
+ struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
- switch (foh->obj_class) {
- 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.
- */
- int rc_state = trx->mo.nm_state.administrative;
- /* 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,
- rc_state);
- abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr,
- trx->nr, 0xff);
- break;
- }
+ /* nanoBTS may report several Object Versions; the first one will
+ * be used by default unless requested explicitly before OPSTARTing. */
+ mo->ipaccess.obj_version = versions[0];
+
+ OSMO_STRBUF_PRINTF(sb, "%u (default)", versions[0]);
+ for (uint16_t i = 1; i < TLVP_LEN(&tp, NM_ATT_IPACC_OBJ_VERSION); i++)
+ OSMO_STRBUF_PRINTF(sb, ", %u", versions[i]);
+ LOGPFOH(DNM, LOGL_INFO, foh, "IPA Object Versions supported: %s\n", buf);
}
+
+ osmo_fsm_inst_dispatch(mo->fi, NM_EV_SW_ACT_REP, NULL);
return 0;
}
-static void nm_rx_opstart_ack_chan(struct msgb *oml_msg)
+static void nm_rx_opstart_ack(struct msgb *oml_msg)
{
- struct gsm_bts_trx_ts *ts;
- ts = abis_nm_get_ts(oml_msg);
- if (!ts)
- /* error already logged in abis_nm_get_ts() */
+ const 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_abis_mo *mo;
+
+ if (!is_ipa_abisip_bts(bts))
return;
- if (!ts->fi) {
- LOG_TS(ts, LOGL_ERROR, "Channel OPSTART ACK for uninitialized TS\n");
+
+ mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ if (mo == NULL)
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Rx OPSTART ACK for non-existent MO\n");
+ else
+ osmo_fsm_inst_dispatch(mo->fi, NM_EV_OPSTART_ACK, NULL);
+}
+
+static void nm_rx_opstart_nack(struct msgb *oml_msg)
+{
+ const 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_abis_mo *mo;
+
+ if (!is_ipa_abisip_bts(bts))
+ return;
+
+ mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ if (mo == NULL)
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Rx OPSTART NACK for non-existent MO\n");
+ else
+ osmo_fsm_inst_dispatch(mo->fi, NM_EV_OPSTART_NACK, NULL);
+}
+
+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_abis_mo *mo;
+
+ if (!is_ipa_abisip_bts(bts))
+ return;
+
+ mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ if (mo == NULL)
+ LOGPFOH(DNM, LOGL_ERROR, foh, "Rx Get Attribute Report for non-existent MO\n");
+ else
+ osmo_fsm_inst_dispatch(mo->fi, NM_EV_GET_ATTR_REP, NULL);
+}
+
+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 (!is_ipa_abisip_bts(bts))
+ return;
+
+ 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);
+ struct e1inp_sign_link *sign_link = oml_msg->dst;
+ struct gsm_bts *bts = sign_link->trx->bts;
+ struct gsm_bts_trx *trx;
+
+ if (!is_ipa_abisip_bts(bts))
+ return;
- osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_READY, NULL);
+ trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
+ 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_opstart_ack(struct msgb *oml_msg)
+static void nm_rx_set_chan_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;
+ struct gsm_bts_trx_ts *ts;
+
+ if (!is_ipa_abisip_bts(bts))
+ return;
+
+ 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_CHANNEL:
- nm_rx_opstart_ack_chan(oml_msg);
+ 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;
+ }
+
+ 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;
@@ -337,12 +434,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;
}
@@ -378,7 +495,7 @@ find_bts_by_unitid(struct gsm_network *net, uint16_t site_id, uint16_t bts_id)
struct gsm_bts *bts;
llist_for_each_entry(bts, &net->bts_list, list) {
- if (!is_ipaccess_bts(bts))
+ if (!is_ipa_abisip_bts(bts))
continue;
if (bts->ip_access.site_id == site_id &&
@@ -391,13 +508,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);
@@ -407,6 +524,11 @@ 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;
+ struct timespec tp;
+ int rc;
/* First of all, remove deferred drop if enabled */
osmo_timer_del(&bts->oml_drop_link_timer);
@@ -417,17 +539,45 @@ void ipaccess_drop_oml(struct gsm_bts *bts, const char *reason)
LOG_BTS(bts, DLINP, LOGL_NOTICE, "Dropping OML link: %s\n", 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);
+ rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
+ bts->updowntime = (rc < 0) ? 0 : tp.tv_sec; /* we don't need sub-second precision for downtime */
+ 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);
+ }
+ }
- gsm_bts_all_ts_dispatch(bts, TS_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);
bts->ip_access.flags = 0;
+ if (bts->model->features_get_reported) {
+ /* Reset the feature vector */
+ memset(bts->_features_data, 0, sizeof(bts->_features_data));
+ bts->features_known = false;
+ }
+
/*
* Go through the list and see if we are the depndency of a BTS
* and then drop the BTS. This can lead to some recursion but it
@@ -480,7 +630,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)))
@@ -527,15 +677,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, "Identified BTS %u/%u/%u\n",
+ 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. */
@@ -550,17 +709,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);
- bts->uptime = (rc < 0) ? 0 : tp.tv_sec; /* we don't need sub-second precision for uptime */
+ rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
+ bts->updowntime = (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;
@@ -575,12 +738,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,
@@ -588,7 +751,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:
@@ -600,10 +763,12 @@ 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;
+ LOGPIL(line, DLINP, LOGL_NOTICE, "Signalling link down\n");
+
llist_for_each_entry(link, &ts->sign.sign_links, list) {
/* Get bts pointer from the first element of the list. */
if (bts == NULL)
@@ -614,6 +779,8 @@ static void ipaccess_sign_link_down(struct e1inp_line *line)
}
if (bts != NULL)
ipaccess_drop_oml(bts, "link down");
+ else
+ LOGPIL(line, DLINP, LOGL_NOTICE, "Signalling link down for unknown BTS\n");
}
/* This function is called if we receive one OML/RSL message. */
@@ -629,6 +796,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);
@@ -655,3 +825,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 be823ae43..23196fcee 100644
--- a/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
+++ b/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
@@ -1,6 +1,6 @@
/* ip.access nanoBTS specific code, OML attribute table generator */
-/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+/* (C) 2016-2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
@@ -23,21 +23,95 @@
#include <osmocom/core/msgb.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/gsm/bts_features.h>
+
+const struct tlv_definition ipacc_eie_tlv_def = {
+ .def = {
+ /* TODO: add more values from enum ipac_eie */
+ [NM_IPAC_EIE_FREQ_BANDS] = { TLV_TYPE_TL16V },
+ [NM_IPAC_EIE_MAX_TA] = { TLV_TYPE_TL16V },
+ [NM_IPAC_EIE_CIPH_ALGOS] = { TLV_TYPE_TL16V },
+ [NM_IPAC_EIE_CHAN_TYPES] = { TLV_TYPE_TL16V },
+ [NM_IPAC_EIE_CHAN_MODES] = { TLV_TYPE_TL16V },
+ [NM_IPAC_EIE_GPRS_CODING] = { TLV_TYPE_TL16V },
+ [NM_IPAC_EIE_RTP_FEATURES] = { TLV_TYPE_TL16V },
+ [NM_IPAC_EIE_RSL_FEATURES] = { TLV_TYPE_TL16V },
+ }
+};
+
+static inline uint32_t ipacc_parse_supp_flags(const struct abis_om_fom_hdr *foh,
+ const struct value_string *flags,
+ const struct tlv_p_entry *e,
+ const char *text)
+{
+ uint32_t u32 = 0;
+
+ for (unsigned int i = 0; i < OSMO_MAX(e->len, 4); i++)
+ u32 |= e->val[i] << (i * 8);
+ for (const struct value_string *vs = flags; vs->value && vs->str; vs++) {
+ if (u32 & vs->value)
+ LOGPFOH(DNM, LOGL_INFO, foh, "%s '%s' is supported\n", text, vs->str);
+ }
+ return u32;
+}
-struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts)
+/* Parse ip.access Supported Features IE */
+int ipacc_parse_supp_features(const struct gsm_bts *bts,
+ const struct abis_om_fom_hdr *foh,
+ const uint8_t *data, uint16_t data_len)
+{
+ const struct tlv_p_entry *e;
+ struct tlv_parsed tp;
+
+ if (tlv_parse(&tp, &ipacc_eie_tlv_def, data, data_len, 0, 0) < 0) {
+ LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__);
+ return -EINVAL;
+ }
+
+ /* TODO: store the flags in the respective MO state */
+ if ((e = TLVP_GET(&tp, NM_IPAC_EIE_FREQ_BANDS)) != NULL)
+ ipacc_parse_supp_flags(foh, abis_nm_ipacc_freq_band_desc, e, "Freq. band");
+ if ((e = TLVP_GET(&tp, NM_IPAC_EIE_CIPH_ALGOS)) != NULL)
+ ipacc_parse_supp_flags(foh, abis_nm_ipacc_ciph_algo_desc, e, "Ciphering algorithm");
+ if ((e = TLVP_GET(&tp, NM_IPAC_EIE_CHAN_TYPES)) != NULL)
+ ipacc_parse_supp_flags(foh, abis_nm_ipacc_chant_desc, e, "Channel type");
+ if ((e = TLVP_GET(&tp, NM_IPAC_EIE_CHAN_MODES)) != NULL)
+ ipacc_parse_supp_flags(foh, abis_nm_ipacc_chanm_desc, e, "Channel mode");
+ if ((e = TLVP_GET(&tp, NM_IPAC_EIE_GPRS_CODING)) != NULL)
+ ipacc_parse_supp_flags(foh, abis_nm_ipacc_gprs_coding_desc, e, "GPRS Coding Scheme");
+ if ((e = TLVP_GET(&tp, NM_IPAC_EIE_RTP_FEATURES)) != NULL)
+ ipacc_parse_supp_flags(foh, abis_nm_ipacc_rtp_feat_desc, e, "RTP Feature");
+ if ((e = TLVP_GET(&tp, NM_IPAC_EIE_RSL_FEATURES)) != NULL)
+ ipacc_parse_supp_flags(foh, abis_nm_ipacc_rsl_feat_desc, e, "RSL Feature");
+ if (TLVP_PRES_LEN(&tp, NM_IPAC_EIE_MAX_TA, 1)) {
+ uint8_t u8 = *TLVP_VAL(&tp, NM_IPAC_EIE_MAX_TA);
+ LOGPFOH(DNM, LOGL_DEBUG, foh, "Max Timing Advance %u\n", u8);
+ }
+
+ return 0;
+}
+
+/* 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");
- memcpy(buf, "\x55\x5b\x61\x67\x6d\x73", 6);
- msgb_tv_fixed_put(msgb, NM_ATT_INTERF_BOUND, 6, buf);
+ msgb = msgb_alloc(1024, __func__);
+ if (!msgb)
+ return NULL;
- /* 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 */
@@ -50,28 +124,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) {
@@ -80,16 +156,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);
@@ -98,127 +177,209 @@ 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];
- msgb = msgb_alloc(1024, "nanobts_attr_bts");
+ uint8_t buf[2];
+ struct abis_nm_ipacc_att_ns_cfg ns_cfg;
+ struct abis_nm_ipacc_att_bssgp_cfg bssgp_cfg;
+ struct gsm_bts *bts = gsm_bts_sm_get_bts(bts_sm);
+
+ msgb = msgb_alloc(1024, __func__);
+ 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));
- msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_CFG, 7, buf);
-
- /* all timers in seconds */
- buf[0] = 3; /* blockimg timer (T1) */
- buf[1] = 3; /* blocking retries */
- buf[2] = 3; /* unblocking retries */
- buf[3] = 3; /* reset timer (T2) */
- buf[4] = 3; /* reset retries */
- buf[5] = 10; /* suspend timer (T3) in 100ms */
- buf[6] = 3; /* suspend retries */
- buf[7] = 10; /* resume timer (T4) in 100ms */
- buf[8] = 3; /* resume retries */
- buf[9] = 10; /* capability update timer (T5) */
- buf[10] = 3; /* capability update retries */
-
- OSMO_ASSERT(ARRAY_SIZE(bts->gprs.cell.timer) < sizeof(buf));
- memcpy(buf, bts->gprs.cell.timer, ARRAY_SIZE(bts->gprs.cell.timer));
- msgb_tl16v_put(msgb, NM_ATT_IPACC_BSSGP_CFG, 11, buf);
+ osmo_static_assert(ARRAY_SIZE(bts_sm->gprs.nse.timer) == 7, nse_timer_array_wrong_size);
+ ns_cfg = (struct abis_nm_ipacc_att_ns_cfg){
+ .un_blocking_timer = bts_sm->gprs.nse.timer[0],
+ .un_blocking_retries = bts_sm->gprs.nse.timer[1],
+ .reset_timer = bts_sm->gprs.nse.timer[2],
+ .reset_retries = bts_sm->gprs.nse.timer[3],
+ .test_timer = bts_sm->gprs.nse.timer[4],
+ .alive_timer = bts_sm->gprs.nse.timer[5],
+ .alive_retries = bts_sm->gprs.nse.timer[6],
+ };
+ msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_CFG, sizeof(ns_cfg), (const uint8_t *)&ns_cfg);
+
+ osmo_static_assert(ARRAY_SIZE(bts->gprs.cell.timer) == 11, cell_timer_array_wrong_size);
+ bssgp_cfg = (struct abis_nm_ipacc_att_bssgp_cfg){
+ .t1_s = bts->gprs.cell.timer[0],
+ .t1_blocking_retries = bts->gprs.cell.timer[1],
+ .t1_unblocking_retries = bts->gprs.cell.timer[2],
+ .t2_s = bts->gprs.cell.timer[3],
+ .t2_retries = bts->gprs.cell.timer[4],
+ .t3_100ms = bts->gprs.cell.timer[5],
+ .t3_retries = bts->gprs.cell.timer[6],
+ .t4_100ms = bts->gprs.cell.timer[7],
+ .t4_retries = bts->gprs.cell.timer[8],
+ .t5_s = bts->gprs.cell.timer[9],
+ .t5_retries = bts->gprs.cell.timer[10],
+ };
+ msgb_tl16v_put(msgb, NM_ATT_IPACC_BSSGP_CFG, sizeof(bssgp_cfg), (const uint8_t *)&bssgp_cfg);
return msgb;
}
-struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts)
+struct msgb *nanobts_gen_set_cell_attr(struct gsm_bts *bts)
{
+ const struct gsm_gprs_cell *cell = &bts->gprs.cell;
+ const struct gprs_rlc_cfg *rlcc = &cell->rlc_cfg;
struct msgb *msgb;
- uint8_t buf[256];
- msgb = msgb_alloc(1024, "nanobts_attr_bts");
+ uint8_t buf[2];
+
+ msgb = msgb_alloc(1024, __func__);
+ if (!msgb)
+ return NULL;
/* routing area code */
buf[0] = bts->gprs.rac;
msgb_tl16v_put(msgb, NM_ATT_IPACC_RAC, 1, buf);
- buf[0] = 5; /* repeat time (50ms) */
- buf[1] = 3; /* repeat count */
+ buf[0] = rlcc->paging.repeat_time / 50; /* units of 50ms */
+ buf[1] = rlcc->paging.repeat_count;
msgb_tl16v_put(msgb, NM_ATT_IPACC_GPRS_PAGING_CFG, 2, buf);
/* BVCI 925 */
- buf[0] = bts->gprs.cell.bvci >> 8;
- buf[1] = bts->gprs.cell.bvci & 0xff;
+ buf[0] = cell->bvci >> 8;
+ buf[1] = cell->bvci & 0xff;
msgb_tl16v_put(msgb, NM_ATT_IPACC_BVCI, 2, buf);
/* all timers in seconds, unless otherwise stated */
- buf[0] = 20; /* T3142 */
- buf[1] = 5; /* T3169 */
- buf[2] = 5; /* T3191 */
- buf[3] = 160; /* T3193 (units of 10ms) */
- buf[4] = 5; /* T3195 */
- buf[5] = 10; /* N3101 */
- buf[6] = 4; /* N3103 */
- buf[7] = 8; /* N3105 */
- buf[8] = 15; /* RLC CV countdown */
- msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG, 9, buf);
-
- if (bts->gprs.mode == BTS_GPRS_EGPRS) {
- buf[0] = 0x8f;
- buf[1] = 0xff;
- } else {
- buf[0] = 0x0f;
+ const struct abis_nm_ipacc_att_rlc_cfg rlc_cfg = {
+ .t3142 = rlcc->parameter[RLC_T3142],
+ .t3169 = rlcc->parameter[RLC_T3169],
+ .t3191 = rlcc->parameter[RLC_T3191],
+ .t3193_10ms = rlcc->parameter[RLC_T3193],
+ .t3195 = rlcc->parameter[RLC_T3195],
+ .n3101 = rlcc->parameter[RLC_N3101],
+ .n3103 = rlcc->parameter[RLC_N3103],
+ .n3105 = rlcc->parameter[RLC_N3105],
+ .rlc_cv_countdown = rlcc->parameter[CV_COUNTDOWN],
+ };
+ msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG, sizeof(rlc_cfg), (const uint8_t *)&rlc_cfg);
+
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ if (cell->mo.ipaccess.obj_version < 4)
+ break;
+ /* fall-through */
+ case GSM_BTS_TYPE_OSMOBTS:
+ /* CS1..CS4 flags encoded in the first octet */
+ buf[0] = rlcc->cs_mask & 0x0f;
+ /* MCS1..MSC8 flags encoded in the second octet */
buf[1] = 0x00;
+ if (bts->gprs.mode == BTS_GPRS_EGPRS) {
+ /* MSC9 is special and also goes to the first octet */
+ if (rlcc->cs_mask & (1 << GPRS_MCS9))
+ buf[0] |= (1 << 7);
+ buf[1] = (rlcc->cs_mask >> 4) & 0xff;
+ }
+ msgb_tl16v_put(msgb, NM_ATT_IPACC_CODING_SCHEMES, 2, buf);
+ break;
+ default:
+ break;
+ }
+
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ if (cell->mo.ipaccess.obj_version < 20)
+ break;
+ /* fall-through */
+ case GSM_BTS_TYPE_OSMOBTS:
+ {
+ const struct abis_nm_ipacc_att_rlc_cfg_2 rlc_cfg_2 = {
+ .t_dl_tbf_ext_10ms = htons(rlcc->parameter[T_DL_TBF_EXT] / 10),
+ .t_ul_tbf_ext_10ms = htons(rlcc->parameter[T_UL_TBF_EXT] / 10),
+ .initial_cs = rlcc->initial_cs,
+ };
+ msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_2,
+ sizeof(rlc_cfg_2), (const uint8_t *)&rlc_cfg_2);
+ break;
+ }
+ default:
+ break;
+ }
+
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ if (cell->mo.ipaccess.obj_version < 30)
+ break;
+ /* fall-through */
+ case GSM_BTS_TYPE_OSMOBTS:
+ {
+ const struct abis_nm_ipacc_att_rlc_cfg_3 rlc_cfg_3 = {
+ .initial_mcs = rlcc->initial_mcs,
+ };
+ msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_3,
+ sizeof(rlc_cfg_3), (const uint8_t *)&rlc_cfg_3);
+ break;
+ }
+ default:
+ break;
}
- msgb_tl16v_put(msgb, NM_ATT_IPACC_CODING_SCHEMES, 2, buf);
-
- buf[0] = 0; /* T downlink TBF extension (0..500, high byte) */
- buf[1] = 250; /* T downlink TBF extension (0..500, low byte) */
- buf[2] = 0; /* T uplink TBF extension (0..500, high byte) */
- buf[3] = 250; /* T uplink TBF extension (0..500, low byte) */
- buf[4] = 2; /* CS2 */
- msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_2, 5, buf);
-
-#if 0
- /* EDGE model only, breaks older models.
- * Should inquire the BTS capabilities */
- buf[0] = 2; /* MCS2 */
- msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_3, 1, buf);
-#endif
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");
+
+ msgb = msgb_alloc(1024, __func__);
+ 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);
- /* remote udp port */
- osmo_store16be(bts->gprs.nsvc[0].remote_port, &buf[0]);
- /* remote ip address */
- osmo_store32be(bts->gprs.nsvc[0].remote_ip, &buf[2]);
- /* local udp port */
- osmo_store16be(bts->gprs.nsvc[0].local_port, &buf[6]);
- msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_LINK_CFG, 8, buf);
+ switch (nsvc->remote.u.sa.sa_family) {
+ case AF_INET6:
+ /* all fields are encoded in network byte order */
+ /* protocol family */
+ buf[0] = OSMO_NSVC_ADDR_IPV6;
+ /* padding */
+ buf[1] = 0x00;
+ /* local udp port */
+ osmo_store16be(nsvc->local_port, &buf[2]);
+ /* remote udp port */
+ memcpy(&buf[4], &nsvc->remote.u.sin6.sin6_port, sizeof(uint16_t));
+ /* remote ip address */
+ 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], &nsvc->remote.u.sin.sin_port, sizeof(uint16_t));
+ /* remote ip address */
+ memcpy(&buf[2], &nsvc->remote.u.sin.sin_addr, sizeof(struct in_addr));
+ /* local udp port */
+ osmo_store16be(nsvc->local_port, &buf[6]);
+ msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_LINK_CFG, 8, buf);
+ break;
+ default:
+ break;
+ }
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");
+
+ msgb = msgb_alloc(1024, __func__);
+ 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 0d0dbb5bd..dc8ff1495 100644
--- a/src/osmo-bsc/bts_nokia_site.c
+++ b/src/osmo-bsc/bts_nokia_site.c
@@ -37,6 +37,7 @@
#include <osmocom/bsc/signal.h>
#include <osmocom/bsc/timeslot_fsm.h>
#include <osmocom/bsc/system_information.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/core/timer.h>
@@ -59,7 +60,7 @@ static int dump_elements(uint8_t * data, int len) __attribute__((unused));
static void bootstrap_om_bts(struct gsm_bts *bts)
{
- LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
+ LOG_BTS(bts, DNM, LOGL_NOTICE, "bootstrapping OML\n");
if (!bts->nokia.skip_reset) {
if (!bts->nokia.did_reset)
@@ -72,14 +73,14 @@ static void bootstrap_om_bts(struct gsm_bts *bts)
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);
+ LOG_TRX(trx, DNM, LOGL_NOTICE, "bootstrapping OML\n");
gsm_trx_all_ts_dispatch(trx, TS_EV_OML_READY, NULL);
}
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:
@@ -762,7 +762,7 @@ static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id,
/* set CA */
if (generate_cell_chan_list(&fu_config[38], trx->bts) != 0) {
- fprintf(stderr, "generate_cell_chan_list failed\n");
+ LOG_TRX(trx, DNM, LOGL_ERROR, "generate_cell_chan_list failed\n");
return 0;
}
@@ -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,12 +809,14 @@ 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;
default:
- fprintf(stderr,
- "unsupported channel config %s for timeslot %d\n",
+ LOG_TRX(trx, DNM, LOGL_ERROR, "unsupported channel config %s for timeslot %d\n",
gsm_pchan_name(ts->pchan_from_config), i);
return 0;
}
@@ -1000,17 +1005,17 @@ void set_real_time(uint8_t * real_time)
build the configuration data
*/
-static int make_bts_config(uint8_t bts_type, int n_trx, uint8_t * fu_config,
+static int make_bts_config(struct gsm_bts *bts, uint8_t bts_type, int n_trx, uint8_t * fu_config,
int need_hopping)
{
/* is it an InSite BTS ? */
if (bts_type == 0x0E || bts_type == 0x0F || bts_type == 0x10) { /* TODO */
if (n_trx != 1) {
- fprintf(stderr, "InSite has only one TRX\n");
+ LOG_BTS(bts, DNM, LOGL_ERROR, "InSite has only one TRX\n");
return 0;
}
if (need_hopping != 0) {
- fprintf(stderr, "InSite does not support hopping\n");
+ LOG_BTS(bts, DNM, LOGL_ERROR, "InSite does not support hopping\n");
return 0;
}
memcpy(fu_config, bts_config_insite, sizeof(bts_config_insite));
@@ -1090,7 +1095,7 @@ static int abis_nm_send(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref,
noh->reference = htons(ref);
memcpy(noh->data, data, len_data);
- DEBUGPC(DNM, "Sending %s\n", get_msg_type_name_string(msg_type));
+ LOG_BTS(bts, DNM, LOGL_DEBUG, "Sending %s\n", get_msg_type_name_string(msg_type));
return abis_nm_sendmsg(bts, msg);
}
@@ -1165,7 +1170,7 @@ static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref)
{
uint8_t *data = reset;
int len_data = sizeof(reset);
- LOGP(DLINP, LOGL_INFO, "Nokia BTS reset timer: %d\n", bts->nokia.bts_reset_timer_cnf);
+ LOG_BTS(bts, DLINP, LOGL_INFO, "Nokia BTS reset timer: %d\n", bts->nokia.bts_reset_timer_cnf);
return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data);
}
@@ -1252,7 +1257,7 @@ static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type,
memcpy(oh->data, data, len_to_send);
}
- DEBUGPC(DNM, "Sending multi-segment %d\n", seq);
+ LOG_BTS(bts, DNM, LOGL_DEBUG, "Sending multi-segment %d\n", seq);
ret = abis_nm_sendmsg(bts, msg);
if (ret < 0)
@@ -1306,7 +1311,7 @@ static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type)
idx++;
}
- ret = make_bts_config(bts_type, idx, config + len, need_hopping);
+ ret = make_bts_config(bts, bts_type, idx, config + len, need_hopping);
len += ret;
#if 0 /* debugging */
@@ -1333,7 +1338,7 @@ static int find_element(uint8_t * data, int len, uint16_t id, uint8_t * value,
GET_NEXT_BYTE;
- /* encoding bit, construced means that other elements are contained */
+ /* encoding bit, constructed means that other elements are contained */
constructed = ((ub & 0x20) ? 1 : 0);
if ((ub & 0x1F) == 0x1F) {
@@ -1392,7 +1397,7 @@ static int dump_elements(uint8_t * data, int len)
GET_NEXT_BYTE;
- /* encoding bit, construced means that other elements are contained */
+ /* encoding bit, constructed means that other elements are contained */
constructed = ((ub & 0x20) ? 1 : 0);
if ((ub & 0x1F) == 0x1F) {
@@ -1453,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;
@@ -1502,8 +1507,8 @@ static void reset_timer_cb(void *_bts)
/* OML link */
line = e1inp_line_find(e1_link->e1_nr);
if (!line) {
- LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to non-existing E1 line %u\n",
- bts->nr, e1_link->e1_nr);
+ LOG_BTS(bts, DLINP, LOGL_ERROR, "BTS OML link referring to non-existing E1 line %u\n",
+ e1_link->e1_nr);
return;
}
@@ -1535,7 +1540,7 @@ static void reset_timer_cb(void *_bts)
- receive ACK, start RSL link(s)
ACK some other messages received from the BTS.
- Probably its also possible to configure the BTS without a reset, this
+ Probably its also possible to configure the BTS without a reset, this
has not been tested yet.
*/
@@ -1555,18 +1560,17 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
int len_data;
if (bts->nokia.wait_reset) {
- LOGP(DNM, LOGL_INFO,
- "Ignore message while waiting for reset\n");
+ LOG_BTS(bts, DNM, LOGL_INFO, "Ignoring message while waiting for reset: %s\n", msgb_hexdump(mb));
return ret;
}
if (oh->length < sizeof(struct abis_om_nokia_hdr)) {
- LOGP(DNM, LOGL_ERROR, "Message too short\n");
+ LOG_BTS(bts, DNM, LOGL_ERROR, "Message too short: %s\n", msgb_hexdump(mb));
return -EINVAL;
}
len_data = oh->length - sizeof(struct abis_om_nokia_hdr);
- LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt));
+ LOG_BTS(bts, DNM, LOGL_INFO, "Rx (0x%02X) %s\n", mt, get_msg_type_name_string(mt));
#if 0 /* debugging */
dump_elements(noh->data, len_data);
#endif
@@ -1576,11 +1580,10 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
if (find_element(noh->data, len_data,
NOKIA_EI_BTS_TYPE, &bts->nokia.bts_type,
sizeof(uint8_t)) == sizeof(uint8_t))
- LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n",
- bts->nokia.bts_type,
- get_bts_type_string(bts->nokia.bts_type));
+ LOG_BTS(bts, DNM, LOGL_INFO, "Rx BTS type = %d (%s)\n", bts->nokia.bts_type,
+ get_bts_type_string(bts->nokia.bts_type));
else
- LOGP(DNM, LOGL_ERROR, "BTS type not found\n");
+ LOG_BTS(bts, DNM, LOGL_ERROR, "BTS type not found in NOKIA_MSG_OMU_STARTED\n");
/* send START_DOWNLOAD_REQ */
abis_nm_download_req(bts, ref);
break;
@@ -1598,14 +1601,13 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
if (find_element
(noh->data, len_data, NOKIA_EI_ACK, &ack,
sizeof(uint8_t)) == sizeof(uint8_t)) {
- LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack);
+ LOG_BTS(bts, DNM, LOGL_INFO, "Rx ACK = %u\n", ack);
if (ack != 1) {
- LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n",
- ack);
+ LOG_BTS(bts, DNM, LOGL_ERROR, "Rx No ACK (%u): don't know how to proceed\n", ack);
/* TODO: properly handle failures (NACK) */
}
} else
- LOGP(DNM, LOGL_ERROR, "ACK not found\n");
+ LOG_BTS(bts, DNM, LOGL_ERROR, "Rx MSG_ACK but no EI_ACK found: %s\n", msgb_hexdump(mb));
/* TODO: the assumption for the following is that no NACK was received */
@@ -1613,8 +1615,8 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
if (!bts->nokia.did_reset) {
bts->nokia.did_reset = 1;
- /*
- TODO: For the InSite processing the received data is
+ /*
+ TODO: For the InSite processing the received data is
blocked in the driver during reset.
Otherwise the LAPD module might assert because the InSite
sends garbage on the E1 line during reset.
@@ -1647,11 +1649,8 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
/* RSL Link */
line = e1inp_line_find(e1_link->e1_nr);
if (!line) {
- LOGP(DLINP, LOGL_ERROR,
- "TRX (%u/%u) RSL link referring "
- "to non-existing E1 line %u\n",
- sign_link->trx->bts->nr, sign_link->trx->nr,
- e1_link->e1_nr);
+ LOG_BTS(bts, DLINP, LOGL_ERROR, "RSL link referring to "
+ "non-existing E1 line %u\n", e1_link->e1_nr);
return -ENOMEM;
}
/* start TRX */
@@ -1681,21 +1680,17 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
sizeof(info));
if (str_len > 0) {
info[str_len] = 0;
- LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d) : %s\n",
- get_severity_string(severity), severity, info);
+ LOG_BTS(bts, DNM, LOGL_NOTICE, "Rx ALARM Severity %s (%d) : %s\n",
+ get_severity_string(severity), severity, info);
} else { /* nothing found, try details */
str_len =
- find_element(noh->data, len_data,
- NOKIA_EI_ALARM_DETAIL, info,
- sizeof(info));
+ find_element(noh->data, len_data, NOKIA_EI_ALARM_DETAIL, info, sizeof(info));
if (str_len > 0) {
uint16_t code;
info[str_len] = 0;
code = (info[0] << 8) + info[1];
- LOGP(DNM, LOGL_INFO,
- "ALARM Severity %s (%d), code 0x%X : %s\n",
- get_severity_string(severity), severity,
- code, info + 2);
+ LOG_BTS(bts, DNM, LOGL_NOTICE, "Rx ALARM Severity %s (%d), code 0x%X : %s\n",
+ get_severity_string(severity), severity, code, info + 2);
}
}
/* send ACK */
@@ -1712,41 +1707,37 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
int abis_nokia_rcvmsg(struct msgb *msg)
{
+ struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst;
+ struct gsm_bts *bts = sign_link->trx->bts;
struct abis_om_hdr *oh = msgb_l2(msg);
int rc = 0;
/* Various consistency checks */
if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
- oh->placement);
+ LOG_BTS(bts, DNM, LOGL_ERROR, "Rx 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);
+ LOG_BTS(bts, DNM, LOGL_ERROR, "Rx ABIS OML sequence 0x%x != 0x00\n", oh->sequence);
return -EINVAL;
}
msg->l3h = (unsigned char *)oh + sizeof(*oh);
switch (oh->mdisc) {
case ABIS_OM_MDISC_FOM:
- LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_FOM\n");
+ LOG_BTS(bts, DNM, LOGL_INFO, "Rx ABIS_OM_MDISC_FOM\n");
rc = abis_nm_rcvmsg_fom(msg);
break;
case ABIS_OM_MDISC_MANUF:
- LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_MANUF\n");
+ LOG_BTS(bts, DNM, LOGL_INFO, "Rx ABIS_OM_MDISC_MANUF: ignoring\n");
break;
case ABIS_OM_MDISC_MMI:
case ABIS_OM_MDISC_TRAU:
- LOGP(DNM, LOGL_ERROR,
- "unimplemented ABIS OML message discriminator 0x%x\n",
- oh->mdisc);
+ LOG_BTS(bts, DNM, LOGL_ERROR, "Rx unimplemented ABIS OML message discriminator 0x%x\n", oh->mdisc);
break;
default:
- LOGP(DNM, LOGL_ERROR,
- "unknown ABIS OML message discriminator 0x%x\n",
- oh->mdisc);
+ LOG_BTS(bts, DNM, LOGL_ERROR, "Rx unknown ABIS OML message discriminator 0x%x\n", oh->mdisc);
return -EINVAL;
}
@@ -1773,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);
@@ -1792,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..9312a2a27
--- /dev/null
+++ b/src/osmo-bsc/bts_osmobts.c
@@ -0,0 +1,210 @@
+/* Osmocom OsmoBTS specific code */
+
+/* (C) 2010-2012 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 <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_send_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;
+
+ /* 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;
+ model_osmobts.features_get_reported = true;
+
+ /* Unlike nanoBTS, osmo-bts does support SI2bis and SI2ter fine */
+ model_osmobts.force_combined_si = false;
+
+ /* Power control API */
+ model_osmobts.power_ctrl_send_c0_power_red = &power_ctrl_send_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 d0fe38b40..9e4f09103 100644
--- a/src/osmo-bsc/bts_siemens_bs11.c
+++ b/src/osmo-bsc/bts_siemens_bs11.c
@@ -30,6 +30,7 @@
#include <osmocom/abis/e1_input.h>
#include <osmocom/bsc/signal.h>
#include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/bts.h>
static int bts_model_bs11_start(struct gsm_network *net);
@@ -61,7 +62,7 @@ static struct gsm_bts_model model_bs11 = {
[NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV },
[NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 },
[NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV },
[NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV },
[NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV },
[NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV },
@@ -400,12 +401,19 @@ static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts)
abis_nm_set_channel_attr(ts, ccomb);
- if (is_ipaccess_bts(ts->trx->bts))
+ if (is_ipa_abisip_bts(ts->trx->bts))
return;
- if (ts_is_tch(ts))
- abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts,
- e1l->e1_ts_ss);
+ switch (ts->pchan_from_config) {
+ case GSM_PCHAN_TCH_F:
+ abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, e1l->e1_ts_ss);
+ break;
+ case GSM_PCHAN_TCH_H:
+ LOG_TS(ts, LOGL_ERROR, "No support for half-rate over E1 yet!\n");
+ break;
+ default:
+ break;
+ }
}
static void nm_reconfig_trx(struct gsm_bts_trx *trx)
@@ -415,57 +423,35 @@ static void nm_reconfig_trx(struct gsm_bts_trx *trx)
patch_nm_tables(trx->bts);
- switch (trx->bts->type) {
- case GSM_BTS_TYPE_BS11:
- /* FIXME: discover this by fetching an attribute */
+ /* FIXME: discover this by fetching an attribute */
#if 0
- trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */
+ trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */
#else
- trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */
+ trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */
#endif
- 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);
-
- /* Set Radio Attributes */
- if (trx == trx->bts->c0)
- abis_nm_set_radio_attr(trx, bs11_attr_radio,
- sizeof(bs11_attr_radio));
- else {
- uint8_t trx1_attr_radio[sizeof(bs11_attr_radio)];
- uint8_t arfcn_low = trx->arfcn & 0xff;
- uint8_t arfcn_high = (trx->arfcn >> 8) & 0x0f;
- memcpy(trx1_attr_radio, bs11_attr_radio,
- sizeof(trx1_attr_radio));
-
- /* patch ARFCN into TRX Attributes */
- trx1_attr_radio[2] &= 0xf0;
- trx1_attr_radio[2] |= arfcn_high;
- trx1_attr_radio[3] = arfcn_low;
-
- abis_nm_set_radio_attr(trx, trx1_attr_radio,
- sizeof(trx1_attr_radio));
- }
- break;
- case GSM_BTS_TYPE_NANOBTS:
- switch (trx->bts->band) {
- case GSM_BAND_850:
- case GSM_BAND_900:
- trx->nominal_power = 20;
- break;
- case GSM_BAND_1800:
- case GSM_BAND_1900:
- trx->nominal_power = 23;
- break;
- default:
- LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n",
- gsm_band_name(trx->bts->band));
- break;
- }
- break;
- default:
- break;
+ 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_primary);
+
+ /* Set Radio Attributes */
+ if (trx == trx->bts->c0)
+ abis_nm_set_radio_attr(trx, bs11_attr_radio,
+ sizeof(bs11_attr_radio));
+ else {
+ uint8_t trx1_attr_radio[sizeof(bs11_attr_radio)];
+ uint8_t arfcn_low = trx->arfcn & 0xff;
+ uint8_t arfcn_high = (trx->arfcn >> 8) & 0x0f;
+ memcpy(trx1_attr_radio, bs11_attr_radio,
+ sizeof(trx1_attr_radio));
+
+ /* patch ARFCN into TRX Attributes */
+ trx1_attr_radio[2] &= 0xf0;
+ trx1_attr_radio[2] |= arfcn_high;
+ trx1_attr_radio[3] = arfcn_low;
+
+ abis_nm_set_radio_attr(trx, trx1_attr_radio,
+ sizeof(trx1_attr_radio));
}
for (i = 0; i < TRX_NR_TS; i++)
@@ -476,17 +462,11 @@ static void nm_reconfig_bts(struct gsm_bts *bts)
{
struct gsm_bts_trx *trx;
- switch (bts->type) {
- case GSM_BTS_TYPE_BS11:
- patch_nm_tables(bts);
- abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/
- abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts));
- abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */
- abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */
- break;
- default:
- break;
- }
+ patch_nm_tables(bts);
+ abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/
+ abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts));
+ abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */
+ abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */
llist_for_each_entry(trx, &bts->trx_list, list)
nm_reconfig_trx(trx);
@@ -523,6 +503,11 @@ static void bootstrap_om_bs11(struct gsm_bts *bts)
/* restart sending event reports */
abis_nm_event_reports(bts, 1);
+
+ /* make the timeslot FSM happy. Siemens doesn't send us
+ * OML state changes for individual timeslots, so we
+ * bring all of them up here */
+ gsm_bts_all_ts_dispatch(bts, TS_EV_OML_READY, NULL);
}
static int shutdown_om(struct gsm_bts *bts)
@@ -539,6 +524,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;
@@ -590,13 +576,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);
@@ -605,5 +584,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 6f9dc77cb..000000000
--- a/src/osmo-bsc/bts_sysmobts.c
+++ /dev/null
@@ -1,64 +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/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);
-
- return gsm_bts_model_register(&model_sysmobts);
-}
diff --git a/src/osmo-bsc/bts_trx.c b/src/osmo-bsc/bts_trx.c
new file mode 100644
index 000000000..4d2588d5b
--- /dev/null
+++ b/src/osmo-bsc/bts_trx.c
@@ -0,0 +1,509 @@
+/* (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_trx.h>
+#include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/lchan_fsm.h>
+#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)
+{
+ struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
+ int k;
+
+ if (!trx)
+ return NULL;
+
+ talloc_set_destructor(trx, gsm_bts_trx_talloc_destructor);
+
+ trx->bts = bts;
+ trx->nr = bts->num_trx++;
+
+ 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);
+
+ for (k = 0; k < TRX_NR_TS; k++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[k];
+ int l;
+
+
+ ts->trx = trx;
+ ts->nr = k;
+ ts->pchan_from_config = ts->pchan_on_init = ts->pchan_is = GSM_PCHAN_NONE;
+ ts->tsc = -1;
+
+ 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);
+
+ ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data);
+ ts->hopping.arfcns.data = ts->hopping.arfcns_data;
+ ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data);
+ ts->hopping.ma.data = ts->hopping.ma_data;
+
+ for (l = 0; l < TS_MAX_LCHAN; l++) {
+ 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;
+}
+
+static char ts2str[255];
+
+char *gsm_trx_name(const struct gsm_bts_trx *trx)
+{
+ if (!trx)
+ snprintf(ts2str, sizeof(ts2str), "(trx=NULL)");
+ else
+ snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)",
+ trx->bts->nr, trx->nr);
+
+ return ts2str;
+}
+
+/* determine logical channel based on TRX and channel number IE */
+struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
+ int *rc)
+{
+ uint8_t ts_nr = chan_nr & 0x07;
+ 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;
+
+ /* 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? */
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr %x cbits %x: ts %s is not capable of GSM_PCHAN_TCH_F\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
+ break;
+ case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0):
+ case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(1):
+ lch_idx = cbits & 0x1; /* TCH/H */
+ ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_H);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_TCH_H\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
+ break;
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0):
+ case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(1):
+ 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);
+ 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);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_SDCCH8_SACCH8C\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
+ break;
+ case ABIS_RSL_CHAN_NR_CBITS_BCCH:
+ case ABIS_RSL_CHAN_NR_CBITS_RACH:
+ case ABIS_RSL_CHAN_NR_CBITS_PCH_AGCH:
+ lch_idx = 0; /* CCCH? */
+ ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_CCCH);
+ if (!ok)
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_CCCH\n",
+ chan_nr, cbits, gsm_ts_and_pchan_name(ts));
+ /* FIXME: we should not return first sdcch4 !!! */
+ break;
+ case ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH:
+ lch_idx = 0;
+ ok = ts_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];
+}
+
+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) {
+ trx->mo.force_rf_lock = locked;
+ return;
+ }
+
+ LOG_TRX(trx, DNM, LOGL_NOTICE, "Requesting administrative state change %s -> %s [%s]\n",
+ get_value_string(abis_nm_adm_state_names, trx->mo.nm_state.administrative),
+ get_value_string(abis_nm_adm_state_names, new_state), reason);
+
+ 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)
+{
+ /* FIXME: How does this behave for BS-11 ? */
+ if (is_ipa_abisip_bts(trx->bts)) {
+ if (!nm_is_running(&trx->mo.nm_state) ||
+ !nm_is_running(&trx->bb_transc.mo.nm_state))
+ return false;
+ } else if (is_ericsson_bts(trx->bts)) {
+ /* The OM2000 -> 12.21 mapping we do doesn't have separate bb_transc MO */
+ if (!nm_is_running(&trx->mo.nm_state))
+ return false;
+ }
+
+ return true;
+}
+
+
+void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ osmo_fsm_inst_dispatch(ts->fi, ts_ev, data);
+ }
+}
+
+bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx)
+{
+ bool combined = false;
+ bool result = true;
+ unsigned int i;
+
+ /* Iterate over all timeslots */
+ for (i = 0; i < 8; i++) {
+ const struct gsm_bts_trx_ts *ts = &trx->ts[i];
+
+ switch (ts->pchan_from_config) {
+ case GSM_PCHAN_CCCH_SDCCH4_CBCH:
+ case GSM_PCHAN_CCCH_SDCCH4:
+ /* CCCH+SDCCH4 can only be configured on TS0 */
+ if (i > 0) {
+ LOGP(DNM, LOGL_ERROR, "Combined CCCH is not allowed "
+ "on TS%u > 0\n", i);
+ result = false;
+ }
+ if (i == 0)
+ combined = true;
+ /* fall-through */
+ case GSM_PCHAN_CCCH:
+ /* 3GPP TS 45.002, Table 3, CCCH: TS (0, 2, 4, 6) */
+ if (i % 2 != 0) {
+ LOGP(DNM, LOGL_ERROR, "%s is not allowed on odd TS%u\n",
+ gsm_pchan_name(ts->pchan_from_config), i);
+ result = false;
+ }
+
+ /* There can be no more CCCHs if TS0/C0 is combined */
+ if (i > 0 && combined) {
+ LOGP(DNM, LOGL_ERROR, "%s is not allowed on TS%u, "
+ "because TS0 is using combined channel configuration\n",
+ gsm_pchan_name(ts->pchan_from_config), i);
+ result = false;
+ }
+ 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) {
+ LOGP(DNM, LOGL_ERROR, "TS0 on C0 must be CCCH/BCCH\n");
+ 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;
+}
+
+static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len)
+{
+ struct gsm_bts *bts = trx->bts;
+ int rc, j;
+
+ if (si_len) {
+ DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i),
+ osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN));
+ } else
+ DEBUGP(DRR, "SI%s: OFF\n", get_value_string(osmo_sitype_strs, i));
+
+ switch (i) {
+ case SYSINFO_TYPE_5:
+ case SYSINFO_TYPE_5bis:
+ case SYSINFO_TYPE_5ter:
+ case SYSINFO_TYPE_6:
+ rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i),
+ si_len ? GSM_BTS_SI(bts, i) : NULL, si_len);
+ break;
+ case SYSINFO_TYPE_2quater:
+ if (si_len == 0) {
+ rc = rsl_bcch_info(trx, i, NULL, 0);
+ break;
+ }
+ rc = 0;
+ for (j = 0; j <= bts->si2q_count; j++)
+ rc = rsl_bcch_info(trx, i, (const uint8_t *)GSM_BTS_SI2Q(bts, j), GSM_MACBLOCK_LEN);
+ break;
+ default:
+ rc = rsl_bcch_info(trx, i, si_len ? GSM_BTS_SI(bts, i) : NULL, si_len);
+ break;
+ }
+
+ return rc;
+}
+
+/* set all system information types for a TRX */
+int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx)
+{
+ 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];
+
+ bts->si_common.cell_sel_par.ms_txpwr_max_ccch =
+ ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
+ bts->si_common.cell_sel_par.neci = bts->network->neci;
+
+ /* Zero/forget the state of the dynamically computed SIs, leeping the static ones */
+ bts->si_valid = bts->si_mode_static;
+
+ /* First, we determine which of the SI messages we actually need */
+
+ if (trx == bts->c0) {
+ /* 1...4 are always present on a C0 TRX */
+ gen_si[n_si++] = SYSINFO_TYPE_1;
+ gen_si[n_si++] = SYSINFO_TYPE_2;
+ gen_si[n_si++] = SYSINFO_TYPE_2bis;
+ gen_si[n_si++] = SYSINFO_TYPE_2ter;
+ gen_si[n_si++] = SYSINFO_TYPE_2quater;
+ gen_si[n_si++] = SYSINFO_TYPE_3;
+ gen_si[n_si++] = SYSINFO_TYPE_4;
+
+ /* 13 is always present on a C0 TRX of a GPRS BTS */
+ if (bts->gprs.mode != BTS_GPRS_NONE)
+ gen_si[n_si++] = SYSINFO_TYPE_13;
+ }
+
+ /* 5 and 6 are always present on every TRX */
+ gen_si[n_si++] = SYSINFO_TYPE_5;
+ gen_si[n_si++] = SYSINFO_TYPE_5bis;
+ gen_si[n_si++] = SYSINFO_TYPE_5ter;
+ gen_si[n_si++] = SYSINFO_TYPE_6;
+
+ /* Second, we generate the selected SI via RSL */
+
+ for (n = 0; n < n_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 << si_type))) {
+ /* Set SI as being valid. gsm_generate_si() might unset
+ * it, if SI is not required. */
+ bts->si_valid |= (1 << si_type);
+ rc = gsm_generate_si(bts, si_type);
+ if (rc < 0)
+ goto err_out;
+ si_len[si_type] = rc;
+ } else {
+ 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++) {
+ 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, si_type)) {
+ if (bts->si_unused_send_empty)
+ 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, si_type, si_len[si_type]);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* Make sure the PCU is aware (in case anything GPRS related has
+ * changed in SI */
+ pcu_info_update(bts);
+
+ return 0;
+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, 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..1e30e8518
--- /dev/null
+++ b/src/osmo-bsc/bts_trx_ctrl.c
@@ -0,0 +1,157 @@
+/*
+ * (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 <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/bsc/ctrl.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/abis_nm.h>
+
+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");
+
+char *trx_lchan_dump_full_ctrl(const void *t, struct gsm_bts_trx *trx)
+{
+ int ts_nr;
+ bool first_ts = true;
+ char *ts_dump, *dump;
+
+ dump = talloc_strdup(t, "");
+ if (!dump)
+ return NULL;
+
+ for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
+ ts_dump = ts_lchan_dump_full_ctrl(t, &trx->ts[ts_nr]);
+ if (!ts_dump)
+ return NULL;
+ if (!strlen(ts_dump))
+ continue;
+ dump = talloc_asprintf_append(dump, first_ts ? "%s" : "\n%s", ts_dump);
+ if (!dump)
+ return NULL;
+ first_ts = false;
+ }
+
+ return dump;
+}
+
+/* Return full information about all logical channels in a TRX.
+ * format: bts.<0-255>.trx.<0-255>.show-lchan.full
+ * result format: New line delimited list of <bts>,<trx>,<ts>,<lchan>,<type>,<connection>,<state>,<last error>,<bs power>,
+ * <ms power>,<interference dbm>, <interference band>,<channel mode>,<imsi>,<tmsi>,<ipa bound ip>,<ipa bound port>,
+ * <ipa bound conn id>,<ipa conn ip>,<ipa conn port>,<ipa conn speech mode>
+ */
+static int get_trx_show_lchan_full(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts_trx *trx = cmd->node;
+
+ cmd->reply = trx_lchan_dump_full_ctrl(cmd, trx);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(trx_show_lchan_full, "show-lchan full");
+
+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 |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_show_lchan_full);
+
+ rc |= bsc_bts_trx_ts_ctrl_cmds_install();
+
+ return rc;
+}
diff --git a/src/osmo-bsc/bts_trx_ts_ctrl.c b/src/osmo-bsc/bts_trx_ts_ctrl.c
new file mode 100644
index 000000000..a1a17f0d2
--- /dev/null
+++ b/src/osmo-bsc/bts_trx_ts_ctrl.c
@@ -0,0 +1,151 @@
+/*
+ * (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 <osmocom/ctrl/control_cmd.h>
+
+#include <osmocom/bsc/ctrl.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/system_information.h>
+
+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");
+
+char *ts_lchan_dump_full_ctrl(const void *t, struct gsm_bts_trx_ts *ts)
+{
+ bool first_lchan = true;
+ char *lchan_dump, *dump;
+ struct gsm_lchan *lchan;
+
+ dump = talloc_strdup(t, "");
+ if (!dump)
+ return NULL;
+
+ ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) {
+ lchan_dump = lchan_dump_full_ctrl(t, lchan);
+ if (!lchan_dump)
+ return NULL;
+ dump = talloc_asprintf_append(dump, first_lchan ? "%s" : "\n%s", lchan_dump);
+ if (!dump)
+ return NULL;
+ first_lchan = false;
+ }
+
+ return dump;
+}
+
+/* Return full information about all logical channels in a timeslot.
+ * format: bts.<0-255>.trx.<0-255>.ts.<0-8>.show-lchan.full
+ * result format: New line delimited list of <bts>,<trx>,<ts>,<lchan>,<type>,<connection>,<state>,<last error>,<bs power>,
+ * <ms power>,<interference dbm>, <interference band>,<channel mode>,<imsi>,<tmsi>,<ipa bound ip>,<ipa bound port>,
+ * <ipa bound conn id>,<ipa conn ip>,<ipa conn port>,<ipa conn speech mode>
+ */
+static int get_ts_show_lchan_full(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_bts_trx_ts *ts = cmd->node;
+
+ cmd->reply = ts_lchan_dump_full_ctrl(cmd, ts);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(ts_show_lchan_full, "show-lchan full");
+
+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);
+ rc |= ctrl_cmd_install(CTRL_NODE_TS, &cmd_ts_show_lchan_full);
+
+ rc |= bsc_bts_trx_ts_lchan_ctrl_cmds_install();
+
+ return rc;
+}
diff --git a/src/osmo-bsc/bts_trx_ts_lchan_ctrl.c b/src/osmo-bsc/bts_trx_ts_lchan_ctrl.c
new file mode 100644
index 000000000..be5e755cb
--- /dev/null
+++ b/src/osmo-bsc/bts_trx_ts_lchan_ctrl.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 by sysmocom s.f.m.c. GmbH
+ *
+ * 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/ctrl/control_cmd.h>
+
+#include <osmocom/bsc/ctrl.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/system_information.h>
+#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/lchan_fsm.h>
+
+static int verify_lchan_ms_power(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+ int ms_power = atoi(cmd->value);
+
+ if (ms_power < 0 || ms_power > 40) {
+ cmd->reply = "Value is out of range";
+ return 1;
+ }
+
+ return 0;
+}
+
+/* power control management: Get lchan's ms power in dBm
+ * format: bts.<0-255>.trx.<0-255>.ts.<0-8>.lchan.<0-8>.ms-power */
+static int get_lchan_ms_power(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_lchan *lchan = cmd->node;
+
+ cmd->reply = talloc_asprintf(cmd, "%u", ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power));
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+
+/* power control management: Set lchan's ms power in dBm.
+ * For static ms power control it will change the ms tx power.
+ * For dynamic ms power control it will limit the maximum power level.
+ * format: bts.<0-255>.trx.<0-255>.ts.<0-8>.lchan.<0-8>.ms-power <ms power>
+ * ms power is in range 0..40 */
+static int set_lchan_ms_power(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_lchan *lchan = cmd->node;
+
+ lchan->ms_power = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, atoi(cmd->value));
+ rsl_chan_ms_power_ctrl(lchan);
+ cmd->reply = "OK";
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE(lchan_ms_power, "ms-power");
+
+
+char *lchan_dump_full_ctrl(const void *t, struct gsm_lchan *lchan)
+{
+ struct in_addr ia;
+ char *interference = ",", *tmsi = "", *ipa_bound = ",,", *ipa_conn = ",,";
+
+ if (lchan->interf_dbm != INTERF_DBM_UNKNOWN) {
+ interference = talloc_asprintf(t, "%d,%u", lchan->interf_dbm, lchan->interf_band);
+ if (!interference)
+ return NULL;
+ }
+
+ if (lchan->conn && lchan->conn->bsub && lchan->conn->bsub->tmsi != GSM_RESERVED_TMSI) {
+ tmsi = talloc_asprintf(t, "0x%08x", lchan->conn->bsub->tmsi);
+ if (!tmsi)
+ return NULL;
+ }
+
+ if (is_ipa_abisip_bts(lchan->ts->trx->bts) && lchan->abis_ip.bound_ip) {
+ ia.s_addr = htonl(lchan->abis_ip.bound_ip);
+ ipa_bound = talloc_asprintf(t, "%s,%u,%u", inet_ntoa(ia), lchan->abis_ip.bound_port,
+ lchan->abis_ip.conn_id);
+ if (!ipa_bound)
+ return NULL;
+ }
+
+ if (is_ipa_abisip_bts(lchan->ts->trx->bts) && lchan->abis_ip.connect_ip) {
+ ia.s_addr = htonl(lchan->abis_ip.connect_ip);
+ ipa_conn = talloc_asprintf(t, "%s,%u,0x%02x", inet_ntoa(ia), lchan->abis_ip.connect_port,
+ lchan->abis_ip.speech_mode);
+ if (!ipa_conn)
+ return NULL;
+ }
+
+ return talloc_asprintf(t, "%u,%u,%u,%u,%s,%u,%s,%s,%u,%u,%s,%s,%s,%s,%s,%s",
+ lchan->ts->trx->bts->nr,
+ lchan->ts->trx->nr,
+ lchan->ts->nr,
+ lchan->nr,
+ gsm_chan_t_name(lchan->type),
+ lchan->conn ? 1 : 0, lchan_state_name(lchan),
+ lchan->fi && lchan->fi->state == LCHAN_ST_BORKEN ? lchan->last_error : "",
+ 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),
+ interference,
+ gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode),
+ lchan->conn && lchan->conn->bsub && strlen(lchan->conn->bsub->imsi) ? lchan->conn->bsub->imsi : "",
+ tmsi,
+ ipa_bound,
+ ipa_conn
+ );
+}
+
+/* Return full information about a logical channel.
+ * format: bts.<0-255>.trx.<0-255>.ts.<0-8>.lchan.<0-8>.show.full
+ * result format: <bts>,<trx>,<ts>,<lchan>,<type>,<connection>,<state>,<last error>,<bs power>,<ms power>,<interference dbm>,
+ * <interference band>,<channel mode>,<imsi>,<tmsi>,<ipa bound ip>,<ipa bound port>,<ipa bound conn id>,<ipa conn ip>,
+ * <ipa conn port>,<ipa conn speech mode>
+ */
+static int get_lchan_show_full(struct ctrl_cmd *cmd, void *data)
+{
+ struct gsm_lchan *lchan = cmd->node;
+ cmd->reply = lchan_dump_full_ctrl(cmd, lchan);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+}
+CTRL_CMD_DEFINE_RO(lchan_show_full, "show full");
+
+
+int bsc_bts_trx_ts_lchan_ctrl_cmds_install(void)
+{
+ int rc = 0;
+
+ rc |= ctrl_cmd_install(CTRL_NODE_LCHAN, &cmd_lchan_ms_power);
+ rc |= ctrl_cmd_install(CTRL_NODE_LCHAN, &cmd_lchan_show_full);
+
+ 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..9a58089a4
--- /dev/null
+++ b/src/osmo-bsc/bts_trx_vty.c
@@ -0,0 +1,881 @@
+/* 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;
+ }
+
+ if (enabled && bts->imm_ass_time != IMM_ASS_TIME_POST_CHAN_ACK) {
+ vty_out(vty,
+ "%% ERROR: 'hopping enabled 1' works only with 'immediate-assignment post-chan-ack'%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_ipa_abisip_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_ipa_abisip_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_ipa_abisip_bts(trx->bts)) {
+ vty_out(vty, " IPA Abis/IP 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_unknown.c b/src/osmo-bsc/bts_unknown.c
index b6b56a81b..b5471ce0c 100644
--- a/src/osmo-bsc/bts_unknown.c
+++ b/src/osmo-bsc/bts_unknown.c
@@ -21,6 +21,7 @@
#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/bsc/abis_nm.h>
diff --git a/src/osmo-bsc/bts_vty.c b/src/osmo-bsc/bts_vty.c
new file mode 100644
index 000000000..24224f64f
--- /dev/null
+++ b/src/osmo-bsc/bts_vty.c
@@ -0,0 +1,5090 @@
+/* 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_ipa_abisip_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_ipa_abisip_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_ipa_abisip_bts(bts)) {
+ vty_out(vty, "%% BTS is not of IPA Abis/IP 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_ipa_abisip_bts(bts)) {
+ vty_out(vty, "%% BTS is not of IPA Abis/IP 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_ipa_abisip_bts(bts)) {
+ vty_out(vty, "%% BTS is not of IPA Abis/IP 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_vgcs_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|vgcs-vbs) "
+ 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"
+ "Channel allocation for VGCS/VBS\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;
+ if (set_all || !strcmp(argv[0], "vgcs-vbs"))
+ bts->chan_alloc_vgcs_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;
+}
+
+DEFUN_ATTR(cfg_bts_rach_expiry_timeout,
+ cfg_bts_rach_expiry_timeout_cmd,
+ "rach expiry-timeout <4-64>",
+ RACH_STR
+ "Set the timeout for channel requests expiry\n"
+ "Maximum timeout before dropping channel requests\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct gsm_bts *bts = vty->index;
+ bts->rach_expiry_timeout = 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_nch_position,
+ cfg_bts_nch_position_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "nch-position num-blocks <1-7> first-block <0-6>",
+ "NCH (Notification Channel) position within CCCH\n"
+ "Number of blocks reserved for NCH\n"
+ "Number of blocks reserved for NCH\n"
+ "First block reserved for NCH\n"
+ "First block reserved for NCH\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int num_blocks = atoi(argv[0]);
+ int first_block = atoi(argv[1]);
+
+ if (osmo_gsm48_si1ro_nch_pos_encode(num_blocks, first_block)) {
+ vty_out(vty, "num-blocks %u first-block %u is not permitted by 3GPP TS 44.010 Table 10.5.2.32.1b%s",
+ num_blocks, first_block, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts->nch.num_blocks = num_blocks;
+ bts->nch.first_block = first_block;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_no_nch_position,
+ cfg_bts_no_nch_position_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "no nch-position",
+ NO_STR "Disable NCH in this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->nch.num_blocks = 0;
+ bts->nch.first_block = 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;
+}
+
+#define NCC_STR "Network Colour Code\n"
+#define NCC_PERMITTED_STR "Set permitted NCCs\n"
+
+DEFUN_USRATTR(cfg_bts_ncc_permitted_all,
+ cfg_bts_ncc_permitted_all_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "ncc-permitted all\n",
+ NCC_PERMITTED_STR
+ "Permit all NCCs (default)\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.ncc_permitted = 0xff;
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_bts_ncc_permitted,
+ cfg_bts_ncc_permitted_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK),
+ "ncc-permitted <1-8> [<1-8>] [<1-8>] [<1-8>] [<1-8>] [<1-8>] [<1-8>]\n",
+ NCC_PERMITTED_STR
+ NCC_STR NCC_STR NCC_STR NCC_STR NCC_STR NCC_STR NCC_STR)
+{
+ struct gsm_bts *bts = vty->index;
+ int i;
+ int ncc_prev = -1;
+
+ if (argc == 1 && !strcmp(argv[0], "all")) {
+ bts->si_common.ncc_permitted = 0xff;
+ return CMD_SUCCESS;
+ }
+
+ bts->si_common.ncc_permitted = 0x00;
+
+ /* Check if NCCs are in order (like get_amr_from_arg) */
+ for (i = 0; i < argc; i++) {
+ int ncc = atoi(argv[i]);
+ if (ncc_prev > ncc) {
+ vty_out(vty, "%% NCCs must be listed in order%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (ncc_prev == ncc) {
+ vty_out(vty, "%% NCCs must be unique%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ncc_prev = ncc;
+ }
+
+ for (i = 0; i < argc; i++)
+ bts->si_common.ncc_permitted |= 1 << (atoi(argv[i]) - 1);
+
+ 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;
+ unsigned int radio_link_timeout = atoi(argv[0]);
+
+ /* According to Table 10.5.2.3.1 in TS 144.018 */
+ if (radio_link_timeout % 4 != 0) {
+ vty_out(vty, "%% Radio link timeout must be a multiple of 4%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gsm_bts_set_radio_link_timeout(bts, radio_link_timeout);
+
+ 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_no_bts_gprs_nsvc,
+ cfg_no_bts_gprs_nsvc_cmd,
+ X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK),
+ "no gprs nsvc <0-1>",
+ NO_STR GPRS_TEXT NSVC_TEXT)
+{
+ struct gsm_bts *bts = vty->index;
+ int idx = atoi(argv[0]);
+
+ GPRS_CHECK_ENABLED(bts);
+
+ bts->site_mgr->gprs.nsvc[idx].enabled = false;
+
+ return CMD_SUCCESS;
+}
+
+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]);
+ bts->site_mgr->gprs.nsvc[idx].enabled = true;
+
+ 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]);
+ bts->site_mgr->gprs.nsvc[idx].enabled = true;
+
+ 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]));
+ bts->site_mgr->gprs.nsvc[idx].enabled = true;
+
+ 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);
+ bts->site_mgr->gprs.nsvc[idx].enabled = true;
+ break;
+ case AF_INET6:
+ osmo_sockaddr_str_to_in6_addr(&remote, &bts->site_mgr->gprs.nsvc[idx].remote.u.sin6.sin6_addr);
+ bts->site_mgr->gprs.nsvc[idx].enabled = true;
+ 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" \
+ "Tbvc-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_ipa_abisip_bts(bts)) {
+ vty_out(vty, "%% This command is only intended for IPA Abis/IP BTS. 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;
+ 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);
+
+ if (bts_earfcn_del(bts, arfcn) != 0)
+ vty_out(vty, "%% Failed to roll-back adding EARFCN %u%s", arfcn, VTY_NEWLINE);
+
+ 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;
+ uint16_t arfcn = atoi(argv[0]);
+ int r = bts_earfcn_del(bts, 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;
+ }
+
+ 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_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_ipa_abisip_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_ipa_abisip_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\n"
+#define AMR_START_TEXT "Initial codec mode to use with AMR\n" \
+ "Automatically\nFirst mode\nSecond mode\nThird mode\nFourth mode\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;
+ enum imm_ass_time imm_ass_time;
+
+ if (!strcmp(argv[0], "pre-ts-ack"))
+ imm_ass_time = IMM_ASS_TIME_PRE_TS_ACK;
+ else if (!strcmp(argv[0], "pre-chan-ack"))
+ imm_ass_time = IMM_ASS_TIME_PRE_CHAN_ACK;
+ else
+ imm_ass_time = IMM_ASS_TIME_POST_CHAN_ACK;
+
+ if (imm_ass_time != IMM_ASS_TIME_POST_CHAN_ACK) {
+ struct gsm_bts_trx *trx;
+
+ /* Early-IA does not work with frequency hopping, because the IMM ASS does not convey an ARFCN when
+ * frequency hopping is in use. Make sure the user knows that. */
+ 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_bts_trx_ts *ts = &trx->ts[ts_nr];
+ if (ts->hopping.enabled) {
+ vty_out(vty, "%% ERROR: 'hopping enabled 1' works only with"
+ " 'immediate-assignment post-chan-ack', see timeslot %s%s",
+ ts->fi->id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ }
+ }
+
+ bts->imm_ass_time = imm_ass_time;
+ 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;
+ 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 (is_ipa_abisip_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_ipa_abisip_bts(bts)) {
+ vty_out(vty, " OML Link: ");
+ e1isl_dump_vty_tcp(vty, bts->oml_link);
+ bts_dump_vty_oml_link_state(vty, bts);
+ } 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);
+}
+
+void bts_dump_vty_oml_link_state(struct vty *vty, struct gsm_bts *bts)
+{
+ unsigned long long sec;
+
+ 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_updowntime(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);
+}
+
+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;
+
+ if (!nsvc->enabled) {
+ vty_out(vty, " no gprs nsvc %u%s", i, VTY_NEWLINE);
+ continue;
+ }
+
+ 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 indentation 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_ncc_permitted(struct vty *vty, const char *prefix, const struct gsm_bts *bts)
+{
+ int i;
+ uint8_t ncc_permitted = bts->si_common.ncc_permitted;
+
+ if (ncc_permitted == 0xff)
+ return;
+
+ vty_out(vty, "%sncc-permitted", prefix);
+
+ for (i = 0; i < 8; i++) {
+ if ((ncc_permitted & (1 << i)))
+ vty_out(vty, " %d", i + 1);
+ }
+
+ vty_out(vty, "%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 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);
+ vty_out(vty, " channel allocator mode vgcs-vbs %s%s",
+ bts->chan_alloc_vgcs_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, " rach expiry-timeout %u%s", bts->rach_expiry_timeout, 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->nch.num_blocks) {
+ vty_out(vty, " nch-position num-blocks %u first-block %u%s",
+ bts->nch.num_blocks, bts->nch.first_block, VTY_NEWLINE);
+ } else {
+ vty_out(vty, " no nch-position%s", 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);
+ }
+ }
+
+ 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_ncc_permitted(vty, " ", bts);
+
+ 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_rach_expiry_timeout_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_no_bts_gprs_nsvc_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_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);
+ install_element(BTS_NODE, &cfg_bts_nch_position_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_nch_position_cmd);
+ install_element(BTS_NODE, &cfg_bts_ncc_permitted_all_cmd);
+ install_element(BTS_NODE, &cfg_bts_ncc_permitted_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 ef93b7a4a..d699e7ede 100644
--- a/src/osmo-bsc/cbch_scheduler.c
+++ b/src/osmo-bsc/cbch_scheduler.c
@@ -28,6 +28,7 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/smscb.h>
#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/bts.h>
/* add all pages of given SMSCB so they appear as soon as possible *after* (included) base_idx. */
static int bts_smscb_sched_add_after(struct bts_smscb_page **sched_arr, int sched_arr_size,
@@ -59,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--;
@@ -131,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,
@@ -242,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;
@@ -255,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 91217ad41..9e17ef373 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>
@@ -35,6 +34,19 @@
* TCP port, we expect the CBC to connect to us. If neither of the two is configured,
* CBSP is effectively disabled */
+const struct value_string bsc_cbc_link_mode_names[] = {
+ { BSC_CBC_LINK_MODE_DISABLED, "disabled" },
+ { BSC_CBC_LINK_MODE_SERVER, "server" },
+ { BSC_CBC_LINK_MODE_CLIENT, "client" },
+ {}
+};
+
+const struct osmo_sockaddr_str bsc_cbc_default_server_local_addr = {
+ .af = AF_INET,
+ .ip = "127.0.0.1",
+ .port = CBSP_TCP_PORT,
+};
+
/*********************************************************************************
* CBSP Server (inbound TCP connection from CBC)
*********************************************************************************/
@@ -42,7 +54,6 @@
static int cbsp_srv_closed_cb(struct osmo_stream_srv *conn)
{
struct bsc_cbc_link *cbc = osmo_stream_srv_get_data(conn);
- //struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
LOGP(DCBS, LOGL_NOTICE, "CBSP Server lost connection from %s\n", cbc->server.sock_name);
talloc_free(cbc->server.sock_name);
@@ -51,29 +62,28 @@ static int cbsp_srv_closed_cb(struct osmo_stream_srv *conn)
return 0;
}
-static int cbsp_srv_cb(struct osmo_stream_srv *conn)
+static int cbsp_srv_read_cb(struct osmo_stream_srv *conn, int res, struct msgb *msg)
{
struct bsc_cbc_link *cbc = osmo_stream_srv_get_data(conn);
- struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
struct osmo_cbsp_decoded *decoded;
- struct msgb *msg;
- int rc;
- /* READ */
- rc = osmo_cbsp_recv_buffered(cbc, ofd->fd, &msg, &cbc->server.msg);
- if (rc <= 0) {
- if (rc == -EAGAIN || rc == -EINTR) {
- /* more data needs to be read */
+ if (res <= 0) {
+ if (res == -EAGAIN || res == -EINTR) {
+ msgb_free(msg);
return 0;
- } else if (rc == -EPIPE || rc == -ECONNRESET) {
- /* lost connection */
- } else if (rc == 0) {
- /* connection closed */
}
+ /*
+ if (rc == -EPIPE || rc == -ECONNRESET) {
+ // lost connection
+ } else if (rc == 0) {
+ // connection closed
+ } */
+ msgb_free(msg);
osmo_stream_srv_destroy(conn);
cbc->server.srv = NULL;
return -EBADF;
}
+
OSMO_ASSERT(msg);
decoded = osmo_cbsp_decode(conn, msg);
if (decoded) {
@@ -105,12 +115,15 @@ static int cbsp_srv_link_accept_cb(struct osmo_stream_srv_link *link, int fd)
return -1;
}
- srv = osmo_stream_srv_create(cbc, link, fd, cbsp_srv_cb, cbsp_srv_closed_cb, cbc);
+ srv = osmo_stream_srv_create2(cbc, link, fd, cbc);
if (!srv) {
LOGP(DCBS, LOGL_ERROR, "Unable to create stream server for %s\n",
osmo_sock_get_name2(fd));
return -1;
}
+ osmo_stream_srv_set_read_cb(srv, cbsp_srv_read_cb);
+ osmo_stream_srv_set_closed_cb(srv, cbsp_srv_closed_cb);
+ osmo_stream_srv_set_segmentation_cb(srv, osmo_cbsp_segmentation_cb);
cbc->server.srv = srv;
if (cbc->server.sock_name)
@@ -129,11 +142,10 @@ static int cbsp_srv_link_accept_cb(struct osmo_stream_srv_link *link, int fd)
static int cbsp_client_connect_cb(struct osmo_stream_cli *cli)
{
struct bsc_cbc_link *cbc = osmo_stream_cli_get_data(cli);
- struct osmo_fd *ofd = osmo_stream_cli_get_ofd(cli);
if (cbc->client.sock_name)
talloc_free(cbc->client.sock_name);
- cbc->client.sock_name = osmo_sock_get_name(cbc, ofd->fd);
+ cbc->client.sock_name = osmo_sock_get_name(cbc, osmo_stream_cli_get_fd(cli));
LOGP(DCBS, LOGL_NOTICE, "CBSP Client connected to CBC: %s\n", cbc->client.sock_name);
@@ -153,28 +165,27 @@ static int cbsp_client_disconnect_cb(struct osmo_stream_cli *cli)
return 0;
}
-static int cbsp_client_read_cb(struct osmo_stream_cli *cli)
+static int cbsp_client_read_cb(struct osmo_stream_cli *cli, int res, struct msgb *msg)
{
struct bsc_cbc_link *cbc = osmo_stream_cli_get_data(cli);
- struct osmo_fd *ofd = osmo_stream_cli_get_ofd(cli);
struct osmo_cbsp_decoded *decoded;
- struct msgb *msg = NULL;
- int rc;
-
- /* READ */
- rc = osmo_cbsp_recv_buffered(cbc, ofd->fd, &msg, &cbc->client.msg);
- if (rc <= 0) {
- if (rc == -EAGAIN || rc == -EINTR) {
- /* more data needs to be read */
+
+ if (res <= 0) {
+ if (res == -EAGAIN || res == -EINTR) {
+ msgb_free(msg);
return 0;
- } else if (rc == -EPIPE || rc == -ECONNRESET) {
- /* lost connection */
- } else if (rc == 0) {
- /* connection closed */
}
+ /*
+ if (rc == -EPIPE || rc == -ECONNRESET) {
+ // lost connection
+ } else if (rc == 0) {
+ // connection closed
+ } */
+ msgb_free(msg);
osmo_stream_cli_reconnect(cli);
return -EBADF;
}
+
OSMO_ASSERT(msg);
decoded = osmo_cbsp_decode(cli, msg);
if (decoded) {
@@ -195,7 +206,7 @@ int bsc_cbc_link_restart(void)
struct bsc_cbc_link *cbc = bsc_gsmnet->cbc;
/* shut down client, if no longer configured */
- if (cbc->client.cli && !cbc->config.cbc_hostname) {
+ if (cbc->client.cli && cbc->mode != BSC_CBC_LINK_MODE_CLIENT) {
LOGP(DCBS, LOGL_NOTICE, "Stopping CBSP client\n");
osmo_stream_cli_close(cbc->client.cli);
osmo_stream_cli_destroy(cbc->client.cli);
@@ -203,7 +214,7 @@ int bsc_cbc_link_restart(void)
}
/* shut down server, if no longer configured */
- if (cbc->config.listen_port == -1) {
+ if (cbc->mode != BSC_CBC_LINK_MODE_SERVER) {
if (cbc->server.srv || cbc->server.link)
LOGP(DCBS, LOGL_NOTICE, "Stopping CBSP server\n");
if (cbc->server.srv) {
@@ -217,37 +228,68 @@ int bsc_cbc_link_restart(void)
}
}
- /* start client, if configured */
- if (cbc->config.cbc_hostname) {
- LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Client (to CBC at %s:%u)\n",
- cbc->config.cbc_hostname, cbc->config.cbc_port);
+ switch (cbc->mode) {
+ case BSC_CBC_LINK_MODE_CLIENT:
+ if (!osmo_sockaddr_str_is_nonzero(&cbc->client.remote_addr)) {
+ LOGP(DCBS, LOGL_ERROR,
+ "Cannot start CBSP in client mode: invalid remote-ip or -port in 'cbc' / 'client')\n");
+ return -1;
+ }
+
+ LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Client (to CBC at " OSMO_SOCKADDR_STR_FMT ")\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->client.remote_addr));
if (!cbc->client.cli) {
cbc->client.cli = osmo_stream_cli_create(cbc);
osmo_stream_cli_set_data(cbc->client.cli, cbc);
osmo_stream_cli_set_connect_cb(cbc->client.cli, cbsp_client_connect_cb);
osmo_stream_cli_set_disconnect_cb(cbc->client.cli, cbsp_client_disconnect_cb);
- osmo_stream_cli_set_read_cb(cbc->client.cli, cbsp_client_read_cb);
+ osmo_stream_cli_set_read_cb2(cbc->client.cli, cbsp_client_read_cb);
+ osmo_stream_cli_set_segmentation_cb(cbc->client.cli, osmo_cbsp_segmentation_cb);
}
/* CBC side */
- osmo_stream_cli_set_addr(cbc->client.cli, cbc->config.cbc_hostname);
- osmo_stream_cli_set_port(cbc->client.cli, cbc->config.cbc_port);
+ osmo_stream_cli_set_addr(cbc->client.cli, cbc->client.remote_addr.ip);
+ osmo_stream_cli_set_port(cbc->client.cli, cbc->client.remote_addr.port);
+ /* local side */
+ if (osmo_sockaddr_str_is_set(&cbc->client.local_addr)) {
+ osmo_stream_cli_set_local_addr(cbc->client.cli, cbc->client.local_addr.ip);
+ osmo_stream_cli_set_local_port(cbc->client.cli, cbc->client.local_addr.port);
+ }
/* Close/Reconnect? */
- osmo_stream_cli_open(cbc->client.cli);
- }
+ if (osmo_stream_cli_open(cbc->client.cli) < 0) {
+ LOGP(DCBS, LOGL_ERROR, "Cannot open CBSP client link to " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->client.remote_addr));
+ return -1;
+ }
+ return 0;
- /* start server, if configured */
- if (cbc->config.listen_port != -1) {
- LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Server (bound to %s:%u)\n",
- cbc->config.listen_hostname, cbc->config.listen_port);
- if (!cbc->server.srv) {
+ case BSC_CBC_LINK_MODE_SERVER:
+ if (!osmo_sockaddr_str_is_set(&cbc->server.local_addr)) {
+ LOGP(DCBS, LOGL_ERROR,
+ "Cannot start CBSP in server mode: invalid local-ip or -port in 'cbc' / 'server')\n");
+ return -1;
+ }
+ LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Server (listening at " OSMO_SOCKADDR_STR_FMT ")\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->server.local_addr));
+ if (!cbc->server.link) {
+ LOGP(DCBS, LOGL_NOTICE, "Creating CBSP Server\n");
cbc->server.link = osmo_stream_srv_link_create(cbc);
osmo_stream_srv_link_set_data(cbc->server.link, cbc);
osmo_stream_srv_link_set_accept_cb(cbc->server.link, cbsp_srv_link_accept_cb);
+
+ osmo_stream_srv_link_set_addr(cbc->server.link, cbc->server.local_addr.ip);
+ osmo_stream_srv_link_set_port(cbc->server.link, cbc->server.local_addr.port);
+
+ if (osmo_stream_srv_link_open(cbc->server.link) < 0) {
+ LOGP(DCBS, LOGL_ERROR, "Cannot open CBSP Server link at " OSMO_SOCKADDR_STR_FMT ")\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(&cbc->server.local_addr));
+ return -1;
+ }
}
- osmo_stream_srv_link_set_addr(cbc->server.link, cbc->config.listen_hostname);
- osmo_stream_srv_link_set_port(cbc->server.link, cbc->config.listen_port);
+ return 0;
+
+ default:
+ return 0;
}
- return 0;
}
/*! Encode + Transmit a 'decoded' CBSP message over given CBC link
@@ -258,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",
@@ -269,149 +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_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);
- osmo_talloc_replace_string(cbc, &cbc->config.cbc_hostname, argv[0]);
- return CMD_SUCCESS;
-}
-DEFUN(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);
- talloc_free(cbc->config.cbc_hostname);
- cbc->config.cbc_hostname = NULL;
- return CMD_SUCCESS;
-}
-
-DEFUN(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);
- cbc->config.cbc_port = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(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);
- cbc->config.listen_port = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-DEFUN(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);
- cbc->config.listen_port = -1;
- return CMD_SUCCESS;
-}
-
-DEFUN(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: 0.0.0.0)\n"
- "Local IP Address where BSC listens for incoming CBC connections\n")
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
- osmo_talloc_replace_string(cbc, &cbc->config.listen_hostname, argv[0]);
- return CMD_SUCCESS;
-}
-
-static struct cmd_node cbc_node = {
- CBC_NODE,
- "%s(config-cbc)# ",
- 1,
-};
-
-static int config_write_cbc(struct vty *vty)
-{
- struct bsc_cbc_link *cbc = vty_cbc_data(vty);
-
- vty_out(vty, "cbc%s", VTY_NEWLINE);
-
- if (cbc->config.cbc_hostname) {
- vty_out(vty, " remote-ip %s%s", cbc->config.cbc_hostname, VTY_NEWLINE);
- vty_out(vty, " remote-port %u%s", cbc->config.cbc_port, VTY_NEWLINE);
- } else
- vty_out(vty, " no remote-ip%s", VTY_NEWLINE);
-
- if (cbc->config.listen_port >= 0) {
- vty_out(vty, " listen-port %u%s", cbc->config.listen_port, VTY_NEWLINE);
- vty_out(vty, " listen-ip %s%s", cbc->config.listen_hostname, VTY_NEWLINE);
- } else
- vty_out(vty, " no listen-port%s", 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);
-
- if (!cbc->config.cbc_hostname)
- vty_out(vty, "CBSP Client Config: Disabled%s", VTY_NEWLINE);
- else {
- vty_out(vty, "CBSP Client Config: CBC IP=%s, CBC Port=%u%s",
- cbc->config.cbc_hostname, cbc->config.cbc_port, VTY_NEWLINE);
- vty_out(vty, "CBSP Client Connection: %s%s",
- cbc->client.sock_name ? cbc->client.sock_name : "Disconnected", VTY_NEWLINE);
- }
- if (cbc->config.listen_port < 0)
- vty_out(vty, "CBSP Server Config: Disabled%s\n", VTY_NEWLINE);
- else {
- vty_out(vty, "CBSP Server Config: Listen IP=%s, Port=%u%s\n",
- cbc->config.listen_hostname, cbc->config.listen_port, VTY_NEWLINE);
- vty_out(vty, "CBSP Server Connection: %s%s",
- cbc->server.sock_name ? cbc->server.sock_name : "Disconnected", VTY_NEWLINE);
- }
- return CMD_SUCCESS;
-}
-
-void cbc_vty_init(void)
-{
- install_element(VIEW_NODE, &show_cbc_cmd);
- install_element(CONFIG_NODE, &cfg_cbc_cmd);
- install_node(&cbc_node, config_write_cbc);
- 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 f23a982a1..cc8da3daf 100644
--- a/src/osmo-bsc/chan_alloc.c
+++ b/src/osmo-bsc/chan_alloc.c
@@ -34,6 +34,7 @@
#include <osmocom/bsc/timeslot_fsm.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/core/talloc.h>
@@ -43,9 +44,13 @@ 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;
- /* skip administratively deactivated tranxsceivers */
+ /* init per-TRX load counters */
+ memset(ll, 0, sizeof(*ll));
+
+ /* skip administratively deactivated transceivers */
if (!trx_is_usable(trx))
continue;
@@ -58,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, because 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;
}
@@ -98,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;
}
@@ -128,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);
@@ -203,9 +203,11 @@ bts_update_t3122_chan_load(struct gsm_bts *bts)
}
/* Check for invalid samples (shouldn't happen). */
- if (total == 0 || used > total) {
+ if (used > total) {
LOG_BTS(bts, DRLL, LOGL_NOTICE, "bogus channel load sample (used=%"PRIu64" / total=%"PRIu32")\n",
used, total);
+ }
+ if (total == 0 || used > total) {
bts->T3122 = 0; /* disable override of network-wide default value */
bts->chan_load_samples_idx = 0; /* invalidate other samples collected so far */
return;
@@ -237,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);
@@ -249,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 be442b542..454b00bea 100644
--- a/src/osmo-bsc/codec_pref.c
+++ b/src/osmo-bsc/codec_pref.c
@@ -27,6 +27,7 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
/* Determine whether a permitted speech value is specifies a half rate or full
* rate codec */
@@ -60,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) {
@@ -87,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) {
@@ -124,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,
@@ -176,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 */
@@ -216,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;
}
@@ -270,7 +270,7 @@ static uint16_t gen_bss_supported_amr_s15_s0(const struct bsc_msc_data *msc, con
static int match_amr_s15_s0(struct channel_mode_and_rate *ch_mode_rate, const struct bsc_msc_data *msc, const struct gsm_bts *bts, const struct gsm0808_speech_codec *sc_match, uint8_t perm_spch)
{
uint16_t amr_s15_s0_supported;
-
+
/* Normally the MSC should never try to advertise an AMR codec
* configuration that we did not previously advertised as supported.
* However, to ensure that no unsupported AMR codec configuration
@@ -278,7 +278,7 @@ static int match_amr_s15_s0(struct channel_mode_and_rate *ch_mode_rate, const st
* and generate an intersection. All further processing is then done
* with this intersection result. At the same time we will make sure
* that the intersection contains at least one rate setting. */
-
+
amr_s15_s0_supported = gen_bss_supported_amr_s15_s0(msc, bts, (perm_spch == GSM0808_PERM_HR3));
/* NOTE: The sc_match pointer points to a speech codec from the speech
@@ -307,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).
@@ -335,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);
@@ -375,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;
@@ -406,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 */
@@ -421,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++;
}
@@ -481,22 +481,28 @@ int check_codec_pref(struct llist_head *mscs)
rc = -1;
}
- bts_gsm48_ie = (struct gsm48_multi_rate_conf *)&bts->mr_full.gsm48_ie;
- rc_rate = calc_amr_rate_intersection(NULL, &msc->amr_conf, bts_gsm48_ie);
- if (rc_rate < 0) {
- LOGP(DMSC, LOGL_FATAL,
- "network amr tch-f mode config of BTS %u does not intersect with amr-config of MSC %u\n",
- bts->nr, msc->nr);
- rc = -1;
+ /* Full rate codec check, only if any full rate TS is configured. */
+ if (test_codec_support_bts_rate(bts, true)) {
+ bts_gsm48_ie = (struct gsm48_multi_rate_conf *)&bts->mr_full.gsm48_ie;
+ rc_rate = calc_amr_rate_intersection(NULL, &msc->amr_conf, bts_gsm48_ie);
+ if (rc_rate < 0) {
+ LOGP(DMSC, LOGL_FATAL,
+ "network amr tch-f mode config of BTS %u does not intersect with amr-config of MSC %u\n",
+ bts->nr, msc->nr);
+ rc = -1;
+ }
}
- bts_gsm48_ie = (struct gsm48_multi_rate_conf *)&bts->mr_half.gsm48_ie;
- rc_rate = calc_amr_rate_intersection(NULL, &msc->amr_conf, bts_gsm48_ie);
- if (rc_rate < 0) {
- LOGP(DMSC, LOGL_FATAL,
- "network amr tch-h mode config of BTS %u does not intersect with amr-config of MSC %u\n",
- bts->nr, msc->nr);
- rc = -1;
+ /* Half rate codec check, only if any half rate TS is configured. */
+ if (test_codec_support_bts_rate(bts, false)) {
+ bts_gsm48_ie = (struct gsm48_multi_rate_conf *)&bts->mr_half.gsm48_ie;
+ rc_rate = calc_amr_rate_intersection(NULL, &msc->amr_conf, bts_gsm48_ie);
+ if (rc_rate < 0) {
+ LOGP(DMSC, LOGL_FATAL,
+ "network amr tch-h mode config of BTS %u does not intersect with amr-config of MSC %u\n",
+ bts->nr, msc->nr);
+ rc = -1;
+ }
}
}
}
diff --git a/src/osmo-bsc/data_rate_pref.c b/src/osmo-bsc/data_rate_pref.c
new file mode 100644
index 000000000..44a733fb2
--- /dev/null
+++ b/src/osmo-bsc/data_rate_pref.c
@@ -0,0 +1,165 @@
+/*
+ * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Oliver Smith
+ *
+ * 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/protocol/gsm_08_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/bsc/data_rate_pref.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/lchan.h>
+
+static int gsm0808_data_rate_transp_to_gsm0858(enum gsm0808_data_rate_transp rate)
+{
+ switch (rate) {
+ case GSM0808_DATA_RATE_TRANSP_32k0:
+ return RSL_CMOD_CSD_T_32k0;
+ case GSM0808_DATA_RATE_TRANSP_28k8:
+ return RSL_CMOD_CSD_T_29k0;
+ case GSM0808_DATA_RATE_TRANSP_14k4:
+ return RSL_CMOD_CSD_T_14k4;
+ case GSM0808_DATA_RATE_TRANSP_9k6:
+ return RSL_CMOD_CSD_T_9k6;
+ case GSM0808_DATA_RATE_TRANSP_4k8:
+ return RSL_CMOD_CSD_T_4k8;
+ case GSM0808_DATA_RATE_TRANSP_2k4:
+ return RSL_CMOD_CSD_T_2k4;
+ case GSM0808_DATA_RATE_TRANSP_1k2:
+ return RSL_CMOD_CSD_T_1k2;
+ case GSM0808_DATA_RATE_TRANSP_600:
+ return RSL_CMOD_CSD_T_600;
+ case GSM0808_DATA_RATE_TRANSP_1200_75:
+ return RSL_CMOD_CSD_T_1200_75;
+ default:
+ LOGP(DMSC, LOGL_ERROR, "Unsupported transparent data rate 0x%x\n", rate);
+ return -1;
+ }
+}
+
+static int gsm0808_data_rate_transp_to_gsm0408(enum gsm0808_data_rate_transp rate)
+{
+ switch (rate) {
+ case GSM0808_DATA_RATE_TRANSP_14k4:
+ return GSM48_CMODE_DATA_14k5;
+ case GSM0808_DATA_RATE_TRANSP_9k6:
+ return GSM48_CMODE_DATA_12k0;
+ case GSM0808_DATA_RATE_TRANSP_4k8:
+ return GSM48_CMODE_DATA_6k0;
+ case GSM0808_DATA_RATE_TRANSP_2k4:
+ case GSM0808_DATA_RATE_TRANSP_1k2:
+ case GSM0808_DATA_RATE_TRANSP_600:
+ case GSM0808_DATA_RATE_TRANSP_1200_75:
+ return GSM48_CMODE_DATA_3k6;
+ default:
+ LOGP(DMSC, LOGL_ERROR, "Unsupported transparent data rate 0x%x\n", rate);
+ return -1;
+ }
+}
+
+static int gsm0808_data_rate_non_transp_to_gsm0408(enum gsm0808_data_rate_non_transp rate, bool full_rate)
+{
+ switch (rate) {
+ case GSM0808_DATA_RATE_NON_TRANSP_12000_6000:
+ if (full_rate)
+ return GSM48_CMODE_DATA_12k0;
+ return GSM48_CMODE_DATA_6k0;
+ case GSM0808_DATA_RATE_NON_TRANSP_14k5:
+ return GSM48_CMODE_DATA_14k5;
+ case GSM0808_DATA_RATE_NON_TRANSP_12k0:
+ return GSM48_CMODE_DATA_12k0;
+ case GSM0808_DATA_RATE_NON_TRANSP_6k0:
+ return GSM48_CMODE_DATA_6k0;
+ default:
+ LOGP(DMSC, LOGL_ERROR, "Unsupported non-transparent data rate 0x%x\n", rate);
+ return -1;
+ }
+}
+
+static int gsm0808_data_rate_non_transp_to_gsm0858(enum gsm0808_data_rate_non_transp rate, bool full_rate)
+{
+ switch (rate) {
+ case GSM0808_DATA_RATE_NON_TRANSP_12000_6000:
+ if (full_rate)
+ return RSL_CMOD_CSD_NT_12k0;
+ return RSL_CMOD_CSD_NT_6k0;
+ case GSM0808_DATA_RATE_NON_TRANSP_14k5:
+ return RSL_CMOD_CSD_NT_14k5;
+ case GSM0808_DATA_RATE_NON_TRANSP_12k0:
+ return RSL_CMOD_CSD_NT_12k0;
+ case GSM0808_DATA_RATE_NON_TRANSP_6k0:
+ return RSL_CMOD_CSD_NT_6k0;
+ case GSM0808_DATA_RATE_NON_TRANSP_43k5:
+ return RSL_CMOD_CSD_NT_43k5;
+ case GSM0808_DATA_RATE_NON_TRANSP_29k0:
+ return RSL_CMOD_CSD_NT_28k8;
+ default:
+ LOGP(DMSC, LOGL_ERROR, "Unsupported non-transparent data rate 0x%x\n", rate);
+ return -1;
+ }
+}
+
+static enum gsm48_chan_mode match_non_transp_data_rate(const struct gsm0808_channel_type *ct, bool full_rate)
+{
+ /* FIXME: Handle ct->data_rate_allowed too if it is set. Find the best
+ * match by comparing the preferred ct->data_rate + all allowed
+ * ct->data_rate_allowed against what's most suitable for the BTS. */
+
+ return gsm0808_data_rate_non_transp_to_gsm0858(ct->data_rate, full_rate);
+}
+
+/*! Match the GSM 08.08 channel type received from the MSC to suitable data for
+ * the BTS, the GSM 04.08 channel mode, channel rate (FR/HR) and GSM 08.58
+ * data rate.
+ * \param[out] ch_mode_rate resulting channel rate, channel mode and data rate
+ * \param[in] ct GSM 08.08 channel type received from MSC.
+ * \param[in] full_rate true means FR is preferred, false means HR
+ * \returns 0 on success, -1 in case no match was found */
+int match_data_rate_pref(struct channel_mode_and_rate *ch_mode_rate,
+ const struct gsm0808_channel_type *ct,
+ const bool full_rate)
+{
+ int rc;
+ *ch_mode_rate = (struct channel_mode_and_rate){};
+ ch_mode_rate->chan_rate = full_rate ? CH_RATE_FULL : CH_RATE_HALF;
+ ch_mode_rate->data_transparent = ct->data_transparent;
+
+ if (ct->data_transparent) {
+ rc = gsm0808_data_rate_transp_to_gsm0858(ct->data_rate);
+ if (rc == -1)
+ return -1;
+ ch_mode_rate->data_rate.t = rc;
+
+ rc = gsm0808_data_rate_transp_to_gsm0408(ct->data_rate);
+ if (rc == -1)
+ return -1;
+ ch_mode_rate->chan_mode = rc;
+ } else {
+ rc = match_non_transp_data_rate(ct, full_rate);
+ if (rc == -1)
+ return -1;
+ ch_mode_rate->data_rate.nt = rc;
+
+ rc = gsm0808_data_rate_non_transp_to_gsm0408(ct->data_rate, full_rate);
+ if (rc == -1)
+ return -1;
+ ch_mode_rate->chan_mode = rc;
+ }
+
+ return 0;
+}
diff --git a/src/osmo-bsc/e1_config.c b/src/osmo-bsc/e1_config.c
index 4389f66c9..dbea3e9e0 100644
--- a/src/osmo-bsc/e1_config.c
+++ b/src/osmo-bsc/e1_config.c
@@ -30,6 +30,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/bts.h>
#define SAPI_L2ML 0
#define SAPI_OML 62
@@ -89,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;
@@ -99,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]);
@@ -195,8 +196,8 @@ 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);
- bts->uptime = (rc < 0) ? 0 : tp.tv_sec; /* we don't need sub-second precision for uptime */
+ rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
+ bts->updowntime = (rc < 0) ? 0 : tp.tv_sec; /* we don't need sub-second precision for uptime */
llist_for_each_entry(trx, &bts->trx_list, list)
e1_reconfig_trx(trx);
diff --git a/src/osmo-bsc/gsm_04_08_rr.c b/src/osmo-bsc/gsm_04_08_rr.c
index f47c31f41..194432125 100644
--- a/src/osmo-bsc/gsm_04_08_rr.c
+++ b/src/osmo-bsc/gsm_04_08_rr.c
@@ -43,18 +43,29 @@
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/bsc/gsm_data.h>
+#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);
}
+int gsm48_sendmsg_unit(struct msgb *msg)
+{
+ if (msg->lchan)
+ msg->dst = rsl_chan_link(msg->lchan);
+
+ msg->l3h = msg->data;
+ return rsl_unit_data_request(msg, 0);
+}
+
/* Section 9.1.8 / Table 9.9 */
struct chreq {
uint8_t val;
@@ -230,19 +241,19 @@ 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_MAX_BITS (4+MAX_EARFCN_LIST*19)
-#define CELL_SEL_IND_AFTER_REL_MAX_BYTES ((CELL_SEL_IND_AFTER_REL_MAX_BITS/8)+1)
+#define CELL_SEL_IND_AFTER_REL_EARCFN_ENTRY (1+16+4+1+1)
+#define CELL_SEL_IND_AFTER_REL_MAX_BITS (3+MAX_EARFCN_LIST*CELL_SEL_IND_AFTER_REL_EARCFN_ENTRY+1)
+#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)
{
@@ -255,20 +266,27 @@ static int generate_cell_sel_ind_after_rel(uint8_t *out, unsigned int out_len, c
/* E-UTRAN Description */
bitvec_set_uint(&bv, 3, 3);
- bitvec_set_bit(&bv, 1);
for (i = 0; i < MAX_EARFCN_LIST; i++) {
const struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
if (e->arfcn[i] == OSMO_EARFCN_INVALID)
continue;
- if (bitvec_tailroom_bits(&bv) < 19) {
+ /* tailroom must fit one more EARFCN plus the final list term bit. */
+ if (bitvec_tailroom_bits(&bv) < CELL_SEL_IND_AFTER_REL_EARCFN_ENTRY + 1) {
LOGP(DRR, LOGL_NOTICE, "%s: Not enough room to store EARFCN %u in the "
"Cell Selection Indicator IE\n", gsm_bts_name(bts), e->arfcn[i]);
} else {
+ bitvec_set_bit(&bv, 1);
bitvec_set_uint(&bv, e->arfcn[i], 16);
- /* No "Measurement Bandwidth" */
- bitvec_set_bit(&bv, 0);
+
+ /* Measurement Bandwidth: 9.1.54 */
+ if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i])
+ bitvec_set_bit(&bv, 0);
+ else {
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, e->meas_bw[i], 3);
+ }
/* No "Not Allowed Cells" */
bitvec_set_bit(&bv, 0);
/* No "TARGET_PCID" */
@@ -276,6 +294,9 @@ static int generate_cell_sel_ind_after_rel(uint8_t *out, unsigned int out_len, c
}
}
+ /* list term */
+ bitvec_set_bit(&bv, 0);
+
rc = bitvec_used_bytes(&bv);
if (rc == 1) {
@@ -287,24 +308,27 @@ static int generate_cell_sel_ind_after_rel(uint8_t *out, unsigned int out_len, c
}
}
+#define REPEAT_RR_RELEASE_UI 3
+
/* 7.1.7 and 9.1.7: RR CHANnel RELease */
-int gsm48_send_rr_release(struct gsm_lchan *lchan)
+int gsm48_send_rr_release(struct gsm_lchan *lchan, bool ui)
{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 RR REL");
+ struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 RR REL"), *msgc;
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
uint8_t *cause;
+ int n;
msg->lchan = lchan;
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_CHAN_REL;
cause = msgb_put(msg, 1);
- cause[0] = GSM48_RR_CAUSE_NORMAL;
+ 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 "
@@ -313,11 +337,21 @@ 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\n",
- lchan->nr, lchan->type);
+ 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);
+ /* Send actual release request to MS (dedicated channel) */
+ if (!ui)
+ return gsm48_sendmsg(msg);
+
+ /* Send actual release request to MS (VGCS channel) */
+ for (n = 1; n < REPEAT_RR_RELEASE_UI; n++) {
+ msgc = msgb_copy(msg, "CHAN RELEASE copy");
+ msgc->lchan = lchan;
+ gsm48_sendmsg_unit(msgc);
+ }
+ return gsm48_sendmsg_unit(msg);
}
int send_siemens_mrpci(struct gsm_lchan *lchan,
@@ -346,7 +380,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);
}
@@ -356,16 +391,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;
@@ -385,12 +418,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)
{
@@ -401,15 +434,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;
@@ -470,99 +505,148 @@ 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_HO << 4) | (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;
- uint8_t *cur;
si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1);
/* Copy the Cell Chan Desc (ARFCNS in this cell) */
- msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC);
- cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN);
- memcpy(cur, si1->cell_channel_description,
- GSM48_HOCMD_CCHDESC_LEN);
- /* Copy the Mobile Allocation */
- msgb_tlv_put(msg, GSM48_IE_MA_BEFORE,
+ msgb_tv_fixed_put(msg, GSM48_IE_CELL_CH_DESC,
+ GSM48_HOCMD_CCHDESC_LEN,
+ si1->cell_channel_description);
+ }
+
+ 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) {
+ msgb_tlv_put(msg, GSM48_IE_MA_AFTER,
new_lchan->ts->hopping.ma_len,
new_lchan->ts->hopping.ma_data);
}
- /* FIXME: optional bits for type of synchronization? */
- msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->tch_mode);
+ /* (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_HO << 4) | (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;
@@ -574,26 +658,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);
}
@@ -623,32 +726,109 @@ 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.type_for == LCHAN_TYPE_FOR_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);
}
+/* TS 44.018 section 9.1.48 */
+int gsm48_send_uplink_release(struct gsm_lchan *lchan, uint8_t cause)
+{
+ struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 UL RELEASE");
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_UPLINK_RELEASE;
+
+ msgb_put_u8(msg, cause);
+
+ return gsm48_sendmsg(msg);
+}
+
+/* TS 44.018 section 9.1.46 */
+int gsm48_send_uplink_busy(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 UL BUSY");
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_UPLINK_BUSY;
+
+ return gsm48_sendmsg_unit(msg);
+}
+
+/* TS 44.018 section 9.1.47 */
+int gsm48_send_uplink_free(struct gsm_lchan *lchan, uint8_t acc_bit, uint8_t *uic)
+{
+ struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 UL FREE");
+ struct gsm48_hdr_sh *sh = (struct gsm48_hdr_sh *) msgb_put(msg, sizeof(*sh) + 22);
+ struct bitvec *bv = bitvec_alloc(22, NULL);
+
+ msg->lchan = lchan;
+ sh->rr_short_pd = GSM48_PDISC_SH_RR;
+ sh->msg_type = GSM48_MT_RR_SH_UL_FREE;
+ sh->l2_header = 0;
+
+ /* < Uplink Access Request bit > */
+ bitvec_set_bit(bv, (acc_bit) ? H : L);
+
+ /* { L | H <Uplink Identity Code bit(6) > } */
+ if (uic) {
+ bitvec_set_bit(bv, H);
+ sh->data[0] = 0x40 | *uic;
+ } else
+ bitvec_set_bit(bv, L);
+
+ /* Emergency mode not supported */
+
+ /* Padding the rest with 0x2B and apply to msg. */
+ bitvec_spare_padding(bv, 175);
+ bitvec_pack(bv, sh->data);
+ bitvec_free(bv);
+
+ return gsm48_sendmsg_unit(msg);
+}
+
int gsm48_rx_rr_modif_ack(struct msgb *msg)
{
- int rc;
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_chan_mode_modify *mod =
(struct gsm48_chan_mode_modify *) gh->data;
@@ -656,41 +836,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. */
+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++;
}
- /* We've successfully modified the MS side of the channel,
- * now go on to modify the BTS side of the channel */
- rc = rsl_chan_mode_modify_req(msg->lchan);
+ /* 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;
- /* FIXME: we not only need to do this after mode modify, but
- * also after channel activation */
- if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN)
- rsl_tx_ipacc_crcx(msg->lchan);
- return rc;
+ return -EINVAL;
}
int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
@@ -698,8 +901,9 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
struct gsm48_hdr *gh = msgb_l3(msg);
uint8_t *data = gh->data;
struct gsm_bts *bts = msg->lchan->ts->trx->bts;
- struct bitvec *nbv = &bts->si_common.neigh_list;
+ struct bitvec *nbv;
struct gsm_meas_rep_cell *mrc;
+ int rc;
if (gh->msg_type != GSM48_MT_RR_MEAS_REP)
return -EINVAL;
@@ -723,11 +927,21 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
return 0;
}
+ /* If the phone reports BA-IND 1 this is a report for the SI5* set.
+ * If we have generated SI5* with manual SI5 neighbor list, the measurements refer to it. */
+ if ((rep->flags & MEAS_REP_F_BA1) && bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP)
+ nbv = &bts->si_common.si5_neigh_list;
+ else
+ nbv = &bts->si_common.neigh_list;
+
/* an encoding nightmare in perfection */
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);
+ rc = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
+ if (rc < 0)
+ goto error;
+ mrc->arfcn = rc;
mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5);
if (rep->num_cell < 2)
return 0;
@@ -735,7 +949,10 @@ 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);
+ rc = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
+ if (rc < 0)
+ goto error;
+ mrc->arfcn = rc;
mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4);
if (rep->num_cell < 3)
return 0;
@@ -743,7 +960,10 @@ 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);
+ rc = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
+ if (rc < 0)
+ goto error;
+ mrc->arfcn = rc;
mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3);
if (rep->num_cell < 4)
return 0;
@@ -751,7 +971,10 @@ 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);
+ rc = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
+ if (rc < 0)
+ goto error;
+ mrc->arfcn = rc;
mrc->bsic = data[11] >> 2;
if (rep->num_cell < 5)
return 0;
@@ -759,7 +982,10 @@ 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);
+ rc = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
+ if (rc < 0)
+ goto error;
+ mrc->arfcn = rc;
mrc->bsic = (data[13] >> 1) & 0x3f;
if (rep->num_cell < 6)
return 0;
@@ -767,10 +993,18 @@ 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);
+ rc = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
+ if (rc < 0)
+ goto error;
+ mrc->arfcn = rc;
mrc->bsic = data[15] & 0x3f;
return 0;
+
+error:
+ LOGP(DRR, LOGL_ERROR, "Invalid BCCH channel list index %d in measurement report\n", mrc->neigh_idx);
+ rep->num_cell = 0;
+ return 0;
}
/* 9.1.29 RR Status */
@@ -942,7 +1176,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)
@@ -963,18 +1197,27 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
case GSM48_MT_RR_CHAN_MODE_MODIF_ACK:
rc = gsm48_rx_rr_modif_ack(msg);
if (rc < 0)
- osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_CHAN_MODE_MODIF_ERROR, &rc);
+ osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_RR_CHAN_MODE_MODIFY_ERROR, &rc);
else
- osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_CHAN_MODE_MODIF_ACK, msg);
+ osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_RR_CHAN_MODE_MODIFY_ACK, msg);
break;
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 */
bsc_dtap(conn, link_id, msg);
break;
+ case GSM48_MT_RR_UPLINK_RELEASE:
+ /* When the calling phone releases the uplink before it has been assigned to the group
+ * channel, it will send an UPLINK RELEASE message on the dedicated channel. The MSC
+ * has to take care of it. (assigning the phone to the group channel) */
+ bsc_dtap(conn, link_id, msg);
+ break;
default:
/* Drop unknown RR message */
LOG_LCHAN(msg->lchan, LOGL_NOTICE, "Unknown RR message: %s\n",
@@ -983,6 +1226,27 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
break;
}
break;
+ case GSM48_PDISC_CC:
+ /* Make sure that EMERGENCY CALLS can not be made if the
+ * 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_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_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;
+ }
+ }
+ /* fall through */
default:
bsc_dtap(conn, link_id, msg);
break;
@@ -993,7 +1257,6 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
{
struct gsm_lchan *lchan;
- int rc;
lchan = msg->lchan;
if (!lchan_may_receive_data(lchan)) {
@@ -1006,21 +1269,7 @@ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
* MSC */
dispatch_dtap(lchan->conn, link_id, msg);
} else {
- /* allocate a new connection */
- lchan->conn = bsc_subscr_con_allocate(msg->lchan->ts->trx->bts->network);
- if (!lchan->conn) {
- lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);
- return -1;
- }
- lchan->conn->lchan = lchan;
-
- /* fwd via bsc_api to send COMPLETE L3 INFO to MSC */
- rc = bsc_compl_l3(lchan->conn, msg, 0);
- if (rc < 0) {
- osmo_fsm_inst_dispatch(lchan->conn->fi, GSCON_EV_A_DISC_IND, NULL);
- return rc;
- }
- /* conn shall release lchan on teardown, also if this Layer 3 Complete is rejected. */
+ return bsc_compl_l3(lchan, msg, 0);
}
return 0;
diff --git a/src/osmo-bsc/gsm_08_08.c b/src/osmo-bsc/gsm_08_08.c
index 2829b67c7..692e35cea 100644
--- a/src/osmo-bsc/gsm_08_08.c
+++ b/src/osmo-bsc/gsm_08_08.c
@@ -25,17 +25,24 @@
#include <osmocom/bsc/paging.h>
#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>
+#include <osmocom/bsc/lcs_ta_req.h>
+#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>
#include <osmocom/gsm/gsm23236.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
+#include <osmocom/bsc/bts.h>
#define LOG_COMPL_L3(pdisc, mtype, loglevel, format, args...) \
LOGP(DRSL, loglevel, "%s %s: " format, gsm48_pdisc_name(pdisc), gsm48_pdisc_msgtype_name(pdisc, mtype), ##args)
@@ -59,24 +66,28 @@ static bool msc_connected(struct gsm_subscriber_connection *conn)
}
/*! BTS->MSC: tell MSC a SAPI was not established. */
-void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
+void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn,
+ uint8_t dlci, enum gsm0808_cause cause)
{
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\n", dlci);
- resp = gsm0808_create_sapi_reject(dlci);
- rate_ctr_inc(&conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_SAPI_N_REJECT]);
+ 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(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;
@@ -85,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;
@@ -180,76 +174,28 @@ static bool is_msc_usable(struct bsc_msc_data *msc, bool is_emerg)
* conn.
* c) All other cases distribute the messages across connected MSCs in a round-robin fashion.
*/
-static struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
+static struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, const struct osmo_mobile_identity *mi,
+ bool is_emerg, bool from_other_plmn)
{
struct gsm_network *net = conn->network;
- struct gsm48_hdr *gh;
- int8_t pdisc;
- uint8_t mtype;
- struct osmo_mobile_identity mi;
struct bsc_msc_data *msc;
struct bsc_msc_data *msc_target = NULL;
struct bsc_msc_data *msc_round_robin_next = NULL;
struct bsc_msc_data *msc_round_robin_first = NULL;
- uint8_t round_robin_next_nr;
- struct bsc_subscr *subscr;
- bool is_emerg = false;
+ unsigned int round_robin_next_nr;
int16_t nri_v = -1;
bool is_null_nri = false;
- if (msgb_l3len(msg) < sizeof(*gh)) {
- LOGP(DRSL, LOGL_ERROR, "There is no GSM48 header here.\n");
- return NULL;
- }
-
- gh = msgb_l3(msg);
- pdisc = gsm48_hdr_pdisc(gh);
- mtype = gsm48_hdr_msg_type(gh);
-
- is_emerg = (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) && is_cm_service_for_emerg(msg);
-
- 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));
- /* There is no Mobile Identity to pick a matching MSC from. 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);" */
- }
-
- /* Has the subscriber been paged from a connected MSC? */
- if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP) {
- subscr = bsc_subscr_find_by_mi(conn->network->bsc_subscribers, &mi);
- struct gsm_bts *bts = conn_get_bts(conn);
- if (bts && subscr) {
- msc_target = paging_get_msc(bts, subscr);
- bsc_subscr_put(subscr);
- if (is_msc_usable(msc_target, 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), msc_target->nr);
- rate_ctr_inc(&msc_target->msc_ctrs->ctr[MSC_CTR_MSCPOOL_SUBSCR_PAGED]);
- return msc_target;
- } else {
- LOG_COMPL_L3(pdisc, mtype, LOGL_DEBUG,
- "%s matches earlier Paging from msc %d, but this MSC is not connected\n",
- osmo_mobile_identity_to_str_c(OTC_SELECT, &mi), msc_target->nr);
- }
- msc_target = NULL;
- }
- }
-
#define LOG_NRI(LOGLEVEL, FORMAT, ARGS...) \
- LOGP(DMSC, LOGLEVEL, "%s NRI(%d)=0x%x=%d: " FORMAT, osmo_mobile_identity_to_str_c(OTC_SELECT, &mi), \
+ LOGP(DMSC, LOGLEVEL, "%s NRI(%d)=0x%x=%d: " FORMAT, osmo_mobile_identity_to_str_c(OTC_SELECT, mi), \
net->nri_bitlen, nri_v, nri_v, ##ARGS)
/* Extract NRI bits from TMSI, possibly indicating which MSC is responsible */
- if (mi.type == GSM_MI_TYPE_TMSI) {
- if (osmo_tmsi_nri_v_get(&nri_v, mi.tmsi, net->nri_bitlen)) {
+ if (mi->type == GSM_MI_TYPE_TMSI) {
+ if (osmo_tmsi_nri_v_get(&nri_v, mi->tmsi, net->nri_bitlen)) {
LOGP(DMSC, LOGL_ERROR, "Unable to retrieve NRI from TMSI, nri_bitlen == %u\n", net->nri_bitlen);
nri_v = -1;
- } else if (is_lu_from_other_plmn(msg)) {
+ } else if (from_other_plmn) {
/* If a subscriber was previously attached to a different PLMN, it might still send the other
* PLMN's TMSI identity in an IMSI Attach. The LU sends a LAI indicating the previous PLMN. If
* it mismatches our PLMN, ignore the NRI. */
@@ -273,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;
}
@@ -285,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;
}
@@ -323,25 +269,23 @@ 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) {
- LOG_COMPL_L3(pdisc, mtype, LOGL_ERROR, "%s%s: No suitable MSC for this Complete Layer 3 request found\n",
- osmo_mobile_identity_to_str_c(OTC_SELECT, &mi), is_emerg ? " FOR EMERGENCY CALL" : "");
- 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;
}
LOGP(DMSC, LOGL_DEBUG, "New subscriber %s: MSC round-robin selects msc %d\n",
- osmo_mobile_identity_to_str_c(OTC_SELECT, &mi), msc_target->nr);
+ 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 */
@@ -353,189 +297,263 @@ static struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
#undef LOG_NRI
}
-static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct osmo_mobile_identity mi;
- struct bsc_subscr *subscr = NULL;
- struct gsm_bts *bts = conn_get_bts(conn);
-
- if (!bts) {
- /* should never happen */
- LOGP(DRSL, LOGL_ERROR, "Paging Response without lchan\n");
- return -1;
- }
-
- if (osmo_mobile_identity_decode_from_l3(&mi, msg, false)) {
- LOGP(DRSL, LOGL_ERROR, "Unable to extract Mobile Identity from Paging Response\n");
- return -1;
- }
-
- subscr = bsc_subscr_find_by_mi(conn->network->bsc_subscribers, &mi);
- if (!subscr) {
- LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n");
- rate_ctr_inc(&conn->lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_PAGING_NO_ACTIVE_PAGING]);
- rate_ctr_inc(&conn->network->bsc_ctrs->ctr[BSC_CTR_PAGING_NO_ACTIVE_PAGING]);
- return -1;
- }
-
- paging_request_stop(&conn->network->bts_list, bts, subscr, conn, msg);
- bsc_subscr_put(subscr);
- return 0;
-}
-
-/* TS 04.08 sec 9.2.15 "Location updating request" */
-static void handle_lu_request(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
+static void parse_powercap(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
- struct gsm48_hdr *gh;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t pdisc = gsm48_hdr_pdisc(gh);
+ uint8_t mtype = gsm48_hdr_msg_type(gh);
struct gsm48_loc_upd_req *lu;
- int8_t rc8;
- struct gsm_bts *bts = conn_get_bts(conn);
-
- if (!bts) {
- /* should never happen */
- LOGP(DRSL, LOGL_ERROR, "LU Request without lchan\n");
- return;
- }
-
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) {
- LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg));
- return;
- }
-
- gh = msgb_l3(msg);
- lu = (struct gsm48_loc_upd_req *) gh->data;
-
- rc8 = osmo_gsm48_rfpowercap2powerclass(bts->band, lu->classmark1.pwr_lev);
- if (rc8 < 0) {
- LOGP(DMSC, LOGL_NOTICE,
- "Unable to decode RF power capability %x from classmark1 during LU.\n",
- lu->classmark1.pwr_lev);
- rc8 = 0;
- }
- conn_update_ms_power_class(conn, rc8);
-}
-
-
-/* TS 04.08 sec 9.2.15 "Location updating request" */
-static void handle_cm_serv_req(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct gsm48_hdr *gh;
struct gsm48_service_request *serv_req;
- struct gsm48_classmark2* cm2;
+ uint8_t pwr_lev;
+ struct gsm_bts *bts;
int8_t rc8;
- struct gsm_bts *bts = conn_get_bts(conn);
- if (!bts) {
- /* should never happen */
- LOGP(DRSL, LOGL_ERROR, "CM Service Request without lchan\n");
- return;
- }
+ switch (pdisc) {
+ case GSM48_PDISC_MM:
+ switch (mtype) {
+ case GSM48_MT_MM_LOC_UPD_REQUEST:
+ if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "rx Location Updating message too short: %u\n", msgb_l3len(msg));
+ return;
+ }
+ lu = (struct gsm48_loc_upd_req *) gh->data;
+ pwr_lev = lu->classmark1.pwr_lev;
+ break;
+
+ case GSM48_MT_MM_CM_SERV_REQ:
+ if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*serv_req)) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "rx CM Service Request message too short: %u\n", msgb_l3len(msg));
+ return;
+ }
+ serv_req = (struct gsm48_service_request *) gh->data;
+ pwr_lev = serv_req->classmark2.pwr_lev;
+ break;
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*serv_req)) {
- LOGP(DMSC, LOGL_ERROR, "CM Serv Req too small to look at: %u\n", msgb_l3len(msg));
+ default:
+ /* No power cap in other messages */
+ return;
+ }
+ break;
+ /* FIXME: pwr_lev in Paging Response? */
+ default:
+ /* No power cap in other messages */
return;
}
- gh = msgb_l3(msg);
- serv_req = (struct gsm48_service_request *) gh->data;
-
- cm2 = (struct gsm48_classmark2*)(((uint8_t*)&serv_req->classmark)+1);
- /* FIXME: one classmark2 is available in libosmocore:
- cm2 = &serv_req->classmark2; */
- rc8 = osmo_gsm48_rfpowercap2powerclass(bts->band, cm2->pwr_lev);
+ bts = conn_get_bts(conn);
+ OSMO_ASSERT(bts);
+ rc8 = osmo_gsm48_rfpowercap2powerclass(bts->band, pwr_lev);
if (rc8 < 0) {
- LOGP(DMSC, LOGL_NOTICE,
- "Unable to decode RF power capability %x from classmark2 during CM Service Req.\n",
- cm2->pwr_lev);
+ LOGPFSML(conn->fi, LOGL_NOTICE, "%s %s: Unable to decode RF power capability 0x%x\n",
+ gsm48_pdisc_name(pdisc), gsm48_pdisc_msgtype_name(pdisc, mtype), pwr_lev);
rc8 = 0;
}
conn_update_ms_power_class(conn, rc8);
}
-int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg)
+static struct gsm_subscriber_connection *bsc_conn_by_bsub(const struct bsc_subscr *bsub)
{
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t pdisc = gsm48_hdr_pdisc(gh);
- uint8_t mtype = gsm48_hdr_msg_type(gh);
+ struct gsm_subscriber_connection *conn;
+ if (!bsub)
+ return NULL;
- if (pdisc == GSM48_PDISC_MM) {
- if (mtype == GSM48_MT_MM_LOC_UPD_REQUEST)
- handle_lu_request(conn, msg);
- else if(mtype == GSM48_MT_MM_CM_SERV_REQ)
- handle_cm_serv_req(conn, msg);
- } else if (pdisc == GSM48_PDISC_RR) {
- if (mtype == GSM48_MT_RR_PAG_RESP)
- handle_page_resp(conn, msg);
+ llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) {
+ if (conn->bsub == bsub)
+ return conn;
}
-
- return 0;
+ return NULL;
}
/*! MS->MSC: New MM context with L3 payload. */
-int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel)
+int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel)
{
+ struct gsm_subscriber_connection *conn = NULL;
+ struct bsc_subscr *bsub = NULL;
+ struct bsc_msc_data *paged_from_msc;
+ enum bsc_paging_reason paging_reasons;
struct bsc_msc_data *msc;
- struct msgb *resp;
+ struct msgb *create_l3;
struct gsm0808_speech_codec_list scl;
+ struct gsm0808_speech_codec_list *use_scl;
int rc = -2;
struct gsm_bts *bts;
struct osmo_cell_global_id *cgi;
+ struct osmo_mobile_identity mi;
+ struct gsm48_hdr *gh;
+ uint8_t pdisc, mtype;
+ bool is_emerg;
+ bool release_lchan = true;
- log_set_context(LOG_CTX_BSC_SUBSCR, conn->bsub);
+ if (msgb_l3len(msg) < sizeof(*gh)) {
+ LOGP(DRSL, LOGL_ERROR, "There is no GSM48 header here.\n");
+ goto early_exit;
+ }
+
+ gh = msgb_l3(msg);
+ pdisc = gsm48_hdr_pdisc(gh);
+ mtype = gsm48_hdr_msg_type(gh);
- LOGP(DMSC, LOGL_INFO, "Tx MSC COMPL L3\n");
+ bts = lchan->ts->trx->bts;
+ OSMO_ASSERT(bts);
- /* find the MSC link we want to use */
- msc = bsc_find_msc(conn, msg);
- if (!msc) {
- LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n");
- rc = -1;
- goto early_fail;
+ /* Normally, if an lchan has no conn yet, it is an all new Complete Layer 3, and we allocate a new conn on the
+ * A-interface. But there are cases where a conn on A already exists for this subscriber (e.g. Perform Location
+ * Request on IDLE MS). The Mobile Identity tells us whether that is the case. */
+ 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 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__);
}
- /* allocate resource for a new connection */
- if (osmo_bsc_sigtran_new_conn(conn, msc) != BSC_CON_SUCCESS)
- goto early_fail;
+ /* 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.
+ *
+ * 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) {
+ /* Typical Complete Layer 3 with a new conn being established. */
+ conn = bsc_subscr_con_allocate(bsc_gsmnet);
+ if (!conn) {
+ LOG_COMPL_L3(pdisc, mtype, LOGL_ERROR, "Failed to allocate conn\n");
+ goto early_exit;
+ }
+ }
+ if (bsub) {
+ /* We got the conn either from new allocation, or by searching for it by bsub. So: */
+ OSMO_ASSERT((!conn->bsub) || (conn->bsub == bsub));
+ if (!conn->bsub) {
+ conn->bsub = bsub;
+ bsc_subscr_get(conn->bsub, BSUB_USE_CONN);
+ }
+ bsc_subscr_put(bsub, __func__);
+ }
+ /* Associate lchan with the conn, and set the id string for logging */
+ gscon_change_primary_lchan(conn, lchan);
+ gscon_update_id(conn);
- bts = conn_get_bts(conn);
- cgi = cgi_for_msc(conn->sccp.msc, bts);
+ log_set_context(LOG_CTX_BSC_SUBSCR, conn->bsub);
- if (!bts || !cgi) {
- /* should never happen */
- LOGP(DMSC, LOGL_ERROR, "Compl L3 without lchan\n");
- rc = -1;
- goto early_fail;
+ is_emerg = (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) && is_cm_service_for_emerg(msg);
+
+ /* When receiving a Paging Response, stop Paging for this subscriber on all cells, and figure out which MSC
+ * sent the Paging Request, if any. */
+ paged_from_msc = NULL;
+ paging_reasons = BSC_PAGING_NONE;
+ if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP) {
+ /* 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. */
+ LOG_COMPL_L3(pdisc, mtype, LOGL_DEBUG,
+ "%s Unsolicited Paging Response, possibly an MT-CSFB call.\n",
+ osmo_mobile_identity_to_str_c(OTC_SELECT, &mi));
+
+ 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(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",
+ osmo_mobile_identity_to_str_c(OTC_SELECT, &mi), paged_from_msc->nr);
+ paged_from_msc = NULL;
+ }
}
- bsc_scan_bts_msg(conn, msg);
+ if (!conn->sccp.msc) {
+ /* The conn was just allocated, and no target MSC has been picked for it yet. */
+ if (paged_from_msc)
+ msc = paged_from_msc;
+ else
+ msc = bsc_find_msc(conn, &mi, is_emerg, is_lu_from_other_plmn(msg));
+ if (!msc) {
+ LOG_COMPL_L3(pdisc, mtype, LOGL_ERROR,
+ "%s%s: No suitable MSC for this Complete Layer 3 request found\n",
+ osmo_mobile_identity_to_str_c(OTC_SELECT, &mi),
+ is_emerg ? " FOR EMERGENCY CALL" : "");
+ goto early_exit;
+ }
+ /* allocate resource for a new connection */
+ if (osmo_bsc_sigtran_new_conn(conn, msc) != BSC_CON_SUCCESS)
+ goto early_exit;
+ } else if (paged_from_msc && conn->sccp.msc != paged_from_msc) {
+ LOG_COMPL_L3(pdisc, mtype, LOGL_ERROR,
+ "%s%s: there is a conn to MSC %u, but there is a pending Paging request from MSC %u\n",
+ osmo_mobile_identity_to_str_c(OTC_SELECT, &mi),
+ is_emerg ? " FOR EMERGENCY CALL" : "",
+ conn->sccp.msc->nr, paged_from_msc->nr);
+ }
+ OSMO_ASSERT(conn->sccp.msc);
+
+ parse_powercap(conn, msg);
+
+ /* If a BSSLAP TA Request from the SMLC is waiting for a TA value, we have one now. */
+ if (conn->lcs.loc_req && conn->lcs.loc_req->ta_req)
+ osmo_fsm_inst_dispatch(conn->lcs.loc_req->ta_req->fi, LCS_TA_REQ_EV_GOT_TA, NULL);
+
+ /* If the Paging was issued only by OsmoBSC for LCS, don't bother to establish Layer 3 to the MSC. */
+ if (paged_from_msc && !(paging_reasons & BSC_PAGING_FROM_CN)) {
+ LOG_COMPL_L3(pdisc, mtype, LOGL_DEBUG,
+ "%s%s: Paging was for Perform Location Request only, not establishing Layer 3\n",
+ osmo_mobile_identity_to_str_c(OTC_SELECT, &mi),
+ is_emerg ? " FOR EMERGENCY CALL" : "");
+ rc = 0;
+ goto early_exit;
+ }
+
+ /* Send the Create Layer 3. */
+ use_scl = NULL;
if (gscon_is_aoip(conn)) {
- gen_bss_supported_codec_list(&scl, msc, bts);
+ gen_bss_supported_codec_list(&scl, conn->sccp.msc, bts);
if (scl.len > 0)
- resp = gsm0808_create_layer3_2(msg, cgi, &scl);
- else {
- /* Note: 3GPP TS 48.008 3.2.1.32, COMPLETE LAYER 3 INFORMATION clearly states that
- * Codec List (BSS Supported) shall be included, if the radio access network
- * supports an IP based user plane interface. It may be intentional that the
- * current configuration does not support any voice codecs, in those cases the
- * network does not support an IP based user plane interface, and therefore the
- * Codec List (BSS Supported) IE can be left out in those situations. */
- resp = gsm0808_create_layer3_2(msg, cgi, NULL);
- }
- } else
- resp = gsm0808_create_layer3_2(msg, cgi, NULL);
-
- if (resp)
- rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_REQ, resp);
- else
- LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n");
-early_fail:
+ use_scl = &scl;
+ /* For AoIP, we should always pass a Codec List (BSS Supported). But osmo-bsc may be configured to
+ * support no voice codecs -- then omit the Codec List. */
+ }
+ cgi = cgi_for_msc(conn->sccp.msc, bts);
+ if (!cgi) {
+ /* should never happen */
+ LOG_COMPL_L3(pdisc, mtype, LOGL_ERROR, "%s: internal error: BTS without identity\n",
+ osmo_mobile_identity_to_str_c(OTC_SELECT, &mi));
+ goto early_exit;
+ }
+ create_l3 = gsm0808_create_layer3_2(msg, cgi, use_scl);
+ if (!create_l3) {
+ LOG_COMPL_L3(pdisc, mtype, LOGL_ERROR, "%s: Failed to compose Create Layer 3 message\n",
+ osmo_mobile_identity_to_str_c(OTC_SELECT, &mi));
+ goto early_exit;
+ }
+ rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MO_COMPL_L3, create_l3);
+ if (!rc)
+ release_lchan = false;
+
+early_exit:
+ if (release_lchan)
+ 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)
{
@@ -546,10 +564,11 @@ void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct ms
LOGP(DMSC, LOGL_INFO, "Tx MSC DTAP LINK_ID=0x%02x\n", link_id);
- bsc_scan_bts_msg(conn, msg);
+ parse_powercap(conn, msg);
+
+ /* convert RSL link ID to DLCI, store in msg->cb */
+ OBSC_LINKID_CB(msg) = RSL_LINK_ID2DLCI(link_id);
- /* Store link_id in msg->cb */
- OBSC_LINKID_CB(msg) = link_id;
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MO_DTAP, msg);
done:
log_set_context(LOG_CTX_BSC_SUBSCR, NULL);
@@ -582,12 +601,33 @@ 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)
msgb_free(resp);
}
+
+bool bsc_chan_ind_requires_rtp_stream(enum gsm0808_chan_indicator ch_indctr)
+{
+ switch (ch_indctr) {
+ case GSM0808_CHAN_SPEECH:
+ case GSM0808_CHAN_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c
index f790b90d8..b11607e3f 100644
--- a/src/osmo-bsc/gsm_data.c
+++ b/src/osmo-bsc/gsm_data.c
@@ -44,11 +44,11 @@
#include <osmocom/bsc/handover_cfg.h>
#include <osmocom/bsc/timeslot_fsm.h>
#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/bsc_msc_data.h>
void *tall_bsc_ctx = NULL;
-static LLIST_HEAD(bts_models);
-
void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr,
uint8_t e1_ts, uint8_t e1_ts_ss)
{
@@ -57,64 +57,18 @@ void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr,
ts->e1_link.e1_ts_ss = e1_ts_ss;
}
-static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
-{
- struct gsm_bts_model *model;
-
- llist_for_each_entry(model, &bts_models, list) {
- if (model->type == type)
- return model;
- }
-
- return NULL;
-}
-
-int gsm_bts_model_register(struct gsm_bts_model *model)
-{
- if (bts_model_find(model->type))
- return -EEXIST;
-
- tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef);
- llist_add_tail(&model->list, &bts_models);
- return 0;
-}
-
-const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = {
- { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" },
- { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" },
- { 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" },
- { 0, NULL }
-};
-
-struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr)
-{
- struct gsm_bts_trx *trx;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->nr == nr)
- return trx;
- }
- return NULL;
-}
-
/* Search for a BTS in the given Location Area; optionally start searching
* with start_bts (for continuing to search after the first result) */
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;
@@ -149,79 +103,25 @@ const char *bts_gprs_mode_name(enum bts_gprs_mode mode)
return get_value_string(bts_gprs_mode_names, mode);
}
-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)) {
- return 0;
- }
- if (mode == BTS_GPRS_EGPRS &&
- !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_EGPRS)) {
- return 0;
- }
-
- return 1;
-}
-
-int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type)
-{
- struct gsm_bts_model *model;
-
- model = bts_model_find(type);
- if (!model)
- return -EINVAL;
-
- bts->type = type;
- bts->model = model;
-
- if (model->start && !model->started) {
- int ret = model->start(bts->network);
- if (ret < 0)
- return ret;
-
- model->started = true;
- }
-
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- /* Set the default OML Stream ID to 0xff */
- bts->oml_tei = 0xff;
- bts->c0->nominal_power = 23;
- break;
- case GSM_BTS_TYPE_RBS2000:
- INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups);
- INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups);
- break;
- case GSM_BTS_TYPE_BS11:
- case GSM_BTS_TYPE_UNKNOWN:
- case GSM_BTS_TYPE_NOKIA_SITE:
- /* Set default BTS reset timer */
- bts->nokia.bts_reset_timer_cnf = 15;
- case _NUM_GSM_BTS_TYPE:
- break;
- }
-
- return 0;
-}
-
struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type,
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);
@@ -248,166 +148,15 @@ void gsm48_ra_id_by_bts(struct gsm48_ra_id *buf, struct gsm_bts *bts)
gsm48_encode_ra(buf, &raid);
}
-/* Assume there are only 256 possible bts */
-osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256);
-static void depends_calc_index_bit(int bts_nr, int *idx, int *bit)
-{
- *idx = bts_nr / (8 * 4);
- *bit = bts_nr % (8 * 4);
-}
-
-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;
-}
-
-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);
-}
-
-int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other)
-{
- int idx, bit;
- depends_calc_index_bit(other->nr, &idx, &bit);
-
- /* Check if there is a depends bit */
- return (base->depends_on[idx] & (1 << bit)) > 0;
-}
-
-static int bts_is_online(struct gsm_bts *bts)
-{
- /* TODO: support E1 BTS too */
- if (!is_ipaccess_bts(bts))
- return 1;
-
- if (!bts->oml_link)
- return 0;
-
- return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED;
-}
-
-int bts_depend_check(struct gsm_bts *bts)
-{
- struct gsm_bts *other_bts;
-
- llist_for_each_entry(other_bts, &bts->network->bts_list, list) {
- if (!bts_depend_is_depedency(bts, other_bts))
- continue;
- if (bts_is_online(other_bts))
- continue;
- return 0;
- }
- return 1;
-}
-
-/* get the radio link timeout (based on SACCH decode errors, according
- * to algorithm specified in TS 05.08 section 5.2. A value of -1
- * indicates we should use an infinitely long timeout, which only works
- * with OsmoBTS as the BTS implementation */
-int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts)
-{
- const struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
-
- if (bts->infinite_radio_link_timeout)
- return -1;
- else {
- /* Encoding as per Table 10.5.21 of TS 04.08 */
- return (cell_options->radio_link_timeout + 1) << 2;
- }
-}
-
-/* set the radio link timeout (based on SACCH decode errors, according
- * to algorithm specified in TS 05.08 Section 5.2. A value of -1
- * indicates we should use an infinitely long timeout, which only works
- * with OsmoBTS as the BTS implementation */
-void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value)
-{
- struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
-
- if (value < 0)
- bts->infinite_radio_link_timeout = true;
- else {
- bts->infinite_radio_link_timeout = false;
- /* Encoding as per Table 10.5.21 of TS 04.08 */
- if (value < 4)
- value = 4;
- if (value > 64)
- value = 64;
- cell_options->radio_link_timeout = (value >> 2) - 1;
- }
-}
-
-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,
-};
-
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;
}
-static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
- uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3)
+void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
+ uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3)
{
mo->bts = bts;
mo->obj_class = obj_class;
@@ -417,66 +166,10 @@ static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
gsm_abis_mo_reset(mo);
}
-const struct value_string bts_attribute_names[] = {
- OSMO_VALUE_STRING(BTS_TYPE_VARIANT),
- OSMO_VALUE_STRING(BTS_SUB_MODEL),
- OSMO_VALUE_STRING(TRX_PHY_VERSION),
- { 0, NULL }
-};
-
-enum bts_attribute str2btsattr(const char *s)
-{
- return get_string_value(bts_attribute_names, s);
-}
-
-const char *btsatttr2str(enum bts_attribute v)
-{
- return get_value_string(bts_attribute_names, v);
-}
-
-const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = {
- { BTS_UNKNOWN, "unknown" },
- { BTS_OSMO_LITECELL15, "osmo-bts-lc15" },
- { BTS_OSMO_OCTPHY, "osmo-bts-octphy" },
- { BTS_OSMO_SYSMO, "osmo-bts-sysmo" },
- { BTS_OSMO_TRX, "omso-bts-trx" },
- { 0, NULL }
-};
-
-enum gsm_bts_type_variant str2btsvariant(const char *arg)
-{
- return get_string_value(osmo_bts_variant_names, arg);
-}
-
-const char *btsvariant2str(enum gsm_bts_type_variant v)
-{
- return get_value_string(osmo_bts_variant_names, v);
-}
-
-const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = {
- { GSM_BTS_TYPE_UNKNOWN, "unknown" },
- { GSM_BTS_TYPE_BS11, "bs11" },
- { GSM_BTS_TYPE_NANOBTS, "nanobts" },
- { GSM_BTS_TYPE_RBS2000, "rbs2000" },
- { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" },
- { GSM_BTS_TYPE_OSMOBTS, "sysmobts" },
- { 0, NULL }
-};
-
-enum gsm_bts_type str2btstype(const char *arg)
-{
- return get_string_value(bts_type_names, arg);
-}
-
-const char *btstype2str(enum gsm_bts_type type)
-{
- return get_value_string(bts_type_names, type);
-}
-
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" },
@@ -491,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,
@@ -524,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);
@@ -542,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" },
@@ -577,44 +265,6 @@ struct gsm_bts *gsm_bts_num(const struct gsm_network *net, int num)
return NULL;
}
-bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai)
-{
- return osmo_plmn_cmp(&lai->plmn, &bts->network->plmn) == 0
- && lai->lac == bts->location_area_code;
-}
-
-bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id)
-{
- const union gsm0808_cell_id_u *id = &cell_id->id;
- if (!bts || !cell_id)
- return false;
-
- switch (cell_id->id_discr) {
- case CELL_IDENT_WHOLE_GLOBAL:
- return gsm_bts_matches_lai(bts, &id->global.lai)
- && id->global.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;
- case CELL_IDENT_CI:
- return id->ci == bts->cell_identity;
- case CELL_IDENT_NO_CELL:
- return false;
- case CELL_IDENT_LAI_AND_LAC:
- return gsm_bts_matches_lai(bts, &id->lai_and_lac);
- case CELL_IDENT_LAC:
- return id->lac == bts->location_area_code;
- case CELL_IDENT_BSS:
- return true;
- case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
- case CELL_IDENT_UTRAN_RNC:
- case CELL_IDENT_UTRAN_LAC_RNC:
- return false;
- default:
- OSMO_ASSERT(false);
- }
-}
-
/* From a list of local BTSes that match the cell_id, return the Nth one, or NULL if there is no such
* match. */
struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net,
@@ -636,402 +286,8 @@ struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net,
return NULL;
}
-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)
-{
- 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;
-}
-
-/* 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)
-{
- 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 gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
- int k;
-
- if (!trx)
- return NULL;
-
- trx->bts = bts;
- trx->nr = bts->num_trx++;
- trx->mo.nm_state.administrative = NM_STATE_UNLOCKED;
-
- gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER,
- bts->nr, trx->nr, 0xff);
- gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC,
- bts->nr, trx->nr, 0xff);
-
- for (k = 0; k < TRX_NR_TS; k++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[k];
- int l;
-
-
- ts->trx = trx;
- ts->nr = k;
- ts->pchan_from_config = ts->pchan_on_init = ts->pchan_is = GSM_PCHAN_NONE;
- ts->tsc = -1;
-
- ts_fsm_alloc(ts);
-
- gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL,
- bts->nr, trx->nr, ts->nr);
-
- ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data);
- ts->hopping.arfcns.data = ts->hopping.arfcns_data;
- ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data);
- 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);
- }
- }
-
- if (trx->nr != 0)
- trx->nominal_power = bts->c0->nominal_power;
-
- llist_add_tail(&trx->list, &bts->trx_list);
-
- return trx;
-}
-
-
-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 = {
- .parameter = {
- [RLC_T3142] = 20,
- [RLC_T3169] = 5,
- [RLC_T3191] = 5,
- [RLC_T3193] = 160, /* 10ms */
- [RLC_T3195] = 5,
- [RLC_N3101] = 10,
- [RLC_N3103] = 4,
- [RLC_N3105] = 8,
- [CV_COUNTDOWN] = 15,
- [T_DL_TBF_EXT] = 250 * 10, /* ms */
- [T_UL_TBF_EXT] = 250 * 10, /* ms */
- },
- .paging = {
- .repeat_time = 5 * 50, /* ms */
- .repeat_count = 3,
- },
- .cs_mask = 0x1fff,
- .initial_cs = 2,
- .initial_mcs = 6,
-};
-
-static void bts_init_cbch_state(struct bts_smscb_chan_state *cstate, struct gsm_bts *bts)
-{
- cstate->bts = bts;
- INIT_LLIST_HEAD(&cstate->messages);
-}
-
-/* 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 *bts = talloc_zero(net, struct gsm_bts);
- struct gsm48_multi_rate_conf mr_cfg;
- int i;
-
- if (!bts)
- return NULL;
-
- bts->nr = bts_num;
- bts->num_trx = 0;
- INIT_LLIST_HEAD(&bts->trx_list);
- bts->network = net;
-
- 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);
- 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;
-
- /* init statistics */
- bts->bts_ctrs = rate_ctr_group_alloc(bts, &bts_ctrg_desc, bts->nr);
- if (!bts->bts_ctrs) {
- talloc_free(bts);
- return NULL;
- }
- bts->bts_statg = osmo_stat_item_group_alloc(bts, &bts_statg_desc, bts->nr);
-
- /* create our primary TRX */
- bts->c0 = gsm_bts_trx_alloc(bts);
- if (!bts->c0) {
- rate_ctr_group_free(bts->bts_ctrs);
- osmo_stat_item_group_free(bts->bts_statg);
- talloc_free(bts);
- return NULL;
- }
- 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->rach_b_thresh = -1;
- bts->rach_ldavg_slots = -1;
-
- bts->paging.free_chans_need = -1;
- INIT_LLIST_HEAD(&bts->paging.pending_requests);
-
- bts->features.data = &bts->_features_data[0];
- bts->features.data_len = sizeof(bts->_features_data);
-
- /* si handling */
- bts->bcch_change_mark = 1;
- bts->chan_load_avg = 0;
-
- /* timer overrides */
- bts->T3122 = 0; /* not overridden by default */
- bts->T3113_dynamic = true; /* dynamic by default */
-
- bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED;
- bts->dtxd = false;
- bts->gprs.ctrl_ack_type_use_block = true; /* use RLC/MAC control block */
- 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->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;
- bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list;
- bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST;
- bts->si_common.si2quater_neigh_list.thresh_hi = 0;
- osmo_earfcn_init(&bts->si_common.si2quater_neigh_list);
- bts->si_common.neigh_list.data = bts->si_common.data.neigh_list;
- bts->si_common.neigh_list.data_len =
- sizeof(bts->si_common.data.neigh_list);
- bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list;
- bts->si_common.si5_neigh_list.data_len =
- sizeof(bts->si_common.data.si5_neigh_list);
- bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc;
- bts->si_common.cell_alloc.data_len =
- sizeof(bts->si_common.data.cell_alloc);
- bts->si_common.rach_control.re = 1; /* no re-establishment */
- 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.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);
- 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->oml_fail_rep);
-
- /* 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){
- .hr = 1,
- .efr = 1,
- .amr = 1,
- };
-
- /* 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) {
- .m4_75 = 1,
- .m5_15 = 0,
- .m5_90 = 1,
- .m6_70 = 0,
- .m7_40 = 1,
- .m7_95 = 0,
- .m10_2 = 0,
- .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) {
- .m4_75 = 1,
- .m5_15 = 0,
- .m5_90 = 1,
- .m6_70 = 0,
- .m7_40 = 1,
- .m7_95 = 0,
- .m10_2 = 0,
- .m12_2 = 0
- };
- 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);
-
- return bts;
-}
-
-/* reset the state of all MO in the BTS */
-void gsm_bts_mo_reset(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
- 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) {
- gsm_abis_mo_reset(&trx->mo);
- gsm_abis_mo_reset(&trx->bb_transc.mo);
-
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[i];
- gsm_abis_mo_reset(&ts->mo);
- }
- }
-}
-
-struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num)
-{
- struct gsm_bts_trx *trx;
-
- if (num >= bts->num_trx)
- return NULL;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->nr == num)
- return trx;
- }
-
- return NULL;
-}
-
static char ts2str[255];
-char *gsm_bts_name(const struct gsm_bts *bts)
-{
- if (!bts)
- snprintf(ts2str, sizeof(ts2str), "(bts=NULL)");
- else
- snprintf(ts2str, sizeof(ts2str), "(bts=%d)", bts->nr);
-
- return ts2str;
-}
-
-char *gsm_trx_name(const struct gsm_bts_trx *trx)
-{
- if (!trx)
- snprintf(ts2str, sizeof(ts2str), "(trx=NULL)");
- else
- snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)",
- trx->bts->nr, trx->nr);
-
- return ts2str;
-}
-
-
char *gsm_ts_name(const struct gsm_bts_trx_ts *ts)
{
snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)",
@@ -1070,96 +326,57 @@ 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,
- const struct abis_om_obj_inst *obj_inst)
+struct gsm_abis_mo *gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class,
+ const struct abis_om_obj_inst *obj_inst)
{
struct gsm_bts_trx *trx;
- struct gsm_abis_mo *mo = NULL;
switch (obj_class) {
case NM_OC_BTS:
- mo = &bts->mo;
- break;
+ return &bts->mo;
case NM_OC_RADIO_CARRIER:
- if (obj_inst->trx_nr >= bts->num_trx) {
- return NULL;
- }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- mo = &trx->mo;
- break;
+ return trx != NULL ? &trx->mo : NULL;
case NM_OC_BASEB_TRANSC:
- if (obj_inst->trx_nr >= bts->num_trx) {
- return NULL;
- }
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- mo = &trx->bb_transc.mo;
- break;
+ return trx != NULL ? &trx->bb_transc.mo : NULL;
case NM_OC_CHANNEL:
- if (obj_inst->trx_nr >= bts->num_trx) {
- return NULL;
- }
- trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
if (obj_inst->ts_nr >= TRX_NR_TS)
return NULL;
- mo = &trx->ts[obj_inst->ts_nr].mo;
- break;
+ trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
+ return trx != NULL ? &trx->ts[obj_inst->ts_nr].mo : NULL;
case NM_OC_SITE_MANAGER:
- mo = &bts->site_mgr.mo;
- break;
+ return &bts->site_mgr->mo;
case NM_OC_BS11:
switch (obj_inst->bts_nr) {
case BS11_OBJ_CCLK:
- mo = &bts->bs11.cclk.mo;
- break;
+ return &bts->bs11.cclk.mo;
case BS11_OBJ_BBSIG:
- if (obj_inst->ts_nr > bts->num_trx)
- return NULL;
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- mo = &trx->bs11.bbsig.mo;
- break;
+ return trx != NULL ? &trx->bs11.bbsig.mo : NULL;
case BS11_OBJ_PA:
- if (obj_inst->ts_nr > bts->num_trx)
- return NULL;
trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- mo = &trx->bs11.pa.mo;
- break;
- default:
- return NULL;
+ return trx != NULL ? &trx->bs11.pa.mo : NULL;
}
break;
case NM_OC_BS11_RACK:
- mo = &bts->bs11.rack.mo;
- break;
+ return &bts->bs11.rack.mo;
case NM_OC_BS11_ENVABTSE:
if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse))
return NULL;
- mo = &bts->bs11.envabtse[obj_inst->trx_nr].mo;
- break;
+ return &bts->bs11.envabtse[obj_inst->trx_nr].mo;
case NM_OC_GPRS_NSE:
- mo = &bts->gprs.nse.mo;
- break;
+ return &bts->site_mgr->gprs.nse.mo;
case NM_OC_GPRS_CELL:
- mo = &bts->gprs.cell.mo;
- break;
+ return &bts->gprs.cell.mo;
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;
- break;
+ return &bts->site_mgr->gprs.nsvc[obj_inst->trx_nr].mo;
}
- return mo;
+
+ return NULL;
}
/* obtain the gsm_nm_state data structure for a given object instance */
@@ -1212,43 +429,51 @@ 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 = ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs;
break;
case GSM_PCHAN_PDCH:
- OSMO_ASSERT(lchan_nr == 0);
- cbits = RSL_CHAN_OSMO_PDCH >> 3;
+ if (lchan_nr != 0)
+ return -EINVAL;
+ cbits = ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH;
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 = ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(lchan_nr);
break;
case GSM_PCHAN_CCCH_SDCCH4:
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
@@ -1258,22 +483,22 @@ uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
* See osmo-bts-xxx/oml.c:opstart_compl().
*/
if (lchan_nr == CCCH_LCHAN)
- chan_nr = 0;
- else
- OSMO_ASSERT(lchan_nr < 4);
- cbits = 0x04;
- cbits += lchan_nr;
+ lchan_nr = 0;
+ else if (lchan_nr > 4)
+ return -EINVAL;
+ cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(lchan_nr);
break;
case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
- OSMO_ASSERT(lchan_nr < 8);
- cbits = 0x08;
- cbits += lchan_nr;
+ if (lchan_nr >= 8)
+ return -EINVAL;
+ cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(lchan_nr);
break;
default:
case GSM_PCHAN_CCCH:
- OSMO_ASSERT(lchan_nr == 0);
- cbits = 0x10;
+ if (lchan_nr != 0)
+ return -EINVAL;
+ cbits = ABIS_RSL_CHAN_NR_CBITS_BCCH;
break;
}
@@ -1282,74 +507,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)
-{
- /* 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);
-}
-
-/* return the gsm_lchan for the CBCH (if it exists at all) */
-struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts)
+/* 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)
{
- struct gsm_lchan *lchan = NULL;
- struct gsm_bts_trx *trx = bts->c0;
-
- 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;
- }
- }
+ 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;
}
-
- return lchan;
+ 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;
}
-/* determine logical channel based on TRX and channel number IE */
-struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
- int *rc)
+int gsm_lchan2chan_nr(const struct gsm_lchan *lchan, bool allow_osmo_cbits)
{
- uint8_t ts_nr = chan_nr & 0x07;
- uint8_t cbits = chan_nr >> 3;
- uint8_t lch_idx;
- struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
- bool ok;
-
- if (rc)
- *rc = -EINVAL;
-
- if (cbits == 0x01) {
- 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) {
- lch_idx = cbits & 0x1; /* TCH/H */
- ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_H);
- } else if ((cbits & 0x1c) == 0x04) {
- lch_idx = cbits & 0x3; /* SDCCH/4 */
- ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_CCCH_SDCCH4);
- } else if ((cbits & 0x18) == 0x08) {
- 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) {
- lch_idx = 0; /* CCCH? */
- ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_CCCH);
- /* FIXME: we should not return first sdcch4 !!! */
- } else if ((chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_OSMO_PDCH) {
- lch_idx = 0;
- ok = (ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH);
- } else
- return NULL;
-
- if (rc && ok)
- *rc = 0;
-
- return &ts->lchan[lch_idx];
+ return gsm_lchan_and_pchan2chan_nr(lchan, lchan->ts->pchan_is, allow_osmo_cbits);
}
static const uint8_t subslots_per_pchan[] = {
@@ -1363,12 +557,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))
@@ -1376,6 +570,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) {
@@ -1392,48 +610,24 @@ bool ts_is_tch(struct gsm_bts_trx_ts *ts)
return pchan_is_tch(ts->pchan_is);
}
-bool trx_is_usable(const struct gsm_bts_trx *trx)
-{
- /* FIXME: How does this behave for BS-11 ? */
- if (is_ipaccess_bts(trx->bts)) {
- if (!nm_is_running(&trx->mo.nm_state) ||
- !nm_is_running(&trx->bb_transc.mo.nm_state))
- return false;
- } else if (is_ericsson_bts(trx->bts)) {
- /* The OM2000 -> 12.21 mapping we do doesn't have separate bb_transc MO */
- if (!nm_is_running(&trx->mo.nm_state))
- return false;
- }
-
- return true;
-}
-
-void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[i];
- osmo_fsm_inst_dispatch(ts->fi, ts_ev, data);
- }
-}
-
-void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data)
-{
- struct gsm_bts_trx *trx;
- llist_for_each_entry(trx, &bts->trx_list, list)
- gsm_trx_all_ts_dispatch(trx, ts_ev, data);
+struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn) {
+ if (!conn || !conn->lchan)
+ return NULL;
+ 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;
@@ -1441,27 +635,44 @@ 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)
+{
+ 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;
+}
+
+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_lchan2chan_nr(lchan);
- _chan_desc_fill_tail(cd, lchan);
+ return gsm48_lchan_and_pchan2chan_desc(cd, lchan, lchan->ts->pchan_is, tsc, allow_osmo_cbits);
}
-/* 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)
+uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts)
{
- cd->chan_nr = gsm_pchan2chan_nr(lchan->ts->pchan_from_config, lchan->ts->nr, lchan->nr);
- _chan_desc_fill_tail(cd, lchan);
+ if (ts->tsc != -1)
+ return ts->tsc;
+ else
+ return ts->trx->bts->bsic & 7;
}
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;
@@ -1493,6 +704,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
@@ -1504,6 +717,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)
{
@@ -1517,11 +746,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;
@@ -1589,11 +819,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;
@@ -1633,72 +864,10 @@ bool ts_is_capable_of_lchant(struct gsm_bts_trx_ts *ts, enum gsm_chan_t type)
}
}
-static 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;
-}
-
-/* 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;
-}
-
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;
@@ -1717,6 +886,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);
@@ -1728,115 +902,127 @@ 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_VGCS_CHANNEL),
+ 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),
{}
};
-bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx)
-{
- bool combined = false;
- bool result = true;
- unsigned int i;
+/* This may be specific to RR Channel Release, and the mappings were chosen by pure naive guessing without a proper
+ * specification available. */
+enum gsm48_rr_cause bsc_gsm48_rr_cause_from_gsm0808_cause(enum gsm0808_cause c)
+{
+ switch (c) {
+ case GSM0808_CAUSE_PREEMPTION:
+ return GSM48_RR_CAUSE_PREMPTIVE_REL;
+ case GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE:
+ case GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS:
+ case GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING:
+ case GSM0808_CAUSE_INCORRECT_VALUE:
+ case GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE:
+ case GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT:
+ return GSM48_RR_CAUSE_PROT_ERROR_UNSPC;
+ case GSM0808_CAUSE_CALL_CONTROL:
+ case GSM0808_CAUSE_HANDOVER_SUCCESSFUL:
+ case GSM0808_CAUSE_BETTER_CELL:
+ case GSM0808_CAUSE_DIRECTED_RETRY:
+ case GSM0808_CAUSE_REDUCE_LOAD_IN_SERVING_CELL:
+ case GSM0808_CAUSE_RELOCATION_TRIGGERED:
+ case GSM0808_CAUSE_ALT_CHAN_CONFIG_REQUESTED:
+ return GSM48_RR_CAUSE_NORMAL;
+ default:
+ return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
+ }
+}
- /* Iterate over all timeslots */
- for (i = 0; i < 8; i++) {
- const struct gsm_bts_trx_ts *ts = &trx->ts[i];
+/* Map RSL_ERR_* cause codes to gsm48_rr_cause codes.
+ * The mappings were chosen by naive guessing without a proper specification available. */
+enum gsm48_rr_cause bsc_gsm48_rr_cause_from_rsl_cause(uint8_t c)
+{
+ switch (c) {
+ case RSL_ERR_NORMAL_UNSPEC:
+ return GSM48_RR_CAUSE_NORMAL;
+ case RSL_ERR_MAND_IE_ERROR:
+ return GSM48_RR_CAUSE_INVALID_MAND_INF;
+ case RSL_ERR_OPT_IE_ERROR:
+ return GSM48_RR_CAUSE_COND_IE_ERROR;
+ case RSL_ERR_INVALID_MESSAGE:
+ case RSL_ERR_MSG_DISCR:
+ case RSL_ERR_MSG_TYPE:
+ case RSL_ERR_MSG_SEQ:
+ case RSL_ERR_IE_ERROR:
+ case RSL_ERR_IE_NONEXIST:
+ case RSL_ERR_IE_LENGTH:
+ case RSL_ERR_IE_CONTENT:
+ case RSL_ERR_PROTO:
+ return GSM48_RR_CAUSE_PROT_ERROR_UNSPC;
+ default:
+ return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
+ }
+}
- switch (ts->pchan_from_config) {
- case GSM_PCHAN_CCCH_SDCCH4_CBCH:
- case GSM_PCHAN_CCCH_SDCCH4:
- /* CCCH+SDCCH4 can only be configured on TS0 */
- if (i > 0) {
- LOGP(DNM, LOGL_ERROR, "Combined CCCH is not allowed "
- "on TS%u > 0\n", i);
- result = false;
- }
- if (i == 0)
- combined = true;
- /* fall-through */
- case GSM_PCHAN_CCCH:
- /* 3GPP TS 45.002, Table 3, CCCH: TS (0, 2, 4, 6) */
- if (i % 2 != 0) {
- LOGP(DNM, LOGL_ERROR, "%s is not allowed on odd TS%u\n",
- gsm_pchan_name(ts->pchan_from_config), i);
- result = false;
- }
-
- /* There can be no more CCCHs if TS0/C0 is combined */
- if (i > 0 && combined) {
- LOGP(DNM, LOGL_ERROR, "%s is not allowed on TS%u, "
- "because TS0 is using combined channel configuration\n",
- gsm_pchan_name(ts->pchan_from_config), i);
- result = false;
- }
- break;
+/* 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 */
+ },
+};
- default:
- /* CCCH on TS0 is mandatory for C0 */
- if (trx->bts->c0 == trx && i == 0) {
- LOGP(DNM, LOGL_ERROR, "TS0 on C0 must be CCCH/BCCH\n");
- result = false;
- }
- }
+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;
}
+}
- return result;
+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 533840024..2fb466cbe 100644
--- a/src/osmo-bsc/handover_decision.c
+++ b/src/osmo-bsc/handover_decision.c
@@ -34,6 +34,7 @@
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/handover_cfg.h>
+#include <osmocom/bsc/bts.h>
/* did we get a RXLEV for a given cell in the given report? */
static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr,
@@ -200,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,
},
@@ -214,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;
@@ -231,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);
@@ -256,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 ab507e84c..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>
@@ -37,6 +40,9 @@
#include <osmocom/bsc/penalty_timers.h>
#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)
@@ -47,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)
@@ -58,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)
@@ -70,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
@@ -97,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 {
@@ -135,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)
@@ -173,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)
{
@@ -234,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)
{
@@ -363,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.
@@ -415,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;
@@ -533,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;
}
}
@@ -543,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;
@@ -571,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 */
@@ -592,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 */
@@ -686,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;
}
@@ -736,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;
}
@@ -775,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;
@@ -790,76 +950,159 @@ 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);
}
-/* verbosely log about a handover candidate */
-static inline void debug_candidate(struct ho_candidate *candidate,
- int8_t rxlev, int tchf_count, int tchh_count)
-{
- struct gsm_lchan *lchan = candidate->lchan;
-
-#define HO_CANDIDATE_FMT(tchx, TCHX) "TCH/" #TCHX "={free %d (want %d), [%s%s%s]%s}"
-#define HO_CANDIDATE_ARGS(tchx, TCHX) \
- tch##tchx##_count, ho_get_hodec2_tch##tchx##_min_slots(candidate->bts->ho), \
- candidate->requirements & REQUIREMENT_A_TCH##TCHX ? "A" : \
- (candidate->requirements & REQUIREMENT_TCH##TCHX##_MASK) == 0? "-" : "", \
- candidate->requirements & REQUIREMENT_B_TCH##TCHX ? "B" : "", \
- candidate->requirements & REQUIREMENT_B_TCH##TCHX ? "C" : "", \
- (candidate->requirements & REQUIREMENT_TCH##TCHX##_MASK) == 0 ? " not a candidate" : \
- ((candidate->requirements & REQUIREMENT_TCH##TCHX##_MASK) == REQUIREMENT_A_TCH##TCHX ? \
+#define REQUIREMENTS_FMT "[%s%s%s]%s"
+#define REQUIREMENTS_ARGS(REQUIREMENTS, TCHX) \
+ (REQUIREMENTS) & REQUIREMENT_A_TCH##TCHX ? "A" : \
+ ((REQUIREMENTS) & REQUIREMENT_TCH##TCHX##_MASK) == 0? "-" : "", \
+ (REQUIREMENTS) & REQUIREMENT_B_TCH##TCHX ? "B" : "", \
+ (REQUIREMENTS) & REQUIREMENT_C_TCH##TCHX ? "C" : "", \
+ ((REQUIREMENTS) & REQUIREMENT_TCH##TCHX##_MASK) == 0 ? " not a candidate" : \
+ (((REQUIREMENTS) & REQUIREMENT_TCH##TCHX##_MASK) == REQUIREMENT_A_TCH##TCHX ? \
" more congestion" : \
- (candidate->requirements & REQUIREMENT_B_TCH##TCHX ? \
+ ((REQUIREMENTS) & REQUIREMENT_B_TCH##TCHX ? \
" good" : \
- /* now has to be candidate->requirements & REQUIREMENT_C_TCHX != 0: */ \
+ /* now has to be (REQUIREMENTS) & REQUIREMENT_C_TCHX != 0: */ \
" less-or-equal congestion"))
- 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->cil)
- LOGPHOLCHANTOREMOTE(lchan, candidate->cil, LOGL_DEBUG,
- "RX level %d -> %d\n",
- rxlev2dbm(rxlev), rxlev2dbm(candidate->avg));
-
- if (candidate->bts == lchan->ts->trx->bts)
- LOGPHOLCHANTOBTS(lchan, candidate->bts, LOGL_DEBUG,
- "RX level %d; "
+/* verbosely log about a handover candidate */
+static inline void debug_candidate(struct ho_candidate *candidate)
+{
+#define HO_CANDIDATE_FMT(tchx, TCHX) "TCH/" #TCHX "={free %d (want %d), " REQUIREMENTS_FMT "}"
+#define HO_CANDIDATE_ARGS(tchx, TCHX) \
+ candidate->target.free_tch##tchx, candidate->target.min_free_tch##tchx, \
+ REQUIREMENTS_ARGS(candidate->requirements, TCHX)
+
+ 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->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->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);
+
+ if (!c.requirements)
+ return;
- debug_candidate(&c, 0, tchf_count, tchh_count);
clist[*candidates] = c;
(*candidates)++;
}
@@ -867,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 */
@@ -901,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;
@@ -919,51 +1157,54 @@ 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;
clist[*candidates] = c;
(*candidates)++;
@@ -971,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);
}
}
}
@@ -1065,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;
@@ -1086,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. */
@@ -1105,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];
@@ -1124,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);
}
@@ -1138,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];
@@ -1157,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);
}
@@ -1178,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;
@@ -1195,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
@@ -1264,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;
@@ -1279,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);
@@ -1311,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;
}
@@ -1346,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:
*
@@ -1407,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;
@@ -1444,6 +1809,10 @@ static int bts_resolve_congestion(struct gsm_bts *bts, int tchf_congestion, int
/* (Do not consider dynamic TS that are in PDCH mode) */
switch (ts->pchan_is) {
case GSM_PCHAN_TCH_F:
+ /* No need to collect TCH/F candidates if no TCH/F needs to be moved. */
+ if (tchf_congestion == 0)
+ continue;
+
lc = &ts->lchan[0];
/* omit if channel not active */
if (lc->type != GSM_LCHAN_TCH_F
@@ -1458,6 +1827,10 @@ static int bts_resolve_congestion(struct gsm_bts *bts, int tchf_congestion, int
collect_candidates_for_lchan(lc, clist, &candidates, NULL, true);
break;
case GSM_PCHAN_TCH_H:
+ /* No need to collect TCH/H candidates if no TCH/H needs to be moved. */
+ if (tchh_congestion == 0)
+ continue;
+
for (j = 0; j < 2; j++) {
lc = &ts->lchan[j];
/* omit if channel not active */
@@ -1487,71 +1860,31 @@ static int bts_resolve_congestion(struct gsm_bts *bts, int tchf_congestion, int
if (log_check_level(DHODEC, LOGL_DEBUG)) {
LOGPHOBTS(bts, LOGL_DEBUG, "Considering %u candidates to solve congestion:\n", candidates);
for (i = 0; i < candidates; i++) {
- LOGPHOCAND(&clist[i], LOGL_DEBUG, "#%d: req=0x%x avg-rxlev=%d\n",
- i, clist[i].requirements, clist[i].avg);
+
+ 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].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
@@ -1575,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
@@ -1723,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 */
@@ -1820,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;
@@ -1847,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)
@@ -1880,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;
@@ -1890,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. */
@@ -1918,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 0b88b27d8..24766a5ea 100644
--- a/src/osmo-bsc/handover_fsm.c
+++ b/src/osmo-bsc/handover_fsm.c
@@ -43,6 +43,10 @@
#include <osmocom/bsc/osmo_bsc_lcls.h>
#include <osmocom/bsc/codec_pref.h>
#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) \
@@ -57,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) \
@@ -79,12 +83,39 @@
/* Assume presence of local var 'conn' as struct gsm_subscriber_connection.
* This is a macro to preserve the source file and line number in logging. */
-#define ho_count(counter) do { \
- LOG_HO(conn, LOGL_DEBUG, "incrementing rate counter: %s %s\n", \
+#define ho_count_bsc(counter) do { \
+ /* If a handover target could not be found, the counter index may be -1. */ \
+ if (counter < 0) \
+ break; \
+ 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
+ * This is a macro to preserve the source file and line number in logging. */
+#define ho_count_bts(bts, counter) do { \
+ /* If a handover target could not be found, the counter index may be -1. */ \
+ if (counter < 0) \
+ break; \
+ LOG_HO(conn, LOGL_DEBUG, "(BTS) incrementing rate counter: %s %s\n", \
+ bts_ctr_description[counter].name, \
+ bts_ctr_description[counter].description); \
+ if (bts) \
+ rate_ctr_inc(rate_ctr_group_get_ctr(bts->bts_ctrs, counter)); \
+ else \
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->network->bts_unknown_ctrs, counter)); \
+ } while (0)
+
+/* 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)
static uint8_t g_next_ho_ref = 1;
@@ -108,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),
@@ -120,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) {
@@ -137,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));
@@ -162,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 },
};
@@ -185,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);
@@ -201,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.
@@ -241,7 +269,7 @@ static void ho_fsm_update_id(struct osmo_fsm_inst *fi, const char *label)
if (conn->fi->id)
osmo_fsm_inst_update_id_f(fi, "%s_%s", label, conn->fi->id);
else
- osmo_fsm_inst_update_id_f(fi, "%s_conn%u", label, conn->sccp.conn_id);
+ osmo_fsm_inst_update_id_f(fi, "%s_conn%u", label, conn->sccp.conn.conn_id);
}
static void handover_reset(struct gsm_subscriber_connection *conn)
@@ -249,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) {
@@ -264,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);
}
@@ -289,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");
@@ -304,15 +333,18 @@ void handover_start(struct handover_out_req *req)
* is to always create a handover_fsm instance, even if the target cell is not resolved yet. Any failure should
* then call handover_end(), which ensures that the conn snaps back to a valid state. */
handover_fsm_alloc(conn);
+ ho_count(conn_get_bts(conn), CTR_HANDOVER_ATTEMPTED);
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,
- conn, search_for, true))
- goto no_handover;
+ if (find_handover_target_cell(&local_target_cell, &remote_target_cells,
+ conn, search_for, true)) {
+ handover_end(conn, HO_RESULT_ERROR);
+ return;
+ }
if (local_target_cell) {
ho->new_bts = local_target_cell;
@@ -320,16 +352,13 @@ 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;
}
/* should never reach this, because find_handover_target_cell() would have returned error. */
OSMO_ASSERT(false);
-
-no_handover:
- handover_end(conn, HO_RESULT_FAIL_NO_CHANNEL);
}
/*! Hand over the specified logical channel to the specified new BTS and possibly change the lchan type.
@@ -340,28 +369,35 @@ static void handover_start_intra_bsc(struct gsm_subscriber_connection *conn)
struct handover *ho = &conn->ho;
struct osmo_fsm_inst *fi = conn->ho.fi;
struct lchan_activate_info info;
+ struct gsm_bts *bts = conn_get_bts(conn);
OSMO_ASSERT(ho->new_bts);
OSMO_ASSERT(ho->new_lchan_type != GSM_LCHAN_NONE);
OSMO_ASSERT(!ho->new_lchan);
- ho->scope = (ho->new_bts == conn->lchan->ts->trx->bts) ? HO_INTRA_CELL : HO_INTRA_BSC;
+ 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)
+ if (ho->scope & HO_INTRA_CELL) {
+ ho_count(bts, CTR_INTRA_CELL_HO_ATTEMPTED);
ho_fsm_update_id(fi, "intraCell");
- else
+ } else {
+ ho_count(bts, CTR_INTRA_BSC_HO_ATTEMPTED);
ho_fsm_update_id(fi, "intraBSC");
-
- ho_count(BSC_CTR_HANDOVER_ATTEMPTED);
+ 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));
@@ -369,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,
+ .ch_indctr = conn->lchan->current_ch_indctr,
.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)
@@ -394,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"
@@ -424,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)) {
@@ -453,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;
@@ -523,21 +604,30 @@ static bool parse_ho_request(struct gsm_subscriber_connection *conn, const struc
return false;
}
- /* A lot of IEs remain ignored... */
-
- return true;
-}
+ if ((e = TLVP_GET(tp, GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION))) {
+ parse_old2new_bss_info(conn, e->val, e->len, req);
+ }
-static bool chan_mode_is_tch(enum gsm48_chan_mode mode)
-{
- switch (mode) {
- case GSM48_CMODE_SPEECH_V1:
- case GSM48_CMODE_SPEECH_EFR:
- case GSM48_CMODE_SPEECH_AMR:
- return true;
- default:
+ /* 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;
}
void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
@@ -550,6 +640,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);
@@ -565,12 +656,10 @@ 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;
}
- ho_count(BSC_CTR_INTER_BSC_HO_IN_ATTEMPTED);
-
/* Figure out which cell to handover to. */
for (match_idx = 0; ; match_idx++) {
struct gsm_bts *bts;
@@ -599,7 +688,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;
@@ -611,6 +703,9 @@ void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
break;
}
+ ho_count(ho->new_bts, CTR_HANDOVER_ATTEMPTED);
+ ho_count(ho->new_bts, CTR_INTER_BSC_HO_IN_ATTEMPTED);
+
if (!ho->new_bts) {
ho_fail(HO_RESULT_ERROR, "No local cell matches the target %s",
req->cell_id_target_name);
@@ -629,26 +724,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,
- .requires_voice_stream = chan_mode_is_tch(ch_mode_rate.chan_mode),
+ .ch_mode_rate = ch_mode_rate,
+ .ch_indctr = req->ct.ch_indctr,
.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;
}
@@ -656,37 +762,71 @@ 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);
}
-#define FUNC_RESULT_COUNTER(name) \
-static int result_counter_##name(enum handover_result result) \
+/* 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) \
{ \
switch (result) { \
case HO_RESULT_OK: \
- return BSC_CTR_##name##_COMPLETED; \
+ return obj##_CTR_##name##_COMPLETED; \
case HO_RESULT_FAIL_NO_CHANNEL: \
- return BSC_CTR_##name##_NO_CHANNEL; \
+ return obj##_CTR_##name##_NO_CHANNEL; \
case HO_RESULT_FAIL_RR_HO_FAIL: \
- return BSC_CTR_##name##_FAILED; \
+ return obj##_CTR_##name##_FAILED; \
case HO_RESULT_FAIL_TIMEOUT: \
- return BSC_CTR_##name##_TIMEOUT; \
+ return obj##_CTR_##name##_TIMEOUT; \
case HO_RESULT_CONN_RELEASE: \
- return BSC_CTR_##name##_STOPPED; \
+ return obj##_CTR_##name##_STOPPED; \
default: \
case HO_RESULT_ERROR: \
- return BSC_CTR_##name##_ERROR; \
+ return obj##_CTR_##name##_ERROR; \
} \
}
-FUNC_RESULT_COUNTER(ASSIGNMENT)
-FUNC_RESULT_COUNTER(HANDOVER)
-FUNC_RESULT_COUNTER(INTER_BSC_HO_IN)
+FUNC_RESULT_COUNTER(BSC, HANDOVER)
+FUNC_RESULT_COUNTER(BSC, INTRA_CELL_HO)
+FUNC_RESULT_COUNTER(BSC, INTRA_BSC_HO)
+FUNC_RESULT_COUNTER(BSC, INTER_BSC_HO_IN)
-static int result_counter_INTER_BSC_HO_OUT(enum handover_result result) {
+/* 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:
return BSC_CTR_INTER_BSC_HO_OUT_COMPLETED;
+ case HO_RESULT_FAIL_RR_HO_FAIL:
+ return BSC_CTR_INTER_BSC_HO_OUT_FAILED;
case HO_RESULT_FAIL_TIMEOUT:
return BSC_CTR_INTER_BSC_HO_OUT_TIMEOUT;
case HO_RESULT_CONN_RELEASE:
@@ -697,25 +837,64 @@ static int result_counter_INTER_BSC_HO_OUT(enum handover_result result) {
}
}
-static int result_counter(enum handover_scope scope, enum handover_result result)
+static int result_counter_bsc(enum handover_scope scope, enum handover_result result)
{
switch (scope) {
+ default:
+ return -1;
case HO_INTRA_CELL:
- return result_counter_ASSIGNMENT(result);
+ return result_counter_BSC_INTRA_CELL_HO(result);
+ case HO_INTRA_BSC:
+ return result_counter_BSC_INTRA_BSC_HO(result);
+ case HO_INTER_BSC_OUT:
+ return result_counter_BSC_INTER_BSC_HO_OUT(result);
+ case HO_INTER_BSC_IN:
+ return result_counter_BSC_INTER_BSC_HO_IN(result);
+ }
+}
+
+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:
+ return BTS_CTR_INTER_BSC_HO_OUT_COMPLETED;
+ case HO_RESULT_FAIL_RR_HO_FAIL:
+ return BTS_CTR_INTER_BSC_HO_OUT_FAILED;
+ case HO_RESULT_FAIL_TIMEOUT:
+ return BTS_CTR_INTER_BSC_HO_OUT_TIMEOUT;
+ case HO_RESULT_CONN_RELEASE:
+ return BTS_CTR_INTER_BSC_HO_OUT_STOPPED;
+ default:
+ case HO_RESULT_ERROR:
+ return BTS_CTR_INTER_BSC_HO_OUT_ERROR;
+ }
+}
+
+static int result_counter_bts(enum handover_scope scope, enum handover_result result)
+{
+ switch (scope) {
default:
- LOGP(DHO, LOGL_ERROR, "invalid enum handover_scope value: %s\n",
- handover_scope_name(scope));
- /* use "normal" HO_INTRA_BSC counter... */
- case HO_NO_HANDOVER:
+ return -1;
+ case HO_INTRA_CELL:
+ return result_counter_BTS_INTRA_CELL_HO(result);
case HO_INTRA_BSC:
- return result_counter_HANDOVER(result);
+ return result_counter_BTS_INTRA_BSC_HO(result);
case HO_INTER_BSC_OUT:
- return result_counter_INTER_BSC_HO_OUT(result);
+ return result_counter_BTS_INTER_BSC_HO_OUT(result);
case HO_INTER_BSC_IN:
- return result_counter_INTER_BSC_HO_IN(result);
+ return result_counter_BTS_INTER_BSC_HO_IN(result);
}
}
+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;
@@ -741,7 +920,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;
@@ -749,19 +928,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) {
+ if (bsc_chan_ind_requires_rtp_stream(ho->new_lchan->activate.info.ch_indctr)) {
/* 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;
}
@@ -773,7 +953,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");
@@ -786,6 +966,7 @@ void handover_end(struct gsm_subscriber_connection *conn, enum handover_result r
{
struct handover_decision_callbacks *hdc;
struct handover *ho = &conn->ho;
+ struct gsm_bts *bts = conn_get_bts(conn);
/* Sanity -- an error result ensures beyond doubt that we don't use the new lchan below
* when the handover isn't actually allowed to change this conn. */
@@ -810,7 +991,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);
@@ -836,7 +1017,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;
}
@@ -857,13 +1039,42 @@ void handover_end(struct gsm_subscriber_connection *conn, enum handover_result r
if (hdc && hdc->on_handover_end)
hdc->on_handover_end(conn, result);
- ho_count(result_counter(ho->scope, result));
+ /* HO_INTER_BSC_IN has the source BTS on a remote BSS, so count all of those on the target BTS; also count
+ * errors onto the HO target BTS if no lchan was obtained. */
+ if (ho->scope & HO_INTER_BSC_IN)
+ bts = ho->new_bts;
+
+ ho_count_bsc(result_counter_BSC_HANDOVER(result));
+ 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);
}
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_HANDOVER_END, &result);
@@ -871,7 +1082,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;
}
@@ -906,7 +1117,7 @@ static void ho_fsm_wait_lchan_active(struct osmo_fsm_inst *fi, uint32_t event, v
* So create an MSC side endpoint CI only if a voice lchan is established for an incoming inter-BSC
* handover on AoIP. Otherwise go on to send a Handover Command and wait for the Detect.
*/
- if (ho->new_lchan->activate.info.requires_voice_stream
+ if (bsc_chan_ind_requires_rtp_stream(ho->new_lchan->activate.info.ch_indctr)
&& (ho->scope & HO_INTER_BSC_IN)
&& gscon_is_aoip(conn))
ho_fsm_state_chg(HO_ST_WAIT_MGW_ENDPOINT_TO_MSC);
@@ -1000,6 +1211,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) {
@@ -1142,10 +1354,11 @@ static void handover_start_inter_bsc_out(struct gsm_subscriber_connection *conn,
int rc;
struct handover *ho = &conn->ho;
struct osmo_fsm_inst *fi = conn->ho.fi;
+ struct gsm_bts *bts = conn_get_bts(conn);
ho->scope = HO_INTER_BSC_OUT;
ho_fsm_update_id(fi, "interBSCout");
- ho_count(BSC_CTR_INTER_BSC_HO_OUT_ATTEMPTED);
+ ho_count(bts, CTR_INTER_BSC_HO_OUT_ATTEMPTED);
rc = bsc_tx_bssmap_ho_required(conn->lchan, target_cells);
if (rc) {
@@ -1194,6 +1407,8 @@ static void ho_out_fsm_wait_clear(struct osmo_fsm_inst *fi, uint32_t event, void
{
struct gsm_subscriber_connection *conn = ho_fi_conn(fi);
switch (event) {
+ /* See also ho_fsm_allstate_action() for ho_success() on HO_EV_CONN_RELEASING */
+
case HO_EV_RR_HO_FAIL:
ho_fail(HO_RESULT_FAIL_RR_HO_FAIL, "Received RR Handover Failure message");
return;
diff --git a/src/osmo-bsc/handover_logic.c b/src/osmo-bsc/handover_logic.c
index 5be838340..1e1b6c319 100644
--- a/src/osmo-bsc/handover_logic.c
+++ b/src/osmo-bsc/handover_logic.c
@@ -43,6 +43,7 @@
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/gsm0808_utils.h>
@@ -111,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)
@@ -125,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
@@ -137,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)
@@ -160,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");
@@ -173,45 +171,47 @@ 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;
- struct gsm_bts *wildcard_match = NULL;
+ int i;
LOG_HO(conn, LOGL_DEBUG, "No explicit neighbors, regarding all local cells as neighbors\n");
- 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, true)) {
- 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),
- local_target_cell->nr, bts->nr);
- return -EINVAL;
+ /* For i == 0, look for an exact 1:1 match of all ident_key fields.
+ * For i == 1, interpret wildcard values, when no exact match exists. */
+ for (i = 0; i < 2; i++) {
+ bool exact_match = !i;
+ llist_for_each_entry(bts, &net->bts_list, list) {
+ 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",
+ cell_ab_to_str_c(OTC_SELECT, search_for),
+ local_target_cell->nr, bts->nr);
+ return -EINVAL;
+ }
+ local_target_cell = bts;
}
- local_target_cell = bts;
}
- if (neighbor_ident_key_match(&bts_key, search_for, false))
- wildcard_match = bts;
+ if (local_target_cell)
+ break;
}
- if (!local_target_cell)
- local_target_cell = wildcard_match;
-
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;
}
@@ -219,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;
}
@@ -240,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 e9333ec85..afc12d9d1 100644
--- a/src/osmo-bsc/handover_vty.c
+++ b/src/osmo-bsc/handover_vty.c
@@ -27,6 +27,7 @@
#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/handover_cfg.h>
#include <osmocom/bsc/handover_decision_2.h>
+#include <osmocom/bsc/bts.h>
static struct handover_cfg *ho_cfg_from_vty(struct vty *vty)
{
@@ -46,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]; \
@@ -103,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));
@@ -168,11 +170,10 @@ 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);
ho_vty_init_cmds(BTS_NODE);
}
-
diff --git a/src/osmo-bsc/lb.c b/src/osmo-bsc/lb.c
new file mode 100644
index 000000000..511a54528
--- /dev/null
+++ b/src/osmo-bsc/lb.c
@@ -0,0 +1,827 @@
+/* Lb interface low level SCCP handling */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.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/bsc/lb.h>
+
+#include <osmocom/gsm/bssmap_le.h>
+#include <osmocom/sigtran/sccp_helpers.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/vty.h>
+#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>
+#include <osmocom/bsc/gsm_data.h>
+
+/* Send reset to SMLC */
+int bssmap_le_tx_reset(void)
+{
+ struct osmo_ss7_instance *ss7;
+ struct msgb *msg;
+ struct bssap_le_pdu reset = {
+ .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+ .bssmap_le = {
+ .msg_type = BSSMAP_LE_MSGT_RESET,
+ .reset = GSM0808_CAUSE_EQUIPMENT_FAILURE,
+ },
+ };
+
+ 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(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(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(void)
+{
+ struct osmo_ss7_instance *ss7;
+ struct msgb *msg;
+ struct bssap_le_pdu reset_ack = {
+ .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+ .bssmap_le = {
+ .msg_type = BSSMAP_LE_MSGT_RESET_ACK,
+ },
+ };
+
+ ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc->cs7_instance);
+ OSMO_ASSERT(ss7);
+ 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(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,
+ const struct osmo_sccp_user *scu)
+{
+ struct osmo_ss7_instance *ss7;
+ struct bssap_le_pdu bssap_le;
+ 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)) {
+ LOGP(DLCS, LOGL_ERROR, "Rx BSSMAP-LE UnitData from unknown remote address: %s\n",
+ osmo_sccp_addr_name(ss7, smlc_addr));
+ 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(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG));
+ return -EINVAL;
+ }
+
+ if (bssap_le.discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE) {
+ LOGP(DLCS, LOGL_ERROR, "Rx BSSAP-LE: discr %d not implemented\n", bssap_le.discr);
+ return -ENOTSUP;
+ }
+
+ switch (bssap_le.bssmap_le.msg_type) {
+ case BSSMAP_LE_MSGT_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 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(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 osmo_fsm_inst_dispatch(bsc_gsmnet->smlc->bssmap_reset->fi, BSSMAP_RESET_EV_RX_RESET_ACK, NULL);
+
+ default:
+ 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;
+ }
+}
+
+static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
+{
+ struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph;
+ struct osmo_sccp_user *scu = _scu;
+ struct osmo_sccp_instance *sccp = osmo_sccp_get_sccp(scu);
+ struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(sccp);
+ struct gsm_subscriber_connection *conn;
+ int rc = 0;
+
+ switch (OSMO_PRIM_HDR(&scu_prim->oph)) {
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
+ /* Handle inbound UnitData */
+ DEBUGP(DLCS, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
+ rc = handle_unitdata_from_smlc(&scu_prim->u.unitdata.calling_addr, oph->msg, scu);
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
+ /* Handle inbound connections. A Location Request is always started on the A interface, and OsmoBSC
+ * forwards this to the SMLC by performing an N-CONNECT from BSC -> SMLC. This is the reverse
+ * direction: N-CONNECT from SMLC -> BSC, which should never happen. */
+ LOGP(DLCS, LOGL_ERROR, "N-CONNECT.ind(X->%u): inbound connect from SMLC is not expected to happen\n",
+ scu_prim->u.connect.conn_id);
+ rc = osmo_sccp_tx_disconn(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, 0);
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
+ /* Handle inbound confirmation of outbound connection */
+ DEBUGP(DLCS, "N-CONNECT.cnf(%u)\n", scu_prim->u.connect.conn_id);
+ conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.connect.conn_id);
+ if (conn) {
+ conn->lcs.lb.state = SUBSCR_SCCP_ST_CONNECTED;
+ if (msgb_l2len(oph->msg) > 0) {
+ rc = lcs_loc_req_rx_bssmap_le(conn, oph->msg);
+ }
+ } else {
+ LOGP(DLCS, LOGL_ERROR, "N-CONNECT.cfm(%u) for unknown conn\n", scu_prim->u.connect.conn_id);
+ rc = -EINVAL;
+ }
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
+ /* Handle incoming connection oriented data */
+ DEBUGP(DLCS, "N-DATA.ind(%u)\n", scu_prim->u.data.conn_id);
+
+ conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.data.conn_id);
+ if (!conn) {
+ LOGP(DLCS, LOGL_ERROR, "N-DATA.ind(%u) for unknown conn_id\n", scu_prim->u.data.conn_id);
+ rc = -EINVAL;
+ } else if (conn->lcs.lb.state != SUBSCR_SCCP_ST_CONNECTED) {
+ LOGP(DLCS, LOGL_ERROR, "N-DATA.ind(%u) for conn that is not confirmed\n",
+ scu_prim->u.data.conn_id);
+ rc = -EINVAL;
+ } else {
+ rc = lcs_loc_req_rx_bssmap_le(conn, oph->msg);
+ }
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
+ DEBUGP(DLCS, "N-DISCONNECT.ind(%u, %s, cause=%i)\n", scu_prim->u.disconnect.conn_id,
+ osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)),
+ scu_prim->u.disconnect.cause);
+ /* indication of disconnect */
+ conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.disconnect.conn_id);
+ if (!conn) {
+ LOGP(DLCS, LOGL_ERROR, "N-DISCONNECT.ind for unknown conn_id %u\n",
+ scu_prim->u.disconnect.conn_id);
+ rc = -EINVAL;
+ } else {
+ bsc_sccp_inst_unregister_gscon(bsc_sccp, &conn->lcs.lb.conn);
+ conn->lcs.lb.conn.conn_id = SCCP_CONN_ID_UNSET;
+ conn->lcs.lb.state = SUBSCR_SCCP_ST_NONE;
+ if (msgb_l2len(oph->msg) > 0) {
+ rc = lcs_loc_req_rx_bssmap_le(conn, oph->msg);
+ }
+ }
+ break;
+
+ default:
+ LOGP(DLCS, LOGL_ERROR, "Unhandled SIGTRAN primitive %s.%s\n",
+ osmo_scu_prim_type_name(oph->primitive),
+ get_value_string(osmo_prim_op_names, oph->operation));
+ break;
+ }
+
+ msgb_free(oph->msg);
+ return rc;
+}
+
+static int lb_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg)
+{
+ struct osmo_ss7_instance *ss7;
+ uint32_t conn_id;
+ int rc;
+ struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(bsc_gsmnet->smlc->sccp);
+
+ OSMO_ASSERT(conn);
+ OSMO_ASSERT(msg);
+
+ if (conn->lcs.lb.state != SUBSCR_SCCP_ST_NONE) {
+ LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR,
+ "Cannot open BSSMAP-LE conn to SMLC, another conn is still active for this subscriber\n");
+ return -EINVAL;
+ }
+
+ conn_id = bsc_sccp_inst_next_conn_id(bsc_sccp);
+ if (conn_id == SCCP_CONN_ID_UNSET) {
+ LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR, "Unable to allocate SCCP Connection ID for BSSMAP-LE to SMLC\n");
+ return -ENOSPC;
+ }
+ conn->lcs.lb.conn.conn_id = conn_id;
+ if (bsc_sccp_inst_register_gscon(bsc_sccp, &conn->lcs.lb.conn) < 0) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to register Lb SCCP connection (id=%u)\n", conn->lcs.lb.conn.conn_id);
+ return -1;
+ }
+ ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc->cs7_instance);
+ OSMO_ASSERT(ss7);
+ LOGPFSMSL(conn->fi, DLCS, LOGL_INFO, "Opening new SCCP connection (id=%u) to SMLC: %s\n", conn_id,
+ 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);
+ if (rc >= 0) {
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_SUCCESS));
+ conn->lcs.lb.state = SUBSCR_SCCP_ST_WAIT_CONN_CONF;
+ } else {
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_ERR_SEND));
+ goto failed_unregister_conn_id;
+ }
+
+ return rc;
+
+failed_unregister_conn_id:
+ bsc_sccp_inst_unregister_gscon(bsc_sccp, &conn->lcs.lb.conn);
+ conn->lcs.lb.conn.conn_id = SCCP_CONN_ID_UNSET;
+ return rc;
+}
+
+void lb_close_conn(struct gsm_subscriber_connection *conn)
+{
+ struct bsc_sccp_inst *bsc_sccp;
+ if (conn->lcs.lb.state == SUBSCR_SCCP_ST_NONE)
+ return;
+ OSMO_ASSERT(conn->lcs.lb.conn.conn_id != SCCP_CONN_ID_UNSET);
+
+ osmo_sccp_tx_disconn(bsc_gsmnet->smlc->sccp_user, conn->lcs.lb.conn.conn_id, &bsc_gsmnet->smlc->bsc_addr, 0);
+
+ conn->lcs.lb.state = SUBSCR_SCCP_ST_NONE;
+ bsc_sccp = osmo_sccp_get_priv(bsc_gsmnet->smlc->sccp);
+ bsc_sccp_inst_unregister_gscon(bsc_sccp, &conn->lcs.lb.conn);
+ conn->lcs.lb.conn.conn_id = SCCP_CONN_ID_UNSET;
+}
+
+/* Send data to SMLC, take ownership of *msg */
+int lb_send(struct gsm_subscriber_connection *conn, const struct bssap_le_pdu *bssap_le)
+{
+ int rc;
+ struct msgb *msg;
+
+ 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",
+ osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));
+ return -EINVAL;
+ }
+
+ if (conn->lcs.lb.state == SUBSCR_SCCP_ST_NONE) {
+ rc = lb_open_conn(conn, msg);
+ goto count_tx;
+ }
+
+ 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.conn_id, msg);
+ if (rc >= 0)
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_SUCCESS));
+ else
+ rate_ctr_inc(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_ERR_SEND));
+
+count_tx:
+ if (rc < 0)
+ return rc;
+
+ switch (bssap_le->bssmap_le.msg_type) {
+ case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:
+ 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(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(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(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(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(rate_ctr_group_get_ctr(bsc_gsmnet->smlc->ctrs, SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_ABORT));
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Default point-code to be used as local address (BSC) */
+#define BSC_DEFAULT_PC "0.23.3"
+
+/* Default point-code to be used as remote address (SMLC) */
+#define SMLC_DEFAULT_PC "0.23.6"
+
+#define DEFAULT_ASP_LOCAL_IP "localhost"
+#define DEFAULT_ASP_REMOTE_IP "localhost"
+
+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;
+ struct osmo_sccp_instance *sccp;
+ enum osmo_ss7_asp_protocol used_proto = OSMO_SS7_ASP_PROT_M3UA;
+ char inst_name[32];
+ const char *smlc_name = "smlc";
+ struct bsc_sccp_inst *bsc_sccp;
+
+ /* 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);
+ 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,
+ osmo_ss7_pointcode_parse(NULL, SMLC_DEFAULT_PC),
+ OSMO_SCCP_SSN_SMLC_BSSAP_LE);
+
+ /* Set up SCCP user and one ASP+AS */
+ snprintf(inst_name, sizeof(inst_name), "Lb-%u-%s", cs7_inst->cfg.id, osmo_ss7_asp_protocol_name(used_proto));
+ LOGP(DLCS, LOGL_NOTICE, "Initializing SCCP connection for Lb/%s on cs7 instance %u\n",
+ osmo_ss7_asp_protocol_name(used_proto), cs7_inst->cfg.id);
+
+ /* SS7 Protocol stack */
+ default_pc = osmo_ss7_pointcode_parse(NULL, BSC_DEFAULT_PC);
+ sccp = osmo_sccp_simple_client_on_ss7_id(tall_bsc_ctx, cs7_inst->cfg.id, inst_name,
+ default_pc, used_proto,
+ 0, DEFAULT_ASP_LOCAL_IP,
+ 0, DEFAULT_ASP_REMOTE_IP);
+ if (!sccp)
+ return -EINVAL;
+ bsc_gsmnet->smlc->sccp = sccp;
+ bsc_sccp = bsc_sccp_inst_alloc(tall_bsc_ctx);
+ bsc_sccp->sccp = sccp;
+ osmo_sccp_set_priv(sccp, bsc_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,
+ 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)) {
+ 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));
+ return -EINVAL;
+ }
+
+ 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));
+ 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));
+ 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));
+
+ 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;
+}
+
+/*********************************************************************************
+ * VTY Interface (Configuration + Introspection)
+ *********************************************************************************/
+
+DEFUN(cfg_smlc, cfg_smlc_cmd,
+ "smlc", "Configure Lb Link to Serving Mobile Location Centre\n")
+{
+ vty->node = SMLC_NODE;
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node smlc_node = {
+ SMLC_NODE,
+ "%s(config-smlc)# ",
+ 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) {
+ if (addr->ssn != want_ssn)
+ vty_out(vty,
+ "setting an SSN (%u) different from the standard (%u) is not allowed, will use standard SSN for address: %s%s",
+ addr->ssn, want_ssn, osmo_sccp_addr_dump(addr), VTY_NEWLINE);
+ }
+
+ addr->presence |= OSMO_SCCP_ADDR_T_SSN;
+ 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",
+ "Local SCCP address of this BSC towards the SMLC\n" "Name of cs7 addressbook entry\n")
+{
+ 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);
+ if (!ss7) {
+ vty_out(vty, "Error: No such SCCP addressbook entry: '%s'%s", bsc_addr_name, VTY_NEWLINE);
+ return CMD_ERR_INCOMPLETE;
+ }
+
+ if (!smlc_set_cs7_instance(vty, "smlc / bsc-addr", bsc_addr_name, ss7))
+ return CMD_WARNING;
+
+ 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;
+}
+
+DEFUN(cfg_smlc_cs7_smlc_addr,
+ cfg_smlc_cs7_smlc_addr_cmd,
+ "smlc-addr NAME",
+ "Remote SCCP address of the SMLC\n" "Name of cs7 addressbook entry\n")
+{
+ 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);
+ if (!ss7) {
+ vty_out(vty, "Error: No such SCCP addressbook entry: '%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;
+
+ 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->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);
+ }
+ if (bsc_gsmnet->smlc->smlc_addr_name) {
+ vty_out(vty, " smlc-addr %s%s",
+ bsc_gsmnet->smlc->smlc_addr_name, VTY_NEWLINE);
+ }
+
+ return 0;
+}
+
+DEFUN(show_smlc, show_smlc_cmd,
+ "show smlc",
+ SHOW_STR "Display state of SMLC / Lb\n")
+{
+ vty_out(vty, "not implemented%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+void smlc_vty_init(void)
+{
+ install_element_ve(&show_smlc_cmd);
+
+ 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);
+}
+
+const struct rate_ctr_desc smlc_ctr_description[] = {
+ [SMLC_CTR_BSSMAP_LE_RX_UNKNOWN_PEER] = {
+ "bssmap_le:rx:unknown_peer",
+ "Number of received BSSMAP-LE messages from an unknown Calling SCCP address"
+ },
+ [SMLC_CTR_BSSMAP_LE_RX_UDT_RESET] = {
+ "bssmap_le:rx:udt:reset:request",
+ "Number of received BSSMAP-LE UDT RESET messages"
+ },
+ [SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK] = {
+ "bssmap_le:rx:udt:reset:ack",
+ "Number of received BSSMAP-LE UDT RESET ACKNOWLEDGE messages"
+ },
+ [SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG] = {
+ "bssmap_le:rx:udt:err:inval",
+ "Number of received invalid BSSMAP-LE UDT messages"
+ },
+ [SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG] = {
+ "bssmap_le:rx:dt1:err:inval",
+ "Number of received invalid BSSMAP-LE"
+ },
+ [SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS] = {
+ "bssmap_le:rx:dt1:location:response_success",
+ "Number of received BSSMAP-LE Perform Location Response messages containing a location estimate"
+ },
+ [SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE] = {
+ "bssmap_le:rx:dt1:location:response_failure",
+ "Number of received BSSMAP-LE Perform Location Response messages containing a failure cause"
+ },
+
+ [SMLC_CTR_BSSMAP_LE_TX_ERR_INVALID_MSG] = {
+ "bssmap_le:tx:err:inval",
+ "Number of outgoing BSSMAP-LE messages that are invalid (a bug?)"
+ },
+ [SMLC_CTR_BSSMAP_LE_TX_ERR_CONN_NOT_READY] = {
+ "bssmap_le:tx:err:conn_not_ready",
+ "Number of BSSMAP-LE messages we tried to send when the connection was not ready yet"
+ },
+ [SMLC_CTR_BSSMAP_LE_TX_ERR_SEND] = {
+ "bssmap_le:tx:err:send",
+ "Number of socket errors while sending BSSMAP-LE messages"
+ },
+ [SMLC_CTR_BSSMAP_LE_TX_SUCCESS] = {
+ "bssmap_le:tx:success",
+ "Number of successfully sent BSSMAP-LE messages"
+ },
+
+ [SMLC_CTR_BSSMAP_LE_TX_UDT_RESET] = {
+ "bssmap_le:tx:udt:reset:request",
+ "Number of transmitted BSSMAP-LE UDT RESET messages"
+ },
+ [SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK] = {
+ "bssmap_le:tx:udt:reset:ack",
+ "Number of transmitted BSSMAP-LE UDT RESET ACK messages"
+ },
+ [SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_REQUEST] = {
+ "bssmap_le:tx:dt1:location:response",
+ "Number of transmitted BSSMAP-LE DT1 Perform Location Request messages"
+ },
+ [SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_ABORT] = {
+ "bssmap_le:rx:dt1:location:abort",
+ "Number of received BSSMAP-LE Perform Location Abort messages"
+ },
+
+ [SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_REQUEST] = {
+ "bssmap_le:rx:dt1:bsslap:ta_request",
+ "Number of received BSSMAP-LE Connection Oriented Information messages"
+ " with BSSLAP APDU containing TA Request"
+ },
+
+ [SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_RESPONSE] = {
+ "bssmap_le:tx:dt1:bsslap:ta_response",
+ "Number of sent BSSMAP-LE Connection Oriented Information messages"
+ " with BSSLAP APDU containing TA Response"
+ },
+ [SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_REJECT] = {
+ "bssmap_le:tx:dt1:bsslap:reject",
+ "Number of sent BSSMAP-LE Connection Oriented Information messages"
+ " with BSSLAP APDU containing Reject"
+ },
+ [SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_RESET] = {
+ "bssmap_le:tx:dt1:bsslap:reset",
+ "Number of sent BSSMAP-LE Connection Oriented Information messages"
+ " with BSSLAP APDU containing Reset"
+ },
+ [SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_ABORT] = {
+ "bssmap_le:tx:dt1:bsslap:abort",
+ "Number of sent BSSMAP-LE Connection Oriented Information messages"
+ " with BSSLAP APDU containing Abort"
+ },
+
+};
+
+const struct rate_ctr_group_desc smlc_ctrg_desc = {
+ "smlc",
+ "serving mobile location centre",
+ OSMO_STATS_CLASS_GLOBAL,
+ ARRAY_SIZE(smlc_ctr_description),
+ smlc_ctr_description,
+};
diff --git a/src/osmo-bsc/lchan.c b/src/osmo-bsc/lchan.c
new file mode 100644
index 000000000..45d8d96f5
--- /dev/null
+++ b/src/osmo-bsc/lchan.c
@@ -0,0 +1,150 @@
+/* (C) 2022 by sysmocom - s.f.m.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 a1bf85701..55875f0b4 100644
--- a/src/osmo-bsc/lchan_fsm.c
+++ b/src/osmo-bsc/lchan_fsm.c
@@ -35,11 +35,15 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/bsc_rll.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/bsc/assignment_fsm.h>
#include <osmocom/bsc/handover_fsm.h>
#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>
+#include <osmocom/bsc/vgcs_fsm.h>
static struct osmo_fsm lchan_fsm;
@@ -59,36 +63,78 @@ bool lchan_may_receive_data(struct gsm_lchan *lchan)
switch (lchan->fi->state) {
case LCHAN_ST_WAIT_RLL_RTP_ESTABLISH:
case LCHAN_ST_ESTABLISHED:
+ case LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK:
return true;
default:
return false;
}
}
-void lchan_set_last_error(struct gsm_lchan *lchan, const char *fmt, ...)
+bool lchan_is_asci(struct gsm_lchan *lchan)
+{
+ if (lchan->activate.info.type_for == LCHAN_TYPE_FOR_VGCS ||
+ lchan->activate.info.type_for == LCHAN_TYPE_FOR_VBS)
+ return true;
+ return false;
+}
+
+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;
+
+ switch (lchan->modify.info.modify_for) {
- if (fmt) {
- va_start(ap, fmt);
- lchan->last_error = talloc_vasprintf(lchan->ts->trx, fmt, ap);
- va_end(ap);
+ case MODIFY_FOR_ASSIGNMENT:
+ osmo_fsm_inst_dispatch(lchan->conn->assignment.fi, ASSIGNMENT_EV_LCHAN_MODIFIED, lchan);
+ break;
- LOG_LCHAN(lchan, LOGL_ERROR, "%s\n", lchan->last_error);
+ 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;
- if (last_error_was)
- talloc_free(last_error_was);
+ 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;
+
+ 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)
{
@@ -98,7 +144,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",
@@ -110,14 +156,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) {
@@ -135,11 +193,32 @@ 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_VGCS_CHANNEL:
+ LOG_LCHAN(lchan, LOGL_NOTICE, "Signalling VGCS Assignment FSM of error (%s)\n",
+ lchan->last_error ? : "unknown error");
+ if (!for_conn) {
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "lchan activation for VGCS Assignment failed, but activation request has no conn\n");
+ break;
+ }
+ if (!for_conn->vgcs_chan.fi) {
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "lchan activation for VGCS Assignment failed, but conn has no ongoing"
+ " assignment procedure\n");
+ break;
+ }
+ _osmo_fsm_inst_dispatch(for_conn->vgcs_chan.fi, VGCS_EV_LCHAN_ERROR, lchan, file, line);
+ break;
+
+ 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");
@@ -153,25 +232,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,
@@ -181,18 +277,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);
@@ -201,6 +297,26 @@ static void lchan_on_fully_established(struct gsm_lchan *lchan)
* we will try to roll back a modified RTP connection. */
break;
+ case ACTIVATE_FOR_VGCS_CHANNEL:
+ if (!lchan->conn) {
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "lchan activation for VGCS assignment succeeded, but lchan has no conn\n");
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
+ break;
+ }
+ if (!lchan->conn->vgcs_chan.fi) {
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "lchan Activation for VGCS assignment requested, but conn has no VGCS resource FSM.\n");
+ lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL, NULL);
+ break;
+ }
+ osmo_fsm_inst_dispatch(lchan->conn->vgcs_chan.fi, VGCS_EV_LCHAN_ACTIVE, lchan);
+ 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));
@@ -209,13 +325,18 @@ 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=23001 },
- [LCHAN_ST_WAIT_ACTIV_ACK] = { .T=-23002 },
- [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=993111 },
+ [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 = -6 },
+ [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 },
+ [LCHAN_ST_BORKEN] = { .T = -28 },
+ [LCHAN_ST_RECOVER_WAIT_ACTIV_ACK] = { .T = -6 },
+ [LCHAN_ST_RECOVER_WAIT_RF_RELEASE_ACK] = { .T = -6 },
};
/* Transition to a state, using the T timer defined in lchan_fsm_timeouts.
@@ -237,7 +358,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); \
@@ -246,7 +367,7 @@ struct osmo_tdef_state_timeout lchan_fsm_timeouts[32] = {
else \
LOG_LCHAN(_lchan, LOGL_DEBUG, "After failure handling, already in state %s\n", \
osmo_fsm_state_name(fsm, state_chg)); \
- } while(0)
+ } while (0)
/* Which state to transition to when lchan_fail() is called in a given state. */
uint32_t lchan_fsm_on_error[32] = {
@@ -260,6 +381,10 @@ uint32_t lchan_fsm_on_error[32] = {
[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_WAIT_RF_RELEASE_ACK,
+ [LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK] = LCHAN_ST_WAIT_RF_RELEASE_ACK,
+ [LCHAN_ST_RECOVER_WAIT_ACTIV_ACK] = LCHAN_ST_BORKEN,
+ [LCHAN_ST_RECOVER_WAIT_RF_RELEASE_ACK] = LCHAN_ST_BORKEN,
};
#define lchan_fail(fmt, args...) lchan_fail_to(lchan_fsm_on_error[fi->state], fmt, ## args)
@@ -270,6 +395,16 @@ void lchan_activate(struct gsm_lchan *lchan, struct lchan_activate_info *info)
OSMO_ASSERT(lchan && info);
+ if ((info->type_for == LCHAN_TYPE_FOR_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;
@@ -278,7 +413,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");
@@ -295,7 +430,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");
@@ -317,6 +452,28 @@ void lchan_activate(struct gsm_lchan *lchan, struct lchan_activate_info *info)
}
break;
+ case ACTIVATE_FOR_VGCS_CHANNEL:
+ if (!info->for_conn
+ || !info->for_conn->fi) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "Activation requested, but no conn\n");
+ goto abort;
+ }
+ if (!info->for_conn->vgcs_chan.fi) {
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "Activation for VGCS assignment requested, but conn has no VGCS resource FSM.\n");
+ goto abort;
+ }
+ if (info->for_conn->vgcs_chan.new_lchan != lchan) {
+ LOG_LCHAN(lchan, LOGL_ERROR,
+ "Activation for VGCS assignment requested, but conn's VGCS assignment state does"
+ " not reflect this lchan to be activated (instead: %s)\n",
+ info->for_conn->vgcs_chan.new_lchan ?
+ gsm_lchan_name(info->for_conn->vgcs_chan.new_lchan)
+ : "NULL");
+ goto abort;
+ }
+ break;
+
default:
break;
}
@@ -337,23 +494,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_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);
+ OSMO_ASSERT(lchan && info);
+
+ if ((info->type_for == LCHAN_TYPE_FOR_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)
+{
+ 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);
@@ -365,6 +549,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
@@ -378,8 +563,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);
@@ -402,83 +590,127 @@ static void lchan_reset(struct gsm_lchan *lchan)
.meas_rep_last_seen_nr = 255,
.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,
};
}
static void lchan_fsm_unused_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;
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
+ * has become unused now. */
+ abis_rsl_chan_rqd_queue_poll(bts);
}
-/* Configure the multirate setting on this channel. */
-static int lchan_mr_config(struct gsm_lchan *lchan, const struct gsm48_multi_rate_conf *mr_conf)
+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)
{
- bool full_rate = (lchan->type == GSM_LCHAN_TCH_F);
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
struct gsm_bts *bts = lchan->ts->trx->bts;
- struct bsc_msc_data *msc = lchan->conn->sccp.msc;
- struct amr_multirate_conf *mr;
+
+ /* We also need to poll the channel request queue when the FSM enters the WAIT_AFTER_ERROR
+ * state. In case of an emergency call the channel request queue will skip the waiting
+ * period. */
+ abis_rsl_chan_rqd_queue_poll(bts);
+}
+
+/* Configure the multirate setting on this channel. */
+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)
+{
int rc;
- int rc_rate;
- struct gsm48_multi_rate_conf mr_conf_filtered;
- const struct gsm48_multi_rate_conf *mr_conf_bts;
-
- /* 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;
-
- /* 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, &msc->amr_conf, mr_conf);
- if (rc_rate < 0) {
- LOG_LCHAN(lchan, LOGL_ERROR,
- "can not encode multirate configuration (invalid amr rate setting, MSC)\n");
+ 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_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;
}
- /* 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;
+ /* Do not include 12.2 kbps rate when S1 is set. */
+ 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;
}
- /* Ensure that the resulting filtered conf is coherent with the
- * configuration that is set for the BTS and the specified rate */
- 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;
+ 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;
+ }
}
- /* 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");
+ 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;
}
- 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;
+
+ /* 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;
+
+ /* 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;
}
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;
@@ -490,10 +722,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;
@@ -503,92 +741,110 @@ 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.type_for == LCHAN_TYPE_FOR_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;
+ }
+ }
+
+ lchan->activate.ch_indctr = lchan->activate.info.ch_indctr;
+ 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);
- struct gsm48_multi_rate_conf mr_conf;
struct gsm_bts *bts = lchan->ts->trx->bts;
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;
+ bool requires_rtp_stream;
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 (info->chan_mode == GSM48_CMODE_SPEECH_AMR) {
- if (gsm48_mr_cfg_from_gsm0808_sc_cfg(&mr_conf, info->s15_s0) < 0) {
- lchan_fail("Can not determine multirate configuration, S15-S0 (%04x) are ambiguous!\n",
- info->s15_s0);
- return;
- }
-
- /* Do not include 12.2 kbps rate when S1 is set. */
- if (lchan->type == GSM_LCHAN_TCH_H && (info->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 (lchan_mr_config(lchan, &mr_conf) < 0) {
- lchan_fail("Can not generate multirate configuration IE\n");
- return;
- }
- lchan->s15_s0 = info->s15_s0;
+ if ((ms_power_dbm = ms_pwr_dbm(bts->band, old_lchan->ms_power)) == 0)
+ ms_power_dbm = bts->ms_max_power;
+ }
+ lchan_update_ms_power_ctrl_level(lchan, ms_power_dbm);
+
+ /* Default BS Power reduction value (in dB) */
+ lchan->bs_power_db = (bts->bs_power_ctrl.mode == GSM_PWR_CTRL_MODE_DYN_BTS) ?
+ bts->bs_power_ctrl.bs_power_max_db :
+ bts->bs_power_ctrl.bs_power_val_db;
+ /* BS Power Control is generally not allowed on the BCCH/CCCH carrier.
+ * However, we allow it in the BCCH carrier power reduction mode of operation. */
+ if (lchan->ts->trx == bts->c0) {
+ lchan->bs_power_db = OSMO_MIN(lchan->ts->c0_max_power_red_db,
+ lchan->bs_power_db);
}
- switch (info->chan_mode) {
+ if (lchan_activate_set_ch_mode_rate_and_mr_config(lchan))
+ return;
- case GSM48_CMODE_SIGN:
- lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
- lchan->tch_mode = GSM48_CMODE_SIGN;
- 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;
- 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;
-
- 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);
+ requires_rtp_stream = bsc_chan_ind_requires_rtp_stream(lchan->activate.info.ch_indctr);
LOG_LCHAN(lchan, LOGL_INFO,
- "Activation requested: %s voice=%s MGW-ci=%s type=%s tch-mode=%s encr-alg=A5/%u ck=%s\n",
+ "Activation requested: %s rtp=%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 ?
+ requires_rtp_stream ? "yes" : "no",
+ requires_rtp_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");
@@ -597,8 +853,20 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
osmo_fsm_inst_dispatch(lchan->ts->fi, TS_EV_LCHAN_REQUESTED, lchan);
/* Prepare an MGW endpoint CI if appropriate. */
- if (lchan->activate.info.requires_voice_stream)
+ if (requires_rtp_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)
@@ -619,7 +887,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;
@@ -642,24 +910,47 @@ 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:
+ case ACTIVATE_FOR_VGCS_CHANNEL:
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);
@@ -679,6 +970,7 @@ static void lchan_fsm_wait_activ_ack(struct osmo_fsm_inst *fi, uint32_t event, v
if (data) {
uint32_t next_state;
lchan->release.rsl_error_cause = *(uint8_t*)data;
+ lchan->release.rr_cause = bsc_gsm48_rr_cause_from_rsl_cause(lchan->release.rsl_error_cause);
lchan->release.in_error = true;
if (lchan->release.rsl_error_cause != RSL_ERR_RCH_ALR_ACTV_ALLOC)
next_state = LCHAN_ST_BORKEN;
@@ -691,6 +983,7 @@ static void lchan_fsm_wait_activ_ack(struct osmo_fsm_inst *fi, uint32_t event, v
rsl_err_name(lchan->release.rsl_error_cause), lchan->release.rsl_error_cause);
} else {
lchan->release.rsl_error_cause = RSL_ERR_IE_NONEXIST;
+ lchan->release.rr_cause = bsc_gsm48_rr_cause_from_rsl_cause(lchan->release.rsl_error_cause);
lchan->release.in_error = true;
lchan_fail_to(LCHAN_ST_BORKEN, "Chan Activ NACK without cause IE");
}
@@ -716,10 +1009,24 @@ 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->current_ch_indctr = lchan->activate.ch_indctr;
+ lchan->vamos.enabled = (lchan->activate.info.type_for == LCHAN_TYPE_FOR_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;
@@ -727,51 +1034,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. */
@@ -790,23 +1095,44 @@ static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)
static void lchan_fsm_wait_rll_rtp_establish_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ bool requires_rtp_stream = bsc_chan_ind_requires_rtp_stream(lchan->activate.info.ch_indctr);
+
if (lchan->fi_rtp)
osmo_fsm_inst_dispatch(lchan->fi_rtp, LCHAN_RTP_EV_LCHAN_READY, 0);
+ /* Prepare an MGW endpoint CI if appropriate (late). */
+ else if (requires_rtp_stream)
+ lchan_rtp_fsm_start(lchan);
+
+ /* When activating a channel for VTY or VGCS/VBS, skip waiting for activity from
+ * lchan_rtp_fsm, but only if no rtp stream is required. */
+ if ((lchan->activate.info.activ_for == ACTIVATE_FOR_VTY ||
+ lchan->activate.info.activ_for == ACTIVATE_FOR_VGCS_CHANNEL) &&
+ !requires_rtp_stream) {
+ lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);
+ }
}
static void lchan_fsm_wait_rll_rtp_establish(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ bool requires_rtp_stream = bsc_chan_ind_requires_rtp_stream(lchan->activate.info.ch_indctr);
+
switch (event) {
case LCHAN_EV_RLL_ESTABLISH_IND:
- if (!lchan->activate.info.requires_voice_stream
- || lchan_rtp_established(lchan))
+ if (!requires_rtp_stream || lchan_rtp_established(lchan)) {
+ LOG_LCHAN(lchan, LOGL_DEBUG,
+ "%s\n",
+ (requires_rtp_stream ?
+ "RTP already established earlier" : "no voice stream required"));
lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);
+ }
return;
case LCHAN_EV_RTP_READY:
- if (lchan->sapis[0] != LCHAN_SAPI_UNUSED)
+ /* If RLL was established or if it does not need to be establised, because of VGCS/VBS channel. */
+ if (lchan->sapis[0] != LCHAN_SAPI_UNUSED ||
+ lchan->activate.info.activ_for == ACTIVATE_FOR_VGCS_CHANNEL)
lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);
return;
@@ -818,7 +1144,90 @@ 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;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+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->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)
+{
+ switch (event) {
+
+ case LCHAN_EV_RR_CHAN_MODE_MODIFY_ACK:
+ lchan_fsm_state_chg(LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK);
+ return;
+
+ case LCHAN_EV_RR_CHAN_MODE_MODIFY_ERROR:
+ 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;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void lchan_fsm_wait_rsl_chan_mode_modify_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ int rc;
+
+ 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",
+ osmo_fsm_inst_state_name(fi));
+ }
+}
+
+static void lchan_fsm_wait_rsl_chan_mode_modify_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ switch (event) {
+
+ case LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK:
+ /* 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->current_ch_indctr = lchan->modify.ch_indctr;
+ lchan->tsc_set = lchan->modify.tsc_set;
+ lchan->tsc = lchan->modify.tsc;
+ lchan->vamos.enabled = (lchan->modify.info.type_for == LCHAN_TYPE_FOR_VAMOS);
+
+ if (bsc_chan_ind_requires_rtp_stream(lchan->modify.info.ch_indctr) && !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,
+ .ch_indctr = lchan->modify.info.ch_indctr,
+ .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 {
+ 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",
osmo_fsm_event_name(fi->fsm, event),
osmo_fsm_inst_state_name(fi));
return;
@@ -891,11 +1300,22 @@ 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);
+ /* A VGCS/VBS channel must stay active, even if all SAPIs are released.
+ * When a talker releases, the channel is available for the listeners and the next talker. The actual
+ * channel release is performed by the VGCS/VBS call control. */
+ if (!lchan_is_asci(lchan)) {
+ 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 */
@@ -904,6 +1324,9 @@ 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_modify_info *modif_info;
+ struct osmo_mgcpc_ep_ci *use_mgwep_ci;
+ bool requires_rtp_stream = bsc_chan_ind_requires_rtp_stream(lchan->modify.info.ch_indctr);
switch (event) {
case LCHAN_EV_RLL_ESTABLISH_IND:
@@ -913,7 +1336,8 @@ static void lchan_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void
case LCHAN_EV_RLL_REL_IND:
case LCHAN_EV_RLL_REL_CONF:
handle_rll_rel_ind_or_conf(fi, event, data);
- if (!lchan_active_sapis(lchan, 0))
+ /* Only release channel, if there is no SAPI and this channel is not a VGCS channel. */
+ if (!lchan_active_sapis(lchan, 0) && !lchan_is_asci(lchan))
lchan_fsm_state_chg(LCHAN_ST_WAIT_RLL_RTP_RELEASED);
return;
@@ -925,11 +1349,62 @@ 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);
+
+ lchan->modify.ch_mode_rate = lchan->modify.info.ch_mode_rate;
+ lchan->modify.ch_mode_rate.chan_mode = (lchan->modify.info.type_for == LCHAN_TYPE_FOR_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;
+ }
+
+ 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;
+ }
+ }
+
+ lchan->modify.ch_indctr = lchan->modify.info.ch_indctr;
+
+ /* 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 rtp=%s MGW-ci=%s type=%s tch-mode=%s tsc=%d/%u\n",
+ lchan_modify_for_name(lchan->modify.info.modify_for),
+ requires_rtp_stream ? "yes" : "no",
+ requires_rtp_stream ?
+ (use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new")
+ : "none",
+ gsm_chan_t_name(lchan->type),
+ gsm48_chan_mode_name(lchan->modify.ch_mode_rate.chan_mode),
+ lchan->modify.tsc_set, lchan->modify.tsc);
+
+ lchan_fsm_state_chg(LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK);
+ return;
+
default:
OSMO_ASSERT(false);
}
@@ -952,8 +1427,14 @@ static bool should_sacch_deact(struct gsm_lchan *lchan)
static void lchan_do_release(struct gsm_lchan *lchan)
{
- if (lchan->release.do_rr_release && lchan->sapis[0] != LCHAN_SAPI_UNUSED)
- gsm48_send_rr_release(lchan);
+ if (lchan->release.do_rr_release) {
+ /* To main DCCH in dedicated and group transmit mode */
+ if (lchan->sapis[0] != LCHAN_SAPI_UNUSED)
+ gsm48_send_rr_release(lchan, false);
+ /* As UI to all listeners in group receive mode */
+ if (lchan_is_asci(lchan))
+ gsm48_send_rr_release(lchan, true);
+ }
if (lchan->fi_rtp)
osmo_fsm_inst_dispatch(lchan->fi_rtp, LCHAN_RTP_EV_RELEASE, 0);
@@ -1015,7 +1496,7 @@ static void lchan_fsm_wait_rll_rtp_released(struct osmo_fsm_inst *fi, uint32_t e
* TODO: that's how the code was before lchan FSM, is this correct/useful? */
handle_rll_rel_ind_or_conf(fi, event, data);
break;
-
+
case LCHAN_EV_RTP_RELEASED:
case LCHAN_EV_RTP_ERROR:
break;
@@ -1037,8 +1518,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);
@@ -1063,6 +1545,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);
}
@@ -1071,6 +1560,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:
@@ -1085,59 +1575,60 @@ static void lchan_fsm_borken_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta
case LCHAN_ST_BORKEN:
ctr = BTS_CTR_LCHAN_BORKEN_FROM_BORKEN;
break;
+ case LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK:
+ ctr = BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RR_CHAN_MODE_MODIFY_ACK;
+ break;
+ case LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK:
+ ctr = BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RSL_CHAN_MODE_MODIFY_ACK;
+ break;
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);
lchan_fsm_state_chg(LCHAN_ST_WAIT_RF_RELEASE_ACK);
return;
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:
@@ -1145,6 +1636,71 @@ static void lchan_fsm_borken(struct osmo_fsm_inst *fi, uint32_t event, void *dat
}
}
+static void lchan_fsm_recover_wait_activ_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ int rc;
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+
+ LOG_LCHAN(lchan, LOGL_INFO, "attempting to recover from BORKEN lchan\n");
+
+ lchan->type = GSM_LCHAN_SDCCH;
+ lchan->activate.info.ta_known = true;
+
+ chan_counts_ts_update(lchan->ts);
+
+ rc = rsl_tx_chan_activ(lchan, RSL_ACT_INTRA_NORM_ASS, 0);
+ if (rc)
+ lchan_fail("Tx Chan Activ failed: %s (%d)", strerror(-rc), rc);
+}
+
+static void lchan_fsm_recover_wait_activ_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+
+ switch (event) {
+
+ case LCHAN_EV_RSL_CHAN_ACTIV_ACK:
+ lchan_fsm_state_chg(LCHAN_ST_RECOVER_WAIT_RF_RELEASE_ACK);
+ break;
+
+ case LCHAN_EV_RSL_CHAN_ACTIV_NACK:
+ /* If an earlier lchan activ got through to the BTS, but the
+ * ACK did not get back to the BSC, it may still be active on
+ * the BTS side. Proceed to release it. */
+ LOG_LCHAN(lchan, LOGL_NOTICE, "received NACK for activation of BORKEN lchan, assuming still active\n");
+ lchan_fsm_state_chg(LCHAN_ST_RECOVER_WAIT_RF_RELEASE_ACK);
+ break;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void lchan_fsm_recover_wait_rf_release_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ int rc;
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+
+ rc = rsl_tx_rf_chan_release(lchan);
+ if (rc)
+ lchan_fail("Tx RSL RF Channel Release failed: %s (%d)\n", strerror(-rc), rc);
+}
+
+static void lchan_fsm_recover_wait_rf_release_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ switch (event) {
+
+ case LCHAN_EV_RSL_RF_CHAN_REL_ACK:
+ LOG_LCHAN(lchan, LOGL_NOTICE, "successfully recovered BORKEN lchan\n");
+ lchan_fsm_state_chg(LCHAN_ST_UNUSED);
+ break;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
#define S(x) (1 << (x))
static const struct osmo_fsm_state lchan_fsm_states[] = {
@@ -1163,6 +1719,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)
,
@@ -1216,6 +1773,35 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
| S(LCHAN_ST_WAIT_RLL_RTP_RELEASED)
,
},
+ [LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK] = {
+ .name = "WAIT_CHAN_RR_MODE_MODIFY_ACK",
+ .onenter = lchan_fsm_wait_rr_chan_mode_modify_ack_onenter,
+ .action = lchan_fsm_wait_rr_chan_mode_modify_ack,
+ .in_event_mask = 0
+ | S(LCHAN_EV_RR_CHAN_MODE_MODIFY_ACK)
+ | S(LCHAN_EV_RR_CHAN_MODE_MODIFY_ERROR)
+ ,
+ .out_state_mask = 0
+ | 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] = {
+ .name = "WAIT_RSL_CHAN_MODE_MODIFY_ACK",
+ .onenter = lchan_fsm_wait_rsl_chan_mode_modify_ack_onenter,
+ .action = lchan_fsm_wait_rsl_chan_mode_modify_ack,
+ .in_event_mask = 0
+ | S(LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK)
+ | S(LCHAN_EV_RSL_CHAN_MODE_MODIFY_NACK)
+ ,
+ .out_state_mask = 0
+ | 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] = {
.name = "ESTABLISHED",
.onenter = lchan_fsm_established_onenter,
@@ -1226,12 +1812,14 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
| S(LCHAN_EV_RLL_ESTABLISH_IND) /* ignored */
| S(LCHAN_EV_RTP_ERROR)
| S(LCHAN_EV_RTP_RELEASED)
+ | S(LCHAN_EV_REQUEST_MODE_MODIFY)
,
.out_state_mask = 0
| S(LCHAN_ST_UNUSED)
| S(LCHAN_ST_WAIT_RLL_RTP_RELEASED)
| S(LCHAN_ST_WAIT_BEFORE_RF_RELEASE)
| S(LCHAN_ST_WAIT_RF_RELEASE_ACK)
+ | S(LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK)
,
},
[LCHAN_ST_WAIT_RLL_RTP_RELEASED] = {
@@ -1267,6 +1855,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
@@ -1277,6 +1866,7 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
},
[LCHAN_ST_WAIT_AFTER_ERROR] = {
.name = "WAIT_AFTER_ERROR",
+ .onenter = lchan_fsm_wait_after_error_onenter,
.in_event_mask = 0
| S(LCHAN_EV_RTP_RELEASED) /* ignore late lchan_rtp_fsm release events */
,
@@ -1294,11 +1884,38 @@ 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)
| S(LCHAN_ST_UNUSED)
| S(LCHAN_ST_WAIT_AFTER_ERROR)
+ | S(LCHAN_ST_RECOVER_WAIT_ACTIV_ACK)
+ ,
+ },
+ [LCHAN_ST_RECOVER_WAIT_ACTIV_ACK] = {
+ .name = "RECOVER_WAIT_ACTIV_ACK",
+ .onenter = lchan_fsm_recover_wait_activ_ack_onenter,
+ .action = lchan_fsm_recover_wait_activ_ack,
+ .in_event_mask = 0
+ | S(LCHAN_EV_RSL_CHAN_ACTIV_ACK)
+ | S(LCHAN_EV_RSL_CHAN_ACTIV_NACK)
+ ,
+ .out_state_mask = 0
+ | S(LCHAN_ST_BORKEN)
+ | S(LCHAN_ST_RECOVER_WAIT_RF_RELEASE_ACK)
+ ,
+ },
+ [LCHAN_ST_RECOVER_WAIT_RF_RELEASE_ACK] = {
+ .name = "RECOVER_WAIT_RF_RELEASE_ACK",
+ .onenter = lchan_fsm_recover_wait_rf_release_ack_onenter,
+ .action = lchan_fsm_recover_wait_rf_release_ack,
+ .in_event_mask = 0
+ | S(LCHAN_EV_RSL_RF_CHAN_REL_ACK)
+ ,
+ .out_state_mask = 0
+ | S(LCHAN_ST_BORKEN)
+ | S(LCHAN_ST_UNUSED)
,
},
};
@@ -1317,25 +1934,49 @@ static const struct value_string lchan_fsm_event_names[] = {
OSMO_VALUE_STRING(LCHAN_EV_RLL_REL_CONF),
OSMO_VALUE_STRING(LCHAN_EV_RSL_RF_CHAN_REL_ACK),
OSMO_VALUE_STRING(LCHAN_EV_RLL_ERR_IND),
- OSMO_VALUE_STRING(LCHAN_EV_CHAN_MODE_MODIF_ACK),
- OSMO_VALUE_STRING(LCHAN_EV_CHAN_MODE_MODIF_ERROR),
+ OSMO_VALUE_STRING(LCHAN_EV_RR_CHAN_MODE_MODIFY_ACK),
+ 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),
{}
};
-void lchan_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+static void lchan_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
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
+ * 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:
return;
}
}
-int lchan_fsm_timer_cb(struct osmo_fsm_inst *fi)
+void lchan_fsm_skip_error(struct gsm_lchan *lchan)
+{
+ struct osmo_fsm_inst *fi = lchan->fi;
+ if (fi->state == LCHAN_ST_WAIT_AFTER_ERROR)
+ lchan_fsm_state_chg(LCHAN_ST_UNUSED);
+}
+
+static int lchan_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
switch (fi->state) {
@@ -1348,18 +1989,31 @@ int lchan_fsm_timer_cb(struct osmo_fsm_inst *fi)
lchan_fsm_state_chg(LCHAN_ST_UNUSED);
return 0;
+ case LCHAN_ST_BORKEN:
+ lchan_fsm_state_chg(LCHAN_ST_RECOVER_WAIT_ACTIV_ACK);
+ return 0;
+
default:
lchan->release.in_error = true;
lchan->release.rsl_error_cause = RSL_ERR_INTERWORKING;
- lchan_fail("Timeout");
+ lchan->release.rr_cause = bsc_gsm48_rr_cause_from_rsl_cause(lchan->release.rsl_error_cause);
+ if (fi->state == LCHAN_ST_WAIT_RLL_RTP_ESTABLISH) {
+ lchan_fail("Timeout (rll_ready=%s,rtp_require=%s,voice_ready=%s)",
+ (lchan->sapis[0] != LCHAN_SAPI_UNUSED) ? "yes" : "no",
+ bsc_chan_ind_requires_rtp_stream(lchan->activate.info.ch_indctr) ? "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)
@@ -1369,8 +2023,12 @@ void lchan_release(struct gsm_lchan *lchan, bool do_rr_release,
struct osmo_fsm_inst *fi = lchan->fi;
lchan->release.in_error = err;
- lchan->release.rsl_error_cause = cause_rr;
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. */
@@ -1409,12 +2067,12 @@ exit_release_handler:
lchan->release.in_release_handler = false;
}
-void lchan_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+static void lchan_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause 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) {
@@ -1457,6 +2115,7 @@ static struct osmo_fsm lchan_fsm = {
.allstate_action = lchan_fsm_allstate_action,
.allstate_event_mask = 0
| S(LCHAN_EV_TS_ERROR)
+ | S(LCHAN_EV_RLL_ERR_IND)
,
.timer_cb = lchan_fsm_timer_cb,
.cleanup = lchan_fsm_cleanup,
diff --git a/src/osmo-bsc/lchan_rtp_fsm.c b/src/osmo-bsc/lchan_rtp_fsm.c
index 4be979002..e8384c604 100644
--- a/src/osmo-bsc/lchan_rtp_fsm.c
+++ b/src/osmo-bsc/lchan_rtp_fsm.c
@@ -30,6 +30,8 @@
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#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;
@@ -42,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=-23004 },
- [LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK] = { .T=-23005 },
- [LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK] = { .T=-23006 },
- [LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = { .T=-23004 },
+ [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.
@@ -62,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);
}
@@ -136,9 +138,16 @@ 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_ipa_abisip_bts(lchan->ts->trx->bts)) {
+ LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "Audio link to-BTS via E1, skipping IPACC\n");
+ lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_LCHAN_READY);
+ return;
+ }
if (use_mgwep_ci) {
LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "MGW endpoint already available: %s\n",
@@ -147,7 +156,7 @@ static void lchan_rtp_fsm_wait_mgw_endpoint_available_onenter(struct osmo_fsm_in
return;
}
- mgwep = gscon_ensure_mgw_endpoint(lchan->conn, lchan->activate.info.msc_assigned_cic);
+ mgwep = gscon_ensure_mgw_endpoint(lchan->conn, lchan->activate.info.msc_assigned_cic, lchan);
if (!mgwep) {
lchan_rtp_fail("Internal error: cannot obtain MGW endpoint handle for conn");
return;
@@ -155,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;
+ crcx_info.call_id = lchan->conn->sccp.conn.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);
@@ -171,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;
@@ -244,10 +290,10 @@ static void lchan_rtp_fsm_post_lchan_ready(struct osmo_fsm_inst *fi)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
- if (is_ipaccess_bts(lchan->ts->trx->bts))
+ if (is_ipa_abisip_bts(lchan->ts->trx->bts))
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK);
else
- lchan_rtp_fsm_switch_rtp(fi);
+ lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED);
}
static void lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
@@ -261,20 +307,41 @@ 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);
+ if (lchan->current_ch_indctr == GSM0808_CHAN_DATA) {
+ enum rsl_ipac_rtp_csd_format_d format_d = RSL_IPAC_RTP_CSD_TRAU_BTS;
+
+ if (lchan->activate.ch_mode_rate.data_transparent) {
+ val = ipacc_rtp_csd_fmt_transp(&lchan->activate.ch_mode_rate, format_d);
+ if (val < 0) {
+ lchan_rtp_fail("Cannot determine Abis/IP RTP CSD format for rsl_cmod_csd_t=%d",
+ lchan->activate.ch_mode_rate.data_rate.t);
+ return;
+ }
+ } else {
+ val = ipacc_rtp_csd_fmt_non_transp(&lchan->activate.ch_mode_rate, format_d);
+ if (val < 0) {
+ lchan_rtp_fail("Cannot determine Abis/IP RTP CSD format for rsl_cmod_csd_nt=%d",
+ lchan->activate.ch_mode_rate.data_rate.nt);
+ return;
+ }
+ }
+ lchan->abis_ip.rtp_csd_fmt = val;
+ } else {
+ 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",
+ 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->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));
- return;
- }
- lchan->abis_ip.speech_mode = val;
-
- val = ipacc_payload_type(lchan->tch_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;
@@ -294,10 +361,7 @@ static void lchan_rtp_fsm_wait_ipacc_crcx_ack(struct osmo_fsm_inst *fi, uint32_t
switch (event) {
case LCHAN_RTP_EV_IPACC_CRCX_ACK:
- /* the CRCX ACK parsing has already noted the RTP port information at
- * lchan->abis_ip.bound_*, see ipac_parse_rtp(). We'll use that in
- * lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter(). */
- lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK);
+ lchan_rtp_fsm_switch_rtp(fi);
return;
case LCHAN_RTP_EV_IPACC_CRCX_NACK:
@@ -323,6 +387,7 @@ static void lchan_rtp_fsm_wait_ipacc_mdcx_ack_onenter(struct osmo_fsm_inst *fi,
int rc;
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
const struct mgcp_conn_peer *mgw_rtp;
+ struct in_addr sin;
if (lchan->release.requested) {
lchan_rtp_fail("Release requested while activating");
@@ -337,8 +402,13 @@ static void lchan_rtp_fsm_wait_ipacc_mdcx_ack_onenter(struct osmo_fsm_inst *fi,
return;
}
- /* Other RTP settings were already setup in lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter() */
- lchan->abis_ip.connect_ip = ntohl(inet_addr(mgw_rtp->addr));
+ /* Other RTP settings were already set up in lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter() */
+ if (inet_pton(AF_INET, mgw_rtp->addr, &sin) != 1) {
+ /* Only IPv4 addresses are supported in IPACC */
+ lchan_rtp_fail("Invalid remote IPv4 address %s", mgw_rtp->addr);
+ return;
+ }
+ lchan->abis_ip.connect_ip = ntohl(sin.s_addr);
lchan->abis_ip.connect_port = mgw_rtp->port;
/* send-recv */
@@ -353,21 +423,16 @@ static void lchan_rtp_fsm_wait_ipacc_mdcx_ack_onenter(struct osmo_fsm_inst *fi,
static void lchan_rtp_fsm_wait_ipacc_mdcx_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
- struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
switch (event) {
case LCHAN_RTP_EV_IPACC_MDCX_ACK:
- lchan_rtp_fsm_switch_rtp(fi);
+ lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_READY);
return;
case LCHAN_RTP_EV_IPACC_MDCX_NACK:
lchan_rtp_fail("Received NACK on IPACC MDCX");
return;
- case LCHAN_RTP_EV_READY_TO_SWITCH_RTP:
- lchan->activate.info.wait_before_switching_rtp = false;
- return;
-
case LCHAN_RTP_EV_RELEASE:
case LCHAN_RTP_EV_ROLLBACK:
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);
@@ -406,9 +471,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);
@@ -444,6 +516,12 @@ static void lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter(struct osmo_fsm_i
return;
}
+ if (!is_ipa_abisip_bts(lchan->ts->trx->bts)) {
+ LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "Audio link to-BTS via E1, skipping IPACC\n");
+ lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_READY);
+ return;
+ }
+
/* At this point, we are taking over an old lchan's MGW endpoint (if any). */
if (!lchan->mgw_endpoint_ci_bts && old_lchan) {
/* The old lchan shall forget the endpoint now. We might put it back upon ROLLBACK */
@@ -461,10 +539,15 @@ static void lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter(struct osmo_fsm_i
static void lchan_rtp_fsm_wait_mgw_endpoint_configured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
- switch (event) {
+ struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
+ switch (event) {
case LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED:
- lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_READY);
+ if (is_ipa_abisip_bts(lchan->ts->trx->bts))
+ lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK);
+ else {
+ lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_READY);
+ }
return;
case LCHAN_RTP_EV_MGW_ENDPOINT_ERROR:
@@ -506,6 +589,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);
}
@@ -520,7 +609,11 @@ static void lchan_rtp_fsm_rollback_onenter(struct osmo_fsm_inst *fi, uint32_t pr
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);
return;
}
- connect_mgw_endpoint_to_lchan(fi, lchan->mgw_endpoint_ci_bts, old_lchan);
+
+ if (is_ipa_abisip_bts(lchan->ts->trx->bts))
+ connect_mgw_endpoint_to_lchan(fi, lchan->mgw_endpoint_ci_bts, old_lchan);
+ else
+ osmo_fsm_inst_dispatch(fi, LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED, 0);
}
static void lchan_rtp_fsm_rollback(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -629,23 +722,8 @@ static const struct osmo_fsm_state lchan_rtp_fsm_states[] = {
| S(LCHAN_RTP_EV_ROLLBACK)
,
.out_state_mask = 0
- | S(LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK)
- ,
- },
- [LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK] = {
- .name = "WAIT_IPACC_MDCX_ACK",
- .onenter = lchan_rtp_fsm_wait_ipacc_mdcx_ack_onenter,
- .action = lchan_rtp_fsm_wait_ipacc_mdcx_ack,
- .in_event_mask = 0
- | S(LCHAN_RTP_EV_READY_TO_SWITCH_RTP)
- | S(LCHAN_RTP_EV_IPACC_MDCX_ACK)
- | S(LCHAN_RTP_EV_IPACC_MDCX_NACK)
- | S(LCHAN_RTP_EV_RELEASE)
- | S(LCHAN_RTP_EV_ROLLBACK)
- ,
- .out_state_mask = 0
| S(LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP)
- | S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED)
+ | S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED) /*old: LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK*/
,
},
[LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP] = {
@@ -671,6 +749,22 @@ static const struct osmo_fsm_state lchan_rtp_fsm_states[] = {
| S(LCHAN_RTP_EV_ROLLBACK)
,
.out_state_mask = 0
+ | S(LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK)
+ | S(LCHAN_RTP_ST_READY)
+ | S(LCHAN_RTP_ST_ROLLBACK)
+ ,
+ },
+ [LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK] = {
+ .name = "WAIT_IPACC_MDCX_ACK",
+ .onenter = lchan_rtp_fsm_wait_ipacc_mdcx_ack_onenter,
+ .action = lchan_rtp_fsm_wait_ipacc_mdcx_ack,
+ .in_event_mask = 0
+ | S(LCHAN_RTP_EV_IPACC_MDCX_ACK)
+ | S(LCHAN_RTP_EV_IPACC_MDCX_NACK)
+ | S(LCHAN_RTP_EV_RELEASE)
+ | S(LCHAN_RTP_EV_ROLLBACK)
+ ,
+ .out_state_mask = 0
| S(LCHAN_RTP_ST_READY)
| S(LCHAN_RTP_ST_ROLLBACK)
,
@@ -683,6 +777,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)
@@ -729,7 +824,7 @@ static const struct value_string lchan_rtp_fsm_event_names[] = {
{}
};
-int lchan_rtp_fsm_timer_cb(struct osmo_fsm_inst *fi)
+static int lchan_rtp_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
lchan->release.in_error = true;
@@ -738,7 +833,7 @@ int lchan_rtp_fsm_timer_cb(struct osmo_fsm_inst *fi)
return 0;
}
-void lchan_rtp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+static void lchan_rtp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
if (lchan->mgw_endpoint_ci_bts) {
@@ -775,7 +870,13 @@ 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_DATA_14k5:
+ case GSM48_CMODE_DATA_12k0:
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ return CODEC_CLEARMODE;
+
case GSM48_CMODE_SPEECH_V1:
if (full_rate)
return CODEC_GSM_8000_1;
@@ -813,14 +914,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;
}
@@ -832,8 +933,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) */
@@ -845,7 +946,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;
@@ -854,3 +955,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 d63db22d3..c830bd1ee 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>
@@ -28,152 +30,342 @@
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/lchan_select.h>
+#include <osmocom/bsc/bts.h>
+
+struct lchan_select_ts_list {
+ struct gsm_bts_trx_ts **list;
+ unsigned int num;
+};
-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)
+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),
+ OSMO_VALUE_STRING(SELECT_FOR_VGCS),
+ {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;
- }
-
- 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 (unsigned int tn = 0; tn < ts_list->num; tn++) {
+ struct gsm_bts_trx_ts *ts = ts_list->list[tn];
+ int lchans_as_pchan;
- 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 */
+ case GSM48_CMODE_DATA_14k5:
+ case GSM48_CMODE_DATA_12k0:
+ /* these rates work over full-rate 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:
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
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;
+
+ int pwr_a = trx_a->nominal_power - trx_a->max_power_red;
+ int pwr_b = trx_b->nominal_power - trx_b->max_power_red;
- return lchan_select_by_type(bts, type);
+ /* Sort in descending order */
+ return pwr_b - pwr_a;
}
-/* 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)
+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_select_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;
+ case SELECT_FOR_VGCS:
+ chan_alloc_reverse = bts->chan_alloc_vgcs_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;
@@ -185,57 +377,79 @@ struct gsm_lchan *lchan_select_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);
}
- 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));
+ 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,
+ enum lchan_select_reason reason,
+ void *ctx)
+{
+ struct gsm_lchan *lchan = NULL;
+
+ 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));
+
+ lchan = lchan_avail_by_type(bts, type, reason, ctx, true);
+ 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
new file mode 100644
index 000000000..bb0c5e273
--- /dev/null
+++ b/src/osmo-bsc/lcs_loc_req.c
@@ -0,0 +1,615 @@
+/* Handle LCS BSSMAP-LE Perform Location Request */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.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/bsc/lcs_loc_req.h>
+
+#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
+#include <osmocom/bsc/bsc_subscriber.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/lb.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gad.h>
+#include <osmocom/gsm/bsslap.h>
+#include <osmocom/gsm/bssmap_le.h>
+#include <osmocom/gsm/gsm0808_lcs.h>
+#include <osmocom/bsc/lcs_ta_req.h>
+#include <osmocom/bsc/paging.h>
+#include <osmocom/bsc/bts_trx.h>
+#include <osmocom/bsc/bts.h>
+
+enum lcs_loc_req_fsm_state {
+ LCS_LOC_REQ_ST_INIT,
+ LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE,
+ LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING,
+ LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE,
+ LCS_LOC_REQ_ST_FAILED,
+};
+
+static const struct value_string lcs_loc_req_fsm_event_names[] = {
+ OSMO_VALUE_STRING(LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE),
+ OSMO_VALUE_STRING(LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT),
+ OSMO_VALUE_STRING(LCS_LOC_REQ_EV_TA_REQ_START),
+ OSMO_VALUE_STRING(LCS_LOC_REQ_EV_TA_REQ_END),
+ OSMO_VALUE_STRING(LCS_LOC_REQ_EV_HANDOVER_PERFORMED),
+ OSMO_VALUE_STRING(LCS_LOC_REQ_EV_CONN_CLEAR),
+ {}
+};
+
+static struct osmo_fsm lcs_loc_req_fsm;
+
+static const struct osmo_tdef_state_timeout lcs_loc_req_fsm_timeouts[32] = {
+ [LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE] = { .T = -11 },
+};
+
+/* Transition to a state, using the T timer defined in lcs_loc_req_fsm_timeouts.
+ * The actual timeout value is in turn obtained from network->T_defs.
+ * Assumes local variable fi exists. */
+#define lcs_loc_req_fsm_state_chg(FI, STATE) \
+ osmo_tdef_fsm_inst_state_chg(FI, STATE, \
+ lcs_loc_req_fsm_timeouts, \
+ (bsc_gsmnet)->T_defs, \
+ 5)
+
+#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", \
+ 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)
+
+static struct lcs_loc_req *lcs_loc_req_alloc(struct osmo_fsm_inst *parent_fi, uint32_t parent_event_term)
+{
+ struct lcs_loc_req *lcs_loc_req;
+
+ struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&lcs_loc_req_fsm, parent_fi, parent_event_term);
+ OSMO_ASSERT(fi);
+
+ lcs_loc_req = talloc(fi, struct lcs_loc_req);
+ OSMO_ASSERT(lcs_loc_req);
+ fi->priv = lcs_loc_req;
+ *lcs_loc_req = (struct lcs_loc_req){
+ .fi = fi,
+ };
+
+ return lcs_loc_req;
+}
+
+static bool parse_bssmap_perf_loc_req(struct lcs_loc_req *lcs_loc_req, struct msgb *msg)
+{
+ struct tlv_parsed tp_arr[1];
+ struct tlv_parsed *tp = &tp_arr[0];
+ 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)
+
+ payload_length = msg->tail - msg->l4h;
+ if (tlv_parse2(tp_arr, 1, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0) <= 0)
+ PARSE_ERR("Failed to parse IEs");
+
+ if (!(e = TLVP_GET(tp, GSM0808_IE_LOCATION_TYPE)))
+ PARSE_ERR("Missing Location Type IE");
+ if (osmo_bssmap_le_ie_dec_location_type(&lcs_loc_req->req.location_type, -1, -1, NULL, NULL, e->val, e->len))
+ PARSE_ERR("Failed to parse Location Type IE");
+
+ if ((e = TLVP_GET(tp, GSM0808_IE_CELL_IDENTIFIER))) {
+ if (gsm0808_dec_cell_id(&lcs_loc_req->req.cell_id, e->val, e->len) <= 0)
+ PARSE_ERR("Failed to parse Cell Identifier IE");
+ 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)
+ PARSE_ERR("Failed to parse IMSI IE");
+ }
+
+ if ((e = TLVP_GET(tp, GSM0808_IE_IMEI))) {
+ if (osmo_mobile_identity_decode(&lcs_loc_req->req.imei, e->val, e->len, false)
+ || lcs_loc_req->req.imei.type != GSM_MI_TYPE_IMEI)
+ PARSE_ERR("Failed to parse IMEI IE");
+ }
+
+ /* A lot of IEs remain ignored... */
+
+ return true;
+#undef PARSE_ERR
+}
+
+void lcs_loc_req_start(struct gsm_subscriber_connection *conn, struct msgb *loc_req_msg)
+{
+ struct lcs_loc_req *lcs_loc_req;
+
+ if (conn->lcs.loc_req) {
+ LOG_LCS_LOC_REQ(conn, LOGL_ERROR,
+ "Ignoring Perform Location Request, another request is still pending\n");
+ return;
+ }
+
+ lcs_loc_req = lcs_loc_req_alloc(conn->fi, GSCON_EV_LCS_LOC_REQ_END);
+
+ lcs_loc_req->conn = conn;
+ conn->lcs.loc_req = lcs_loc_req;
+
+ if (!parse_bssmap_perf_loc_req(lcs_loc_req, loc_req_msg))
+ return;
+
+ if (!conn->bsub) {
+ if (lcs_loc_req->req.imsi.type != GSM_MI_TYPE_IMSI) {
+ lcs_loc_req_fail(LCS_CAUSE_DATA_MISSING_IN_REQ,
+ "tx Perform Location Request: Missing identity:"
+ " No IMSI included in request, and also no active subscriber");
+ return;
+ }
+
+ conn->bsub = bsc_subscr_find_or_create_by_mi(bsc_gsmnet->bsc_subscribers, &lcs_loc_req->req.imsi,
+ BSUB_USE_CONN);
+ if (!conn->bsub) {
+ lcs_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE,
+ "tx Perform Location Request: Cannot assign subscriber");
+ return;
+ }
+ }
+
+ /* state change to start the timeout */
+ lcs_loc_req_fsm_state_chg(lcs_loc_req->fi, LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE);
+}
+
+static int handle_bssmap_le_conn_oriented_info(struct lcs_loc_req *lcs_loc_req, const struct bssmap_le_pdu *bssmap_le)
+{
+ switch (bssmap_le->conn_oriented_info.apdu.msg_type) {
+ case BSSLAP_MSGT_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);
+ default:
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "rx BSSLAP APDU with unsupported message type %d\n",
+ bssmap_le->conn_oriented_info.apdu.msg_type);
+ return -ENOTSUP;
+ };
+}
+
+int lcs_loc_req_rx_bssmap_le(struct gsm_subscriber_connection *conn, struct msgb *msg)
+{
+ struct lcs_loc_req *lcs_loc_req = conn->lcs.loc_req;
+ struct bssap_le_pdu bssap_le;
+ 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,
+ "Rx BSSMAP-LE message, but no Location Request is ongoing\n");
+ return -EINVAL;
+ }
+
+ 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(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(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG));
+ return -ENOTSUP;
+ }
+
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_DEBUG, "Rx %s\n", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));
+
+ 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(rate_ctr_group_get_ctr(ctrg, SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS));
+ else
+ 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);
+
+ case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
+ return handle_bssmap_le_conn_oriented_info(lcs_loc_req, &bssap_le.bssmap_le);
+
+ default:
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Rx BSSMAP-LE from SMLC with unsupported message type: %s\n",
+ osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));
+ return -ENOTSUP;
+ }
+}
+
+void lcs_loc_req_reset(struct gsm_subscriber_connection *conn)
+{
+ struct lcs_loc_req *lcs_loc_req = conn->lcs.loc_req;
+ if (!lcs_loc_req)
+ return;
+ lcs_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Aborting Location Request due to RESET on Lb");
+}
+
+static int lcs_loc_req_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct lcs_loc_req *lcs_loc_req = fi->priv;
+ lcs_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Timeout");
+ return 1;
+}
+
+static int lcs_loc_req_send(struct lcs_loc_req *lcs_loc_req, const struct bssap_le_pdu *bssap_le)
+{
+ int rc = lb_send(lcs_loc_req->conn, bssap_le);
+ if (rc)
+ lcs_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE,
+ "Failed to send %s", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));
+ return rc;
+}
+
+static void lcs_loc_req_wait_loc_resp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct lcs_loc_req *lcs_loc_req = fi->priv;
+ struct bssap_le_pdu plr;
+ struct gsm_lchan *lchan;
+
+ if (prev_state == LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING) {
+ /* LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING should halt the FSM timeout. As soon as the TA Request is
+ * served, re-entering LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE, but of course there is then no need to
+ * send a second BSSMAP-LE Perform Location Request to the SMLC. */
+ return;
+ }
+
+ if (!lcs_loc_req->req.cell_id_present) {
+ lcs_loc_req_fail(LCS_CAUSE_PROTOCOL_ERROR,
+ "Cannot encode BSSMAP-LE Perform Location Request,"
+ " because mandatory Cell Identity is not known");
+ return;
+ }
+
+ plr = (struct bssap_le_pdu){
+ .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+ .bssmap_le = {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_REQ,
+ .perform_loc_req = {
+ .location_type = lcs_loc_req->req.location_type,
+ .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,
+ },
+ },
+ };
+
+ /* If we already have an active lchan, send the known TA directly to the SMLC */
+ lchan = lcs_loc_req->conn->lchan;
+ if (lchan) {
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_DEBUG,
+ "Active lchan present, including BSSLAP APDU with TA Layer 3\n");
+ plr.bssmap_le.perform_loc_req.apdu_present = true;
+ plr.bssmap_le.perform_loc_req.apdu = (struct bsslap_pdu){
+ .msg_type = BSSLAP_MSGT_TA_LAYER3,
+ .ta_layer3 = {
+ .ta = lchan->last_ta,
+ },
+ };
+ } else {
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_DEBUG,
+ "No active lchan, not including BSSLAP APDU\n");
+ }
+
+ /* Establish Lb connection to SMLC and send the BSSMAP-LE Perform Location Request */
+ lcs_loc_req_send(lcs_loc_req, &plr);
+}
+
+static void lcs_loc_req_bssmap_le_abort(struct lcs_loc_req *lcs_loc_req)
+{
+ struct bssap_le_pdu pla = {
+ .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+ .bssmap_le = {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_ABORT,
+ .perform_loc_abort = {
+ .present = true,
+ .cause_val = LCS_CAUSE_REQUEST_ABORTED,
+ },
+ },
+ };
+
+ lcs_loc_req_send(lcs_loc_req, &pla);
+}
+
+/* After a handover, send the new lchan information to the SMLC via a BSSLAP Reset message.
+ * See 3GPP TS 48.071 4.2.6 Reset. */
+static void lcs_loc_req_handover_performed(struct lcs_loc_req *lcs_loc_req)
+{
+ struct gsm_lchan *lchan = lcs_loc_req->conn->lchan;
+ struct bssap_le_pdu bsslap = {
+ .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+ .bssmap_le = {
+ .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
+ },
+ };
+ struct bsslap_pdu *apdu = &bsslap.bssmap_le.conn_oriented_info.apdu;
+
+ if (!lchan) {
+ /* The handover was out of this BSS. Abort the location procedure. */
+ *apdu = (struct bsslap_pdu){
+ .msg_type = BSSLAP_MSGT_ABORT,
+ .abort = BSSLAP_CAUSE_INTER_BSS_HO,
+ };
+ } else {
+ *apdu = (struct bsslap_pdu){
+ .msg_type = BSSLAP_MSGT_RESET,
+ .reset = {
+ .cell_id = lchan->ts->trx->bts->cell_identity,
+ .ta = lchan->last_ta,
+ .cause = BSSLAP_CAUSE_INTRA_BSS_HO,
+ },
+ };
+ 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);
+}
+
+static void lcs_loc_req_wait_loc_resp_and_ta_req_ongoing_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct lcs_loc_req *lcs_loc_req = fi->priv;
+ const struct bssmap_le_pdu *bssmap_le;
+
+ switch (event) {
+
+ case LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE:
+ bssmap_le = data;
+ OSMO_ASSERT(bssmap_le->msg_type == BSSMAP_LE_MSGT_PERFORM_LOC_RESP);
+ lcs_loc_req->resp = bssmap_le->perform_loc_resp;
+ lcs_loc_req->resp_present = true;
+ lcs_loc_req_fsm_state_chg(fi, LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE);
+ break;
+
+ case LCS_LOC_REQ_EV_TA_REQ_START:
+ if (fi->state != LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING)
+ lcs_loc_req_fsm_state_chg(fi, LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING);
+ break;
+
+ case LCS_LOC_REQ_EV_TA_REQ_END:
+ if (fi->state != LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE)
+ lcs_loc_req_fsm_state_chg(fi, LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE);
+ break;
+
+ case LCS_LOC_REQ_EV_HANDOVER_PERFORMED:
+ lcs_loc_req_handover_performed(lcs_loc_req);
+ break;
+
+ case LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT:
+ case LCS_LOC_REQ_EV_CONN_CLEAR:
+ if (lcs_loc_req->ta_req)
+ osmo_fsm_inst_dispatch(lcs_loc_req->ta_req->fi, LCS_TA_REQ_EV_ABORT, NULL);
+ lcs_loc_req_bssmap_le_abort(lcs_loc_req);
+ osmo_fsm_inst_term(lcs_loc_req->fi, OSMO_FSM_TERM_REGULAR, NULL);
+ break;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void lcs_loc_req_got_loc_resp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct lcs_loc_req *lcs_loc_req = fi->priv;
+ struct msgb *msg;
+ int rc;
+ struct gsm0808_perform_location_response plr = {
+ .location_estimate_present = lcs_loc_req->resp.location_estimate_present,
+ .location_estimate = lcs_loc_req->resp.location_estimate,
+ .lcs_cause = lcs_loc_req->resp.lcs_cause,
+ };
+
+ if (plr.location_estimate_present) {
+ struct osmo_gad gad;
+ struct osmo_gad_err *err;
+ if (osmo_gad_dec(&gad, &err, OTC_SELECT, &plr.location_estimate))
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
+ "Perform Location Response contains Location Estimate with error: %s\n",
+ err->logmsg);
+ else
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_INFO,
+ "Perform Location Response contains Location Estimate: %s\n",
+ osmo_gad_to_str_c(OTC_SELECT, &gad));
+ }
+
+ if (plr.lcs_cause.present) {
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
+ "Perform Location Response contains error cause: %d\n",
+ plr.lcs_cause.cause_val);
+ }
+
+ msg = gsm0808_create_perform_location_response(&plr);
+ if (!msg) {
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
+ "Failed to encode BSSMAP Perform Location Response (A-interface)\n");
+ } else {
+ rc = gscon_sigtran_send(lcs_loc_req->conn, msg);
+ if (rc < 0)
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
+ "Failed to send Perform Location Response (A-interface)\n");
+ else
+ 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);
+}
+
+static void lcs_loc_req_failed_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct lcs_loc_req *lcs_loc_req = fi->priv;
+ struct msgb *msg;
+ int rc;
+ struct bssap_le_pdu pla = {
+ .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+ .bssmap_le = {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_ABORT,
+ .perform_loc_abort = lcs_loc_req->lcs_cause,
+ },
+ };
+ struct gsm0808_perform_location_response plr = {
+ .lcs_cause = lcs_loc_req->lcs_cause,
+ };
+
+ /* If we're paging this subscriber for LCS, stop paging. */
+ 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)
+ lcs_loc_req_send(lcs_loc_req, &pla);
+
+ /* Send Perform Location Result with failure cause to MSC */
+ msg = gsm0808_create_perform_location_response(&plr);
+ if (!msg) {
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
+ "Failed to encode BSSMAP Perform Location Response (A-interface)\n");
+ } else {
+ rc = gscon_sigtran_send(lcs_loc_req->conn, msg);
+ if (rc < 0)
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
+ "Failed to send BSSMAP Perform Location Response (A-interface)\n");
+ else
+ 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);
+}
+
+void lcs_loc_req_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct lcs_loc_req *lcs_loc_req = fi->priv;
+ if (lcs_loc_req->conn && lcs_loc_req->conn->lcs.loc_req == lcs_loc_req)
+ lcs_loc_req->conn->lcs.loc_req = NULL;
+ /* FSM termination will dispatch GSCON_EV_LCS_LOC_REQ_END to the conn FSM */
+}
+
+#define S(x) (1 << (x))
+
+static const struct osmo_fsm_state lcs_loc_req_fsm_states[] = {
+ [LCS_LOC_REQ_ST_INIT] = {
+ .name = "INIT",
+ .out_state_mask = 0
+ | S(LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE)
+ | S(LCS_LOC_REQ_ST_FAILED)
+ ,
+ },
+ [LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE] = {
+ .name = "WAIT_LOCATION_RESPONSE",
+ .in_event_mask = 0
+ | S(LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE)
+ | S(LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT)
+ | S(LCS_LOC_REQ_EV_TA_REQ_START)
+ | S(LCS_LOC_REQ_EV_TA_REQ_END)
+ | S(LCS_LOC_REQ_EV_HANDOVER_PERFORMED)
+ | S(LCS_LOC_REQ_EV_CONN_CLEAR)
+ ,
+ .out_state_mask = 0
+ | S(LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING)
+ | S(LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE)
+ | S(LCS_LOC_REQ_ST_FAILED)
+ ,
+ .onenter = lcs_loc_req_wait_loc_resp_onenter,
+ .action = lcs_loc_req_wait_loc_resp_and_ta_req_ongoing_action,
+ },
+ [LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING] = {
+ .name = "BSSLAP_TA_REQ_ONGOING",
+ .in_event_mask = 0
+ | S(LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE)
+ | S(LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT)
+ | S(LCS_LOC_REQ_EV_TA_REQ_END)
+ | S(LCS_LOC_REQ_EV_HANDOVER_PERFORMED)
+ | S(LCS_LOC_REQ_EV_CONN_CLEAR)
+ ,
+ .out_state_mask = 0
+ | S(LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE)
+ | S(LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE)
+ | S(LCS_LOC_REQ_ST_FAILED)
+ ,
+ .action = lcs_loc_req_wait_loc_resp_and_ta_req_ongoing_action,
+ },
+ [LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE] = {
+ .name = "GOT_LOCATION_RESPONSE",
+ .onenter = lcs_loc_req_got_loc_resp_onenter,
+ },
+ [LCS_LOC_REQ_ST_FAILED] = {
+ .name = "FAILED",
+ .onenter = lcs_loc_req_failed_onenter,
+ },
+};
+
+static struct osmo_fsm lcs_loc_req_fsm = {
+ .name = "lcs_loc_req",
+ .states = lcs_loc_req_fsm_states,
+ .num_states = ARRAY_SIZE(lcs_loc_req_fsm_states),
+ .log_subsys = DLCS,
+ .event_names = lcs_loc_req_fsm_event_names,
+ .timer_cb = lcs_loc_req_fsm_timer_cb,
+ .cleanup = lcs_loc_req_fsm_cleanup,
+};
+
+static __attribute__((constructor)) void lcs_loc_req_fsm_register(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&lcs_loc_req_fsm) == 0);
+}
diff --git a/src/osmo-bsc/lcs_ta_req.c b/src/osmo-bsc/lcs_ta_req.c
new file mode 100644
index 000000000..a1fa87301
--- /dev/null
+++ b/src/osmo-bsc/lcs_ta_req.c
@@ -0,0 +1,305 @@
+/* Handle LCS BSSLAP TA Request */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.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/bsc/lcs_ta_req.h>
+
+#include <osmocom/bsc/lcs_loc_req.h>
+#include <osmocom/bsc/lb.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/paging.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/gsm/bsslap.h>
+
+enum lcs_ta_req_fsm_state {
+ LCS_TA_REQ_ST_INIT,
+ LCS_TA_REQ_ST_WAIT_TA,
+ LCS_TA_REQ_ST_GOT_TA,
+ LCS_TA_REQ_ST_FAILED,
+};
+
+static const struct value_string lcs_ta_req_fsm_event_names[] = {
+ OSMO_VALUE_STRING(LCS_TA_REQ_EV_GOT_TA),
+ OSMO_VALUE_STRING(LCS_TA_REQ_EV_ABORT),
+ {}
+};
+
+static const struct osmo_tdef_state_timeout lcs_ta_req_fsm_timeouts[32] = {
+ [LCS_TA_REQ_ST_WAIT_TA] = { .T = -12 },
+};
+
+/* Transition to a state, using the T timer defined in lcs_ta_req_fsm_timeouts.
+ * The actual timeout value is in turn obtained from network->T_defs.
+ * Assumes local variable fi exists. */
+#define lcs_ta_req_fsm_state_chg(FI, STATE) \
+ osmo_tdef_fsm_inst_state_chg(FI, STATE, \
+ lcs_ta_req_fsm_timeouts, \
+ (bsc_gsmnet)->T_defs, \
+ -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", \
+ 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)
+
+static struct osmo_fsm lcs_ta_req_fsm;
+
+static struct lcs_ta_req *lcs_ta_req_alloc(struct osmo_fsm_inst *parent_fi, uint32_t parent_event_term)
+{
+ struct lcs_ta_req *lcs_ta_req;
+
+ struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&lcs_ta_req_fsm, parent_fi, parent_event_term);
+ OSMO_ASSERT(fi);
+
+ lcs_ta_req = talloc(fi, struct lcs_ta_req);
+ OSMO_ASSERT(lcs_ta_req);
+ fi->priv = lcs_ta_req;
+ *lcs_ta_req = (struct lcs_ta_req){
+ .fi = fi,
+ };
+
+ return lcs_ta_req;
+}
+
+int lcs_ta_req_start(struct lcs_loc_req *lcs_loc_req)
+{
+ struct lcs_ta_req *lcs_ta_req;
+ if (lcs_loc_req->ta_req) {
+ LOG_LCS_TA_REQ(lcs_loc_req->ta_req, LOGL_ERROR,
+ "Cannot start another TA Request FSM, this TA Request is still active\n");
+ return -ENOTSUP;
+ }
+ lcs_ta_req = lcs_ta_req_alloc(lcs_loc_req->fi, LCS_LOC_REQ_EV_TA_REQ_END);
+ if (!lcs_ta_req) {
+ LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Cannot allocate TA Request FSM");
+ return -ENOSPC;
+ }
+ lcs_ta_req->loc_req = lcs_loc_req;
+ lcs_loc_req->ta_req = lcs_ta_req;
+
+ return lcs_ta_req_fsm_state_chg(lcs_ta_req->fi, LCS_TA_REQ_ST_WAIT_TA);
+}
+
+static int lcs_ta_req_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct lcs_ta_req *lcs_ta_req = fi->priv;
+ lcs_ta_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Timeout");
+ return 1;
+}
+
+void lcs_ta_req_wait_ta_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct lcs_ta_req *lcs_ta_req = fi->priv;
+ struct lcs_loc_req *loc_req = lcs_ta_req->loc_req;
+ struct gsm_lchan *lchan;
+ struct bsc_paging_params paging;
+
+ if (osmo_fsm_inst_dispatch(loc_req->fi, LCS_LOC_REQ_EV_TA_REQ_START, lcs_ta_req)) {
+ lcs_ta_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Failed to dispatch LCS_LOC_REQ_EV_TA_REQ_START");
+ return;
+ }
+
+ /* Do we already have an active lchan with knowledge of TA? */
+ lchan = loc_req->conn->lchan;
+ if (lchan) {
+ lcs_ta_req_fsm_state_chg(fi, LCS_TA_REQ_ST_GOT_TA);
+ return;
+ }
+
+ /* No lchan yet, need to start Paging */
+ if (loc_req->req.imsi.type != GSM_MI_TYPE_IMSI) {
+ lcs_ta_req_fail(LCS_CAUSE_PROTOCOL_ERROR,
+ "No IMSI in BSSMAP Location Request and no active lchan, cannot start Paging");
+ 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");
+ paging.cil = (struct gsm0808_cell_id_list2){
+ .id_discr = CELL_IDENT_BSS,
+ };
+ } else {
+ paging.cil = (struct gsm0808_cell_id_list2){
+ .id_discr = loc_req->req.cell_id.id_discr,
+ .id_list = { loc_req->req.cell_id.id },
+ .id_list_len = 1,
+ };
+ }
+
+ bsc_paging_start(&paging);
+}
+
+static void lcs_ta_req_wait_ta_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+
+ case LCS_TA_REQ_EV_GOT_TA:
+ lcs_ta_req_fsm_state_chg(fi, LCS_TA_REQ_ST_GOT_TA);
+ break;
+
+ case LCS_TA_REQ_EV_ABORT:
+ lcs_ta_req_fsm_state_chg(fi, LCS_TA_REQ_ST_FAILED);
+ break;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static int lcs_ta_req_send(struct lcs_ta_req *lcs_ta_req, const struct bssap_le_pdu *bssap_le)
+{
+ int rc = lb_send(lcs_ta_req->loc_req->conn, bssap_le);
+ if (rc)
+ lcs_ta_req_fail(LCS_CAUSE_SYSTEM_FAILURE,
+ "Failed to send %s", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));
+ return rc;
+}
+
+void lcs_ta_req_got_ta_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct lcs_ta_req *lcs_ta_req = fi->priv;
+ struct bssap_le_pdu bsslap_ta_resp;
+ struct gsm_lchan *lchan = lcs_ta_req->loc_req->conn->lchan;
+
+ if (!lchan) {
+ lcs_ta_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Internal error: no lchan");
+ return;
+ }
+
+ bsslap_ta_resp = (struct bssap_le_pdu) {
+ .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+ .bssmap_le = {
+ .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
+ .conn_oriented_info = {
+ .apdu = {
+ .msg_type = BSSLAP_MSGT_TA_RESPONSE,
+ .ta_response = {
+ .cell_id = lchan->ts->trx->bts->cell_identity,
+ .ta = lchan->last_ta,
+ },
+ },
+ },
+ },
+ };
+
+ lcs_ta_req_send(lcs_ta_req, &bsslap_ta_resp);
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+}
+
+void lcs_ta_req_failed_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct lcs_ta_req *lcs_ta_req = fi->priv;
+ struct bssap_le_pdu bsslap_abort;
+
+ bsslap_abort = (struct bssap_le_pdu) {
+ .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+ .bssmap_le = {
+ .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
+ .conn_oriented_info = {
+ .apdu = {
+ .msg_type = BSSLAP_MSGT_ABORT,
+ .abort = BSSLAP_CAUSE_OTHER_RADIO_EVT_FAIL,
+ },
+ },
+ },
+ };
+
+ lcs_ta_req_send(lcs_ta_req, &bsslap_abort);
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+}
+
+void lcs_ta_req_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct lcs_ta_req *lcs_ta_req = fi->priv;
+ if (lcs_ta_req->loc_req->ta_req == lcs_ta_req)
+ lcs_ta_req->loc_req->ta_req = NULL;
+ /* FSM termination will dispatch LCS_LOC_REQ_EV_TA_REQ_END to the lcs_loc_req FSM */
+}
+
+
+#define S(x) (1 << (x))
+
+static const struct osmo_fsm_state lcs_ta_req_fsm_states[] = {
+ [LCS_TA_REQ_ST_INIT] = {
+ .name = "init",
+ .out_state_mask = 0
+ | S(LCS_TA_REQ_ST_WAIT_TA)
+ | S(LCS_TA_REQ_ST_GOT_TA)
+ ,
+ },
+ [LCS_TA_REQ_ST_WAIT_TA] = {
+ .name = "wait_ta",
+ .in_event_mask = 0
+ | S(LCS_TA_REQ_EV_GOT_TA)
+ | S(LCS_TA_REQ_EV_ABORT)
+ ,
+ .out_state_mask = 0
+ | S(LCS_TA_REQ_ST_GOT_TA)
+ | S(LCS_TA_REQ_ST_FAILED)
+ ,
+ .onenter = lcs_ta_req_wait_ta_onenter,
+ .action = lcs_ta_req_wait_ta_action,
+ },
+ [LCS_TA_REQ_ST_GOT_TA] = {
+ .name = "got_ta",
+ .in_event_mask = 0
+ ,
+ .out_state_mask = 0
+ ,
+ .onenter = lcs_ta_req_got_ta_onenter,
+ },
+ [LCS_TA_REQ_ST_FAILED] = {
+ .name = "failed",
+ .onenter = lcs_ta_req_failed_onenter,
+ },
+};
+
+static struct osmo_fsm lcs_ta_req_fsm = {
+ .name = "lcs_ta_req",
+ .states = lcs_ta_req_fsm_states,
+ .num_states = ARRAY_SIZE(lcs_ta_req_fsm_states),
+ .log_subsys = DLCS,
+ .event_names = lcs_ta_req_fsm_event_names,
+ .timer_cb = lcs_ta_req_fsm_timer_cb,
+ .cleanup = lcs_ta_req_fsm_cleanup,
+};
+
+static __attribute__((constructor)) void lcs_ta_req_fsm_register(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&lcs_ta_req_fsm) == 0);
+}
diff --git a/src/osmo-bsc/meas_feed.c b/src/osmo-bsc/meas_feed.c
index 8450f69ca..b18478ff4 100644
--- a/src/osmo-bsc/meas_feed.c
+++ b/src/osmo-bsc/meas_feed.c
@@ -6,7 +6,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
-#include <osmocom/core/write_queue.h>
+#include <osmocom/core/osmo_io.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
@@ -19,15 +19,18 @@
#include <osmocom/bsc/meas_feed.h>
#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;
+ struct osmo_io_fd *io_fd;
char scenario[31+1];
char *dst_host;
uint16_t dst_port;
+ size_t txqueue_max;
};
-static struct meas_feed_state g_mfs = {};
+static struct meas_feed_state g_mfs = { .txqueue_max = MEAS_FEED_TXQUEUE_MAX_LEN_DEFAULT };
static int process_meas_rep(struct gsm_meas_rep *mr)
{
@@ -35,6 +38,8 @@ static int process_meas_rep(struct gsm_meas_rep *mr)
struct meas_feed_meas *mfm;
struct bsc_subscr *bsub;
+ OSMO_ASSERT(g_mfs.io_fd != NULL);
+
/* ignore measurements as long as we don't know who it is */
if (!mr->lchan) {
LOGP(DMEAS, LOGL_DEBUG, "meas_feed: no lchan, not sending report\n");
@@ -47,7 +52,7 @@ static int process_meas_rep(struct gsm_meas_rep *mr)
bsub = mr->lchan->conn->bsub;
- msg = msgb_alloc(sizeof(struct meas_feed_meas), "Meas. Feed");
+ msg = msgb_alloc(sizeof(struct meas_feed_meas), "meas_feed_msg");
if (!msg)
return 0;
@@ -82,7 +87,7 @@ static int process_meas_rep(struct gsm_meas_rep *mr)
mfm->ss_nr = mr->lchan->nr;
/* and send it to the socket */
- if (osmo_wqueue_enqueue(&g_mfs.wqueue, msg) != 0) {
+ if (osmo_iofd_write_msgb(g_mfs.io_fd, msg)) {
LOGP(DMEAS, LOGL_ERROR, "meas_feed %s: sending measurement report failed\n",
gsm_lchan_name(mr->lchan));
msgb_free(msg);
@@ -107,64 +112,54 @@ static int meas_feed_sig_cb(unsigned int subsys, unsigned int signal,
return 0;
}
-static int feed_write_cb(struct osmo_fd *ofd, struct msgb *msg)
+static void meas_feed_close(void)
{
- return write(ofd->fd, msgb_data(msg), msgb_length(msg));
+ if (g_mfs.io_fd == NULL)
+ return;
+ osmo_signal_unregister_handler(SS_LCHAN, meas_feed_sig_cb, NULL);
+ osmo_iofd_close(g_mfs.io_fd);
+ osmo_iofd_free(g_mfs.io_fd);
+ g_mfs.io_fd = NULL;
}
-static int feed_read_cb(struct osmo_fd *ofd)
+static void meas_feed_noop_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
{
- int rc;
- char buf[256];
-
- rc = read(ofd->fd, buf, sizeof(buf));
- ofd->fd &= ~BSC_FD_READ;
-
- return rc;
}
int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port)
{
int rc;
- int already_initialized = 0;
-
- if (g_mfs.wqueue.bfd.fd)
- already_initialized = 1;
-
-
- if (already_initialized &&
- !strcmp(dst_host, g_mfs.dst_host) &&
- dst_port == g_mfs.dst_port)
- return 0;
-
- if (!already_initialized) {
- osmo_wqueue_init(&g_mfs.wqueue, 10);
- g_mfs.wqueue.write_cb = feed_write_cb;
- g_mfs.wqueue.read_cb = feed_read_cb;
- osmo_signal_register_handler(SS_LCHAN, meas_feed_sig_cb, NULL);
- LOGP(DMEAS, LOGL_DEBUG, "meas_feed: registered signal callback\n");
+ /* osmo_io code throws an error if 'write_cb' is NULL, so we set a no-op */
+ struct osmo_io_ops meas_feed_oio = {
+ .read_cb = NULL,
+ .write_cb = meas_feed_noop_cb,
+ .segmentation_cb = NULL
+ };
+ /* Already initialized */
+ if (g_mfs.io_fd != NULL) {
+ /* No change needed, do nothing */
+ if (!strcmp(dst_host, g_mfs.dst_host) && dst_port == g_mfs.dst_port)
+ return 0;
+ meas_feed_close();
}
- if (already_initialized) {
- osmo_wqueue_clear(&g_mfs.wqueue);
- osmo_fd_unregister(&g_mfs.wqueue.bfd);
- close(g_mfs.wqueue.bfd.fd);
- /* don't set to zero, as that would mean 'not yet initialized' */
- g_mfs.wqueue.bfd.fd = -1;
+ rc = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, dst_host, dst_port, OSMO_SOCK_F_CONNECT);
+ if (rc < 0) {
+ osmo_signal_unregister_handler(SS_LCHAN, meas_feed_sig_cb, NULL);
+ return rc;
}
- rc = osmo_sock_init_ofd(&g_mfs.wqueue.bfd, AF_UNSPEC, SOCK_DGRAM,
- IPPROTO_UDP, dst_host, dst_port,
- OSMO_SOCK_F_CONNECT);
- if (rc < 0)
+ g_mfs.io_fd = osmo_iofd_setup(NULL, rc, "meas_iofd", OSMO_IO_FD_MODE_READ_WRITE, &meas_feed_oio, NULL);
+ if (!g_mfs.io_fd)
+ return -1;
+ osmo_iofd_set_txqueue_max_length(g_mfs.io_fd, g_mfs.txqueue_max);
+ if ((rc = osmo_iofd_register(g_mfs.io_fd, rc)))
return rc;
- g_mfs.wqueue.bfd.when &= ~BSC_FD_READ;
-
- if (g_mfs.dst_host)
- talloc_free(g_mfs.dst_host);
- g_mfs.dst_host = talloc_strdup(NULL, dst_host);
+ osmo_talloc_replace_string(NULL, &g_mfs.dst_host, dst_host);
g_mfs.dst_port = dst_port;
-
+ osmo_signal_register_handler(SS_LCHAN, meas_feed_sig_cb, NULL);
+ LOGP(DMEAS, LOGL_DEBUG, "meas_feed: started %s\n",
+ osmo_sock_get_name2(osmo_iofd_get_fd(g_mfs.io_fd)));
return 0;
}
@@ -174,6 +169,18 @@ void meas_feed_cfg_get(char **host, uint16_t *port)
*host = g_mfs.dst_host;
}
+void meas_feed_txqueue_max_length_set(unsigned int max_length)
+{
+ g_mfs.txqueue_max = max_length;
+ if (g_mfs.io_fd)
+ osmo_iofd_set_txqueue_max_length(g_mfs.io_fd, max_length);
+}
+
+unsigned int meas_feed_txqueue_max_length_get(void)
+{
+ return g_mfs.txqueue_max;
+}
+
void meas_feed_scenario_set(const char *name)
{
osmo_strlcpy(g_mfs.scenario, name, sizeof(g_mfs.scenario));
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 54d6944f5..44b9057f7 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>
@@ -30,43 +33,7 @@
#include <osmocom/bsc/vty.h>
#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/gsm_data.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;
-}
+#include <osmocom/bsc/bts.h>
#define NEIGHBOR_ADD_CMD "neighbor "
#define NEIGHBOR_DEL_CMD "no neighbor "
@@ -75,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];
@@ -140,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,
@@ -524,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 resolution 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],
@@ -632,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 353099ddd..e5d3fad28 100644
--- a/src/osmo-bsc/net_init.c
+++ b/src/osmo-bsc/net_init.c
@@ -25,36 +25,77 @@
#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=993111, .default_val=4, .desc="Wait time after lchan was released in error (should be T3111 + 2s)" },
- { .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=993210, .default_val=20, .desc="After L3 Complete, wait for MSC to confirm" },
- { .T=999, .default_val=60, .desc="After Clear Request, wait for MSC to Clear Command (sanity)" },
- { .T=992427, .default_val=4, .desc="MGCP timeout (2427 is the default MGCP port number)" },
+ { .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, .min_val = 1, .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 = -28, .default_val = 30, .desc = "Interval at which to try to recover a BORKEN lchan" },
+ { .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=-1, .default_val=4, .desc="MGCP response timeout" },
- { .T=-2, .default_val=30, .desc="RTP stream establishing timeout" },
+ { .T = -2427, .default_val = 5, .desc = "timeout for MGCP response from MGW" },
+ {}
+};
+
+struct osmo_tdef_group bsc_tdef_group[] = {
+ { .name = "net", .tdefs = gsm_network_T_defs, .desc = "GSM network" },
+ { .name = "mgw", .tdefs = g_mgw_tdefs, .desc = "MGW (Media Gateway) interface" },
{}
};
@@ -79,8 +120,7 @@ struct gsm_network *gsm_network_init(void *ctx)
INIT_LLIST_HEAD(&net->subscr_conns);
- net->bsc_subscribers = talloc_zero(net, struct llist_head);
- INIT_LLIST_HEAD(net->bsc_subscribers);
+ net->bsc_subscribers = bsc_subscr_store_alloc(net);
INIT_LLIST_HEAD(&net->bts_list);
net->num_bts = 0;
@@ -94,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..a781cb1dc
--- /dev/null
+++ b/src/osmo-bsc/nm_bb_transc_fsm.c
@@ -0,0 +1,454 @@
+/* NM BaseBand Transceiver FSM */
+
+/* (C) 2020 by 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 <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.sw_act_rep_received = false;
+ 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 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_SW_ACT_REP:
+ bb_transc->mo.sw_act_rep_received = true;
+ break;
+ 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;
+
+ /* nanoBTS only: delay until SW Activated Report is received, which
+ * tells us the IPA Object version (may be used to set attr conditionally). */
+ if (is_nanobts(trx->bts) && !bb_transc->mo.sw_act_rep_received)
+ return;
+
+ /* Request TRX-level attributes */
+ if (!bb_transc->mo.get_attr_sent && !bb_transc->mo.get_attr_rep_received) {
+ uint8_t attr_buf[3]; /* enlarge if needed */
+ uint8_t *ptr = &attr_buf[0];
+
+ *(ptr++) = NM_ATT_MANUF_STATE;
+ *(ptr++) = NM_ATT_SW_CONFIG;
+ if (is_ipa_abisip_bts(trx->bts))
+ *(ptr++) = NM_ATT_IPACC_SUPP_FEATURES;
+
+ OSMO_ASSERT((ptr - attr_buf) <= sizeof(attr_buf));
+ abis_nm_get_attr(trx->bts, NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff,
+ &attr_buf[0], (ptr - attr_buf));
+ bb_transc->mo.get_attr_sent = true;
+ }
+
+ 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_SW_ACT_REP:
+ bb_transc->mo.sw_act_rep_received = true;
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, false);
+ break;
+ 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_SW_ACT_REP:
+ bb_transc->mo.sw_act_rep_received = true;
+ configure_loop(bb_transc, &bb_transc->mo.nm_state, true);
+ break;
+ 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_SW_ACT_REP) |
+ 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_SW_ACT_REP) |
+ 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..aaccea0bf
--- /dev/null
+++ b/src/osmo-bsc/nm_bts_fsm.c
@@ -0,0 +1,443 @@
+/* NM BTS FSM */
+
+/* (C) 2020 by 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 <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.sw_act_rep_received = 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;
+ 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 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_SW_ACT_REP:
+ bts->mo.sw_act_rep_received = true;
+ break;
+ 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;
+
+ /* nanoBTS only: delay until SW Activated Report is received, which
+ * tells us the IPA Object version (may be used to set attr conditionally). */
+ if (is_nanobts(bts) && !bts->mo.sw_act_rep_received)
+ return;
+
+ /* Request generic BTS-level attributes */
+ if (!bts->mo.get_attr_sent && !bts->mo.get_attr_rep_received) {
+ uint8_t attr_buf[3]; /* enlarge if needed */
+ uint8_t *ptr = &attr_buf[0];
+
+ *(ptr++) = NM_ATT_MANUF_ID;
+ *(ptr++) = NM_ATT_SW_CONFIG;
+ if (is_ipa_abisip_bts(bts))
+ *(ptr++) = NM_ATT_IPACC_SUPP_FEATURES;
+
+ OSMO_ASSERT((ptr - attr_buf) <= sizeof(attr_buf));
+ abis_nm_get_attr(bts, NM_OC_BTS, 0, 0xff, 0xff,
+ &attr_buf[0], (ptr - attr_buf));
+ bts->mo.get_attr_sent = true;
+ }
+
+ 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 interference 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_SW_ACT_REP:
+ bts->mo.sw_act_rep_received = true;
+ configure_loop(bts, &bts->mo.nm_state, false);
+ break;
+ 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_SW_ACT_REP:
+ bts->mo.sw_act_rep_received = true;
+ configure_loop(bts, &bts->mo.nm_state, true);
+ break;
+ 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;
+
+ /* Resume power saving on the BCCH carrier, if was enabled */
+ if (bts->c0_max_power_red_db > 0) {
+ LOG_BTS(bts, DRSL, LOGL_NOTICE, "Resuming BCCH carrier power reduction "
+ "operation mode (maximum %u dB)\n", bts->c0_max_power_red_db);
+ gsm_bts_send_c0_power_red(bts, bts->c0_max_power_red_db);
+ }
+}
+
+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_SW_ACT_REP) |
+ 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_SW_ACT_REP) |
+ 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..24a1ec3c6
--- /dev/null
+++ b/src/osmo-bsc/nm_bts_sm_fsm.c
@@ -0,0 +1,326 @@
+/* NM BTS Site Manager FSM */
+
+/* (C) 2020 by 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 <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.sw_act_rep_received = false;
+ 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:
+ site_mgr->mo.sw_act_rep_received = true;
+ break;
+ 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;
+
+ /* nanoBTS only: delay until SW Activated Report is received, which
+ * tells us the IPA Object version (may be used to set attr conditionally). */
+ if (is_nanobts(bts) && !site_mgr->mo.sw_act_rep_received)
+ 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 gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+ struct nm_statechg_signal_data *nsd;
+ const struct gsm_nm_state *new_state;
+
+ switch (event) {
+ case NM_EV_SW_ACT_REP:
+ site_mgr->mo.sw_act_rep_received = true;
+ configure_loop(site_mgr, &site_mgr->mo.nm_state, false);
+ break;
+ 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_SW_ACT_REP:
+ site_mgr->mo.sw_act_rep_received = true;
+ configure_loop(site_mgr, &site_mgr->mo.nm_state, true);
+ 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_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_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_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_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_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..2f389d7ac
--- /dev/null
+++ b/src/osmo-bsc/nm_channel_fsm.c
@@ -0,0 +1,376 @@
+/* NM Radio Channel FSM */
+
+/* (C) 2020 by 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 <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>
+#include <osmocom/bsc/timeslot_fsm.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_SW_ACT_REP:
+ configure_loop(ts, &ts->mo.nm_state, false);
+ break;
+ 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_SW_ACT_REP:
+ configure_loop(ts, &ts->mo.nm_state, true);
+ break;
+ 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;
+
+ osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_READY, NULL);
+}
+
+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) {
+ osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_DOWN, NULL);
+ 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_SW_ACT_REP) |
+ 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_SW_ACT_REP) |
+ 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..7719f0672
--- /dev/null
+++ b/src/osmo-bsc/nm_common_fsm.c
@@ -0,0 +1,90 @@
+/* NM FSM, common bits */
+
+/* (C) 2020 by 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 <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..10f8d07de
--- /dev/null
+++ b/src/osmo-bsc/nm_gprs_cell_fsm.c
@@ -0,0 +1,432 @@
+/* NM GPRS Cell FSM */
+
+/* (C) 2020 by 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 <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.sw_act_rep_received = false;
+ cell->mo.get_attr_sent = false;
+ cell->mo.get_attr_rep_received = false;
+ 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 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_SW_ACT_REP:
+ cell->mo.sw_act_rep_received = true;
+ break;
+ 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;
+
+ /* nanoBTS only: delay until SW Activated Report is received, which
+ * tells us the IPA Object version (may be used to set attr conditionally). */
+ if (is_nanobts(bts) && !cell->mo.sw_act_rep_received)
+ return;
+
+ if (!cell->mo.get_attr_sent && !cell->mo.get_attr_rep_received) {
+ uint8_t attr_buf[2]; /* enlarge if needed */
+ uint8_t *ptr = &attr_buf[0];
+
+ *(ptr++) = NM_ATT_SW_CONFIG;
+ if (is_ipa_abisip_bts(bts))
+ *(ptr++) = NM_ATT_IPACC_SUPP_FEATURES;
+
+ OSMO_ASSERT((ptr - attr_buf) <= sizeof(attr_buf));
+ abis_nm_get_attr(bts, NM_OC_GPRS_CELL,
+ bts->bts_nr, 0x00, 0xff,
+ &attr_buf[0], (ptr - attr_buf));
+ cell->mo.get_attr_sent = true;
+ }
+
+ /* OS#6172: old osmo-bts versions do NACK Get Attributes for GPRS Cell,
+ * so we do not check if cell->mo.get_attr_rep_received is set here. */
+ 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_SW_ACT_REP:
+ cell->mo.sw_act_rep_received = true;
+ configure_loop(cell, &cell->mo.nm_state, false);
+ break;
+ case NM_EV_GET_ATTR_REP:
+ cell->mo.get_attr_rep_received = true;
+ cell->mo.get_attr_sent = false;
+ configure_loop(cell, &cell->mo.nm_state, false);
+ return;
+ 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_SW_ACT_REP:
+ cell->mo.sw_act_rep_received = true;
+ configure_loop(cell, &cell->mo.nm_state, true);
+ break;
+ case NM_EV_GET_ATTR_REP:
+ cell->mo.get_attr_rep_received = true;
+ cell->mo.get_attr_sent = false;
+ configure_loop(cell, &cell->mo.nm_state, true);
+ return;
+ 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_SW_ACT_REP) |
+ 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_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_SW_ACT_REP) |
+ 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_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..295f5fbce
--- /dev/null
+++ b/src/osmo-bsc/nm_gprs_nse_fsm.c
@@ -0,0 +1,403 @@
+/* NM GPRS NSE FSM */
+
+/* (C) 2020 by 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 <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.sw_act_rep_received = false;
+ 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 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_SW_ACT_REP:
+ nse->mo.sw_act_rep_received = true;
+ break;
+ 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;
+
+ /* nanoBTS only: delay until SW Activated Report is received, which
+ * tells us the IPA Object version (may be used to set attr conditionally). */
+ if (is_nanobts(bts) && !nse->mo.sw_act_rep_received)
+ 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_SW_ACT_REP:
+ nse->mo.sw_act_rep_received = true;
+ configure_loop(nse, &nse->mo.nm_state, false);
+ break;
+ 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_SW_ACT_REP:
+ nse->mo.sw_act_rep_received = true;
+ configure_loop(nse, &nse->mo.nm_state, true);
+ break;
+ 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_SW_ACT_REP) |
+ 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_SW_ACT_REP) |
+ 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..c37d46ad0
--- /dev/null
+++ b/src/osmo-bsc/nm_gprs_nsvc_fsm.c
@@ -0,0 +1,434 @@
+/* NM GPRS NSVC FSM */
+
+/* (C) 2020 by sysmocom - s.f.m.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 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 <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.sw_act_rep_received = false;
+ 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 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_SW_ACT_REP:
+ nsvc->mo.sw_act_rep_received = true;
+ break;
+ case NM_EV_FEATURE_NEGOTIATED:
+ 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(const struct gsm_gprs_nsvc *nsvc)
+{
+ /* If not configured (enabled) at all. */
+ if (!nsvc->enabled)
+ return false;
+
+ /* remote address must be valid */
+ if (osmo_sockaddr_is_any(&nsvc->remote))
+ return false;
+ /* remote port must be valid */
+ switch (nsvc->remote.u.sa.sa_family) {
+ case AF_INET:
+ return nsvc->remote.u.sin.sin_port != 0;
+ case AF_INET6:
+ return nsvc->remote.u.sin6.sin6_port != 0;
+ 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;
+
+ /* nanoBTS only: delay until SW Activated Report is received, which
+ * tells us the IPA Object version (may be used to set attr conditionally). */
+ if (is_nanobts(nsvc->bts) && !nsvc->mo.sw_act_rep_received)
+ 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_SW_ACT_REP:
+ nsvc->mo.sw_act_rep_received = true;
+ /* fall-through */
+ 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_SW_ACT_REP:
+ nsvc->mo.sw_act_rep_received = true;
+ /* fall-through */
+ 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_SW_ACT_REP) |
+ 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_SW_ACT_REP) |
+ 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..f5d7c270f
--- /dev/null
+++ b/src/osmo-bsc/nm_rcarrier_fsm.c
@@ -0,0 +1,455 @@
+/* NM Radio Carrier FSM */
+
+/* (C) 2020 by 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 <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.sw_act_rep_received = false;
+ trx->mo.get_attr_sent = false;
+ trx->mo.get_attr_rep_received = false;
+ 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 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_SW_ACT_REP:
+ trx->mo.sw_act_rep_received = true;
+ break;
+ 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;
+
+ /* nanoBTS only: delay until SW Activated Report is received, which
+ * tells us the IPA Object version (may be used to set attr conditionally). */
+ if (is_nanobts(trx->bts) && !trx->mo.sw_act_rep_received)
+ return;
+
+ if (!trx->mo.get_attr_sent && !trx->mo.get_attr_rep_received) {
+ uint8_t attr_buf[2]; /* enlarge if needed */
+ uint8_t *ptr = &attr_buf[0];
+
+ *(ptr++) = NM_ATT_SW_CONFIG;
+ if (is_ipa_abisip_bts(trx->bts))
+ *(ptr++) = NM_ATT_IPACC_SUPP_FEATURES;
+
+ OSMO_ASSERT((ptr - attr_buf) <= sizeof(attr_buf));
+ abis_nm_get_attr(trx->bts, NM_OC_RADIO_CARRIER,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ &attr_buf[0], (ptr - attr_buf));
+ trx->mo.get_attr_sent = true;
+ }
+
+ /* OS#6172: old osmo-bts versions do NACK Get Attributes for Radio Carrier,
+ * so we do not check if trx->mo.get_attr_rep_received is set here. */
+ 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_SW_ACT_REP:
+ trx->mo.sw_act_rep_received = true;
+ configure_loop(trx, &trx->mo.nm_state, false);
+ break;
+ case NM_EV_GET_ATTR_REP:
+ trx->mo.get_attr_rep_received = true;
+ trx->mo.get_attr_sent = false;
+ configure_loop(trx, &trx->mo.nm_state, false);
+ return;
+ 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_SW_ACT_REP:
+ trx->mo.sw_act_rep_received = true;
+ configure_loop(trx, &trx->mo.nm_state, true);
+ break;
+ case NM_EV_GET_ATTR_REP:
+ trx->mo.get_attr_rep_received = true;
+ trx->mo.get_attr_sent = false;
+ configure_loop(trx, &trx->mo.nm_state, true);
+ return;
+ 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_SW_ACT_REP) |
+ 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_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_SW_ACT_REP) |
+ 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_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 2deb7f46b..2a9805444 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -29,10 +29,14 @@
#include <osmocom/bsc/bsc_subscriber.h>
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/codec_pref.h>
+#include <osmocom/bsc/data_rate_pref.h>
#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>
@@ -43,6 +47,10 @@
#include <osmocom/core/fsm.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/bsc/lcs_loc_req.h>
+#include <osmocom/bsc/bssmap_reset.h>
+#include <osmocom/bsc/assignment_fsm.h>
+#include <osmocom/bsc/vgcs_fsm.h>
#define IP_V4_ADDR_LEN 4
@@ -59,7 +67,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");
@@ -94,196 +102,160 @@ 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(struct bsc_msc_data *msc, struct gsm_bts *bts,
- uint32_t tmsi, uint32_t lac, const char *mi_string, uint8_t chan_needed)
+static void page_subscriber(const struct bsc_paging_params *params, struct gsm_bts *bts, uint32_t lac)
{
- struct bsc_subscr *subscr;
int ret;
- subscr = bsc_subscr_find_or_create_by_imsi(msc->network->bsc_subscribers,
- mi_string);
-
- if (subscr)
- log_set_context(LOG_CTX_BSC_SUBSCR, subscr);
-
- LOGP(DMSC, LOGL_INFO, "Paging request from MSC BTS: %d IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n",
- bts->nr, mi_string, tmsi, tmsi, lac);
-
- if (!subscr) {
- LOGP(DMSC, LOGL_ERROR, "Paging request failed: Could not allocate subscriber for %s\n", mi_string);
+ if (!bsc_grace_allow_new_connection(bsc_gsmnet, bts)) {
+ LOG_PAGING_BTS(params, bts, DMSC, LOGL_DEBUG, "RF-locked: not paging on this BTS\n");
return;
}
- subscr->lac = lac;
- subscr->tmsi = tmsi;
+ LOG_PAGING_BTS(params, bts, DMSC, LOGL_INFO, "Paging on LAC %u\n", lac);
- ret = bsc_grace_paging_request(msc->network->rf_ctrl->policy, subscr, chan_needed, msc, bts);
+ ret = paging_request_bts(params, bts);
if (ret == 0)
- LOGP(DMSC, LOGL_INFO, "Paging request failed or repeated paging: BTS: %d IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n",
- bts->nr, mi_string, tmsi, tmsi, lac);
-
- /* the paging code has grabbed its own references */
- bsc_subscr_put(subscr);
-
- log_set_context(LOG_CTX_BSC_SUBSCR, NULL);
+ LOG_PAGING_BTS(params, bts, DMSC, LOGL_INFO,
+ "Paging request failed, or repeated paging on LAC %u\n", lac);
}
-static void
-page_all_bts(struct bsc_msc_data *msc, uint32_t tmsi, const char *mi_string, uint8_t chan_needed)
+static void page_all_bts(const struct bsc_paging_params *params)
{
struct gsm_bts *bts;
- llist_for_each_entry(bts, &msc->network->bts_list, list)
- page_subscriber(msc, bts, tmsi, GSM_LAC_RESERVED_ALL_BTS, mi_string, chan_needed);
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list)
+ page_subscriber(params, bts, GSM_LAC_RESERVED_ALL_BTS);
}
-static void
-page_cgi(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil,
- uint32_t tmsi, const char *mi_string, uint8_t chan_needed)
+static void page_cgi(const struct bsc_paging_params *params)
{
int i;
- for (i = 0; i < cil->id_list_len; i++) {
- struct osmo_cell_global_id *id = &cil->id_list[i].global;
- if (!osmo_plmn_cmp(&id->lai.plmn, &msc->network->plmn)) {
+ for (i = 0; i < params->cil.id_list_len; i++) {
+ const struct osmo_cell_global_id *id = &params->cil.id_list[i].global;
+ if (!osmo_plmn_cmp(&id->lai.plmn, &bsc_gsmnet->plmn)) {
int paged = 0;
struct gsm_bts *bts;
- llist_for_each_entry(bts, &msc->network->bts_list, list) {
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
if (bts->location_area_code != id->lai.lac)
continue;
if (bts->cell_identity != id->cell_identity)
continue;
- page_subscriber(msc, bts, tmsi, id->lai.lac, mi_string, chan_needed);
+ page_subscriber(params, bts, id->lai.lac);
paged = 1;
}
if (!paged) {
- LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with LAC %d and CI %d not found\n",
- mi_string, id->lai.lac, id->cell_identity);
+ LOG_PAGING(params, DMSC, LOGL_NOTICE, "BTS with LAC %u and CI %u not found\n",
+ id->lai.lac, id->cell_identity);
}
} else {
- LOGP(DMSC, LOGL_DEBUG, "Paging IMSI %s: MCC-MNC in Cell Identifier List "
- "(%s) do not match our network (%s)\n",
- mi_string, osmo_plmn_name(&id->lai.plmn),
- osmo_plmn_name2(&msc->network->plmn));
+ LOG_PAGING(params, DMSC, LOGL_DEBUG,
+ "MCC-MNC in Cell Identifier List (%s) do not match our network (%s)\n",
+ osmo_plmn_name_c(OTC_SELECT, &id->lai.plmn),
+ osmo_plmn_name_c(OTC_SELECT, &bsc_gsmnet->plmn));
}
}
}
-static void
-page_lac_and_ci(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil,
- uint32_t tmsi, const char *mi_string, uint8_t chan_needed)
+static void page_lac_and_ci(const struct bsc_paging_params *params)
{
int i;
- for (i = 0; i < cil->id_list_len; i++) {
- struct osmo_lac_and_ci_id *id = &cil->id_list[i].lac_and_ci;
+ for (i = 0; i < params->cil.id_list_len; i++) {
+ const struct osmo_lac_and_ci_id *id = &params->cil.id_list[i].lac_and_ci;
int paged = 0;
struct gsm_bts *bts;
- llist_for_each_entry(bts, &msc->network->bts_list, list) {
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
if (bts->location_area_code != id->lac)
continue;
if (bts->cell_identity != id->ci)
continue;
- page_subscriber(msc, bts, tmsi, id->lac, mi_string, chan_needed);
+ page_subscriber(params, bts, id->lac);
paged = 1;
}
if (!paged) {
- LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with LAC %d and CI %d not found\n",
- mi_string, id->lac, id->ci);
+ LOG_PAGING(params, DMSC, LOGL_NOTICE, "BTS with LAC %u and CI %u not found\n", id->lac, id->ci);
}
}
}
-static void
-page_ci(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil,
- uint32_t tmsi, const char *mi_string, uint8_t chan_needed)
+static void page_ci(const struct bsc_paging_params *params)
{
int i;
- for (i = 0; i < cil->id_list_len; i++) {
- uint16_t ci = cil->id_list[i].ci;
+ for (i = 0; i < params->cil.id_list_len; i++) {
+ uint16_t ci = params->cil.id_list[i].ci;
int paged = 0;
struct gsm_bts *bts;
- llist_for_each_entry(bts, &msc->network->bts_list, list) {
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
if (bts->cell_identity != ci)
continue;
- page_subscriber(msc, bts, tmsi, GSM_LAC_RESERVED_ALL_BTS, mi_string, chan_needed);
+ page_subscriber(params, bts, GSM_LAC_RESERVED_ALL_BTS);
paged = 1;
}
if (!paged) {
- LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with CI %d not found\n",
- mi_string, ci);
+ LOG_PAGING(params, DMSC, LOGL_NOTICE, "BTS with CI %u not found\n", ci);
}
}
}
-static void
-page_lai_and_lac(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil,
- uint32_t tmsi, const char *mi_string, uint8_t chan_needed)
+static void page_lai_and_lac(const struct bsc_paging_params *params)
{
int i;
- for (i = 0; i < cil->id_list_len; i++) {
- struct osmo_location_area_id *id = &cil->id_list[i].lai_and_lac;
- if (!osmo_plmn_cmp(&id->plmn, &msc->network->plmn)) {
+ for (i = 0; i < params->cil.id_list_len; i++) {
+ const struct osmo_location_area_id *id = &params->cil.id_list[i].lai_and_lac;
+ if (!osmo_plmn_cmp(&id->plmn, &bsc_gsmnet->plmn)) {
int paged = 0;
struct gsm_bts *bts;
- llist_for_each_entry(bts, &msc->network->bts_list, list) {
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
if (bts->location_area_code != id->lac)
continue;
- page_subscriber(msc, bts, tmsi, id->lac, mi_string, chan_needed);
+ page_subscriber(params, bts, id->lac);
paged = 1;
}
if (!paged) {
- LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with LAC %d not found\n",
- mi_string, id->lac);
+ LOG_PAGING(params, DMSC, LOGL_NOTICE, "BTS with LAC %u not found\n", id->lac);
}
} else {
- LOGP(DMSC, LOGL_DEBUG, "Paging IMSI %s: MCC-MNC in Cell Identifier List "
- "(%s) do not match our network (%s)\n",
- mi_string, osmo_plmn_name(&id->plmn),
- osmo_plmn_name2(&msc->network->plmn));
+ LOG_PAGING(params, DMSC, LOGL_DEBUG,
+ "MCC-MNC in Cell Identifier List (%s) do not match our network (%s)\n",
+ osmo_plmn_name_c(OTC_SELECT, &id->plmn),
+ osmo_plmn_name_c(OTC_SELECT, &bsc_gsmnet->plmn));
}
}
}
-static void
-page_lac(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil,
- uint32_t tmsi, const char *mi_string, uint8_t chan_needed)
+static void page_lac(const struct bsc_paging_params *params)
{
int i;
- for (i = 0; i < cil->id_list_len; i++) {
- uint16_t lac = cil->id_list[i].lac;
+ for (i = 0; i < params->cil.id_list_len; i++) {
+ uint16_t lac = params->cil.id_list[i].lac;
int paged = 0;
struct gsm_bts *bts;
- llist_for_each_entry(bts, &msc->network->bts_list, list) {
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
if (bts->location_area_code != lac)
continue;
- page_subscriber(msc, bts, tmsi, lac, mi_string, chan_needed);
+ page_subscriber(params, bts, lac);
paged = 1;
}
if (!paged) {
- LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with LAC %d not found\n",
- mi_string, lac);
+ LOG_PAGING(params, DMSC, LOGL_NOTICE, "BTS with LAC %u not found\n", lac);
}
}
}
@@ -293,15 +265,19 @@ static int bssmap_handle_paging(struct bsc_msc_data *msc,
struct msgb *msg, unsigned int payload_length)
{
struct tlv_parsed tp;
- struct osmo_mobile_identity mi_imsi;
- uint32_t tmsi = GSM_RESERVED_TMSI;
uint8_t data_length;
int remain;
const uint8_t *data;
- uint8_t chan_needed = RSL_CHANNEED_ANY;
- struct gsm0808_cell_id_list2 cil;
+ struct bsc_paging_params paging = {
+ .reason = BSC_PAGING_FROM_CN,
+ .msc = 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)) {
@@ -320,7 +296,7 @@ static int bssmap_handle_paging(struct bsc_msc_data *msc,
if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI) &&
TLVP_LEN(&tp, GSM0808_IE_TMSI) == 4) {
- tmsi = ntohl(tlvp_val32_unal(&tp, GSM0808_IE_TMSI));
+ paging.tmsi = ntohl(tlvp_val32_unal(&tp, GSM0808_IE_TMSI));
remain -= TLVP_LEN(&tp, GSM0808_IE_TMSI);
}
@@ -332,8 +308,8 @@ static int bssmap_handle_paging(struct bsc_msc_data *msc,
/*
* parse the IMSI
*/
- 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) {
+ if (osmo_mobile_identity_decode(&paging.imsi, TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI), false)
+ || paging.imsi.type != GSM_MI_TYPE_IMSI) {
LOGP(DMSC, LOGL_ERROR, "Paging: could not parse IMSI\n");
return -1;
}
@@ -345,123 +321,135 @@ static int bssmap_handle_paging(struct bsc_msc_data *msc,
*/
data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
- if (gsm0808_dec_cell_id_list2(&cil, data, data_length) < 0) {
- LOGP(DMSC, LOGL_ERROR, "Paging %s: Could not parse Cell Identifier List\n",
- osmo_mobile_identity_to_str_c(OTC_SELECT, &mi_imsi));
+ if (gsm0808_dec_cell_id_list2(&paging.cil, data, data_length) < 0) {
+ LOG_PAGING(&paging, DMSC, LOGL_ERROR, "Could not parse Cell Identifier List\n");
return -1;
}
+ if (paging.cil.id_discr == CELL_IDENT_BSS && data_length != 1) {
+ LOG_PAGING(&paging, DMSC, LOGL_ERROR, "Cell Identifier List for BSS (0x%x)"
+ " has invalid length: %u, paging entire BSS anyway (%s)\n",
+ CELL_IDENT_BSS, data_length, osmo_hexdump(data, data_length));
+ }
remain = 0;
if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1)
- chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03;
+ paging.chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03;
if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) {
- LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n");
+ LOG_PAGING(&paging, DMSC, LOGL_ERROR, "eMLPP IE present, but eMLPP is not handled\n");
}
- rate_ctr_inc(&msc->network->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED]);
+ return bsc_paging_start(&paging);
+}
+
+int bsc_paging_start(struct bsc_paging_params *params)
+{
+ 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,
+ BSUB_USE_PAGING_START);
+ if (!params->bsub) {
+ LOG_PAGING(params, DMSC, LOGL_ERROR, "Paging request failed: Could not allocate subscriber\n");
+ return -EINVAL;
+ }
+ }
+ if (params->tmsi != GSM_RESERVED_TMSI) {
+ if (bsc_subscr_set_tmsi(params->bsub, params->tmsi) < 0) {
+ LOG_PAGING(params, DMSC, LOGL_ERROR, "Paging request failed: Could not set TMSI on subscriber\n");
+ return -EINVAL;
+ }
+ }
+ log_set_context(LOG_CTX_BSC_SUBSCR, params->bsub);
- switch (cil.id_discr) {
+ switch (params->cil.id_discr) {
case CELL_IDENT_NO_CELL:
- page_all_bts(msc, tmsi, mi_imsi.imsi, chan_needed);
+ page_all_bts(params);
break;
case CELL_IDENT_WHOLE_GLOBAL:
- page_cgi(msc, &cil, tmsi, mi_imsi.imsi, chan_needed);
+ page_cgi(params);
break;
case CELL_IDENT_LAC_AND_CI:
- page_lac_and_ci(msc, &cil, tmsi, mi_imsi.imsi, chan_needed);
+ page_lac_and_ci(params);
break;
case CELL_IDENT_CI:
- page_ci(msc, &cil, tmsi, mi_imsi.imsi, chan_needed);
+ page_ci(params);
break;
case CELL_IDENT_LAI_AND_LAC:
- page_lai_and_lac(msc, &cil, tmsi, mi_imsi.imsi, chan_needed);
+ page_lai_and_lac(params);
break;
case CELL_IDENT_LAC:
- page_lac(msc, &cil, tmsi, mi_imsi.imsi, chan_needed);
+ page_lac(params);
break;
case CELL_IDENT_BSS:
- if (data_length != 1) {
- LOGP(DMSC, LOGL_ERROR, "Paging %s: Cell Identifier List for BSS (0x%x)"
- " has invalid length: %u, paging entire BSS anyway (%s)\n",
- osmo_mobile_identity_to_str_c(OTC_SELECT, &mi_imsi),
- CELL_IDENT_BSS, data_length, osmo_hexdump(data, data_length));
- }
- page_all_bts(msc, tmsi, mi_imsi.imsi, chan_needed);
+ page_all_bts(params);
break;
default:
- LOGP(DMSC, LOGL_NOTICE, "Paging %s: unimplemented Cell Identifier List (0x%x),"
- " paging entire BSS instead (%s)\n",
- osmo_mobile_identity_to_str_c(OTC_SELECT, &mi_imsi),
- cil.id_discr, osmo_hexdump(data, data_length));
- page_all_bts(msc, tmsi, mi_imsi.imsi, chan_needed);
+ LOG_PAGING(params, DMSC, LOGL_NOTICE,
+ "unimplemented Cell Identifier List type (0x%x), paging entire BSS instead\n",
+ params->cil.id_discr);
+ page_all_bts(params);
break;
}
+ bsc_subscr_put(params->bsub, BSUB_USE_PAGING_START);
+ log_set_context(LOG_CTX_BSC_SUBSCR, NULL);
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)
+static int bssmap_handle_clear_cmd(struct gsm_subscriber_connection *conn,
+ struct msgb *msg, unsigned int length)
{
- if (cipher > 0 && key == NULL) {
- LOGP(DRSL, LOGL_ERROR, "%s: Need to have an encryption key.\n",
- bsc_subscr_name(conn->bsub));
- return -1;
- }
+ struct tlv_parsed tp;
+ enum gsm0808_cause cause_0808;
- if (len > MAX_A5_KEY_LEN) {
- LOGP(DRSL, LOGL_ERROR, "%s: The key is too long: %d\n",
- bsc_subscr_name(conn->bsub), len);
+ 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;
}
- 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);
+ 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. */
+ cause_0808 = GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE;
}
- 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;
- bool is_csfb = false;
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
-
- /* FIXME: Check for mandatory cause IE, and use that in RR RELEASE cause! */
- if (TLVP_PRESENT(&tp, GSM0808_IE_CSFB_INDICATION))
- 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, &is_csfb);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CLEAR_CMD, &cause_0808);
return 0;
}
@@ -487,8 +475,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;
}
@@ -501,7 +490,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;
@@ -535,9 +528,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) "
@@ -546,13 +536,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:
@@ -562,7 +590,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;
}
@@ -614,16 +642,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);
@@ -643,25 +669,85 @@ 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
+/* Select a preferred and an alternative data rate depending on the available
* capabilities. This decision does not include the actual channel load 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,
- struct gsm_subscriber_connection *conn)
+static int select_data_rates(struct assignment_request *req, struct gsm0808_channel_type *ct,
+ struct gsm_subscriber_connection *conn)
+{
+ int rc, i, nc = 0;
+
+ switch (ct->ch_rate_type) {
+ case GSM0808_DATA_FULL_BM:
+ rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, true);
+ nc += (rc == 0) ? 1 : 0;
+ break;
+ case GSM0808_DATA_HALF_LM:
+ rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, false);
+ nc += (rc == 0) ? 1 : 0;
+ break;
+ case GSM0808_DATA_FULL_PREF_NO_CHANGE:
+ case GSM0808_DATA_FULL_PREF:
+ rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, true);
+ nc += (rc == 0) ? 1 : 0;
+ rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, false);
+ nc += (rc == 0) ? 1 : 0;
+ break;
+ case GSM0808_DATA_HALF_PREF_NO_CHANGE:
+ case GSM0808_DATA_HALF_PREF:
+ rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, false);
+ nc += (rc == 0) ? 1 : 0;
+ rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, true);
+ nc += (rc == 0) ? 1 : 0;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ if (!nc) {
+ LOGP(DMSC, LOGL_ERROR, "No supported data rate found for channel_type ="
+ " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[%s] }\n",
+ ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nc; i++) {
+ DEBUGP(DMSC, "Found matching data rate (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_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));
+ }
+
+ req->n_ch_mode_rate = nc;
+
+ return 0;
+}
+
+/* Select a preferred and an alternative codec rate depending on the available
+ * 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, const struct gsm0808_channel_type *ct,
+ struct gsm_subscriber_connection *conn, struct gsm_bts *bts)
{
int rc, i, nc = 0;
struct bsc_msc_data *msc;
- struct gsm_bts *bts = conn_get_bts(conn);
if (!bts) {
LOGP(DMSC, LOGL_ERROR, "No lchan, cannot select codecs\n");
@@ -672,34 +758,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;
@@ -719,8 +805,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));
}
@@ -735,49 +821,263 @@ 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 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_data(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;
+
+ /* According to 3GPP TS 48.008 § 3.2.1.1 note 13, the codec list IE
+ * shall be included for aoip unless channel type is signalling. */
+ if (bssmap_handle_ass_req_tp_codec_list(conn, tp, aoip, cause) < 0)
+ return -1;
+
+ rc = select_data_rates(req, ct, conn);
+ if (rc < 0) {
+ *cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int bssmap_handle_ass_req_ct_speech(struct gsm_subscriber_connection *conn, struct gsm_bts *bts,
+ 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, bts);
+ 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.
*
@@ -788,12 +1088,8 @@ 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;
+ struct gsm0808_group_callref gc;
uint8_t cause;
int rc;
struct assignment_request req = {};
@@ -804,9 +1100,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)) {
@@ -824,151 +1121,93 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn,
goto reject;
}
- bssmap_handle_ass_req_lcls(conn, &tp);
+ /* Check for assignment to VGCS channel. */
+ if (TLVP_PRESENT(&tp, GSM0808_IE_GROUP_CALL_REFERENCE)) {
+ struct gsm_bts *bts = conn_get_bts(conn);
- /* Currently we only support a limited subset of all
- * possible channel types, such as multi-slot or CSD */
- switch (ct.ch_indctr) {
- case GSM0808_CHAN_DATA:
- LOGP(DMSC, LOGL_ERROR, "Unsupported channel type, currently only speech is supported!\n");
- 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;
- }
+ OSMO_ASSERT(bts);
+ /* Decode Group Call Reference. */
+ rc = gsm0808_dec_group_callref(&gc, TLVP_VAL(&tp, GSM0808_IE_GROUP_CALL_REFERENCE),
+ TLVP_LEN(&tp, GSM0808_IE_GROUP_CALL_REFERENCE));
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to decode Group Call Reference.\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ 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;
+ req.target_lchan = vgcs_vbs_find_lchan(bts, &gc);
+ if (!req.target_lchan) {
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
goto reject;
}
+ req.assign_for = ASSIGN_FOR_BSSMAP_REQ;
+ req.vgcs = true;
- 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");
+ /* Copy timing advance. */
+ if (conn->lchan) {
+ req.target_lchan->activate.info.ta_known = conn->lchan->activate.info.ta_known;
+ req.target_lchan->activate.info.ta = conn->lchan->activate.info.ta;
}
- /* 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;
- }
- }
+ /* Send reactivation on target lchan to prepare VGCS channel for assignment.
+ * See patent EP 1 858 275 A1. */
+ rsl_tx_chan_activ(req.target_lchan, RSL_ACT_TYPE_REACT | RSL_ACT_INTRA_NORM_ASS, 0);
- 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;
- }
+ return osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_ASSIGNMENT_START, &req);
+ }
- req = (struct assignment_request){
- .aoip = aoip,
- .msc_assigned_cic = cic,
- .use_osmux = use_osmux,
- .osmux_cid = osmux_cid,
- };
+ bssmap_handle_ass_req_lcls(conn, &tp);
- /* 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;
+ /* Currently we only support a limited subset of all
+ * possible channel types, such as multi-slot */
+ switch (ct.ch_indctr) {
+ case GSM0808_CHAN_DATA:
+ if (bssmap_handle_ass_req_ct_data(conn, &tp, &ct, &req, &cause) < 0)
+ goto reject;
+ break;
+ case GSM0808_CHAN_SPEECH:
+ if (bssmap_handle_ass_req_ct_speech(conn, conn_get_bts(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;
goto reject;
}
+ req.ch_indctr = ct.ch_indctr;
+
return osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_ASSIGNMENT_START, &req);
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_ASSIGNMENT_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
@@ -989,7 +1228,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)) {
@@ -1027,7 +1269,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)) {
@@ -1064,6 +1309,125 @@ static int bssmap_handle_confusion(struct gsm_subscriber_connection *conn,
return 0;
}
+/* Common ID; 3GPP TS 48.008 3.2.1.68 */
+static int bssmap_handle_common_id(struct gsm_subscriber_connection *conn,
+ struct msgb *msg, unsigned int length)
+{
+ struct tlv_parsed tp;
+
+ 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)) {
+ LOGPFSML(conn->fi, LOGL_ERROR,
+ "CommonID: missing mandatory IMSI IE: %s\n",
+ osmo_hexdump(msg->l4h, length));
+ return -EINVAL;
+ }
+
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_COMMON_ID_IND, &tp);
+
+ return 0;
+}
+
+/* Handle (VGCS) UPLINK REQUEST ACKNOWLEDGE:
+ *
+ * See 3GPP TS 48.008 §3.2.1.58
+ */
+static int bssmap_handle_uplink_rqst_acknowledge(struct gsm_subscriber_connection *conn,
+ struct msgb *msg, unsigned int length)
+{
+ struct tlv_parsed tp;
+
+ 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;
+ }
+
+ if (conn->vgcs_call.fi)
+ osmo_fsm_inst_dispatch(conn->vgcs_call.fi, VGCS_EV_MSC_ACK, NULL);
+ return 0;
+}
+
+/* Handle (VGCS) UPLINK REJECT COMMAND message.
+ *
+ * See 3GPP TS 48.008 §3.2.1.61
+ */
+static int bssmap_handle_uplink_reject_cmd(struct gsm_subscriber_connection *conn,
+ struct msgb *msg, unsigned int length)
+{
+ struct tlv_parsed tp;
+
+ 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;
+ }
+
+ if (conn->vgcs_call.fi)
+ osmo_fsm_inst_dispatch(conn->vgcs_call.fi, VGCS_EV_MSC_REJECT, NULL);
+ return 0;
+}
+
+/* Handle (VGCS) UPLINK RELEASE COMMAND message, MSC indicating an error to us:
+ *
+ * See 3GPP TS 48.008 §3.2.1.62
+ */
+static int bssmap_handle_uplink_release_cmd(struct gsm_subscriber_connection *conn,
+ struct msgb *msg, unsigned int length)
+{
+ struct tlv_parsed tp;
+
+ 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;
+ }
+
+ if (conn->vgcs_call.fi)
+ osmo_fsm_inst_dispatch(conn->vgcs_call.fi, VGCS_EV_MSC_RELEASE, NULL);
+ return 0;
+}
+
+/* Handle (VGCS) UPLINK SEIZED COMMAND message:
+ *
+ * See 3GPP TS 48.008 §3.2.1.63
+ */
+static int bssmap_handle_uplink_seized_cmd(struct gsm_subscriber_connection *conn,
+ struct msgb *msg, unsigned int length)
+{
+ struct tlv_parsed tp;
+
+ 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;
+ }
+
+ if (conn->vgcs_call.fi)
+ osmo_fsm_inst_dispatch(conn->vgcs_call.fi, VGCS_EV_MSC_SEIZE, NULL);
+ return 0;
+}
+
+/* Handle VGCS/VBS ADDITIONAL INFO message:
+ *
+ * See 3GPP TS 48.008 §3.2.1.78
+ */
+static int bssmap_handle_vgcs_addl_info(struct gsm_subscriber_connection *conn,
+ struct msgb *msg, unsigned int length)
+{
+ struct tlv_parsed tp;
+
+ 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;
+ }
+
+ LOGPFSML(conn->fi, LOGL_ERROR, "VGCS ADDITIONAL INFO is not supported.\n");
+
+ return 0;
+}
+
static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc,
struct msgb *msg, unsigned int length)
{
@@ -1124,14 +1488,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);
@@ -1144,6 +1512,45 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn,
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_CONFUSION]);
ret = bssmap_handle_confusion(conn, msg, length);
break;
+ case BSS_MAP_MSG_COMMON_ID:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_COMMON_ID]);
+ ret = bssmap_handle_common_id(conn, msg, length);
+ break;
+ case BSS_MAP_MSG_PERFORM_LOCATION_RQST:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_REQUEST]);
+ lcs_loc_req_start(conn, msg);
+ ret = 0;
+ break;
+ case BSS_MAP_MSG_PERFORM_LOCATION_ABORT:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_ABORT]);
+ if (conn->lcs.loc_req) {
+ 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");
+ ret = 0;
+ }
+ break;
+ case BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_UPLINK_RQST_ACKNOWLEDGE]);
+ ret = bssmap_handle_uplink_rqst_acknowledge(conn, msg, length);
+ break;
+ case BSS_MAP_MSG_UPLINK_REJECT_CMD:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_UPLINK_REJECT_CMD]);
+ ret = bssmap_handle_uplink_reject_cmd(conn, msg, length);
+ break;
+ case BSS_MAP_MSG_UPLINK_RELEASE_CMD:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_UPLINK_RELEASE_CMD]);
+ ret = bssmap_handle_uplink_release_cmd(conn, msg, length);
+ break;
+ case BSS_MAP_MSG_UPLINK_SEIZED_CMD:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_UPLINK_SEIZED_CMD]);
+ ret = bssmap_handle_uplink_seized_cmd(conn, msg, length);
+ break;
+ case BSS_MAP_MSG_VGCS_ADDL_INFO:
+ rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_VGCS_ADDL_INFO]);
+ ret = bssmap_handle_vgcs_addl_info(conn, msg, length);
+ break;
default:
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_UNKNOWN]);
LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n",
@@ -1188,7 +1595,7 @@ static int dtap_rcvmsg(struct gsm_subscriber_connection *conn,
}
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_DTAP]);
- LOGP(DMSC, LOGL_INFO, "Rx MSC DTAP, SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0);
+ LOGP(DMSC, LOGL_INFO, "Rx MSC DTAP, SAPI: %s CHAN: %u\n", gsm0406_dlci_sapi_name(header->dlci_sapi), header->dlci_cc);
/* forward the data */
gsm48 = gsm48_msgb_alloc_name("GSM 04.08 DTAP RCV");
@@ -1203,9 +1610,14 @@ 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;
- dtap_rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MT_DTAP, gsm48);
+
+ /* convert DLCI to RSL link ID, store in msg->cb */
+ OBSC_LINKID_CB(gsm48) = DLCI2RSL_LINK_ID(header->link_id);
+
+ if (conn->vgcs_call.fi)
+ dtap_rc = osmo_fsm_inst_dispatch(conn->vgcs_call.fi, VGCS_EV_MSC_DTAP, gsm48);
+ else
+ dtap_rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MT_DTAP, gsm48);
return dtap_rc;
}
@@ -1299,6 +1711,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,
@@ -1306,12 +1719,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");
@@ -1324,14 +1746,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;
}
@@ -1349,14 +1771,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) && bsc_chan_ind_requires_rtp_stream(new_lchan->activate.info.ch_indctr)) {
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"
@@ -1372,9 +1797,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);
@@ -1390,7 +1828,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);
}
@@ -1404,18 +1842,18 @@ 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,
};
/* 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);
+ if (bsc_chan_ind_requires_rtp_stream(ho->new_lchan->activate.info.ch_indctr) && gscon_is_aoip(conn)) {
+ 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) {
@@ -1430,7 +1868,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");
@@ -1452,9 +1890,186 @@ 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",
rc, strerror(-rc));
}
+
+/* Send SETUP ACKNOWLEDGE to MSC. */
+void bsc_tx_setup_ack(struct gsm_subscriber_connection *conn, struct gsm0808_vgcs_feature_flags *ff)
+{
+ struct msgb *resp;
+ struct gsm0808_vgcs_vbs_setup_ack sa = {};
+
+ if (ff) {
+ sa.vgcs_feature_flags_present = true;
+ sa.flags = *ff;
+ }
+ resp = gsm0808_create_vgcs_vbs_setup_ack(&sa);
+ OSMO_ASSERT(resp);
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_SETUP_ACK));
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+}
+
+/* Send SETUP REFUSE to MSC. */
+void bsc_tx_setup_refuse(struct gsm_subscriber_connection *conn, uint8_t cause)
+{
+ struct msgb *resp;
+ resp = gsm0808_create_vgcs_vbs_setup_refuse(cause);
+
+ OSMO_ASSERT(resp);
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_SETUP_REFUSE));
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+}
+
+/* Send ASSIGNMENT FAILURE to MSC. */
+void bsc_tx_vgcs_vbs_assignment_fail(struct gsm_subscriber_connection *conn, uint8_t cause)
+{
+ struct msgb *resp;
+ struct gsm0808_vgcs_vbs_assign_fail af = {
+ .cause = cause,
+ };
+
+ resp = gsm0808_create_vgcs_vbs_assign_fail(&af);
+ OSMO_ASSERT(resp);
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_ASSIGN_FAIL));
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+}
+
+/* Send ASSIGNMENT RESULT to MSC. */
+void bsc_tx_vgcs_vbs_assignment_result(struct gsm_subscriber_connection *conn, struct gsm0808_channel_type *ct,
+ struct gsm0808_cell_id *ci, uint32_t call_id)
+{
+ struct gsm_lchan *lchan = conn->lchan;
+ struct msgb *resp;
+ struct gsm0808_vgcs_vbs_assign_res ar = {
+ .channel_type = *ct,
+ .cell_identifier = *ci,
+ };
+ int perm_spch;
+ uint8_t osmux_cid;
+
+ /* Chosen Channel */
+ ar.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode);
+ if (!ar.chosen_channel) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to compose Chosen Channel for mode=%s type=%s",
+ get_value_string(gsm48_chan_mode_names, lchan->current_ch_mode_rate.chan_mode),
+ gsm_chan_t_name(lchan->type));
+ bsc_tx_vgcs_vbs_assignment_fail(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ return;
+ }
+ ar.chosen_channel_present = true;
+
+ /* Generate RTP related fields. */
+ if (gscon_is_aoip(conn)) {
+ /* AoIP Transport Layer Address (BSS) */
+ if (!osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(conn->user_plane.mgw_endpoint_ci_msc,
+ &ar.aoip_transport_layer)) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to compose RTP address of MGW -> MSC");
+ bsc_tx_vgcs_vbs_assignment_fail(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ return;
+ }
+ ar.aoip_transport_layer_present = true;
+
+ /* Call Identifier */
+ ar.call_id = call_id;
+ ar.call_id_present = true;
+
+ /* Osmux */
+ if (conn->assignment.req.use_osmux) {
+ if (!osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(conn->user_plane.mgw_endpoint_ci_msc,
+ &osmux_cid)) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to compose Osmux CID of MGW -> MSC");
+ bsc_tx_vgcs_vbs_assignment_fail(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ return;
+ }
+ }
+
+ /* Extrapolate speech codec from speech mode */
+ perm_spch = gsm0808_permitted_speech(lchan->type, lchan->current_ch_mode_rate.chan_mode);
+ gsm0808_speech_codec_from_chan_type(&ar.codec_msc_chosen, perm_spch);
+ ar.codec_msc_chosen.cfg = conn->lchan->current_ch_mode_rate.s15_s0;
+ ar.codec_present = true;
+ }
+
+ resp = gsm0808_create_vgcs_vbs_assign_res(&ar);
+ OSMO_ASSERT(resp);
+ if (conn->assignment.req.use_osmux)
+ bssap_extend_osmux(resp, osmux_cid);
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_ASSIGN_RESULT));
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+}
+
+/* Send UPLINK REQUEST to MSC. */
+void bsc_tx_uplink_req(struct gsm_subscriber_connection *conn)
+{
+ struct msgb *resp;
+ struct gsm0808_uplink_request ur = {};
+
+ resp = gsm0808_create_uplink_request(&ur);
+ OSMO_ASSERT(resp);
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_UPLINK_RQST));
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+}
+
+/* Send UPLINK REQUEST CONFIRMATION to MSC. */
+void bsc_tx_uplink_req_conf(struct gsm_subscriber_connection *conn, struct gsm0808_cell_id *ci, uint8_t *l3_info,
+ uint8_t length)
+{
+ struct msgb *resp;
+ struct gsm0808_uplink_request_cnf ur = {
+ .cell_identifier = *ci,
+ };
+
+ OSMO_ASSERT(length <= LAYER_3_INFORMATION_MAXLEN);
+ if (length) {
+ memcpy(ur.l3.l3, l3_info, length);
+ ur.l3.l3_len = length;
+ }
+ resp = gsm0808_create_uplink_request_cnf(&ur);
+ OSMO_ASSERT(resp);
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_UPLINK_RQST_CONFIRMATION));
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+}
+
+/* Send UPLINK APPLICATION DATA to MSC. */
+void bsc_tx_uplink_app_data(struct gsm_subscriber_connection *conn, struct gsm0808_cell_id *ci, uint8_t *l3_info,
+ uint8_t length)
+{
+ struct msgb *resp;
+ struct gsm0808_uplink_app_data ad = {
+ .cell_identifier = *ci,
+ };
+
+ OSMO_ASSERT(length <= LAYER_3_INFORMATION_MAXLEN);
+ memcpy(ad.l3.l3, l3_info, length);
+ ad.l3.l3_len = length;
+ resp = gsm0808_create_uplink_app_data(&ad);
+ OSMO_ASSERT(resp);
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_UPLINK_APP_DATA));
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+}
+
+/* Send UPLINK RELEASE INDICATION to MSC. */
+void bsc_tx_uplink_release_ind(struct gsm_subscriber_connection *conn, uint8_t cause)
+{
+ struct msgb *resp;
+ struct gsm0808_uplink_release_ind ri = {
+ .cause = cause,
+ };
+
+ resp = gsm0808_create_uplink_release_ind(&ri);
+ OSMO_ASSERT(resp);
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(conn->sccp.msc->msc_ctrs, MSC_CTR_BSSMAP_TX_DT1_UPLINK_RELEASE_INDICATION));
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+}
diff --git a/src/osmo-bsc/osmo_bsc_filter.c b/src/osmo-bsc/osmo_bsc_filter.c
index 08c3a5048..2b58ccf54 100644
--- a/src/osmo-bsc/osmo_bsc_filter.c
+++ b/src/osmo-bsc/osmo_bsc_filter.c
@@ -26,6 +26,7 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/bts.h>
#include <stdlib.h>
@@ -91,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;
@@ -129,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_grace.c b/src/osmo-bsc/osmo_bsc_grace.c
index 56edee569..19ffe65aa 100644
--- a/src/osmo-bsc/osmo_bsc_grace.c
+++ b/src/osmo-bsc/osmo_bsc_grace.c
@@ -25,6 +25,7 @@
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/signal.h>
#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/bts.h>
int bsc_grace_allow_new_connection(struct gsm_network *network, struct gsm_bts *bts)
{
@@ -32,42 +33,3 @@ int bsc_grace_allow_new_connection(struct gsm_network *network, struct gsm_bts *
return 1;
return network->rf_ctrl->policy == S_RF_ON;
}
-
-
-/* Return value is like paging_request_bts():
- * returns 1 on success (one BTS was paged); 0 in case of error (e.g. TRX down) */
-static int locked_paging_bts(struct gsm_bts *bts,
- struct bsc_subscr *subscr,
- int chan_needed,
- struct bsc_msc_data *msc)
-{
- /* Return error if the BTS is not excluded from the lock. */
- if (!bts->excl_from_rf_lock)
- return 0;
-
- /* in case of no lac patching is in place, check the BTS */
- if (msc->core_lac == -1 && subscr->lac != bts->location_area_code)
- return 0;
-
- return paging_request_bts(bts, subscr, chan_needed, msc);
-}
-
-/**
- * Page a subscriber in an MSC.
- * \param[in] rf_policy if not S_RF_ON, page only BTSs which are not excluded from the RF lock
- * \param[in] subscr subscriber we want to page
- * \param[in] chan_needed value of the GSM0808_IE_CHANNEL_NEEDED IE
- * \param[in] msc MSC which has issued this paging
- * \param[in] bts The BTS to issue the paging on
- * \returns 1 if paging was issued to the BTS, 0 if not
- */
-int bsc_grace_paging_request(enum signal_rf rf_policy,
- struct bsc_subscr *subscr,
- int chan_needed,
- struct bsc_msc_data *msc,
- struct gsm_bts *bts)
-{
- if (rf_policy == S_RF_ON)
- return paging_request_bts(bts, subscr, chan_needed, msc);
- return locked_paging_bts(bts, subscr, chan_needed, msc);
-}
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 dbd970154..8da319993 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -38,6 +38,8 @@
#include <osmocom/bsc/assignment_fsm.h>
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/smscb.h>
+#include <osmocom/bsc/lb.h>
+#include <osmocom/bsc/meas_feed.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/ctrl/control_if.h>
@@ -53,8 +55,10 @@
#include <osmocom/vty/ports.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/command.h>
+#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>
@@ -64,8 +68,11 @@
#include <osmocom/bsc/e1_config.h>
#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>
@@ -81,19 +88,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");
@@ -101,11 +107,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)
@@ -121,15 +153,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;
@@ -140,14 +171,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;
@@ -207,7 +232,7 @@ static int oml_msg_nack(struct nm_nack_signal_data *nack)
return 0;
}
- if (is_ipaccess_bts(nack->bts))
+ if (is_ipa_abisip_bts(nack->bts))
ipaccess_drop_oml_deferred(nack->bts);
return 0;
@@ -230,75 +255,74 @@ 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, bitnum, n_chan;
int i;
/* re-set the MA to all-zero */
- ma->cur_bit = 0;
ts->hopping.ma_len = 0;
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 = num_cell_arfcns / 8;
- if (num_cell_arfcns % 8)
- ts->hopping.ma_len++;
+ ts->hopping.ma_len = OSMO_BYTES_FOR_BITS(num_cell_arfcns);
+ ma->cur_bit = (ts->hopping.ma_len * 8) - 1;
- n_chan = 0;
- for (i = 0; i < 1024; i++) {
+ for (i = 1; i < 1024; i++) {
if (!bitvec_get_bit_pos(cell_chan, i))
continue;
/* set the corresponding bit in the MA */
- bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan;
if (bitvec_get_bit_pos(ts_arfcn, i))
- bitvec_set_bit_pos(ma, bitnum, 1);
+ bitvec_set_bit_pos(ma, ma->cur_bit, 1);
else
- bitvec_set_bit_pos(ma, bitnum, 0);
- n_chan++;
+ bitvec_set_bit_pos(ma, ma->cur_bit, 0);
+ ma->cur_bit--;
}
/* ARFCN 0 is special: It is coded last in the bitmask */
if (bitvec_get_bit_pos(cell_chan, 0)) {
- n_chan++;
/* set the corresponding bit in the MA */
- bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan;
if (bitvec_get_bit_pos(ts_arfcn, 0))
- bitvec_set_bit_pos(ma, bitnum, 1);
+ bitvec_set_bit_pos(ma, ma->cur_bit, 1);
else
- bitvec_set_bit_pos(ma, bitnum, 0);
+ 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)
{
+ struct gsm_bts *bts = trx->bts;
unsigned int i;
+ int rc;
- LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) "
+ LOG_TRX(trx, DRSL, LOGL_NOTICE, "bootstrapping RSL "
"on ARFCN %u using MCC-MNC %s LAC=%u CID=%u BSIC=%u\n",
- trx->bts->nr, trx->nr, trx->arfcn,
- osmo_plmn_name(&bsc_gsmnet->plmn),
- trx->bts->location_area_code,
- trx->bts->cell_identity, trx->bts->bsic);
+ trx->arfcn, osmo_plmn_name(&bsc_gsmnet->plmn),
+ bts->location_area_code, bts->cell_identity, bts->bsic);
- if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) {
+ if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) {
rsl_nokia_si_begin(trx);
}
@@ -310,169 +334,76 @@ static void bootstrap_rsl(struct gsm_bts_trx *trx)
if (trx_is_usable(trx))
acc_ramp_trigger(&trx->bts->acc_ramp);
- gsm_bts_trx_set_system_infos(trx);
+ if (gsm_bts_trx_set_system_infos(trx) != 0) {
+ LOG_TRX(trx, DRSL, LOGL_ERROR, "Failed to generate System Information\n");
+ return;
+ }
- if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) {
+ if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) {
/* channel unspecific, power reduction in 2 dB steps */
rsl_bs_power_control(trx, 0xFF, trx->max_power_red / 2);
rsl_nokia_si_end(trx);
}
+ if (bts->model->power_ctrl_send_def_params != NULL) {
+ rc = 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(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)
{
- 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);
- }
+ 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);
}
-/* 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)
+static bool nch_position_compatible_with_combined_ccch(const struct gsm_bts *bts)
{
- 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);
-
- if (subsys != SS_L_INPUT)
- return -EINVAL;
-
- LOGP(DLMI, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__,
- get_value_string(e1inp_signal_names, signal));
- switch (signal) {
- case S_L_INP_TEI_UP:
- if (isd->link_type == E1INP_SIGN_OML) {
- /* 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]);
- }
- }
- if (isd->link_type == E1INP_SIGN_RSL)
- bootstrap_rsl(trx);
+ switch (bts->nch.num_blocks) {
+ case 0:
+ /* no NCH enabled, so we are fine */
+ return true;
+ case 1:
+ if (bts->nch.first_block == 0 || bts->nch.first_block == 1)
+ return true;
break;
- case S_L_INP_TEI_DN:
- LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx);
-
- 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);
- } else if (isd->link_type == E1INP_SIGN_RSL) {
- rate_ctr_inc(&trx->bts->bts_ctrs->ctr[BTS_CTR_BTS_RSL_FAIL]);
- acc_ramp_abort(&trx->bts->acc_ramp);
- all_ts_dispatch_event(trx, TS_EV_RSL_DOWN);
- if (trx->nr == 0)
- osmo_timer_del(&trx->bts->cbch_timer);
- }
-
- gsm_bts_mo_reset(trx->bts);
-
- abis_nm_clear_queue(trx->bts);
+ case 2:
+ if (bts->nch.first_block == 0)
+ return true;
break;
default:
break;
}
- return 0;
+ /* anything else is not permitted */
+ return false;
}
-static int bootstrap_bts(struct gsm_bts *bts)
+static void 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;
@@ -485,6 +416,11 @@ static int bootstrap_bts(struct gsm_bts *bts)
bts->si_common.chan_desc.bs_ag_blks_res);
bts->si_common.chan_desc.bs_ag_blks_res = 2;
}
+
+ if (!nch_position_compatible_with_combined_ccch(bts)) {
+ LOG_BTS(bts, DNM, LOGL_ERROR, "CCCH is combined with SDCCHs, but NCH position/size is "
+ "incompatible with that. Please fix your config!\n");
+ }
} 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);
@@ -493,18 +429,84 @@ static int bootstrap_bts(struct gsm_bts *bts)
bts->si_common.chan_desc.ccch_conf = (n << 1);
}
- bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
-
- bts->si_common.cell_sel_par.acs = 0;
-
- bts->si_common.ncc_permitted = 0xff;
+ if (bts->nch.first_block + bts->nch.num_blocks > bts->si_common.chan_desc.bs_ag_blks_res) {
+ LOG_BTS(bts, DNM, LOGL_ERROR, "Position/Number of NCH blocks (%u..%u) exceeds AGCH (%u)."
+ "Please fix your config!\n", bts->nch.first_block,
+ bts->nch.first_block + bts->nch.num_blocks - 1,
+ bts->si_common.chan_desc.bs_ag_blks_res);
+ }
- bts->chan_load_samples_idx = 0;
+ bts_setup_ramp_init_bts(bts);
/* ACC ramping is initialized from vty/config */
/* Initialize the BTS state */
- gsm_bts_mo_reset(bts);
+ 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 */
+static int inp_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct input_signal_data *isd = signal_data;
+ struct gsm_bts_trx *trx = isd->trx;
+ int rc;
+
+ if (subsys != SS_L_INPUT)
+ return -EINVAL;
+
+ LOGP(DLMI, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__,
+ get_value_string(e1inp_signal_names, signal));
+ switch (signal) {
+ case S_L_INP_TEI_UP:
+ if (isd->link_type == E1INP_SIGN_OML) {
+ /* 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) {
+ 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(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_ipa_abisip_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(rate_ctr_group_get_ctr(trx->bts->bts_ctrs, BTS_CTR_BTS_RSL_FAIL));
+ acc_ramp_abort(&trx->bts->acc_ramp);
+ gsm_trx_all_ts_dispatch(trx, TS_EV_RSL_DOWN, NULL);
+ }
+
+ gsm_bts_sm_mo_reset(trx->bts->site_mgr);
+
+ abis_nm_clear_queue(trx->bts);
+ break;
+ default:
+ break;
+ }
return 0;
}
@@ -516,13 +518,12 @@ static int bsc_network_configure(const char *config_file)
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
- LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file);
+ LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s' (%s)\n", config_file, strerror(-rc));
return rc;
}
/* 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;
@@ -530,11 +531,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");
@@ -552,6 +554,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;
{
@@ -561,6 +568,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;
{
@@ -576,7 +598,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:
@@ -634,31 +655,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);
@@ -774,8 +824,29 @@ static const struct log_info_cat osmo_bsc_categories[] = {
.name = "DCBS",
.description = "Cell Broadcast System",
.enabled = 1, .loglevel = LOGL_NOTICE,
- }
-
+ },
+ [DLCS] = {
+ .name = "DLCS",
+ .description = "Location Services",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DASCI] = {
+ .name = "DASCI",
+ .description = "Advanced Speech Call Items (VGCS/VBS)",
+ .color = "\033[1;38m",
+ .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)
@@ -800,7 +871,45 @@ 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 initializes 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. */
+ if (!mgcp_client_pool_empty(bsc_gsmnet->mgw.mgw_pool)) {
+ pool_members_initalized = mgcp_client_pool_connect(bsc_gsmnet->mgw.mgw_pool);
+ if (!pool_members_initalized) {
+ LOGP(DNM, LOGL_ERROR, "MGW pool failed to initialize any pool members\n");
+ return -EINVAL;
+ }
+ 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 initialization 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)
{
@@ -816,13 +925,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();
@@ -831,8 +940,8 @@ int main(int argc, char **argv)
exit(1);
}
- bsc_gsmnet->mgw.conf = talloc_zero(bsc_gsmnet, struct mgcp_client_conf);
- mgcp_client_conf_init(bsc_gsmnet->mgw.conf);
+ bsc_gsmnet->mgw.conf = mgcp_client_conf_alloc(bsc_gsmnet);
+ bsc_gsmnet->mgw.mgw_pool = mgcp_client_pool_alloc(bsc_gsmnet);
bts_init();
libosmo_abis_init(tall_bsc_ctx);
@@ -843,6 +952,7 @@ int main(int argc, char **argv)
vty_init(&vty_info);
bsc_vty_init(bsc_gsmnet);
ctrl_vty_init(tall_bsc_ctx);
+ osmo_cpu_sched_vty_init(tall_bsc_ctx);
logging_vty_add_deprecated_subsys(tall_bsc_ctx, "cc");
logging_vty_add_deprecated_subsys(tall_bsc_ctx, "mgcp");
logging_vty_add_deprecated_subsys(tall_bsc_ctx, "nat");
@@ -858,11 +968,11 @@ 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();
+ meas_feed_txqueue_max_length_set(MEAS_FEED_TXQUEUE_MAX_LEN_DEFAULT);
/* Read the config */
rc = bsc_network_configure(config_file);
@@ -871,11 +981,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);
@@ -887,6 +1000,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);
@@ -908,6 +1034,9 @@ int main(int argc, char **argv)
}
}
+ if (bsc_mgw_setup() != 0)
+ exit(1);
+
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");
@@ -915,15 +1044,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);
@@ -932,6 +1052,7 @@ int main(int argc, char **argv)
handover_decision_1_init();
hodec2_init(bsc_gsmnet);
bsc_cbc_link_restart();
+ lb_start_or_stop();
signal(SIGINT, &signal_handler);
signal(SIGTERM, &signal_handler);
@@ -940,6 +1061,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) {
@@ -948,7 +1072,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_mgcp.c b/src/osmo-bsc/osmo_bsc_mgcp.c
index bec793032..d7de4d264 100644
--- a/src/osmo-bsc/osmo_bsc_mgcp.c
+++ b/src/osmo-bsc/osmo_bsc_mgcp.c
@@ -2,6 +2,7 @@
* SCCPlite MGCP handling
*
* (C) 2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2023 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
@@ -19,6 +20,15 @@
*
*/
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
+#include <osmocom/mgcp_client/mgcp_client.h>
+
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/osmo_bsc.h>
#include <osmocom/bsc/gsm_data.h>
@@ -33,33 +43,136 @@
static struct bsc_msc_data *msc_from_asp(struct osmo_ss7_asp *asp)
{
int msc_nr;
+ const char *asp_name = osmo_ss7_asp_get_name(asp);
/* this is rather ugly, as we of course have MTP-level routing between
* the local SCCP user (BSC) and the AS/ASPs. However, for the most simple
* SCCPlite case, there is a 1:1 mapping between ASP and AS, and using
* the libosmo-sigtran "simple client", the names are "as[p]-clnt-msc-%u",
* as set in osmo_bsc_sigtran_init() */
- if (sscanf(asp->cfg.name, "asp-clnt-msc-%u", &msc_nr) != 1) {
- LOGP(DMSC, LOGL_ERROR, "Cannot find to which MSC the ASP %s belongs\n", asp->cfg.name);
+ if (!asp_name || sscanf(asp_name, "asp-clnt-msc-%u", &msc_nr) != 1) {
+ LOGP(DMSC, LOGL_ERROR, "Cannot find to which MSC the ASP '%s' belongs\n", asp_name);
return NULL;
}
return osmo_msc_data_find(bsc_gsmnet, msc_nr);
}
+/* negative on error, zero upon success */
+static int parse_local_endpoint_name(char *buf, size_t buf_len, const char *data)
+{
+ char line[1024];
+ char *epstart, *sep;
+ const char *start = data;
+ char *eol = strpbrk(start, "\r\n");
+
+ if (!eol)
+ return -1;
+
+ if (eol - start > sizeof(line))
+ return -1;
+ memcpy(line, start, eol - start);
+ line[eol - start] = '\0';
+
+ if (!(epstart = strchr(line, ' ')))
+ return -1;
+ epstart++;
+ /* epstart now points to trans */
+
+ if (!(epstart = strchr(epstart, ' ')))
+ return -1;
+ epstart++;
+ /* epstart now points to endpoint */
+ if (!(sep = strchr(epstart, '@')))
+ return -1;
+ if (sep - epstart >= buf_len)
+ return -1;
+
+ *sep = '\0';
+ osmo_strlcpy(buf, epstart, buf_len);
+ return 0;
+}
+
/* We received an IPA-encapsulated MGCP message from a MSC. Transfers msg ownership. */
int bsc_sccplite_rx_mgcp(struct osmo_ss7_asp *asp, struct msgb *msg)
{
struct bsc_msc_data *msc;
+ struct gsm_subscriber_connection *conn;
+ char rcv_ep_local_name[1024];
+ struct osmo_sockaddr_str osa_str = {};
+ struct osmo_sockaddr osa = {};
+ socklen_t dest_len;
+ struct mgcp_client *mgcp_cli = NULL;
int rc;
- LOGP(DMSC, LOGL_NOTICE, "%s: Received IPA-encapsulated MGCP: %s\n", asp->cfg.name, msg->l2h);
+ LOGP(DMSC, LOGL_INFO, "%s: Received IPA-encapsulated MGCP: %s\n",
+ osmo_ss7_asp_get_name(asp), msg->l2h);
+
msc = msc_from_asp(asp);
- if (msc) {
- /* we don't have a write queue here as we simply expect the socket buffers
- * to be large enough to deal with whatever small/infrequent MGCP messages */
- rc = send(msc->mgcp_ipa.ofd.fd, msgb_l2(msg), msgb_l2len(msg), 0);
- } else
+ if (!msc) {
rc = 0;
+ goto free_msg_ret;
+ }
+
+ rc = parse_local_endpoint_name(rcv_ep_local_name, sizeof(rcv_ep_local_name), (const char *)msg->l2h);
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to parse CIC\n",
+ osmo_ss7_asp_get_name(asp));
+ goto free_msg_ret;
+ }
+
+ /* Lookup which conn attached to the MSC holds an MGW endpoint with the
+ * name Endpoint Number as the one provided in the MGCP msg we received
+ * from MSC. Since CIC are unique per MSC, that's the same MGW in the
+ * pool where we have to forward the MGCP message. */
+ llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) {
+ const char *ep_local_name;
+ if (conn->sccp.msc != msc)
+ continue; /* Only conns belonging to this MSC */
+ if (!conn->user_plane.mgw_endpoint)
+ continue;
+ ep_local_name = osmo_mgcpc_ep_local_name(conn->user_plane.mgw_endpoint);
+ LOGPFSMSL(conn->fi, DMSC, LOGL_DEBUG, "ep_local_name='%s' vs rcv_ep_local_name='%s'\n",
+ ep_local_name ? : "(null)", rcv_ep_local_name);
+ if (!ep_local_name)
+ continue;
+ if (strcmp(ep_local_name, rcv_ep_local_name) != 0)
+ continue;
+ mgcp_cli = osmo_mgcpc_ep_client(conn->user_plane.mgw_endpoint);
+ if (!mgcp_cli)
+ continue;
+ break;
+ }
+
+ if (!mgcp_cli) {
+ LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to find associated MGW\n",
+ osmo_ss7_asp_get_name(asp));
+ rc = 0;
+ goto free_msg_ret;
+ }
+
+ rc = osmo_sockaddr_str_from_str(&osa_str, mgcp_client_remote_addr_str(mgcp_cli),
+ mgcp_client_remote_port(mgcp_cli));
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to parse MGCP address %s:%u\n",
+ osmo_ss7_asp_get_name(asp), mgcp_client_remote_addr_str(mgcp_cli), mgcp_client_remote_port(mgcp_cli));
+ goto free_msg_ret;
+ }
+
+ LOGP(DMSC, LOGL_NOTICE, "%s: Forwarding IPA-encapsulated MGCP to MGW at " OSMO_SOCKADDR_STR_FMT "\n",
+ osmo_ss7_asp_get_name(asp), OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(&osa_str));
+
+ rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to parse MGCP address " OSMO_SOCKADDR_STR_FMT "\n",
+ osmo_ss7_asp_get_name(asp), OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(&osa_str));
+ goto free_msg_ret;
+ }
+ dest_len = osmo_sockaddr_size(&osa);
+
+ /* we don't have a write queue here as we simply expect the socket buffers
+ * to be large enough to deal with whatever small/infrequent MGCP messages */
+ rc = sendto(msc->mgcp_ipa.ofd.fd, msgb_l2(msg), msgb_l2len(msg), 0, &osa.u.sa, dest_len);
+free_msg_ret:
msgb_free(msg);
return rc;
}
@@ -71,7 +184,7 @@ int bsc_sccplite_mgcp_proxy_cb(struct osmo_fd *ofd, unsigned int what)
struct msgb *msg;
int rc;
- if (!(what & BSC_FD_READ))
+ if (!(what & OSMO_FD_READ))
return 0;
msg = msgb_alloc_headroom(1024, 16, "MGCP->IPA");
diff --git a/src/osmo-bsc/osmo_bsc_msc.c b/src/osmo-bsc/osmo_bsc_msc.c
index f9691462c..24255924f 100644
--- a/src/osmo-bsc/osmo_bsc_msc.c
+++ b/src/osmo-bsc/osmo_bsc_msc.c
@@ -29,6 +29,7 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/socket.h>
@@ -39,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>
@@ -46,60 +48,281 @@
static const struct rate_ctr_desc msc_ctr_description[] = {
/* Rx message counters (per specific message) */
- [MSC_CTR_BSSMAP_RX_UDT_RESET_ACKNOWLEDGE] = {"bssmap:rx:udt:reset:ack", "Number of received BSSMAP UDT RESET ACKNOWLEDGE messages"},
- [MSC_CTR_BSSMAP_RX_UDT_RESET] = {"bssmap:rx:udt:reset:request", "Number of received BSSMAP UDT RESET messages"},
- [MSC_CTR_BSSMAP_RX_UDT_PAGING] = {"bssmap:rx:udt:paging", "Number of received BSSMAP UDT PAGING messages"},
- [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_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_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"},
- [MSC_CTR_BSSMAP_RX_DT1_UNKNOWN] = {"bssmap:rx:dt1:err_unknown", "Number of received BSSMAP unknown DT1 messages"},
- [MSC_CTR_BSSMAP_RX_DT1_DTAP] = {"bssmap:rx:dt1:dtap:good", "Number of received BSSMAP DTAP messages"},
- [MSC_CTR_BSSMAP_RX_DT1_DTAP_ERROR] = {"bssmap:rx:dt1:dtap:error", "Number of received BSSMAP DTAP messages with errors"},
+ [MSC_CTR_BSSMAP_RX_UDT_RESET_ACKNOWLEDGE] = {
+ "bssmap:rx:udt:reset:ack",
+ "Number of received BSSMAP UDT RESET ACKNOWLEDGE messages"
+ },
+ [MSC_CTR_BSSMAP_RX_UDT_RESET] = {
+ "bssmap:rx:udt:reset:request",
+ "Number of received BSSMAP UDT RESET messages"
+ },
+ [MSC_CTR_BSSMAP_RX_UDT_PAGING] = {
+ "bssmap:rx:udt:paging",
+ "Number of received BSSMAP UDT PAGING messages"
+ },
+ [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_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"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_COMMON_ID] = {
+ "bssmap:rx:dt1:common_id",
+ "Number of received BSSMAP DT1 COMMON ID messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_UNKNOWN] = {
+ "bssmap:rx:dt1:err_unknown",
+ "Number of received BSSMAP unknown DT1 messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_DTAP] = {
+ "bssmap:rx:dt1:dtap:good",
+ "Number of received BSSMAP DTAP messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_DTAP_ERROR] = {
+ "bssmap:rx:dt1:dtap:error",
+ "Number of received BSSMAP DTAP messages with errors"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_REQUEST] = {
+ "bssmap:rx:dt1:location:request",
+ "Number of received BSSMAP Perform Location Request messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_ABORT] = {
+ "bssmap:tx:dt1:location:abort",
+ "Number of received BSSMAP Perform Location Abort messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_VGCS_VBS_SETUP] = {
+ "bssmap:rx:dt1:vgcs_vbs_setup",
+ "Number of received BSSMAP DT1 VGCS/VBS SETUP messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_VGCS_VBS_ASSIGN_RQST] = {
+ "bssmap:rx:dt1:vgcs_vbs_assignment:req",
+ "Number of received BSSMAP DT1 VGCS/VBS ASSIGNMENT messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_UPLINK_RQST_ACKNOWLEDGE] = {
+ "bssmap:rx:dt1:uplink_rqst:ack",
+ "Number of received BSSMAP DT1 UPLINK REQUEST ACKNOWLEDGE messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_UPLINK_REJECT_CMD] = {
+ "bssmap:rx:dt1:uplink_reject:cmd",
+ "Number of received BSSMAP DT1 UPLINK REJECT COMMAND messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_UPLINK_RELEASE_CMD] = {
+ "bssmap:rx:dt1:uplink_release:cmd",
+ "Number of received BSSMAP DT1 UPLINK RELEASE COMMAND messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_UPLINK_SEIZED_CMD] = {
+ "bssmap:rx:dt1:uplink_seized:cmd",
+ "Number of received BSSMAP DT1 UPLINK SEIZED COMMAND messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_VGCS_ADDL_INFO] = {
+ "bssmap:rx:dt1:vgcs_addl_info",
+ "Number of received BSSMAP DT1 VGCS/VBS ASSITIONAL INFORMATION messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_VGCS_SMS] = {
+ "bssmap:rx:dt1:vgcs_sms",
+ "Number of received BSSMAP DT1 VGCS SMS messages"
+ },
+ [MSC_CTR_BSSMAP_RX_DT1_NOTIFICATION_DATA] = {
+ "bssmap:rx:dt1:notification_data",
+ "Number of received BSSMAP DT1 NOTIFICATION DATA messages"
+ },
/* Tx message counters (per message type)
*
* The counters here follow the logic of the osmo_bsc_sigtran_send() function
* which receives DT1 messages from the upper layers and actually sends them to the MSC.
* These counters cover all messages passed to the function by the upper layers: */
- [MSC_CTR_BSSMAP_TX_BSS_MANAGEMENT] = {"bssmap:tx:type:bss_management", "Number of transmitted BSS MANAGEMENT messages"},
- [MSC_CTR_BSSMAP_TX_DTAP] = {"bssmap:tx:type:dtap", "Number of transmitted DTAP messages"},
- [MSC_CTR_BSSMAP_TX_UNKNOWN] = {"bssmap:tx:type:err_unknown", "Number of transmitted messages with unknown type (an error in our code?)"},
- [MSC_CTR_BSSMAP_TX_SHORT] = {"bssmap:tx:type:err_short", "Number of transmitted messages which are too short (an error in our code?)"},
+ [MSC_CTR_BSSMAP_TX_BSS_MANAGEMENT] = {
+ "bssmap:tx:type:bss_management",
+ "Number of transmitted BSS MANAGEMENT messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DTAP] = {
+ "bssmap:tx:type:dtap",
+ "Number of transmitted DTAP messages"
+ },
+ [MSC_CTR_BSSMAP_TX_UNKNOWN] = {
+ "bssmap:tx:type:err_unknown",
+ "Number of transmitted messages with unknown type (an error in our code?)"
+ },
+ [MSC_CTR_BSSMAP_TX_SHORT] = {
+ "bssmap:tx:type:err_short",
+ "Number of transmitted messages which are too short (an error in our code?)"
+ },
/* The next counters are also counted in the osmo_bsc_sigtran_send() function and
* sum up to the exactly same number as the counters above but instead of message
* classes they split by the result of the sending attempt: */
- [MSC_CTR_BSSMAP_TX_ERR_CONN_NOT_READY] = {"bssmap:tx:result:err_conn_not_ready", "Number of BSSMAP messages we tried to send when the connection was not ready yet"},
- [MSC_CTR_BSSMAP_TX_ERR_SEND] = {"bssmap:tx:result:err_send", "Number of socket errors while sending BSSMAP messages"},
- [MSC_CTR_BSSMAP_TX_SUCCESS] = {"bssmap:tx:result:success", "Number of successfully sent BSSMAP messages"},
+ [MSC_CTR_BSSMAP_TX_ERR_CONN_NOT_READY] = {
+ "bssmap:tx:result:err_conn_not_ready",
+ "Number of BSSMAP messages we tried to send when the connection was not ready yet"
+ },
+ [MSC_CTR_BSSMAP_TX_ERR_SEND] = {
+ "bssmap:tx:result:err_send",
+ "Number of socket errors while sending BSSMAP messages"
+ },
+ [MSC_CTR_BSSMAP_TX_SUCCESS] = {
+ "bssmap:tx:result:success",
+ "Number of successfully sent BSSMAP messages"
+ },
/* Tx message counters (per specific message)
*
* Theoretically, the DT1 counters should sum up to the same number as the Tx counters
* above but since these counters are coming from the upper layers, there might be
* some difference if we forget some code path. */
- [MSC_CTR_BSSMAP_TX_UDT_RESET] = {"bssmap:tx:udt:reset:request", "Number of transmitted BSSMAP UDT RESET messages"},
- [MSC_CTR_BSSMAP_TX_UDT_RESET_ACK] = {"bssmap:tx:udt:reset:ack", "Number of transmitted BSSMAP UDT RESET ACK messages"},
- [MSC_CTR_BSSMAP_TX_DT1_CLEAR_RQST] = {"bssmap:tx:dt1:clear:rqst", "Number of transmitted BSSMAP DT1 CLEAR RQSTtx messages"},
- [MSC_CTR_BSSMAP_TX_DT1_CLEAR_COMPLETE] = {"bssmap:tx:dt1:clear:complete", "Number of transmitted BSSMAP DT1 CLEAR COMPLETE messages"},
- [MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_FAILURE] = {"bssmap:tx:dt1:assigment:failure", "Number of transmitted BSSMAP DT1 ASSIGMENT FAILURE messages"},
- [MSC_CTR_BSSMAP_TX_DT1_ASSIGMENT_COMPLETE] = {"bssmap:tx:dt1:assigment:complete", "Number of transmitted BSSMAP DT1 ASSIGMENT COMPLETE messages"},
- [MSC_CTR_BSSMAP_TX_DT1_SAPI_N_REJECT] = {"bssmap:tx:dt1:sapi_n:reject", "Number of transmitted BSSMAP DT1 SAPI N REJECT messages"},
- [MSC_CTR_BSSMAP_TX_DT1_CIPHER_COMPLETE] = {"bssmap:tx:dt1:cipher_mode:complete", "Number of transmitted BSSMAP DT1 CIPHER COMPLETE messages"},
- [MSC_CTR_BSSMAP_TX_DT1_CIPHER_REJECT] = {"bssmap:tx:dt1:cipher_mode:reject", "Number of transmitted BSSMAP DT1 CIPHER REJECT messages"},
- [MSC_CTR_BSSMAP_TX_DT1_CLASSMARK_UPDATE] = {"bssmap:tx:dt1:classmark:update", "Number of transmitted BSSMAP DT1 CLASSMARK UPDATE messages"},
- [MSC_CTR_BSSMAP_TX_DT1_LCLS_CONNECT_CTRL_ACK] = {"bssmap:tx:dt1:lcls_connect_ctrl:ack", "Number of transmitted BSSMAP DT1 LCLS CONNECT CTRL ACK messages"},
- [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_REQUIRED] = {"bssmap:tx:dt1:handover:required", "Number of transmitted BSSMAP DT1 HANDOVER REQUIRED messages"},
- [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_PERFORMED] = {"bssmap:tx:dt1:handover:performed", "Number of transmitted BSSMAP DT1 HANDOVER PERFORMED messages"},
- [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_RQST_ACKNOWLEDGE] = {"bssmap:tx:dt1:handover:rqst_acknowledge", "Number of transmitted BSSMAP DT1 HANDOVER RQST ACKNOWLEDGE messages"},
- [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_DETECT] = {"bssmap:tx:dt1:handover:detect", "Number of transmitted BSSMAP DT1 HANDOVER DETECT messages"},
- [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_COMPLETE] = {"bssmap:tx:dt1:handover:complete", "Number of transmitted BSSMAP DT1 HANDOVER COMPLETE messages"},
- [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_FAILURE] = {"bssmap:tx:dt1:handover:failure", "Number of transmitted BSSMAP DT1 HANDOVER FAILURE messages"},
- [MSC_CTR_BSSMAP_TX_DT1_DTAP] = {"bssmap:tx:dt1:dtap", "Number of transmitted BSSMAP DT1 DTAP messages"},
+ [MSC_CTR_BSSMAP_TX_UDT_RESET] = {
+ "bssmap:tx:udt:reset:request",
+ "Number of transmitted BSSMAP UDT RESET messages"
+ },
+ [MSC_CTR_BSSMAP_TX_UDT_RESET_ACK] = {
+ "bssmap:tx:udt:reset:ack",
+ "Number of transmitted BSSMAP UDT RESET ACK messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_CLEAR_RQST] = {
+ "bssmap:tx:dt1:clear:rqst",
+ "Number of transmitted BSSMAP DT1 CLEAR RQSTtx messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_CLEAR_COMPLETE] = {
+ "bssmap:tx:dt1:clear:complete",
+ "Number of transmitted BSSMAP DT1 CLEAR COMPLETE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_ASSIGNMENT_FAILURE] = {
+ "bssmap:tx:dt1:assignment:failure",
+ "Number of transmitted BSSMAP DT1 ASSIGNMENT FAILURE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_ASSIGNMENT_COMPLETE] = {
+ "bssmap:tx:dt1:assignment:complete",
+ "Number of transmitted BSSMAP DT1 ASSIGNMENT COMPLETE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_SAPI_N_REJECT] = {
+ "bssmap:tx:dt1:sapi_n:reject",
+ "Number of transmitted BSSMAP DT1 SAPI N REJECT messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_CIPHER_COMPLETE] = {
+ "bssmap:tx:dt1:cipher_mode:complete",
+ "Number of transmitted BSSMAP DT1 CIPHER COMPLETE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_CIPHER_REJECT] = {
+ "bssmap:tx:dt1:cipher_mode:reject",
+ "Number of transmitted BSSMAP DT1 CIPHER REJECT messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_CLASSMARK_UPDATE] = {
+ "bssmap:tx:dt1:classmark:update",
+ "Number of transmitted BSSMAP DT1 CLASSMARK UPDATE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_LCLS_CONNECT_CTRL_ACK] = {
+ "bssmap:tx:dt1:lcls_connect_ctrl:ack",
+ "Number of transmitted BSSMAP DT1 LCLS CONNECT CTRL ACK messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_REQUIRED] = {
+ "bssmap:tx:dt1:handover:required",
+ "Number of transmitted BSSMAP DT1 HANDOVER REQUIRED messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_PERFORMED] = {
+ "bssmap:tx:dt1:handover:performed",
+ "Number of transmitted BSSMAP DT1 HANDOVER PERFORMED messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_RQST_ACKNOWLEDGE] = {
+ "bssmap:tx:dt1:handover:rqst_acknowledge",
+ "Number of transmitted BSSMAP DT1 HANDOVER RQST ACKNOWLEDGE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_DETECT] = {
+ "bssmap:tx:dt1:handover:detect",
+ "Number of transmitted BSSMAP DT1 HANDOVER DETECT messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_COMPLETE] = {
+ "bssmap:tx:dt1:handover:complete",
+ "Number of transmitted BSSMAP DT1 HANDOVER COMPLETE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_HANDOVER_FAILURE] = {
+ "bssmap:tx:dt1:handover:failure",
+ "Number of transmitted BSSMAP DT1 HANDOVER FAILURE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_DTAP] = {
+ "bssmap:tx:dt1:dtap",
+ "Number of transmitted BSSMAP DT1 DTAP messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS] = {
+ "bssmap:tx:dt1:location:response_success",
+ "Number of transmitted BSSMAP Perform Location Response messages containing a location estimate"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE] = {
+ "bssmap:tx:dt1:location:response_failure",
+ "Number of transmitted BSSMAP Perform Location Response messages containing a failure cause"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_SETUP_ACK] = {
+ "bssmap:tx:dt1:vgcs_vbs_setup:ack",
+ "Number of transmitted BSSMAP DT1 VGCS/VBS SETUP ACK messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_SETUP_REFUSE] = {
+ "bssmap:tx:dt1:vgcs_vbs_setup:refuse",
+ "Number of transmitted BSSMAP DT1 VGCS/VBS SETUP REFUSE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_ASSIGN_RESULT] = {
+ "bssmap:tx:dt1:vgcs_vbs_assignment:res",
+ "Number of transmitted BSSMAP DT1 VGCS/VBS ASSIGNMENT RESULT messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_ASSIGN_FAIL] = {
+ "bssmap:tx:dt1:vgcs_vbs_assignment:fail",
+ "Number of transmitted BSSMAP DT1 VGCS/VBS ASSIGNMENT FAILURE messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_QUEUING_INDICATION] = {
+ "bssmap:tx:dt1:vgcs_vbs_queuing:ind",
+ "Number of transmitted BSSMAP DT1 VGCS/VBS QUEUING INDICATION messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_UPLINK_RQST] = {
+ "bssmap:tx:dt1:uplink_rqst",
+ "Number of transmitted BSSMAP DT1 UPLINK REQUEST messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_ASSIGNMENT_STATUS] = {
+ "bssmap:tx:dt1:vgcs_vbs_assignment:status",
+ "Number of transmitted BSSMAP DT1 VGCS/VBS ASSIGNMENT STATUS messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_VGCS_VBS_AREA_CELL_INFO] = {
+ "bssmap:tx:dt1:vgcs_vbs_area_cell:info",
+ "Number of transmitted BSSMAP DT1 VGCS/VBS AREA CELL INFO messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_UPLINK_RQST_CONFIRMATION] = {
+ "bssmap:tx:dt1:uplink_rqst:cnf",
+ "Number of transmitted BSSMAP DT1 UPLINK REQUEST CONFIRMATION messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_UPLINK_RELEASE_INDICATION] = {
+ "bssmap:tx:dt1:uplink_release:ind",
+ "Number of transmitted BSSMAP DT1 UPLINK RELEASE INDICATION messages"
+ },
+ [MSC_CTR_BSSMAP_TX_DT1_UPLINK_APP_DATA] = {
+ "bssmap:tx:dt1:uplink_app_data",
+ "Number of transmitted BSSMAP DT1 UPLINK APPLICATION DATA messages"
+ },
/* Indicators for MSC pool usage */
[MSC_CTR_MSCPOOL_SUBSCR_NEW] = {
@@ -139,8 +362,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 = {
@@ -153,24 +376,22 @@ 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;
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;
- rc = osmo_sock_init2_ofd(&msc->mgcp_ipa.ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+ 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,
- OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
+ NULL, 0, OSMO_SOCK_F_BIND);
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;
}
@@ -188,7 +409,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);
@@ -221,8 +441,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;
@@ -234,27 +452,19 @@ 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;
-
- osmo_fd_setup(&msc_data->mgcp_ipa.ofd, -1, BSC_FD_READ, &bsc_sccplite_mgcp_proxy_cb, msc_data, 0);
- msc_data->mgcp_ipa.local_addr = talloc_strdup(msc_data, "0.0.0.0");
+ 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 */
msc_data->mgcp_ipa.local_port = 0; /* dynamic */
msc_data->nri_ranges = osmo_nri_ranges_alloc(msc_data);
@@ -277,8 +487,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 afc6c8d8f..a4d0f2d74 100644
--- a/src/osmo-bsc/osmo_bsc_sigtran.c
+++ b/src/osmo-bsc/osmo_bsc_sigtran.c
@@ -34,53 +34,17 @@
#include <osmocom/bsc/a_reset.h>
#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/bsc/bssmap_reset.h>
#include <osmocom/mgcp_client/mgcp_common.h>
/* A pointer to a list with all involved MSCs
* (a copy of the pointer location submitted with osmo_bsc_sigtran_init() */
static struct llist_head *msc_list;
-#define RESET_INTERVAL 1 /* sek */
-#define SCCP_MSG_MAXSIZE 1024
-#define CS7_POINTCODE_DEFAULT_OFFSET 2
-#define DEFAULT_ASP_REMOTE_IP "127.0.0.1"
-
-/* The SCCP stack will not assign connection IDs to us automatically, we
- * will do this ourselves using a counter variable, that counts one up
- * for every new connection */
-static uint32_t conn_id_counter;
-
-/* Helper function to Check if the given connection id is already assigned */
-static struct gsm_subscriber_connection *get_bsc_conn_by_conn_id(int conn_id)
-{
- conn_id &= 0xFFFFFF;
- struct gsm_subscriber_connection *conn;
-
- llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) {
- if (conn->sccp.conn_id == conn_id)
- return conn;
- }
-
- return NULL;
-}
-
-/* Pick a free connection id */
-static int pick_free_conn_id(const struct bsc_msc_data *msc)
-{
- int conn_id = conn_id_counter;
- int i;
-
- for (i = 0; i < 0xFFFFFF; i++) {
- conn_id++;
- conn_id &= 0xFFFFFF;
- if (get_bsc_conn_by_conn_id(conn_id) == false) {
- conn_id_counter = conn_id;
- return conn_id;
- }
- }
-
- return -1;
-}
+#define DEFAULT_ASP_LOCAL_IP "localhost"
+#define DEFAULT_ASP_REMOTE_IP "localhost"
/* Patch regular BSSMAP RESET to add extra T to announce Osmux support (osmocom extension) */
static void _gsm0808_extend_announce_osmux(struct msgb *msg)
@@ -91,20 +55,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);
}
@@ -118,18 +82,18 @@ 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);
}
-/* Find an MSC by its sigtran point code */
+/* Find an MSC by its remote SCCP address */
static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_addr)
{
struct osmo_ss7_instance *ss7;
@@ -144,6 +108,21 @@ static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_add
return NULL;
}
+/* Find an MSC by its remote sigtran point code on a given cs7 instance. */
+static struct bsc_msc_data *get_msc_by_pc(struct osmo_ss7_instance *cs7, uint32_t pc)
+{
+ struct bsc_msc_data *msc;
+ llist_for_each_entry(msc, msc_list, entry) {
+ if (msc->a.cs7_instance != cs7->cfg.id)
+ continue;
+ if ((msc->a.msc_addr.presence & OSMO_SCCP_ADDR_T_PC) == 0)
+ continue;
+ if (msc->a.msc_addr.pc == pc)
+ return msc;
+ }
+ return NULL;
+}
+
/* Received data from MSC, use the connection id which MSC it is */
static int handle_data_from_msc(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
@@ -174,10 +153,12 @@ static int handle_unitdata_from_msc(const struct osmo_sccp_addr *msc_addr, struc
static int handle_n_connect_from_msc(struct osmo_sccp_user *scu, struct osmo_scu_prim *scu_prim)
{
struct bsc_msc_data *msc = get_msc_by_addr(&scu_prim->u.connect.calling_addr);
+ struct osmo_sccp_instance *sccp = osmo_sccp_get_sccp(scu);
+ struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(sccp);
struct gsm_subscriber_connection *conn;
int rc = 0;
- conn = get_bsc_conn_by_conn_id(scu_prim->u.connect.conn_id);
+ conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.connect.conn_id);
if (conn) {
LOGP(DMSC, LOGL_NOTICE,
"(calling_addr=%s conn_id=%u) N-CONNECT.ind with already used conn_id, ignoring\n",
@@ -205,7 +186,14 @@ static int handle_n_connect_from_msc(struct osmo_sccp_user *scu, struct osmo_scu
if (!conn)
return -ENOMEM;
conn->sccp.msc = msc;
- conn->sccp.conn_id = scu_prim->u.connect.conn_id;
+ conn->sccp.conn.conn_id = scu_prim->u.connect.conn_id;
+ if (bsc_sccp_inst_register_gscon(bsc_sccp, &conn->sccp.conn) < 0) {
+ LOGP(DMSC, LOGL_NOTICE, "(calling_addr=%s conn_id=%u) N-CONNECT.ind failed registering conn\n",
+ osmo_sccp_addr_dump(&scu_prim->u.connect.calling_addr), scu_prim->u.connect.conn_id);
+ osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REQUEST, NULL);
+ rc = -ENOENT;
+ goto refuse;
+ }
/* Take actions asked for by the enclosed PDU */
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_IND, scu_prim);
@@ -216,11 +204,101 @@ refuse:
return rc;
}
+static void handle_pcstate_ind(struct osmo_ss7_instance *cs7, const struct osmo_scu_pcstate_param *pcst)
+{
+ struct bsc_msc_data *msc;
+ bool connected;
+ bool disconnected;
+
+ LOGP(DMSC, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u sp_status=%s remote_sccp_status=%s\n",
+ pcst->affected_pc, osmo_sccp_sp_status_name(pcst->sp_status),
+ osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
+
+ /* If we don't care about that point-code, ignore PCSTATE. */
+ msc = get_msc_by_pc(cs7, pcst->affected_pc);
+ if (!msc)
+ return;
+
+ /* See if this marks the point code to have become available, or to have been lost.
+ *
+ * I want to detect two events:
+ * - connection event (both indicators say PC is reachable).
+ * - disconnection event (at least one indicator says the PC is not reachable).
+ *
+ * There are two separate incoming indicators with various possible values -- the incoming events can be:
+ *
+ * - neither connection nor disconnection indicated -- just indicating congestion
+ * connected == false, disconnected == false --> do nothing.
+ * - both incoming values indicate that we are connected
+ * --> trigger connected
+ * - both indicate we are disconnected
+ * --> trigger disconnected
+ * - one value indicates 'connected', the other indicates 'disconnected'
+ * --> trigger disconnected
+ *
+ * Congestion could imply that we're connected, but it does not indicate that a PC's reachability changed, so no need to
+ * trigger on that.
+ */
+ connected = false;
+ disconnected = false;
+
+ switch (pcst->sp_status) {
+ case OSMO_SCCP_SP_S_ACCESSIBLE:
+ connected = true;
+ break;
+ case OSMO_SCCP_SP_S_INACCESSIBLE:
+ disconnected = true;
+ break;
+ default:
+ case OSMO_SCCP_SP_S_CONGESTED:
+ /* Neither connecting nor disconnecting */
+ break;
+ }
+
+ switch (pcst->remote_sccp_status) {
+ case OSMO_SCCP_REM_SCCP_S_AVAILABLE:
+ if (!disconnected)
+ connected = true;
+ break;
+ case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN:
+ case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED:
+ case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE:
+ disconnected = true;
+ connected = false;
+ break;
+ default:
+ case OSMO_SCCP_REM_SCCP_S_CONGESTED:
+ /* Neither connecting nor disconnecting */
+ break;
+ }
+
+ if (disconnected && a_reset_conn_ready(msc)) {
+ LOGP(DMSC, LOGL_NOTICE,
+ "(msc%d) now unreachable: N-PCSTATE ind: pc=%u sp_status=%s remote_sccp_status=%s\n",
+ msc->nr, pcst->affected_pc,
+ osmo_sccp_sp_status_name(pcst->sp_status),
+ osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
+ /* A previously usable MSC has disconnected. Kick the BSSMAP back to DISC state. */
+ bssmap_reset_set_disconnected(msc->a.bssmap_reset);
+ } else if (connected && !a_reset_conn_ready(msc)) {
+ LOGP(DMSC, LOGL_NOTICE,
+ "(msc%d) now available: N-PCSTATE ind: pc=%u sp_status=%s remote_sccp_status=%s\n",
+ msc->nr, pcst->affected_pc,
+ osmo_sccp_sp_status_name(pcst->sp_status),
+ osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
+ /* A previously unusable MSC has become reachable. Trigger immediate BSSMAP RESET -- we would resend a
+ * RESET either way, but we might as well do it now to speed up connecting. */
+ bssmap_reset_resend_reset(msc->a.bssmap_reset);
+ }
+}
+
/* Callback function, called by the SCCP stack when data arrives */
static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
{
struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph;
struct osmo_sccp_user *scu = _scu;
+ struct osmo_sccp_instance *sccp = osmo_sccp_get_sccp(scu);
+ struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(sccp);
struct gsm_subscriber_connection *conn;
int rc = 0;
@@ -241,7 +319,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
/* Handle outbound connection confirmation */
DEBUGP(DMSC, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id,
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
- conn = get_bsc_conn_by_conn_id(scu_prim->u.connect.conn_id);
+ conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.connect.conn_id);
if (conn) {
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_CFM, scu_prim);
conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED;
@@ -260,7 +338,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
/* Incoming data is a sign of a vital connection */
- conn = get_bsc_conn_by_conn_id(scu_prim->u.data.conn_id);
+ conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.data.conn_id);
if (conn) {
a_reset_conn_success(conn->sccp.msc);
handle_data_from_msc(conn, oph->msg);
@@ -272,7 +350,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)),
scu_prim->u.disconnect.cause);
/* indication of disconnect */
- conn = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id);
+ conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.disconnect.conn_id);
if (conn) {
conn->sccp.state = SUBSCR_SCCP_ST_NONE;
if (msgb_l2len(oph->msg) > 0)
@@ -281,6 +359,10 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
}
break;
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
+ handle_pcstate_ind(osmo_sccp_get_ss7(sccp), &scu_prim->u.pcstate);
+ break;
+
default:
LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN operation %s on primitive %u\n",
get_value_string(osmo_prim_op_names, oph->operation), oph->primitive);
@@ -303,11 +385,11 @@ enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, s
ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
OSMO_ASSERT(ss7);
- LOGP(DMSC, LOGL_INFO, "Initializing resources for new SCCP connection to MSC: %s...\n",
- osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
+ LOGP(DMSC, LOGL_INFO, "Initializing resources for new SCCP connection to MSC %d: %s...\n",
+ msc->nr, osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
if (a_reset_conn_ready(msc) == false) {
- LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n");
+ LOGP(DMSC, LOGL_ERROR, "MSC %d is not connected. Dropping.\n", msc->nr);
return BSC_CON_REJECT_NO_LINK;
}
@@ -322,17 +404,19 @@ 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;
- int conn_id;
+ struct bsc_sccp_inst *bsc_sccp;
+ uint32_t conn_id;
int rc;
OSMO_ASSERT(conn);
OSMO_ASSERT(msg);
OSMO_ASSERT(conn->sccp.msc);
- OSMO_ASSERT(conn->sccp.conn_id == -1);
+ OSMO_ASSERT(conn->sccp.conn.conn_id == SCCP_CONN_ID_UNSET);
msc = conn->sccp.msc;
@@ -341,16 +425,21 @@ int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct ms
return -EINVAL;
}
- conn->sccp.conn_id = conn_id = pick_free_conn_id(msc);
- if (conn->sccp.conn_id < 0) {
+ bsc_sccp = osmo_sccp_get_priv(msc->a.sccp);
+ conn->sccp.conn.conn_id = conn_id = bsc_sccp_inst_next_conn_id(bsc_sccp);
+ if (conn->sccp.conn.conn_id == SCCP_CONN_ID_UNSET) {
LOGP(DMSC, LOGL_ERROR, "Unable to allocate SCCP Connection ID\n");
return -1;
}
- LOGP(DMSC, LOGL_DEBUG, "Allocated new connection id: %d\n", conn->sccp.conn_id);
+ if (bsc_sccp_inst_register_gscon(bsc_sccp, &conn->sccp.conn) < 0) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to register SCCP connection (id=%u)\n", conn->sccp.conn.conn_id);
+ return -1;
+ }
+ LOGP(DMSC, LOGL_DEBUG, "Allocated new connection id: %u\n", conn->sccp.conn.conn_id);
ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
OSMO_ASSERT(ss7);
- LOGP(DMSC, LOGL_INFO, "Opening new SCCP connection (id=%i) to MSC: %s\n", conn_id,
- osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
+ LOGP(DMSC, LOGL_INFO, "Opening new SCCP connection (id=%u) to MSC %d: %s\n", conn_id,
+ msc->nr, osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
rc = osmo_sccp_tx_conn_req_msg(msc->a.sccp_user, conn_id, &msc->a.bsc_addr,
&msc->a.msc_addr, msg);
@@ -384,32 +473,32 @@ 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;
}
- conn_id = conn->sccp.conn_id;
+ conn_id = conn->sccp.conn.conn_id;
ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
OSMO_ASSERT(ss7);
@@ -418,19 +507,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) {
@@ -445,18 +537,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"
@@ -532,6 +612,7 @@ int osmo_bsc_sigtran_init(struct llist_head *mscs)
int prev_msc_nr;
struct osmo_sccp_instance *sccp;
+ struct bsc_sccp_inst *bsc_sccp;
llist_for_each_entry(msc, msc_list, entry) {
/* An MSC with invalid cs7 instance id defaults to cs7 instance 0 */
@@ -568,11 +649,17 @@ int osmo_bsc_sigtran_init(struct llist_head *mscs)
/* SS7 Protocol stack */
default_pc = osmo_ss7_pointcode_parse(NULL, BSC_DEFAULT_PC);
- sccp = osmo_sccp_simple_client_on_ss7_id(tall_bsc_ctx, inst->cfg.id, inst_name, default_pc, used_proto, 0, NULL,
+ sccp = osmo_sccp_simple_client_on_ss7_id(tall_bsc_ctx, inst->cfg.id, inst_name,
+ default_pc, used_proto,
+ 0, DEFAULT_ASP_LOCAL_IP,
0, DEFAULT_ASP_REMOTE_IP);
if (!sccp)
return -EINVAL;
+ bsc_sccp = bsc_sccp_inst_alloc(tall_bsc_ctx);
+ bsc_sccp->sccp = sccp;
+ osmo_sccp_set_priv(sccp, bsc_sccp);
+
/* Now that the SCCP client is set up, configure all MSCs on this cs7 instance to use it */
llist_for_each_entry(msc, msc_list, entry) {
char msc_name[32];
@@ -628,7 +715,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);
}
}
@@ -642,7 +729,7 @@ static int asp_rx_unknown(struct osmo_ss7_asp *asp, int ppid_mux, struct msgb *m
struct ipaccess_head *iph;
struct ipaccess_head_ext *iph_ext;
- if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA) {
+ if (osmo_ss7_asp_get_proto(asp) != OSMO_SS7_ASP_PROT_IPA) {
msgb_free(msg);
return 0;
}
diff --git a/src/osmo-bsc/paging.c b/src/osmo-bsc/paging.c
index 7859c69d0..b7842dd52 100644
--- a/src/osmo-bsc/paging.c
+++ b/src/osmo-bsc/paging.c
@@ -53,27 +53,53 @@
#include <osmocom/bsc/gsm_08_08.h>
#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
-/*
- * TODO MSCSPLIT: the paging in libbsc is closely tied to MSC land in that the
- * MSC realm callback functions used to be invoked from the BSC/BTS level. So
- * this entire file needs to be rewired for use with an A interface.
- */
+/* 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 Load Ind is received before this time period, the BTS is considered
+ * to have stopped sending CCCH Load Indication, probably 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);
- 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)
@@ -84,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: imsi: %s tmsi: "
- "0x%08x for ch. type %d (attempt %d)\n", request->bsub->imsi,
- request->bsub->tmsi, 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){
@@ -106,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
@@ -176,63 +213,182 @@ 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
*
* We attempt to iterate once over the list of items but
- * only upto available_slots.
+ * only up to available_slots.
*/
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 */
+ 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;
}
- /*
- * 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);
- return;
- }
-
- request = llist_entry(paging_bts->pending_requests.next,
- struct gsm_paging_request, entry);
-
- /* 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;
- }
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ paging_bts->last_sched_ts = now;
- /* Skip paging if the bts is down. */
- if (!request->bts->oml_link)
- 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);
- /* 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)
@@ -243,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 */
@@ -282,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,34 +500,88 @@ static unsigned int calculate_timer_3113(struct gsm_bts *bts)
* \param[in] type type of radio channel we're requirign
* \param[in] msc MSC which has issue this paging
* \returns 0 on success, negative on error */
-static int _paging_request(struct gsm_bts *bts, struct bsc_subscr *bsub, int type,
- struct bsc_msc_data *msc)
+static int _paging_request(const struct bsc_paging_params *params, struct gsm_bts *bts)
{
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, bsub)) {
- LOG_BTS(bts, DPAG, LOGL_INFO, "Paging request already pending for %s\n",
- bsc_subscr_name(bsub));
- rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_PAGING_ALREADY]);
+ /* 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(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_PAGING_ALREADY));
return -EEXIST;
}
- LOG_BTS(bts, DPAG, LOGL_DEBUG, "Start paging of subscriber %s\n", bsc_subscr_name(bsub));
+ /* 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->bsub = bsc_subscr_get(bsub);
+ req->reason = params->reason;
+ req->bsub = params->bsub;
req->bts = bts;
- req->chan_type = type;
- req->msc = msc;
+ 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 retransmission,
+ * 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;
}
@@ -368,8 +592,7 @@ static int _paging_request(struct gsm_bts *bts, struct bsc_subscr *bsub, int typ
* \param[in] type type of radio channel we're requirign
* \param[in] msc MSC which has issue this paging
* returns 1 on success; 0 in case of error (e.g. TRX down) */
-int paging_request_bts(struct gsm_bts *bts, struct bsc_subscr *bsub, int type,
- struct bsc_msc_data *msc)
+int paging_request_bts(const struct bsc_paging_params *params, struct gsm_bts *bts)
{
int rc;
@@ -377,116 +600,107 @@ int paging_request_bts(struct gsm_bts *bts, struct bsc_subscr *bsub, int type,
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(bts, bsub, type, msc);
+ rc = _paging_request(params, bts);
if (rc < 0)
return 0;
return 1;
}
-/*! Stop paging a given subscriber on a given BTS.
- * If \a conn is non-NULL, we also call the paging call-back function
- * to notify the paging originator that paging has completed.
- * \param[in] bts BTS on which we shall stop paging
- * \param[in] bsub subscriber which we shall stop paging
- * \param[in] conn connection to the subscriber (if any)
- * \param[in] msg message received from subscrbier (if any)
- * \returns 0 if an active paging request was stopped, an error code otherwise. */
-/* we consciously ignore the type of the request here */
-static int _paging_request_stop(struct gsm_bts *bts, struct bsc_subscr *bsub,
- struct gsm_subscriber_connection *conn,
- struct msgb *msg)
+/*! 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
+ */
+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_paging_state *bts_entry = &bts->paging;
- struct gsm_paging_request *req, *req2;
-
- paging_init_if_needed(bts);
+ struct bsc_msc_data *paged_from_msc = NULL;
+ enum bsc_paging_reason reasons = BSC_PAGING_NONE;
+ OSMO_ASSERT(bts);
+ 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_safe(req, req2, &bts_entry->pending_requests,
- entry) {
- if (req->bsub == bsub) {
- /* now give up the data structure */
- paging_remove_request(&bts->paging, req);
- LOG_BTS(bts, DPAG, LOGL_DEBUG, "Stop paging %s\n", bsc_subscr_name(bsub));
- return 0;
+ 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--;
}
- return -ENOENT;
+ *msc_p = paged_from_msc;
+ *reasons_p = reasons;
}
-/*! Stop paging on all other bts'
- * \param[in] bts_list list of BTSs to iterate
- * \param[in] _bts BTS which has received a paging response
- * \param[in] bsub subscriber
- * \param[in] msgb L3 message that we have received from \a bsub on \a _bts */
-void paging_request_stop(struct llist_head *bts_list,
- struct gsm_bts *_bts, struct bsc_subscr *bsub,
- struct gsm_subscriber_connection *conn,
- struct msgb *msg)
+/* Remove all paging requests, for specific reasons only. */
+void paging_request_cancel(struct bsc_subscr *bsub, enum bsc_paging_reason reasons)
{
- struct gsm_bts *bts;
+ struct gsm_paging_request *req, *req2;
+ OSMO_ASSERT(bsub);
- log_set_context(LOG_CTX_BSC_SUBSCR, bsub);
- conn->bsub = bsc_subscr_get(bsub);
- gscon_update_id(conn);
+ /* Avoid accessing bsub after reaching 0 active_paging_request_len,
+ * since it could be freed during put(): */
+ unsigned remaining = bsub->active_paging_requests_len;
- /* Stop this first and dispatch the request */
- if (_bts) {
- if (_paging_request_stop(_bts, bsub, conn, msg) == 0) {
- rate_ctr_inc(&_bts->bts_ctrs->ctr[BTS_CTR_PAGING_RESPONDED]);
- rate_ctr_inc(&_bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_RESPONDED]);
- }
- }
-
- /* Make sure to cancel this everywhere else */
- llist_for_each_entry(bts, bts_list, list) {
- /* Sort of an optimization. */
- if (bts == _bts)
+ 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;
- _paging_request_stop(bts, bsub, NULL, NULL);
+ }
+ /* No reason to keep the paging, remove it: */
+ paging_remove_request(req);
+ remaining--;
+ if (remaining == 0)
+ break;
}
- log_set_context(LOG_CTX_BSC_SUBSCR, NULL);
}
-
/*! Update the BTS paging buffer slots on given BTS */
-void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots)
+static 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 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;
-}
-
-/*! Find any paging data for the given subscriber at the given BTS. */
-struct bsc_msc_data *paging_get_msc(struct gsm_bts *bts, struct bsc_subscr *bsub)
+unsigned int paging_pending_requests_nr(const struct gsm_bts *bts)
{
- struct gsm_paging_request *req;
-
- llist_for_each_entry(req, &bts->paging.pending_requests, entry)
- if (req->bsub == bsub)
- return req->msc;
-
- return NULL;
+ 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). */
@@ -494,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 */
@@ -517,3 +734,131 @@ 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;
+}
+
+/* Callback function to be called every time we receive a signal from CCCH */
+static int ccch_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct ccch_signal_data *sd;
+
+ if (signal != S_CCCH_PAGING_LOAD)
+ return 0;
+
+ sd = signal_data;
+
+ paging_update_buffer_space(sd->bts, sd->pg_buf_space);
+ 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);
+ osmo_signal_register_handler(SS_CCCH, ccch_sig_cb, NULL);
+}
diff --git a/src/osmo-bsc/pcu_sock.c b/src/osmo-bsc/pcu_sock.c
index bb1874637..7b1aeae68 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>
@@ -30,12 +26,14 @@
#include <sys/socket.h>
#include <sys/un.h>
+#include <osmocom/core/byteswap.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#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>
@@ -44,30 +42,29 @@
#include <osmocom/bsc/debug.h>
#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>
+#include <osmocom/bsc/timeslot_fsm.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 int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
static const char *sapi_string[] = {
[PCU_IF_SAPI_RACH] = "RACH",
- [PCU_IF_SAPI_AGCH] = "AGCH",
- [PCU_IF_SAPI_PCH] = "PCH",
[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_2] = "PCH_2",
+ [PCU_IF_SAPI_AGCH_2] = "AGCH_2",
};
-/* Check if BTS has a PCU connection */
-static bool pcu_connected(struct gsm_bts *bts)
+bool pcu_connected(const struct gsm_network *net)
{
- struct pcu_sock_state *state = bts->pcu_state;
+ struct pcu_sock_state *state = net->pcu_state;
if (!state)
return false;
- if (state->conn_bfd.fd <= 0)
+ if (state->upqueue.bfd.fd <= 0)
return false;
return true;
}
@@ -94,6 +91,87 @@ 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)
+{
+ ts_info->maio = ts->hopping.maio;
+ ts_info->hsn = ts->hopping.hsn;
+ ts_info->hopping = 0x1;
+
+ memcpy(&ts_info->ma, ts->hopping.ma_data, ts->hopping.ma_len);
+ 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)
{
@@ -101,15 +179,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, j;
+ 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);
+ LOG_BTS(bts, DPCU, LOGL_INFO, "Sending info for BTS\n");
rlcc = &bts->gprs.cell.rlc_cfg;
@@ -121,9 +198,7 @@ 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_DIRECT_PHY;
/* RAI */
info_ind->mcc = bts->network->plmn.mcc;
@@ -133,11 +208,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;
@@ -179,52 +255,158 @@ 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;
- info_ind->remote_port[i] = nsvc->remote_port;
- info_ind->remote_ip[i] = nsvc->remote_ip;
+ switch (nsvc->remote.u.sas.ss_family) {
+ 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] = 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] = ntohs(nsvc->remote.u.sin6.sin6_port);
+ break;
+ default:
+ info_ind->address_type[i] = PCU_IF_ADDR_TYPE_UNSPEC;
+ break;
+ }
}
for (i = 0; i < ARRAY_SIZE(info_ind->trx); i++) {
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 (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
- ts = &trx->ts[j];
- if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
- && ts->pchan_is == GSM_PCHAN_PDCH) {
- info_ind->trx[i].pdch_mask |= (1 << j);
- info_ind->trx[i].tsc[j] =
- (ts->tsc >= 0) ? ts->tsc : bts->bsic & 7;
- LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: "
- "available (tsc=%d arfcn=%d)\n",
- trx->nr, ts->nr,
- info_ind->trx[i].tsc[j],
- info_ind->trx[i].arfcn);
- }
+ 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);
+ }
+
+ switch (bts->type) {
+ case GSM_BTS_TYPE_RBS2000:
+ info_ind->bts_model = PCU_IF_BTS_MODEL_RBS;
+ break;
+ default:
+ info_ind->bts_model = PCU_IF_BTS_MODEL_UNSPEC;
}
- return pcu_sock_send(bts, msg);
+ return pcu_sock_send(bts->network, msg);
}
-void pcu_info_update(struct gsm_bts *bts)
+static int pcu_tx_e1_ccu_ind(struct gsm_bts *bts)
{
- if (pcu_connected(bts))
- pcu_tx_info_ind(bts);
+ struct gsm_bts_trx *trx;
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct gsm_pcu_if_e1_ccu_ind *e1_ccu_ind;
+ int i;
+
+ 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->network, msg);
+ if (rc < 0)
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* Allow test to overwrite it */
+__attribute__((weak)) void pcu_info_update(struct gsm_bts *bts)
+{
+ if (pcu_connected(bts->network)) {
+ if (bsc_co_located_pcu(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. */
+ if (is_e1_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->network, msg);
}
/* Forward rach indication to PCU */
@@ -236,13 +418,12 @@ int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn,
struct gsm_pcu_if_rach_ind *rach_ind;
/* Bail if no PCU is connected */
- if (!pcu_connected(bts)) {
- LOGP(DRSL, LOGL_ERROR, "BTS %d CHAN RQD(GPRS) but PCU not "
- "connected!\n", bts->nr);
+ if (!pcu_connected(bts->network)) {
+ LOG_BTS(bts, DRSL, LOGL_ERROR, "CHAN RQD(GPRS) but PCU not connected!\n");
return -ENODEV;
}
- LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, "
+ LOG_BTS(bts, DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, "
"fn=%d\n", qta, ra, fn);
msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr);
@@ -258,37 +439,37 @@ int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn,
rach_ind->is_11bit = is_11bit;
rach_ind->burst_type = burst_type;
- return pcu_sock_send(bts, msg);
+ return pcu_sock_send(bts->network, msg);
}
-/* Confirm the sending of an immediate assignment to the pcu */
-int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli)
+int pcu_tx_data_cnf(struct gsm_bts *bts, uint32_t msg_id, uint8_t sapi)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
- struct gsm_pcu_if_data_cnf_dt *data_cnf_dt;
+ struct gsm_pcu_if_data_cnf *data_cnf;
- LOGP(DPCU, LOGL_INFO, "Sending PCH confirm with direct TLLI\n");
+ LOGP(DPCU, LOGL_DEBUG, "Sending DATA.cnf: sapi=%s msg_id=%08x\n",
+ sapi_string[sapi], msg_id);
- msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF_DT, bts->nr);
+ msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF_2, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
- data_cnf_dt = &pcu_prim->u.data_cnf_dt;
+ data_cnf = &pcu_prim->u.data_cnf2;
- data_cnf_dt->sapi = PCU_IF_SAPI_PCH;
- data_cnf_dt->tlli = tlli;
+ data_cnf->sapi = sapi;
+ data_cnf->msg_id = msg_id;
- return pcu_sock_send(bts, msg);
+ return pcu_sock_send(bts->network, msg);
}
/* we need to decode the raw RR paging message (see PCU code
* Encoding::write_paging_request) and extract the mobile identity
* (P-TMSI) from it */
-static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group,
- const uint8_t *raw_rr_msg)
+static int pcu_rx_rr_paging_pch(struct gsm_bts *bts, uint8_t paging_group,
+ const struct gsm_pcu_if_pch *pch)
{
- struct gsm48_paging1 *p1 = (struct gsm48_paging1 *) raw_rr_msg;
+ struct gsm48_paging1 *p1 = (struct gsm48_paging1 *) pch->data;
uint8_t chan_needed;
struct osmo_mobile_identity mi;
int rc;
@@ -298,14 +479,14 @@ static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group,
chan_needed = (p1->cneed2 << 2) | p1->cneed1;
rc = osmo_mobile_identity_decode(&mi, p1->data+1, p1->data[0], false);
if (rc) {
- LOGP(DPCU, LOGL_ERROR, "PCU Sends paging "
- "request type %02x (chan_needed=%02x): Unable to decode Mobile Identity\n",
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "PCU Sends paging "
+ "request type %02x (chan_needed=%02x): Unable to decode Mobile Identity\n",
p1->msg_type, chan_needed);
rc = -EINVAL;
break;
}
- LOGP(DPCU, LOGL_ERROR, "PCU Sends paging "
- "request type %02x (chan_needed=%02x, mi=%s)\n",
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "PCU Sends paging "
+ "request type %02x (chan_needed=%02x, mi=%s)\n",
p1->msg_type, chan_needed, osmo_mobile_identity_to_str_c(OTC_SELECT, &mi));
/* NOTE: We will have to add 2 to mi_len and subtract 2 from
* the mi pointer because rsl_paging_cmd() will perform the
@@ -317,12 +498,12 @@ static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group,
break;
case GSM48_MT_RR_PAG_REQ_2:
case GSM48_MT_RR_PAG_REQ_3:
- LOGP(DPCU, LOGL_ERROR, "PCU Sends unsupported paging "
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "PCU Sends unsupported paging "
"request type %02x\n", p1->msg_type);
rc = -EINVAL;
break;
default:
- LOGP(DPCU, LOGL_ERROR, "PCU Sends unknown paging "
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "PCU Sends unknown paging "
"request type %02x\n", p1->msg_type);
rc = -EINVAL;
break;
@@ -331,77 +512,76 @@ static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group,
return rc;
}
+static int pcu_rx_rr_imm_ass_pch(struct gsm_bts *bts, uint8_t paging_group,
+ const struct gsm_pcu_if_pch *pch, bool confirm)
+{
+ LOG_BTS(bts, DPCU, LOGL_DEBUG, "PCU Sends immediate assignment via PCH (msg_id=0x%08x, IMSI=%s, Paging group=0x%02x)\n",
+ pch->msg_id, pch->imsi, paging_group);
+
+ /* 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 (is_ericsson_bts(bts))
+ return rsl_ericsson_imm_assign_cmd(bts, pch->msg_id, sizeof(pch->data), pch->data, paging_group,
+ confirm);
+
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "BTS model does not support sending immediate assignment via PCH!\n");
+ return -ENOTSUP;
+}
+
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;
+ const struct gsm_pcu_if_pch *pch;
+ const struct gsm_pcu_if_agch *agch;
+ const struct gsm48_imm_ass *gsm48_imm_ass;
- LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d "
+ LOG_BTS(bts, DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d "
"block=%d data=%s\n", sapi_string[data_req->sapi],
data_req->arfcn, data_req->block_nr,
osmo_hexdump(data_req->data, data_req->len));
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));
- pcu_rx_rr_paging(bts, pag_grp, data_req->data+3);
- break;
- case PCU_IF_SAPI_AGCH:
- msg = msgb_alloc(data_req->len, "pcu_agch");
- if (!msg) {
- rc = -ENOMEM;
+ case PCU_IF_SAPI_AGCH_2:
+ if (data_req->len < sizeof(struct gsm_pcu_if_agch)) {
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "Received PCU data request with invalid/small length %d\n",
+ data_req->len);
break;
}
- msg->l3h = msgb_put(msg, data_req->len);
- memcpy(msg->l3h, data_req->data, data_req->len);
- if (rsl_imm_assign_cmd(bts, msg->len, msg->data)) {
- msgb_free(msg);
- rc = -EIO;
- }
- break;
- case PCU_IF_SAPI_AGCH_DT:
- /* DT = direct tlli. A tlli is prefixed */
+ agch = (struct gsm_pcu_if_agch *)data_req->data;
+ if (rsl_imm_assign_cmd(bts, GSM_MACBLOCK_LEN, agch->data))
+ return -EIO;
- if (data_req->len < 5) {
- LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
- "invalid/small length %d\n", data_req->len);
+ /* Send the confirmation immediately. This is as accurate as we can get since from this point on the
+ * BTS hardware is responsible to schedule the sending of the IMMEDIATE ASSIGNMENT */
+ if (agch->confirm)
+ return pcu_tx_data_cnf(bts, agch->msg_id, PCU_IF_SAPI_AGCH_2);
+ break;
+ case PCU_IF_SAPI_PCH_2:
+ if (data_req->len < sizeof(struct gsm_pcu_if_pch)) {
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "Received PCU data request with invalid/small length %d\n",
+ data_req->len);
break;
}
- memcpy(&tlli, data_req->data, 4);
- msg = msgb_alloc(data_req->len - 4, "pcu_agch");
- 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);
+ pch = (struct gsm_pcu_if_pch *)data_req->data;
+ pag_grp = gsm0502_calc_paging_group(&bts->si_common.chan_desc, str_to_imsi(pch->imsi));
+ gsm48_imm_ass = (struct gsm48_imm_ass *)pch->data;
- 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);
+ if (gsm48_imm_ass->msg_type == GSM48_MT_RR_IMM_ASS)
+ return pcu_rx_rr_imm_ass_pch(bts, pag_grp, pch, pch->confirm);
- if (rc) {
- msgb_free(msg);
- rc = -EIO;
- }
+ if (pcu_rx_rr_paging_pch(bts, pag_grp, pch))
+ return -EIO;
+ if (pch->confirm)
+ return pcu_tx_data_cnf(bts, pch->msg_id, PCU_IF_SAPI_PCH_2);
break;
default:
- LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
+ LOG_BTS(bts, DPCU, LOGL_ERROR, "Received PCU data request with "
"unsupported sapi %d\n", data_req->sapi);
rc = -EINVAL;
}
@@ -409,20 +589,121 @@ 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;
+ LOG_BTS(bts, 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;
+
+ LOG_BTS(bts, 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)
+ LOG_BTS(bts, 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 {
+ LOG_BTS(bts, 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:
+ LOG_BTS(bts, 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:
+ LOG_BTS(bts, 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;
- /* FIXME: allow multiple BTS */
- bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
+ bts = gsm_bts_num(net, pcu_prim->bts_nr);
+ if (!bts)
+ return -EINVAL;
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);
@@ -432,15 +713,18 @@ static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
return rc;
}
+static void pcu_sock_close(struct pcu_sock_state *state);
+
/*
* PCU socket interface
*/
-static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg)
+static int pcu_sock_send(struct gsm_network *net, struct msgb *msg)
{
- struct pcu_sock_state *state = bts->pcu_state;
+ struct pcu_sock_state *state = net->pcu_state;
struct osmo_fd *conn_bfd;
struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data;
+ int rc;
if (!state) {
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
@@ -449,7 +733,7 @@ static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg)
msgb_free(msg);
return -EINVAL;
}
- conn_bfd = &state->conn_bfd;
+ conn_bfd = &state->upqueue.bfd;
if (conn_bfd->fd <= 0) {
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, "
@@ -457,31 +741,25 @@ static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg)
msgb_free(msg);
return -EIO;
}
- msgb_enqueue(&state->upqueue, msg);
- conn_bfd->when |= BSC_FD_WRITE;
+ rc = osmo_wqueue_enqueue(&state->upqueue, msg);
+ if (rc < 0) {
+ if (rc == -ENOSPC)
+ LOGP(DPCU, LOGL_NOTICE,
+ "PCU not reacting (more than %u messages waiting). Closing connection\n",
+ state->upqueue.max_length);
+ pcu_sock_close(state);
+ msgb_free(msg);
+ return rc;
+ }
+
return 0;
}
-static void pcu_sock_close(struct pcu_sock_state *state)
+static void pdch_deact_bts(struct gsm_bts *bts)
{
- struct osmo_fd *bfd = &state->conn_bfd;
- struct gsm_bts *bts;
struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
- int i, j;
-
- /* FIXME: allow multiple BTS */
- bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list);
-
- LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n");
-
- close(bfd->fd);
- bfd->fd = -1;
- osmo_fd_unregister(bfd);
-
- /* re-enable the generation of ACCEPT for new connections */
- state->listen_bfd.when |= BSC_FD_READ;
+ int j;
#if 0
/* remove si13, ... */
@@ -490,24 +768,41 @@ static void pcu_sock_close(struct pcu_sock_state *state)
#endif
/* release PDCH */
- for (i = 0; i < 8; i++) {
- trx = gsm_bts_trx_num(bts, i);
- if (!trx)
- break;
- for (j = 0; j < 8; j++) {
- ts = &trx->ts[j];
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[j];
+ /* BSC co-located PCU applies only to Ericsson RBS, which supports only GSM_PCHAN_OSMO_DYN.
+ * So we need to deact only on this pchan kind. */
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
- && ts->pchan_is == GSM_PCHAN_PDCH) {
- printf("l1sap_chan_rel(trx,gsm_lchan2chan_nr(ts->lchan));\n");
+ && ts->pchan_on_init == GSM_PCHAN_OSMO_DYN) {
+ ts_pdch_deact(ts);
}
}
}
+}
- /* flush the queue */
- while (!llist_empty(&state->upqueue)) {
- struct msgb *msg = msgb_dequeue(&state->upqueue);
- msgb_free(msg);
+static void pcu_sock_close(struct pcu_sock_state *state)
+{
+ struct osmo_fd *bfd = &state->upqueue.bfd;
+ struct gsm_bts *bts;
+
+ LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n");
+
+ osmo_fd_unregister(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+
+ /* re-enable the generation of ACCEPT for new connections */
+ osmo_fd_read_enable(&state->listen_bfd);
+
+ /* Disable all PDCHs on all BTSs that are served by the PCU */
+ llist_for_each_entry(bts, &state->net->bts_list, list) {
+ if (bsc_co_located_pcu(bts))
+ pdch_deact_bts(bts);
}
+
+ /* flush the queue */
+ osmo_wqueue_clear(&state->upqueue);
}
static int pcu_sock_read(struct osmo_fd *bfd)
@@ -517,7 +812,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;
@@ -528,12 +823,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. */
@@ -547,45 +851,22 @@ close:
return -1;
}
-static int pcu_sock_write(struct osmo_fd *bfd)
+static int pcu_sock_write(struct osmo_fd *bfd, struct msgb *msg)
{
struct pcu_sock_state *state = bfd->data;
int rc;
- while (!llist_empty(&state->upqueue)) {
- struct msgb *msg, *msg2;
- struct gsm_pcu_if *pcu_prim;
-
- /* peek at the beginning of the queue */
- msg = llist_entry(state->upqueue.next, struct msgb, list);
- pcu_prim = (struct gsm_pcu_if *)msg->data;
-
- bfd->when &= ~BSC_FD_WRITE;
-
- /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
- if (!msgb_length(msg)) {
- LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO "
- "bytes!\n", pcu_prim->msg_type);
- goto dontsend;
- }
-
- /* try to send it over the socket */
- rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
- if (rc == 0)
- goto close;
- if (rc < 0) {
- if (errno == EAGAIN) {
- bfd->when |= BSC_FD_WRITE;
- break;
- }
- goto close;
- }
+ /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
+ OSMO_ASSERT(msgb_length(msg) > 0);
+ /* try to send it over the socket */
+ rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
+ if (OSMO_UNLIKELY(rc == 0))
+ goto close;
+ if (OSMO_UNLIKELY(rc < 0)) {
+ if (errno == EAGAIN)
+ return -EAGAIN;
+ return -1;
-dontsend:
- /* _after_ we send it, we can deueue */
- msg2 = msgb_dequeue(&state->upqueue);
- assert(msg == msg2);
- msgb_free(msg);
}
return 0;
@@ -595,54 +876,53 @@ close:
return -1;
}
-static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
+static void pdch_act_bts(struct gsm_bts *bts)
{
- int rc = 0;
-
- if (flags & BSC_FD_READ)
- rc = pcu_sock_read(bfd);
- if (rc < 0)
- return rc;
-
- if (flags & BSC_FD_WRITE)
- rc = pcu_sock_write(bfd);
+ struct gsm_bts_trx *trx;
+ int j;
- return rc;
+ /* activate PDCH */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[j];
+ /* (See comment in pdch_deact_bts above) */
+ if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
+ && ts->pchan_on_init == GSM_PCHAN_OSMO_DYN) {
+ ts_pdch_act(ts);
+ }
+ }
+ }
}
/* accept connection coming from PCU */
static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
- struct osmo_fd *conn_bfd = &state->conn_bfd;
+ struct osmo_fd *conn_bfd = &state->upqueue.bfd;
struct sockaddr_un un_addr;
+ struct gsm_bts *bts;
socklen_t len;
- int rc;
+ int fd;
len = sizeof(un_addr);
- rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
- if (rc < 0) {
+ fd = accept(bfd->fd, (struct sockaddr *)&un_addr, &len);
+ if (fd < 0) {
LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
- LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have "
- "another active connection ?!?\n");
+ 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 &= ~BSC_FD_READ;
- close(rc);
+ osmo_fd_read_disable(&state->listen_bfd);
+ close(fd);
return 0;
}
- conn_bfd->fd = rc;
- conn_bfd->when = BSC_FD_READ;
- conn_bfd->cb = pcu_sock_cb;
- conn_bfd->data = state;
+ osmo_fd_setup(conn_bfd, fd, OSMO_FD_READ, osmo_wqueue_bfd_cb, state, 0);
if (osmo_fd_register(conn_bfd) != 0) {
- LOGP(DPCU, LOGL_ERROR, "Failed to register new connection "
- "fd\n");
+ LOGP(DPCU, LOGL_ERROR, "Failed to register new connection fd\n");
close(conn_bfd->fd);
conn_bfd->fd = -1;
return -1;
@@ -650,11 +930,17 @@ static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n");
+ /* Activate all PDCHs on all BTSs that are served by the PCU */
+ llist_for_each_entry(bts, &state->net->bts_list, list) {
+ if (bsc_co_located_pcu(bts))
+ pdch_act_bts(bts);
+ }
+
return 0;
}
/* Open connection to PCU */
-int pcu_sock_init(const char *path, struct gsm_bts *bts)
+int pcu_sock_init(struct gsm_network *net)
{
struct pcu_sock_state *state;
struct osmo_fd *bfd;
@@ -664,24 +950,23 @@ int pcu_sock_init(const char *path, struct gsm_bts *bts)
if (!state)
return -ENOMEM;
- INIT_LLIST_HEAD(&state->upqueue);
- state->net = bts->network;
- state->conn_bfd.fd = -1;
+ osmo_wqueue_init(&state->upqueue, net->pcu_sock_wqueue_len_max);
+ state->upqueue.read_cb = pcu_sock_read;
+ state->upqueue.write_cb = pcu_sock_write;
+ state->upqueue.bfd.fd = -1;
+ state->net = net;
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, net->pcu_sock_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 = BSC_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) {
@@ -692,28 +977,28 @@ int pcu_sock_init(const char *path, struct gsm_bts *bts)
return rc;
}
- LOGP(DPCU, LOGL_INFO, "Started listening on PCU socket: %s\n", path);
+ LOGP(DPCU, LOGL_INFO, "Started listening on PCU socket (PCU IF v%u): %s\n",
+ PCU_IF_VERSION, net->pcu_sock_path);
- bts->pcu_state = state;
+ net->pcu_state = state;
return 0;
}
/* Close connection to PCU */
-void pcu_sock_exit(struct gsm_bts *bts)
+void pcu_sock_exit(struct gsm_network *net)
{
- struct pcu_sock_state *state = bts->pcu_state;
+ struct pcu_sock_state *state = net->pcu_state;
struct osmo_fd *bfd, *conn_bfd;
if (!state)
return;
- conn_bfd = &state->conn_bfd;
+ conn_bfd = &state->upqueue.bfd;
if (conn_bfd->fd > 0)
pcu_sock_close(state);
bfd = &state->listen_bfd;
- close(bfd->fd);
osmo_fd_unregister(bfd);
+ close(bfd->fd);
talloc_free(state);
- bts->pcu_state = NULL;
+ net->pcu_state = NULL;
}
-
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..8bf0783bf
--- /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.f.m.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 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 <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 0d806f393..000000000
--- a/src/osmo-bsc/rest_octets.c
+++ /dev/null
@@ -1,898 +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>
-
-/* 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 8d48af9fe..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,10 +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
@@ -57,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)
@@ -70,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)
{
@@ -109,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 */
@@ -241,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
@@ -365,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;
@@ -381,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);
@@ -390,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;
@@ -451,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)
{
@@ -462,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++) {
@@ -489,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++;
}
}
@@ -501,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
@@ -582,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 */
@@ -629,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;
- /* Remove it */
- bts_smscb_del(smscb, chan_state, "KILL");
+ 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;
+ }
+
+ /* stop broadcasting the PN in this BTS */
+ etws_pn_stop(bts, false);
+ }
return 0;
}
@@ -653,6 +802,8 @@ static int bts_rx_reset(struct gsm_bts *bts, const struct osmo_cbsp_decoded *dec
struct bts_smscb_chan_state *chan_state;
struct bts_smscb_message *smscb, *smscb2;
+ LOG_BTS(bts, DCBS, LOGL_NOTICE, "Rx CBSP RESET: clearing all state; disabling broadcast\n");
+
/* remove all SMSCB from CBCH BASIC this BTS */
chan_state = bts_get_smscb_chan(bts, false);
llist_for_each_entry_safe(smscb, smscb2, &chan_state->messages, list)
@@ -663,9 +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);
+
+ /* 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
*********************************************************************************/
@@ -751,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;
@@ -763,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);
@@ -809,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
@@ -831,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));
@@ -846,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 b17a909e5..cc2e788d7 100644
--- a/src/osmo-bsc/system_information.c
+++ b/src/osmo-bsc/system_information.c
@@ -31,16 +31,17 @@
#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_ramp.h>
+#include <osmocom/bsc/acc.h>
#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/bts.h>
struct gsm0808_cell_id_list2;
@@ -51,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;
@@ -168,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;
@@ -210,11 +216,40 @@ static inline uint16_t encode_fdd(uint16_t scramble, bool diversity)
return scramble;
}
+int bts_earfcn_del(struct gsm_bts *bts, uint16_t earfcn)
+{
+ struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
+ int r;
+
+ r = osmo_earfcn_del(e, earfcn);
+
+ if (r < 0)
+ return r;
+
+ /* If the last earfcn was removed, invlidate common neighbours limitations */
+ if (si2q_earfcn_count(e) == 0) {
+ e->thresh_lo_valid = false;
+ e->qrxlm_valid = false;
+ e->prio_valid = false;
+ }
+
+ return r;
+}
+
int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio,
uint8_t qrx, uint8_t meas_bw)
{
struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- int r = osmo_earfcn_add(e, earfcn, (meas_bw < EARFCN_MEAS_BW_INVALID) ? meas_bw : OSMO_EARFCN_MEAS_INVALID);
+ int r;
+
+ /* EARFCN may already exist, so we delete it to avoid duplicates */
+ if (bts_earfcn_del(bts, earfcn) == 0)
+ LOGP(DRR, LOGL_NOTICE, "EARFCN %u is already in the list, modifying\n", earfcn);
+
+ if (meas_bw < EARFCN_MEAS_BW_INVALID)
+ r = osmo_earfcn_add(e, earfcn, meas_bw);
+ else
+ r = osmo_earfcn_add(e, earfcn, OSMO_EARFCN_MEAS_INVALID);
if (r < 0)
return r;
@@ -292,16 +327,21 @@ int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, bool
size_t len = bts->si_common.uarfcn_length, i;
uint8_t si2q;
int pos = uarfcn_sc_pos(bts, arfcn, scramble);
- uint16_t scr = diversity ? encode_fdd(scramble, true) : encode_fdd(scramble, false),
+ uint16_t scr = encode_fdd(scramble, diversity),
*ual = bts->si_common.data.uarfcn_list,
*scl = bts->si_common.data.scramble_list;
+ if (pos >= 0) {
+ LOGP(DRR, LOGL_NOTICE,
+ "EARFCN (%u, %u) is already in the list, modifying\n",
+ arfcn, scramble);
+ scl[pos] = scr;
+ return 0;
+ }
+
if (len == MAX_EARFCN_LIST)
return -ENOMEM;
- if (pos >= 0)
- return -EADDRINUSE;
-
/* find the suitable position for arfcn if any */
pos = uarfcn_sc_pos(bts, arfcn, SC_BOUND);
i = (pos < 0) ? len : pos;
@@ -425,7 +465,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)
{
/*
@@ -434,22 +474,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;
};
@@ -462,8 +502,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;
@@ -482,8 +522,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));
@@ -498,20 +538,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 R-GSM / E-GSM ARFCNs 955..1023 */
+ for (i = 955; 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;
}
@@ -547,7 +596,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;
@@ -568,51 +617,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
@@ -640,7 +701,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)
@@ -649,21 +710,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 */
@@ -689,7 +752,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)
@@ -722,14 +785,25 @@ static int generate_si1(enum osmo_sysinfo_type t, struct gsm_bts *bts)
list_arfcn(si1->cell_channel_description, 0xce, "Serving cell:");
si1->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si1->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si1->rach_control);
/*
* 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));
+ if (bts->nch.num_blocks) {
+ rc = osmo_gsm48_si1ro_nch_pos_encode(bts->nch.num_blocks, bts->nch.first_block);
+ if (rc < 0) {
+ LOGP(DRR, LOGL_ERROR, "Unable to encode NCH position (num_blocks=%u, first_block=%u)\n",
+ bts->nch.num_blocks, bts->nch.first_block);
+ rc = osmo_gsm48_rest_octets_si1_encode(si1->rest_octets, NULL, is_dcs_net(bts));
+ } else {
+ uint8_t nch_pos = rc;
+ rc = osmo_gsm48_rest_octets_si1_encode(si1->rest_octets, &nch_pos, is_dcs_net(bts));
+ }
+ } else {
+ rc = osmo_gsm48_rest_octets_si1_encode(si1->rest_octets, NULL, is_dcs_net(bts));
+ }
return sizeof(*si1) + rc;
}
@@ -754,12 +828,25 @@ static int generate_si2(enum osmo_sysinfo_type t, struct gsm_bts *bts)
si2->ncc_permitted = bts->si_common.ncc_permitted;
si2->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si2->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si2->rach_control);
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;
@@ -789,8 +876,7 @@ static int generate_si2bis(enum osmo_sysinfo_type t, struct gsm_bts *bts)
bts->si_valid &= ~(1 << SYSINFO_TYPE_2bis);
si2b->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si2b->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si2b->rach_control);
/* SI2bis Rest Octets as per 3GPP TS 44.018 §10.5.2.33 */
rc = rest_octets_si2bis(si2b->rest_octets);
@@ -798,6 +884,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;
@@ -867,7 +971,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,
},
@@ -905,14 +1009,17 @@ 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;
si3->cell_options = bts->si_common.cell_options;
si3->cell_sel_par = bts->si_common.cell_sel_par;
si3->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si3->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si3->rach_control);
/* allow/disallow DTXu */
gsm48_set_dtx(&si3->cell_options, bts->dtxu, bts->dtxu, true);
@@ -937,7 +1044,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;
}
@@ -947,7 +1054,7 @@ static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts)
int rc;
struct gsm48_system_information_type_4 *si4 = (struct gsm48_system_information_type_4 *) GSM_BTS_SI(bts, t);
struct gsm_lchan *cbch_lchan;
- uint8_t *restoct = si4->data;
+ uint8_t *tail = si4->data;
/* length of all IEs present except SI4 rest octets and l2_plen */
int l2_plen = sizeof(*si4) - 1;
@@ -961,19 +1068,33 @@ static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts)
gsm48_generate_lai2(&si4->lai, bts_lai(bts));
si4->cell_sel_par = bts->si_common.cell_sel_par;
si4->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si4->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si4->rach_control);
/* Optional: CBCH Channel Description + CBCH Mobile Allocation */
cbch_lchan = gsm_bts_get_cbch(bts);
if (cbch_lchan) {
+ const struct gsm_bts_trx_ts *ts = cbch_lchan->ts;
struct gsm48_chan_desc cd;
- gsm48_lchan2chan_desc_as_configured(&cd, cbch_lchan);
- tv_fixed_put(si4->data, GSM48_IE_CBCH_CHAN_DESC, 3,
- (uint8_t *) &cd);
- l2_plen += 3 + 1;
- restoct += 3 + 1;
- /* we don't use hopping and thus don't need a CBCH MA */
+
+ /* 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);
+
+ /* 10.5.2.21 (TLV) CBCH Mobile Allocation IE */
+ if (ts->hopping.enabled) {
+ /* Prevent potential buffer overflow */
+ if (ts->hopping.ma_len > 2)
+ return -ENOMEM;
+ tail = tlv_put(tail, GSM48_IE_CBCH_MOB_AL,
+ ts->hopping.ma_len,
+ ts->hopping.ma_data);
+ l2_plen += 2 + ts->hopping.ma_len;
+ }
}
si4->header.l2_plen = GSM48_LEN2PLEN(l2_plen);
@@ -981,7 +1102,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(restoct, &si_info, (uint8_t *)GSM_BTS_SI(bts, t) + GSM_MACBLOCK_LEN - restoct);
+ 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;
}
@@ -994,15 +1115,10 @@ 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:
+ /* Abis/IP needs l2_plen!! */
+ if (is_ipa_abisip_bts(bts)) {
*output++ = GSM48_LEN2PLEN(l2_plen);
l2_plen++;
- break;
- default:
- break;
}
si5 = (struct gsm48_system_information_type_5 *) output;
@@ -1030,15 +1146,10 @@ 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:
+ /* Abis/IP needs l2_plen!! */
+ if (is_ipa_abisip_bts(bts)) {
*output++ = GSM48_LEN2PLEN(l2_plen);
l2_plen++;
- break;
- default:
- break;
}
si5b = (struct gsm48_system_information_type_5bis *) output;
@@ -1074,15 +1185,10 @@ 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:
+ /* Abis/IP needs l2_plen!! */
+ if (is_ipa_abisip_bts(bts)) {
*output++ = GSM48_LEN2PLEN(l2_plen);
l2_plen++;
- break;
- default:
- break;
}
si5t = (struct gsm48_system_information_type_5ter *) output;
@@ -1106,21 +1212,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:
+ /* Abis/IP needs l2_plen!! */
+ if (is_ipa_abisip_bts(bts)) {
*output++ = GSM48_LEN2PLEN(l2_plen);
l2_plen++;
- break;
- default:
- break;
}
si6 = (struct gsm48_system_information_type_6 *) output;
@@ -1137,48 +1240,280 @@ 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 si10_rest_octets_encode(struct gsm_bts *s_bts, struct bitvec *bv, struct gsm_bts *n_bts, uint8_t index)
+{
+ /* The BA-IND must be equal to the BA-IND in SI5*. */
+ /* <BA ind : bit(1)> */
+ bitvec_set_bit(bv, 1);
+
+ /* Do we have neighbor cells ? */
+ /* { L <spare padding> | H <neighbour information> } */
+ if (!n_bts)
+ return 0;
+ bitvec_set_bit(bv, H);
+
+ /* <first frequency: bit(5)> */
+ bitvec_set_uint(bv, index, 5);
+
+ /* <bsic : bit(6)> */
+ bitvec_set_uint(bv, n_bts->bsic, 6);
+
+ /* We do not provide empty cell information. */
+ /* { H <cell parameters> | L } */
+ bitvec_set_bit(bv, H);
+
+ /* If cell is barred, we do not need further cell info. */
+ /* <cell barred> | L <further cell info> */
+ if (n_bts->si_common.rach_control.cell_bar) {
+ /* H */
+ bitvec_set_bit(bv, H);
+ /* We are done with the first cell info. */
+ return 0;
+ }
+ bitvec_set_bit(bv, L);
+
+ /* If LA is different for serving cell, we need to add CRH. */
+ /* { H <cell reselect hysteresis : bit(3)> | L } */
+ if (s_bts->location_area_code != n_bts->location_area_code) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_uint(bv, n_bts->si_common.cell_sel_par.cell_resel_hyst, 3);
+ } else
+ bitvec_set_bit(bv, L);
+
+ /* <ms txpwr max cch : bit(5)> */
+ bitvec_set_uint(bv, n_bts->si_common.cell_sel_par.ms_txpwr_max_ccch, 5);
+
+ /* <rxlev access min : bit(6)> */
+ bitvec_set_uint(bv, n_bts->si_common.cell_sel_par.rxlev_acc_min, 6);
+
+ /* <cell reselect offset : bit(6)> */
+ if (n_bts->si_common.cell_ro_sel_par.present)
+ bitvec_set_uint(bv, n_bts->si_common.cell_ro_sel_par.cell_resel_off, 6);
+ else
+ bitvec_set_uint(bv, 0, 6);
+
+ /* <temporary offset : bit(3)> */
+ if (n_bts->si_common.cell_ro_sel_par.present)
+ bitvec_set_uint(bv, n_bts->si_common.cell_ro_sel_par.temp_offs, 3);
+ else
+ bitvec_set_uint(bv, 0, 3);
+
+ /* <penalty time : bit(5)> */
+ if (n_bts->si_common.cell_ro_sel_par.present)
+ bitvec_set_uint(bv, n_bts->si_common.cell_ro_sel_par.penalty_time, 5);
+ else
+ bitvec_set_uint(bv, 0, 5);
+
+ /* We are done with the first cell info. */
+ return 0;
+}
+
+static int si10_rest_octets_encode_other(struct gsm_bts *s_bts, struct bitvec *bv, struct gsm_bts *l_bts,
+ struct gsm_bts *n_bts, uint8_t last_index, uint8_t index)
+{
+ int rc;
+
+ /* If the bv buffer would overflow, then the bits are not written. Each bitvec_set_* call will return
+ * an error code. This error is returned with this function. */
+
+ /* { H <info field> } */
+ bitvec_set_bit(bv, H);
+
+ /* How many frequency indices do we skip? */
+ /* <next frequency>** L <differential cell info> */
+ while ((last_index = (last_index + 1) & 0x1f) != index) {
+ /* H */
+ bitvec_set_bit(bv, H);
+ }
+ bitvec_set_bit(bv, L);
+
+ /* If NCC is equal, just send BCC, otherwise send complete BSIC. */
+ /* { H <BCC : bit(3)> | L <bsic : bit(6)> } */
+ if ((l_bts->bsic & 0x38) == (n_bts->bsic & 0x38))
+ bitvec_set_uint(bv, n_bts->bsic & 0x07, 3);
+ else
+ bitvec_set_uint(bv, n_bts->bsic, 6);
+
+ /* We do not provide empty cell information. */
+ /* { H <cell parameters> | L } */
+ bitvec_set_bit(bv, H);
+
+ /* If cell is barred, we do not need further cell info. */
+ /* <cell barred> | L <further cell info> */
+ if (n_bts->si_common.rach_control.cell_bar) {
+ /* H */
+ rc = bitvec_set_bit(bv, H);
+ /* We are done with the other cell info. */
+ return rc;
+ }
+ bitvec_set_bit(bv, L);
+
+ /* If LA is different for serving cell, we need to add CRH. */
+ /* { H <cell reselect hysteresis : bit(3)> | L } */
+ if (s_bts->location_area_code != n_bts->location_area_code) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_uint(bv, n_bts->si_common.cell_sel_par.cell_resel_hyst, 3);
+ } else
+ bitvec_set_bit(bv, L);
+
+ /* { H <ms txpwr max cch : bit(5)> | L } */
+ if (l_bts->si_common.cell_sel_par.ms_txpwr_max_ccch != n_bts->si_common.cell_sel_par.ms_txpwr_max_ccch) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_uint(bv, n_bts->si_common.cell_sel_par.ms_txpwr_max_ccch, 5);
+ } else
+ bitvec_set_bit(bv, L);
+
+ /* { H <rxlev access min : bit(6)> | L } */
+ if (l_bts->si_common.cell_sel_par.rxlev_acc_min != n_bts->si_common.cell_sel_par.rxlev_acc_min) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_uint(bv, n_bts->si_common.cell_sel_par.rxlev_acc_min, 6);
+ } else
+ bitvec_set_bit(bv, L);
+
+ /* { H <cell reselect offset : bit(6)> | L } */
+ if (l_bts->si_common.cell_ro_sel_par.present != n_bts->si_common.cell_ro_sel_par.present ||
+ (n_bts->si_common.cell_ro_sel_par.present &&
+ l_bts->si_common.cell_ro_sel_par.cell_resel_off != n_bts->si_common.cell_ro_sel_par.cell_resel_off)) {
+ bitvec_set_bit(bv, H);
+ if (n_bts->si_common.cell_ro_sel_par.present)
+ bitvec_set_uint(bv, n_bts->si_common.cell_ro_sel_par.cell_resel_off, 6);
+ else
+ bitvec_set_uint(bv, 0, 6);
+ } else
+ bitvec_set_bit(bv, L);
+
+ /* { H <temporary offset : bit(3)> | L } */
+ if (l_bts->si_common.cell_ro_sel_par.present != n_bts->si_common.cell_ro_sel_par.present ||
+ (n_bts->si_common.cell_ro_sel_par.present &&
+ l_bts->si_common.cell_ro_sel_par.temp_offs != n_bts->si_common.cell_ro_sel_par.temp_offs)) {
+ bitvec_set_bit(bv, H);
+ if (n_bts->si_common.cell_ro_sel_par.present)
+ bitvec_set_uint(bv, n_bts->si_common.cell_ro_sel_par.temp_offs, 3);
+ else
+ bitvec_set_uint(bv, 0, 3);
+ } else
+ bitvec_set_bit(bv, L);
+
+ /* { H <penalty time : bit(5)> | L } */
+ if (l_bts->si_common.cell_ro_sel_par.present != n_bts->si_common.cell_ro_sel_par.present ||
+ (n_bts->si_common.cell_ro_sel_par.present &&
+ l_bts->si_common.cell_ro_sel_par.penalty_time != n_bts->si_common.cell_ro_sel_par.penalty_time)) {
+ bitvec_set_bit(bv, H);
+ if (n_bts->si_common.cell_ro_sel_par.present)
+ rc = bitvec_set_uint(bv, n_bts->si_common.cell_ro_sel_par.penalty_time, 5);
+ else
+ rc = bitvec_set_uint(bv, 0, 5);
+ } else
+ rc = bitvec_set_bit(bv, L);
+
+ /* We are done with the other cell info. */
+ return rc;
+}
+
+/* Generate SI 10 and return the number of bits used in the rest octet. */
+int gsm_generate_si10(struct gsm48_system_information_type_10 *si10, size_t len,
+ const struct gsm_subscriber_connection *conn)
+{
+ struct bitvec *nbv;
+ struct gsm_bts *s_bts = conn->lchan->ts->trx->bts, *l_bts = NULL;
+ int i, last_i = -1;
+ bool any_neighbor = false;
+ int rc;
+
+ struct bitvec bv = {
+ .data_len = len - sizeof(*si10),
+ .data = si10->rest_octets,
+ };
+
+ si10->rr_short_pd = 0; /* 3GPP TS 24.007 §11.3.2.1 */
+ si10->msg_type = GSM48_MT_RR_SH_SI10;
+ si10->l2_header = 0; /* 3GPP TS 44.006 §6.4a */
+
+ /* If we have gernerated SI5 with separate SI5 list, the used frequency indexes refer to it. */
+ if (s_bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP)
+ nbv = &s_bts->si_common.si5_neigh_list;
+ else
+ nbv = &s_bts->si_common.neigh_list;
+
+ /* Get up to 32 possible neighbor frequencies that SI10 can refer to. */
+ for (i = 0; i < 32; i++) {
+ struct gsm_bts *c_bts, *n_bts;
+ struct gsm_subscriber_connection *c;
+ unsigned int save_cur_bit;
+ int16_t arfcn;
+ arfcn = neigh_list_get_arfcn(s_bts, nbv, i);
+ /* End of list */
+ if (arfcn < 0)
+ break;
+ /* Is this neighbour used for this group call? */
+ n_bts = NULL;
+ llist_for_each_entry(c, &conn->vgcs_chan.call->vgcs_call.chan_list, vgcs_chan.list) {
+ if (c == conn)
+ continue;
+ if (!c->lchan)
+ continue;
+ c_bts = c->lchan->ts->trx->bts;
+ if (c_bts->c0->arfcn != arfcn)
+ continue;
+ n_bts = c_bts;
+ break;
+ }
+ if (n_bts) {
+ if (!any_neighbor) {
+ /* First neighbor, so generate rest octets with first cell info. */
+ LOGP(DRR, LOGL_INFO, "SI 10 with cell ID %d.\n", n_bts->cell_identity);
+ rc = si10_rest_octets_encode(s_bts, &bv, n_bts, i);
+ if (rc < 0)
+ return rc;
+ any_neighbor = true;
+ } else {
+ /* Save current position, so we can restore to last position in case of failure. */
+ save_cur_bit = bv.cur_bit;
+ /* Nth neighbor, so add rest octets with differential cell info. */
+ LOGP(DRR, LOGL_INFO, "Append cell ID %d to SI 10.\n", n_bts->cell_identity);
+ OSMO_ASSERT(l_bts && last_i >= 0);
+ rc = si10_rest_octets_encode_other(s_bts, &bv, l_bts, n_bts, last_i, i);
+ if (rc < 0) {
+ LOGP(DRR, LOGL_INFO, "Skip cell ID %d, SI 10 would overflow.\n",
+ n_bts->cell_identity);
+ /* Resore last position. */
+ bv.cur_bit = save_cur_bit;
+ break;
+ }
+ }
+ last_i = i;
+ l_bts = n_bts;
+ }
+ }
+
+ /* If no neighbor exists, generate rest octets without any neighbor info. */
+ if (!any_neighbor) {
+ LOGP(DRR, LOGL_INFO, "SI 10 without any neighbor cell.\n");
+ rc = si10_rest_octets_encode(s_bts, &bv, NULL, 0);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* Do spare padding. We cannot do it earlier, because encoding might corrupt it if differential cell info
+ * does not fit into the message. */
+ while ((bv.cur_bit & 7))
+ bitvec_set_bit(&bv, L);
+ memset(bv.data + bv.cur_bit / 8, GSM_MACBLOCK_PADDING, bv.data_len - bv.cur_bit / 8);
+
+ return len;
+}
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);
@@ -1187,27 +1522,68 @@ 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,
+ /* 3GPP TS 45.002 6.5.6:
+ * "On BCCH, the operator should limit DRX_TIMER_MAX [...] to 4 seconds" */
+ .drx_timer_max = 4,
+ .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;
@@ -1241,9 +1617,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;
@@ -1254,7 +1627,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 4816dafb5..9618f9280 100644
--- a/src/osmo-bsc/timeslot_fsm.c
+++ b/src/osmo-bsc/timeslot_fsm.c
@@ -28,6 +28,8 @@
#include <osmocom/bsc/lchan_fsm.h>
#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;
@@ -47,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);
}
@@ -62,6 +64,15 @@ void ts_fsm_alloc(struct gsm_bts_trx_ts *ts)
OSMO_ASSERT(ts->trx);
ts->fi = osmo_fsm_inst_alloc(&ts_fsm, ts->trx, ts, LOGL_DEBUG, NULL);
OSMO_ASSERT(ts->fi);
+ 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 {
@@ -83,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 */
@@ -110,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++;
@@ -123,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;
@@ -135,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);
}
}
@@ -144,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;
@@ -185,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;
@@ -203,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)
@@ -263,11 +317,45 @@ 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_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+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_pdch_act(struct osmo_fsm_inst *fi)
{
struct gsm_bts_trx_ts *ts = ts_fi_ts(fi);
struct gsm_bts *bts = ts->trx->bts;
+ if (bts->gprs.mode == BTS_GPRS_NONE) {
+ LOG_TS(ts, LOGL_DEBUG, "GPRS mode is 'none': not activating PDCH.\n");
+ return;
+ }
+
+ if (!ts->pdch_act_allowed) {
+ LOG_TS(ts, LOGL_DEBUG, "PDCH is disabled for this timeslot,"
+ " either due to a PDCH ACT NACK, or from manual VTY command:"
+ " not activating PDCH. (last error: %s)\n",
+ ts->last_errmsg ? : "-");
+ return;
+ }
+
+ if (bsc_co_located_pcu(bts) && !pcu_connected(bts->network)) {
+ LOG_TS(ts, LOGL_DEBUG, "PCU not connected: not activating PDCH.\n");
+ return;
+ }
+
+ osmo_fsm_inst_state_chg(fi, TS_ST_WAIT_PDCH_ACT, CHAN_ACT_DEACT_TIMEOUT,
+ T_CHAN_ACT_DEACT);
+}
+
+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);
+
+ 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)) {
@@ -278,21 +366,9 @@ 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");
- return;
- }
- if (!ts->pdch_act_allowed) {
- LOG_TS(ts, LOGL_DEBUG, "PDCH is disabled for this timeslot,"
- " either due to a PDCH ACT NACK, or from manual VTY command:"
- " not activating PDCH. (last error: %s)\n",
- ts->last_errmsg ? : "-");
- return;
- }
- osmo_fsm_inst_state_chg(fi, TS_ST_WAIT_PDCH_ACT, CHAN_ACT_DEACT_TIMEOUT,
- T_CHAN_ACT_DEACT);
+ ts_fsm_unused_pdch_act(fi);
break;
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
@@ -337,6 +413,10 @@ static void ts_fsm_unused(struct osmo_fsm_inst *fi, uint32_t event, void *data)
/* ignored. */
return;
+ case TS_EV_PDCH_ACT:
+ ts_fsm_unused_pdch_act(fi);
+ return;
+
default:
OSMO_ASSERT(false);
}
@@ -364,6 +444,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:
@@ -372,9 +453,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;
@@ -414,10 +495,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",
@@ -459,6 +540,10 @@ static void ts_fsm_pdch(struct osmo_fsm_inst *fi, uint32_t event, void *data)
}
}
+ case TS_EV_PDCH_DEACT:
+ ts_fsm_pdch_deact(fi);
+ return;
+
case TS_EV_LCHAN_UNUSED:
/* ignored */
return;
@@ -489,11 +574,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",
@@ -509,7 +594,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");
@@ -554,7 +639,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;
@@ -571,7 +656,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);
}
@@ -583,7 +668,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);
}
@@ -602,7 +687,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;
}
@@ -612,10 +697,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);
}
@@ -657,6 +744,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:
@@ -683,8 +771,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)
@@ -708,8 +796,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;
}
@@ -721,8 +809,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;
}
@@ -760,7 +848,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;
@@ -769,7 +858,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;
@@ -781,9 +870,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);
}
}
@@ -792,6 +882,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)
@@ -811,6 +902,7 @@ static const struct osmo_fsm_state ts_fsm_states[] = {
.in_event_mask = 0
| S(TS_EV_LCHAN_REQUESTED)
| S(TS_EV_LCHAN_UNUSED)
+ | S(TS_EV_PDCH_ACT)
,
.out_state_mask = 0
| S(TS_ST_WAIT_PDCH_ACT)
@@ -843,6 +935,7 @@ static const struct osmo_fsm_state ts_fsm_states[] = {
.in_event_mask = 0
| S(TS_EV_LCHAN_REQUESTED)
| S(TS_EV_LCHAN_UNUSED)
+ | S(TS_EV_PDCH_DEACT)
,
.out_state_mask = 0
| S(TS_ST_WAIT_PDCH_DEACT)
@@ -863,7 +956,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)
,
@@ -915,6 +1007,8 @@ static const struct value_string ts_fsm_event_names[] = {
OSMO_VALUE_STRING(TS_EV_PDCH_ACT_NACK),
OSMO_VALUE_STRING(TS_EV_PDCH_DEACT_ACK),
OSMO_VALUE_STRING(TS_EV_PDCH_DEACT_NACK),
+ OSMO_VALUE_STRING(TS_EV_PDCH_DEACT),
+ OSMO_VALUE_STRING(TS_EV_PDCH_ACT),
{}
};
@@ -938,7 +1032,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);
@@ -979,7 +1073,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;
@@ -1002,7 +1096,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;
@@ -1020,5 +1114,31 @@ 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;
+}
+
+void ts_pdch_act(struct gsm_bts_trx_ts *ts)
+{
+ /* We send the activation event only when the timeslot is UNUSED. In all other cases the timeslot FSM
+ * will automatically handle the activation at some later point. */
+ if (ts->fi->state != TS_ST_UNUSED)
+ return;
+
+ osmo_fsm_inst_dispatch(ts->fi, TS_EV_PDCH_ACT, NULL);
+}
+
+void ts_pdch_deact(struct gsm_bts_trx_ts *ts)
+{
+ /* We send the deactivation event only when the timeslot is active as PDCH. The timeslot FSM will check
+ * if the PCU is available before activating the PDCH again. */
+ if (ts->fi->state != TS_ST_PDCH)
+ return;
+
+ osmo_fsm_inst_dispatch(ts->fi, TS_EV_PDCH_DEACT, NULL);
}
diff --git a/src/osmo-bsc/vgcs_fsm.c b/src/osmo-bsc/vgcs_fsm.c
new file mode 100644
index 000000000..1f2bbefec
--- /dev/null
+++ b/src/osmo-bsc/vgcs_fsm.c
@@ -0,0 +1,1318 @@
+/* Handle VGCS/VBCS calls. (Voice Group/Broadcast Call Service). */
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * Author: Andreas Eversberg
+ *
+ * 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/>.
+ */
+
+/* The process consists of two state machnes:
+ *
+ * The VGCS call state machine handles the voice group/broadcast call control.
+ * There is one instance for every call. It controls the uplink states of the
+ * call. They will be reported to the MSC or can be changed by the MSC.
+ * One SCCP connection for is associated with the state machine. This is used
+ * to talk to the MSC about state changes.
+ *
+ * The VGCS channel state machine handles the channel states in each cell.
+ * There is one instance for every cell and every call. The instances are
+ * linked to the call state process. It controls the uplink states of the
+ * channel. They will be reported to the call state machine or can be changed
+ * by the call state machine.
+ * One SCCP connection for every cell is associated with the state machine.
+ * It is used to perform VGCS channel assignment.
+ *
+ */
+
+#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
+#include <osmocom/bsc/osmo_bsc.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/vgcs_fsm.h>
+#include <osmocom/bsc/handover_fsm.h>
+#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/lchan_select.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
+#include <osmocom/bsc/assignment_fsm.h>
+#include <osmocom/bsc/gsm_08_08.h>
+#include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/bts_trx.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/system_information.h>
+
+#define S(x) (1 << (x))
+
+#define LOG_CALL(conn, level, fmt, args...) \
+ LOGP(DASCI, level, \
+ (conn->vgcs_call.sf == GSM0808_SF_VGCS) ? ("VGCS callref %s: " fmt) : ("VBS callref %s: " fmt), \
+ gsm44068_group_id_string(conn->vgcs_call.call_ref), ##args)
+#define LOG_CHAN(conn, level, fmt, args...) \
+ LOGP(DASCI, level, \
+ (conn->vgcs_chan.sf == GSM0808_SF_VGCS) ? ("VGCS callref %s, cell %s: " fmt) \
+ : ("VBS callref %s, cell %s: " fmt), \
+ gsm44068_group_id_string(conn->vgcs_chan.call_ref), conn->vgcs_chan.ci_str, ##args)
+
+const char *gsm44068_group_id_string(uint32_t callref)
+{
+ static char string[9];
+
+ snprintf(string, sizeof(string), "%08u", callref);
+
+ return string;
+}
+
+static struct osmo_fsm vgcs_call_fsm;
+static struct osmo_fsm vgcs_chan_fsm;
+
+static __attribute__((constructor)) void vgcs_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&vgcs_call_fsm) == 0);
+ OSMO_ASSERT(osmo_fsm_register(&vgcs_chan_fsm) == 0);
+}
+
+static const struct value_string vgcs_fsm_event_names[] = {
+ OSMO_VALUE_STRING(VGCS_EV_SETUP),
+ OSMO_VALUE_STRING(VGCS_EV_ASSIGN_REQ),
+ OSMO_VALUE_STRING(VGCS_EV_TALKER_DET),
+ OSMO_VALUE_STRING(VGCS_EV_LISTENER_DET),
+ OSMO_VALUE_STRING(VGCS_EV_MSC_ACK),
+ OSMO_VALUE_STRING(VGCS_EV_MSC_REJECT),
+ OSMO_VALUE_STRING(VGCS_EV_MSC_SEIZE),
+ OSMO_VALUE_STRING(VGCS_EV_MSC_RELEASE),
+ OSMO_VALUE_STRING(VGCS_EV_MSC_DTAP),
+ OSMO_VALUE_STRING(VGCS_EV_LCHAN_ACTIVE),
+ OSMO_VALUE_STRING(VGCS_EV_LCHAN_ERROR),
+ OSMO_VALUE_STRING(VGCS_EV_MGW_OK),
+ OSMO_VALUE_STRING(VGCS_EV_MGW_FAIL),
+ OSMO_VALUE_STRING(VGCS_EV_TALKER_EST),
+ OSMO_VALUE_STRING(VGCS_EV_TALKER_DATA),
+ OSMO_VALUE_STRING(VGCS_EV_TALKER_REL),
+ OSMO_VALUE_STRING(VGCS_EV_TALKER_FAIL),
+ OSMO_VALUE_STRING(VGCS_EV_BLOCK),
+ OSMO_VALUE_STRING(VGCS_EV_REJECT),
+ OSMO_VALUE_STRING(VGCS_EV_UNBLOCK),
+ OSMO_VALUE_STRING(VGCS_EV_CLEANUP),
+ OSMO_VALUE_STRING(VGCS_EV_CALLING_ASSIGNED),
+ { }
+};
+
+static struct gsm_subscriber_connection *find_calling_subscr_conn(struct gsm_subscriber_connection *conn)
+{
+ struct gsm_subscriber_connection *c;
+
+ llist_for_each_entry(c, &conn->network->subscr_conns, entry) {
+ if (!c->assignment.fi)
+ continue;
+ if (c->assignment.req.target_lchan != conn->lchan)
+ continue;
+ return c;
+ }
+
+ return NULL;
+}
+
+/*
+ * VGCS call FSM
+ */
+
+/* Add/update SI10. It must be called whenever a channel is activated or failed. */
+static void si10_update(struct gsm_subscriber_connection *conn)
+{
+ struct gsm_subscriber_connection *c;
+ uint8_t si10[SI10_LENGTH];
+ int rc;
+
+ /* Skip SI10 update, if not all channels have been activated or failed. */
+ llist_for_each_entry(c, &conn->vgcs_call.chan_list, vgcs_chan.list) {
+ if (c->vgcs_chan.fi->state == VGCS_CHAN_ST_WAIT_EST) {
+ LOG_CALL(conn, LOGL_DEBUG, "There is a channel, not yet active. No SI10 update now.\n");
+ return;
+ }
+ }
+
+ LOG_CALL(conn, LOGL_DEBUG, "New channel(s) added, updating SI10 for all channels.\n");
+
+ /* Go through all channels. */
+ llist_for_each_entry(c, &conn->vgcs_call.chan_list, vgcs_chan.list) {
+ /* Skip all channels that failed to activate or have not been aktivated yet.
+ * There shouldn't be any channel in that state now. */
+ if (!c->lchan)
+ continue;
+ /* Encode SI 10 for this channel. Skip, if it fails. */
+ rc = gsm_generate_si10((struct gsm48_system_information_type_10 *)si10, sizeof(si10), c);
+ if (rc < 0)
+ continue;
+ /* Add SI 10 to SACCH of this channel c. */
+ rsl_sacch_info_modify(c->lchan, RSL_SYSTEM_INFO_10, si10, sizeof(si10));
+ }
+}
+
+static void vgcs_call_detach_and_destroy(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct gsm_subscriber_connection *conn = fi->priv, *c;
+ struct msgb *msg;
+
+ /* Flush message queue. */
+ while ((msg = msgb_dequeue(&conn->vgcs_call.l3_queue)))
+ msgb_free(msg);
+
+ /* Detach all cell instances. */
+ while (!llist_empty(&conn->vgcs_call.chan_list)) {
+ c = llist_entry(conn->vgcs_call.chan_list.next, struct gsm_subscriber_connection, vgcs_chan.list);
+ c->vgcs_chan.call = NULL;
+ llist_del(&c->vgcs_chan.list);
+ }
+
+ /* No Talker. */
+ conn->vgcs_call.talker = NULL;
+
+ /* Remove pointer of FSM. */
+ conn->vgcs_call.fi = NULL;
+}
+
+static void vgcs_call_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ switch (event) {
+ case VGCS_EV_SETUP:
+ LOG_CALL(conn, LOGL_DEBUG, "VGCS/VBS SETUP from MSC.\n");
+ /* MSC sends VGCS/VBS SETUP for a new call. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CALL_ST_IDLE, 0, 0);
+ /* Remove unsupported features. */
+ conn->vgcs_call.ff.tp_ind = 0;
+ conn->vgcs_call.ff.as_ind_circuit = 0;
+ conn->vgcs_call.ff.as_ind_link = 0;
+ conn->vgcs_call.ff.bss_res = 0;
+ conn->vgcs_call.ff.tcp = 0;
+ /* Acknowlege the call. */
+ bsc_tx_setup_ack(conn, &conn->vgcs_call.ff);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void vgcs_call_fsm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv, *c;
+ struct handover_rr_detect_data *d = data;
+ struct msgb *msg;
+
+ switch (event) {
+ case VGCS_EV_TALKER_DET:
+ LOG_CALL(conn, LOGL_DEBUG, "Talker detected.\n");
+ /* Talker detected on a channel, call becomes busy. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CALL_ST_BUSY, 0, 0);
+ conn->vgcs_call.talker = d->msg->lchan->conn;
+ /* Reset pending states. */
+ while ((msg = msgb_dequeue(&conn->vgcs_call.l3_queue)))
+ msgb_free(msg);
+ conn->vgcs_call.msc_ack = false;
+ conn->vgcs_call.talker_rel = false;
+ /* Report busy uplink to the MSC. */
+ bsc_tx_uplink_req(conn);
+ /* Block all other channels. */
+ llist_for_each_entry(c, &conn->vgcs_call.chan_list, vgcs_chan.list) {
+ if (c == conn->vgcs_call.talker)
+ continue;
+ osmo_fsm_inst_dispatch(c->vgcs_chan.fi, VGCS_EV_BLOCK, NULL);
+ }
+ break;
+ case VGCS_EV_LISTENER_DET:
+ LOG_CALL(conn, LOGL_DEBUG, "Listener detected.\n");
+ // Listener detection not supported.
+ break;
+ case VGCS_EV_MSC_SEIZE:
+ LOG_CALL(conn, LOGL_DEBUG, "MSC seizes all channels.\n");
+ /* MSC seizes call (talker on a different BSS), call becomes blocked. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CALL_ST_BLOCKED, 0, 0);
+ /* Block all channels. */
+ llist_for_each_entry(c, &conn->vgcs_call.chan_list, vgcs_chan.list)
+ osmo_fsm_inst_dispatch(c->vgcs_chan.fi, VGCS_EV_BLOCK, NULL);
+ break;
+ case VGCS_EV_MSC_RELEASE:
+ /* Ignore, because there is no blocked channel in this state. */
+ break;
+ case VGCS_EV_MSC_REJECT:
+ LOG_CALL(conn, LOGL_DEBUG, "MSC rejects talker on uplink.\n");
+ /* Race condition: Talker released before the MSC rejects the talker. Ignore! */
+ break;
+ case VGCS_EV_CLEANUP:
+ LOG_CALL(conn, LOGL_DEBUG, "SCCP connection clearing.\n");
+ osmo_fsm_inst_term(conn->vgcs_call.fi, 0, NULL);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+/* Get L3 info from message, if exists. Return the length or otherwise return 0. */
+int l3_data_from_msg(struct msgb *msg, uint8_t **l3_info)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+
+ /* No space for L3 info */
+ if (msgb_l2len(msg) < sizeof(*rllh) + 3 || rllh->data[0] != RSL_IE_L3_INFO)
+ return 0;
+
+ *l3_info = msg->l3h = &rllh->data[3];
+ return msgb_l3len(msg);
+}
+
+static void vgcs_call_fsm_busy(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv, *c;
+ struct msgb *msg = data;
+ uint8_t cause = (data) ? *(uint8_t *)data : 0;
+ uint8_t *l3_info;
+ int l3_len;
+ int rc;
+
+ switch (event) {
+ case VGCS_EV_TALKER_EST:
+ LOG_CALL(conn, LOGL_DEBUG, "Talker established uplink.\n");
+ /* Talker established L2 connection. Sent L3 info to MSC, if MSC already acked, otherwise enqueue. */
+ if (conn->vgcs_call.msc_ack) {
+ LOG_CALL(conn, LOGL_DEBUG, "Sending establishment messages to MSC.\n");
+ l3_len = l3_data_from_msg(msg, &l3_info);
+ if (conn->vgcs_call.talker)
+ bsc_tx_uplink_req_conf(conn, &conn->vgcs_call.talker->vgcs_chan.ci, l3_info, l3_len);
+ else
+ LOG_CALL(conn, LOGL_ERROR, "Talker establishes, but talker not set, please fix!\n");
+ } else {
+ LOG_CALL(conn, LOGL_DEBUG, "No uplink request ack from MSC yet, queue message.\n");
+ msg = msgb_copy(msg, "Queued Talker establishment");
+ if (msg)
+ msgb_enqueue(&conn->vgcs_call.l3_queue, msg);
+ }
+ break;
+ case VGCS_EV_TALKER_DATA:
+ LOG_CALL(conn, LOGL_DEBUG, "Talker sent data on uplink.\n");
+ /* Talker sends data. Sent L3 info to MSC, if MSC already acked, otherwise enqueue. */
+ if (conn->vgcs_call.msc_ack) {
+ LOG_CALL(conn, LOGL_DEBUG, "Sending data messages to MSC.\n");
+ bsc_dtap(conn, 0, msg);
+ } else {
+ LOG_CALL(conn, LOGL_DEBUG, "No uplink request ack from MSC yet, queue message.\n");
+ msg = msgb_copy(msg, "Queued DTAP");
+ if (msg)
+ msgb_enqueue(&conn->vgcs_call.l3_queue, msg);
+ }
+ break;
+ case VGCS_EV_MSC_DTAP:
+ LOG_CALL(conn, LOGL_DEBUG, "MSC sends DTAP message to talker.\n");
+ if (!conn->vgcs_call.talker) {
+ msgb_free(data);
+ break;
+ }
+ rc = osmo_fsm_inst_dispatch(conn->vgcs_call.talker->vgcs_chan.fi, VGCS_EV_MSC_DTAP, data);
+ if (rc < 0)
+ msgb_free(data);
+ break;
+ case VGCS_EV_TALKER_REL:
+ LOG_CALL(conn, LOGL_DEBUG, "Talker released on uplink.\n");
+ if (!conn->vgcs_call.msc_ack) {
+ LOG_CALL(conn, LOGL_DEBUG, "Talker released before MSC acknowleded or rejected.\n");
+ conn->vgcs_call.talker_rel = true;
+ conn->vgcs_call.talker_cause = cause;
+ break;
+ }
+talker_released:
+ /* Talker released channel, call becomes idle. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CALL_ST_IDLE, 0, 0);
+ conn->vgcs_call.talker = NULL;
+ /* Report free uplink to the MSC. */
+ bsc_tx_uplink_release_ind(conn, cause);
+ /* Unblock all other channels. */
+ llist_for_each_entry(c, &conn->vgcs_call.chan_list, vgcs_chan.list) {
+ if (c == conn->vgcs_call.talker)
+ continue;
+ osmo_fsm_inst_dispatch(c->vgcs_chan.fi, VGCS_EV_UNBLOCK, NULL);
+ }
+ break;
+ case VGCS_EV_MSC_SEIZE:
+ LOG_CALL(conn, LOGL_DEBUG, "MSC seizes all channels. (channels are blocked)\n");
+ /* Race condition: MSC seizes call (talker on a different BSS), call becomes blocked. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CALL_ST_BLOCKED, 0, 0);
+ /* Reject talker. (Forward to chan FSM.) */
+ if (conn->vgcs_call.talker) {
+ osmo_fsm_inst_dispatch(conn->vgcs_call.talker->vgcs_chan.fi, VGCS_EV_REJECT, NULL);
+ conn->vgcs_call.talker = NULL;
+ }
+ /* Block all channels. */
+ llist_for_each_entry(c, &conn->vgcs_call.chan_list, vgcs_chan.list)
+ osmo_fsm_inst_dispatch(c->vgcs_chan.fi, VGCS_EV_BLOCK, NULL);
+ break;
+ case VGCS_EV_MSC_ACK:
+ LOG_CALL(conn, LOGL_DEBUG, "MSC acks talker on uplink.\n");
+ /* MSC acknowledges uplink. Send L3 info to MSC, if talker already established. */
+ conn->vgcs_call.msc_ack = true;
+ /* Send establish message via UPLINK REQUEST CONFIRM, if already received. */
+ msg = msgb_dequeue(&conn->vgcs_call.l3_queue);
+ if (msg) {
+ LOG_CALL(conn, LOGL_DEBUG, "Sending queued establishment messages to MSC.\n");
+ l3_len = l3_data_from_msg(msg, &l3_info);
+ if (conn->vgcs_call.talker)
+ bsc_tx_uplink_req_conf(conn, &conn->vgcs_call.talker->vgcs_chan.ci, l3_info, l3_len);
+ else
+ LOG_CALL(conn, LOGL_ERROR, "MSC acks taker, but talker not set, please fix!\n");
+ msgb_free(msg);
+ }
+ /* Send data messages via UPLINK APPLICATION DATA, if already received. */
+ while ((msg = msgb_dequeue(&conn->vgcs_call.l3_queue))) {
+ LOG_CALL(conn, LOGL_DEBUG, "Sending queued DTAP messages to MSC.\n");
+ bsc_dtap(conn, 0, msg);
+ msgb_free(msg);
+ }
+ /* If there is a pending talker release. */
+ if (conn->vgcs_call.talker_rel) {
+ LOG_CALL(conn, LOGL_DEBUG, "Sending queued talker release messages to MSC.\n");
+ cause = conn->vgcs_call.talker_cause;
+ goto talker_released;
+ }
+ break;
+ case VGCS_EV_MSC_REJECT:
+ LOG_CALL(conn, LOGL_DEBUG, "MSC rejects talker on uplink.\n");
+ /* MSC rejects talker, call becomes idle. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CALL_ST_IDLE, 0, 0);
+ /* Reject talker. (Forward to chan FSM.) */
+ if (conn->vgcs_call.talker)
+ osmo_fsm_inst_dispatch(conn->vgcs_call.talker->vgcs_chan.fi, VGCS_EV_REJECT, NULL);
+ else
+ LOG_CALL(conn, LOGL_ERROR, "MSC rejects, but talker not set, please fix!\n");
+ conn->vgcs_call.talker = NULL;
+ /* Unblock all other channels. */
+ llist_for_each_entry(c, &conn->vgcs_call.chan_list, vgcs_chan.list) {
+ if (c == conn->vgcs_call.talker)
+ continue;
+ osmo_fsm_inst_dispatch(c->vgcs_chan.fi, VGCS_EV_UNBLOCK, NULL);
+ }
+ break;
+ case VGCS_EV_CLEANUP:
+ LOG_CALL(conn, LOGL_DEBUG, "SCCP connection clearing.\n");
+ osmo_fsm_inst_term(conn->vgcs_call.fi, 0, NULL);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void vgcs_call_fsm_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv, *c;
+ struct msgb *msg;
+
+ switch (event) {
+ case VGCS_EV_CALLING_ASSIGNED:
+ LOG_CALL(conn, LOGL_DEBUG, "Calling subscriber assigned and now on uplink.\n");
+ /* Talker detected on a channel, call becomes busy. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CALL_ST_BUSY, 0, 0);
+ conn->vgcs_call.talker = data;
+ /* Reset pending states, but imply that MSC acked this uplink session. */
+ while ((msg = msgb_dequeue(&conn->vgcs_call.l3_queue)))
+ msgb_free(msg);
+ conn->vgcs_call.msc_ack = true;
+ break;
+ case VGCS_EV_TALKER_REL:
+ LOG_CALL(conn, LOGL_DEBUG, "Talker released on uplink.\n");
+ /* Talker release was complete. Ignore. */
+ break;
+ case VGCS_EV_MSC_RELEASE:
+ LOG_CALL(conn, LOGL_DEBUG, "MSC releases all channels. (channels are free)\n");
+ /* MSC releases call (no mor talker on a different BSS), call becomes idle */
+ osmo_fsm_inst_state_chg(fi, VGCS_CALL_ST_IDLE, 0, 0);
+ /* Unblock all channels. */
+ llist_for_each_entry(c, &conn->vgcs_call.chan_list, vgcs_chan.list)
+ osmo_fsm_inst_dispatch(c->vgcs_chan.fi, VGCS_EV_UNBLOCK, NULL);
+ break;
+ case VGCS_EV_CLEANUP:
+ LOG_CALL(conn, LOGL_DEBUG, "SCCP connection clearing.\n");
+ osmo_fsm_inst_term(conn->vgcs_call.fi, 0, NULL);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static const struct osmo_fsm_state vgcs_call_fsm_states[] = {
+ [VGCS_CALL_ST_NULL] = {
+ .name = "NULL",
+ .in_event_mask = S(VGCS_EV_SETUP),
+ .out_state_mask = S(VGCS_CALL_ST_IDLE),
+ .action = vgcs_call_fsm_null,
+ },
+ [VGCS_CALL_ST_IDLE] = {
+ .name = "IDLE",
+ .in_event_mask = S(VGCS_EV_TALKER_DET) |
+ S(VGCS_EV_MSC_SEIZE) |
+ S(VGCS_EV_MSC_RELEASE) |
+ S(VGCS_EV_MSC_REJECT) |
+ S(VGCS_EV_CLEANUP),
+ .out_state_mask = S(VGCS_CALL_ST_BUSY) |
+ S(VGCS_CALL_ST_BLOCKED) |
+ S(VGCS_CALL_ST_NULL),
+ .action = vgcs_call_fsm_idle,
+ },
+ [VGCS_CALL_ST_BUSY] = {
+ .name = "BUSY",
+ .in_event_mask = S(VGCS_EV_TALKER_EST) |
+ S(VGCS_EV_TALKER_DATA) |
+ S(VGCS_EV_MSC_DTAP) |
+ S(VGCS_EV_TALKER_REL) |
+ S(VGCS_EV_MSC_SEIZE) |
+ S(VGCS_EV_MSC_ACK) |
+ S(VGCS_EV_MSC_REJECT) |
+ S(VGCS_EV_CLEANUP),
+ .out_state_mask = S(VGCS_CALL_ST_IDLE) |
+ S(VGCS_CALL_ST_BLOCKED) |
+ S(VGCS_CALL_ST_NULL),
+ .action = vgcs_call_fsm_busy,
+ },
+ [VGCS_CALL_ST_BLOCKED] = {
+ .name = "BLOCKED",
+ .in_event_mask = S(VGCS_EV_CALLING_ASSIGNED) |
+ S(VGCS_EV_TALKER_REL) |
+ S(VGCS_EV_MSC_RELEASE) |
+ S(VGCS_EV_CLEANUP),
+ .out_state_mask = S(VGCS_CALL_ST_IDLE) |
+ S(VGCS_CALL_ST_BUSY) |
+ S(VGCS_CALL_ST_NULL),
+ .action = vgcs_call_fsm_blocked,
+ },
+};
+
+static struct osmo_fsm vgcs_call_fsm = {
+ .name = "vgcs_call",
+ .states = vgcs_call_fsm_states,
+ .num_states = ARRAY_SIZE(vgcs_call_fsm_states),
+ .log_subsys = DASCI,
+ .event_names = vgcs_fsm_event_names,
+ .cleanup = vgcs_call_detach_and_destroy,
+};
+
+/* Handle VGCS/VBS SETUP message.
+ *
+ * See 3GPP TS 48.008 §3.2.1.50
+ */
+int vgcs_vbs_call_start(struct gsm_subscriber_connection *conn, struct msgb *msg)
+{
+ int payload_length = msg->tail - msg->l4h;
+ struct tlv_parsed tp;
+ struct gsm_subscriber_connection *c;
+ struct gsm0808_group_callref *gc = &conn->vgcs_call.gc_ie;
+ int rc;
+ uint8_t cause;
+
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, payload_length - 1) < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ cause = GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS;
+ goto reject;
+ }
+
+ /* Check for mandatory Group Call Reference. */
+ if (!TLVP_PRESENT(&tp, GSM0808_IE_GROUP_CALL_REFERENCE)) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Mandatory group call reference not present.\n");
+ cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
+ goto reject;
+ }
+
+ /* Decode Group Call Reference. */
+ rc = gsm0808_dec_group_callref(gc, TLVP_VAL(&tp, GSM0808_IE_GROUP_CALL_REFERENCE),
+ TLVP_LEN(&tp, GSM0808_IE_GROUP_CALL_REFERENCE));
+ if (rc < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Unable to decode group call reference.\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+ conn->vgcs_call.sf = gc->sf;
+ conn->vgcs_call.call_ref = (osmo_load32be_ext_2(gc->call_ref_hi, 3) << 3) | gc->call_ref_lo;
+
+ /* Check for duplicated callref. */
+ llist_for_each_entry(c, &conn->network->subscr_conns, entry) {
+ if (!c->vgcs_call.fi)
+ continue;
+ if (c == conn)
+ continue;
+ if (conn->vgcs_call.sf == c->vgcs_call.sf
+ && conn->vgcs_call.call_ref == c->vgcs_call.call_ref) {
+ LOG_CALL(conn, LOGL_ERROR, "A %s call with callref %s already exists.\n",
+ (conn->vgcs_call.sf == GSM0808_SF_VGCS) ? "VGCS" : "VBS",
+ gsm44068_group_id_string(conn->vgcs_call.call_ref));
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+ }
+
+ /* Decode VGCS Feature Flags */
+ if (TLVP_PRESENT(&tp, GSM0808_IE_VGCS_FEATURE_FLAGS)) {
+ rc = gsm0808_dec_vgcs_feature_flags(&conn->vgcs_call.ff,
+ TLVP_VAL(&tp, GSM0808_IE_VGCS_FEATURE_FLAGS),
+ TLVP_LEN(&tp, GSM0808_IE_VGCS_FEATURE_FLAGS));
+ if (rc < 0) {
+ LOG_CALL(conn, LOGL_ERROR, "Unable to decode feature flags.\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+ conn->vgcs_call.ff_present = true;
+ }
+
+ /* Create VGCS FSM. */
+ conn->vgcs_call.fi = osmo_fsm_inst_alloc(&vgcs_call_fsm, conn->network, conn, LOGL_DEBUG, NULL);
+ if (!conn->vgcs_call.fi) {
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+
+ /* Init list of cells that are used by the call. */
+ INIT_LLIST_HEAD(&conn->vgcs_call.chan_list);
+
+ /* Init L3 queue. */
+ INIT_LLIST_HEAD(&conn->vgcs_call.l3_queue);
+
+ osmo_fsm_inst_dispatch(conn->vgcs_call.fi, VGCS_EV_SETUP, NULL);
+ return 0;
+reject:
+ bsc_tx_setup_refuse(conn, cause);
+ return -EINVAL;
+}
+
+/*
+ * VGCS chan FSM
+ */
+
+static void vgcs_chan_detach_and_destroy(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ if (conn->vgcs_chan.fi->state != VGCS_CHAN_ST_WAIT_EST) {
+ /* Remove call from notification channel. */
+ if (conn->lchan)
+ rsl_notification_cmd(conn->lchan->ts->trx->bts, NULL, &conn->vgcs_chan.gc_ie, NULL);
+ else
+ LOG_CHAN(conn, LOGL_ERROR, "Unable to remove notification, lchan is already gone.\n");
+ }
+
+ /* Detach from call, if not already. */
+ if (conn->vgcs_chan.call) {
+ llist_del(&conn->vgcs_chan.list);
+ conn->vgcs_chan.call = NULL;
+ }
+
+ /* Remove pointer of FSM. */
+ conn->vgcs_chan.fi = NULL;
+}
+
+static void uplink_released(struct gsm_subscriber_connection *conn)
+{
+ LOG_CHAN(conn, LOGL_DEBUG, "Uplink is now released.\n");
+ /* Go into blocked or free state. */
+ if (conn->vgcs_chan.call && conn->vgcs_chan.call->vgcs_call.fi
+ && conn->vgcs_chan.call->vgcs_call.fi->state == VGCS_CALL_ST_IDLE)
+ osmo_fsm_inst_state_chg(conn->vgcs_chan.fi, VGCS_CHAN_ST_ACTIVE_FREE, 0, 0);
+ else
+ osmo_fsm_inst_state_chg(conn->vgcs_chan.fi, VGCS_CHAN_ST_ACTIVE_BLOCKED, 0, 0);
+}
+
+static void vgcs_chan_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct lchan_activate_info info;
+
+ switch (event) {
+ case VGCS_EV_ASSIGN_REQ:
+ LOG_CHAN(conn, LOGL_DEBUG, "MSC assigns channel.\n");
+ /* MSC requests channel assignment. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_WAIT_EST, 0, 0);
+ /* Requesting channel from BTS. */
+ info = (struct lchan_activate_info){
+ .activ_for = ACTIVATE_FOR_VGCS_CHANNEL,
+ .for_conn = conn,
+ .chreq_reason = GSM_CHREQ_REASON_OTHER,
+ .ch_mode_rate = conn->vgcs_chan.ch_mode_rate,
+ .ch_indctr = conn->vgcs_chan.ct.ch_indctr,
+ /* TSC is used from TS config. */
+ .encr = conn->vgcs_chan.new_lchan->encr,
+ /* Timing advance of 0 is used until channel is activated for uplink. */
+ .ta_known = true,
+ .ta = 0,
+ };
+ if (conn->vgcs_chan.call->vgcs_call.sf == GSM0808_SF_VGCS)
+ info.type_for = LCHAN_TYPE_FOR_VGCS;
+ else
+ info.type_for = LCHAN_TYPE_FOR_VBS;
+ /* Activate lchan. If an error occurs, this the function call may trigger VGCS_EV_LCHAN_ERROR event.
+ * This means that this must be the last action in this handler. */
+ lchan_activate(conn->vgcs_chan.new_lchan, &info);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void vgcs_chan_fsm_wait_est(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ const struct mgcp_conn_peer *mgw_info;
+
+ switch (event) {
+ case VGCS_EV_LCHAN_ACTIVE:
+ LOG_CHAN(conn, LOGL_DEBUG, "lchan is active.\n");
+ /* If no MGW is used. */
+ if (!gscon_is_aoip(conn)) {
+ LOG_CHAN(conn, LOGL_DEBUG, "Not connecting MGW endpoint, no AoIP connection.\n");
+ goto no_aoip;
+ }
+ /* Send activation to MGW. */
+ LOG_CHAN(conn, LOGL_DEBUG, "Connecting MGW endpoint to the MSC's RTP port: %s:%u\n",
+ conn->vgcs_chan.msc_rtp_addr, conn->vgcs_chan.msc_rtp_port);
+ /* Connect MGW. The function call may trigger VGCS_EV_MGW_OK event.
+ * This means that this must be the last action in this handler.
+ * If this function fails, VGCS_EV_MGW_FAIL will not trigger. */
+ if (!gscon_connect_mgw_to_msc(conn,
+ conn->vgcs_chan.new_lchan,
+ conn->vgcs_chan.msc_rtp_addr,
+ conn->vgcs_chan.msc_rtp_port,
+ fi,
+ VGCS_EV_MGW_OK,
+ VGCS_EV_MGW_FAIL,
+ NULL,
+ NULL)) {
+ /* Report failure to MSC. */
+ bsc_tx_vgcs_vbs_assignment_fail(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ break;
+ }
+ break;
+ case VGCS_EV_LCHAN_ERROR:
+ LOG_CHAN(conn, LOGL_DEBUG, "lchan failed.\n");
+ /* BTS reports failure on channel request. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_NULL, 0, 0);
+ /* Add/update SI10. */
+ if (conn->vgcs_chan.call)
+ si10_update(conn->vgcs_chan.call);
+ /* Report failure to MSC. */
+ bsc_tx_vgcs_vbs_assignment_fail(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ break;
+ case VGCS_EV_MGW_OK:
+ LOG_CHAN(conn, LOGL_DEBUG, "MGW endpoint connected.\n");
+ /* MGW reports success. */
+ mgw_info = osmo_mgcpc_ep_ci_get_rtp_info(conn->user_plane.mgw_endpoint_ci_msc);
+ if (!mgw_info) {
+ LOG_CHAN(conn, LOGL_ERROR, "Unable to retrieve RTP port info allocated by MGW for"
+ " the MSC side.");
+ /* Report failure to MSC. */
+ bsc_tx_vgcs_vbs_assignment_fail(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ break;
+ }
+ LOG_CHAN(conn, LOGL_DEBUG, "MGW's MSC side CI: %s:%u\n", mgw_info->addr, mgw_info->port);
+no_aoip:
+ /* Channel established from BTS. */
+ gscon_change_primary_lchan(conn, conn->vgcs_chan.new_lchan);
+ /* Change state according to call state. */
+ if (conn->vgcs_chan.call && conn->vgcs_chan.call->vgcs_call.fi
+ && conn->vgcs_chan.call->vgcs_call.fi->state == VGCS_CALL_ST_IDLE)
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_FREE, 0, 0);
+ else
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_BLOCKED, 0, 0);
+ if (conn->vgcs_chan.call) {
+ /* Add call to notification channel. */
+ rsl_notification_cmd(conn->lchan->ts->trx->bts, conn->lchan, &conn->vgcs_chan.gc_ie, NULL);
+ /* Add/update SI10. */
+ si10_update(conn->vgcs_chan.call);
+ }
+ /* Report result to MSC. */
+ bsc_tx_vgcs_vbs_assignment_result(conn, &conn->vgcs_chan.ct, &conn->vgcs_chan.ci,
+ conn->vgcs_chan.call_id);
+ break;
+ case VGCS_EV_MGW_FAIL:
+ LOG_CHAN(conn, LOGL_DEBUG, "MGW endpoint failed.\n");
+ /* MGW reports failure. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_NULL, 0, 0);
+ /* Add/update SI10. */
+ if (conn->vgcs_chan.call)
+ si10_update(conn->vgcs_chan.call);
+ /* Report failure to MSC. */
+ bsc_tx_vgcs_vbs_assignment_fail(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ break;
+ case VGCS_EV_CLEANUP:
+ LOG_CHAN(conn, LOGL_DEBUG, "SCCP connection clearing.\n");
+ /* MSC wants to terminate. */
+ osmo_fsm_inst_term(conn->vgcs_chan.fi, 0, NULL);
+ break;
+ case VGCS_EV_BLOCK:
+ case VGCS_EV_UNBLOCK:
+ /* Ignore, because channel is not yet ready. */
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void vgcs_chan_fsm_active_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv, *cc;
+
+ switch (event) {
+ case VGCS_EV_UNBLOCK:
+ LOG_CHAN(conn, LOGL_DEBUG, "Unblocking channel.\n");
+ /* No uplink is used in other cell. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_FREE, 0, 0);
+ break;
+ case VGCS_EV_TALKER_DET:
+ LOG_CHAN(conn, LOGL_DEBUG, "Talker detected on blocked channel.\n");
+ if (conn->vgcs_chan.call->vgcs_call.sf == GSM0808_SF_VBS)
+ LOG_CHAN(conn, LOGL_ERROR, "Talker detection not allowed on VBS channel.\n");
+ /* Race condition: BTS detected a talker. Waiting for talker to establish or fail. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_REL, 0, 0);
+ break;
+ case VGCS_EV_TALKER_EST:
+ cc = find_calling_subscr_conn(conn);
+ if (!cc) {
+ LOG_CHAN(conn, LOGL_ERROR, "No assignment requested from MSC!\n");
+ /* Uplink is used while blocked. Waiting for channel to be release. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_REL, 0, 0);
+ /* Send UPLINK RELEASE to MS. */
+ gsm48_send_uplink_release(conn->lchan, GSM48_RR_CAUSE_NORMAL);
+ /* Go into blocked or free state. */
+ uplink_released(conn);
+ break;
+ }
+ /* Talker is assigning to this channel. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_EST, 0, 0);
+ /* Report talker detection to call state machine. */
+ if (conn->vgcs_chan.call)
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.call->vgcs_call.fi, VGCS_EV_CALLING_ASSIGNED, conn);
+ /* Repeat notification for the MS that has been assigned. */
+ rsl_notification_cmd(conn->lchan->ts->trx->bts, conn->lchan, &conn->vgcs_chan.gc_ie, NULL);
+ break;
+ case VGCS_EV_CLEANUP:
+ LOG_CHAN(conn, LOGL_DEBUG, "SCCP connection clearing.\n");
+ /* MSC wants to terminate. */
+ osmo_fsm_inst_term(conn->vgcs_chan.fi, 0, NULL);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void vgcs_chan_fsm_enter_active_free(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ /* Send UPLINK FREE message to BTS. This hits on every state change (and or timer start). */
+ LOG_CHAN(conn, LOGL_DEBUG, "Sending UPLINK FREE message to channel.\n");
+ gsm48_send_uplink_free(conn->lchan, 0, NULL);
+}
+
+static void vgcs_chan_fsm_active_free(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ switch (event) {
+ case VGCS_EV_BLOCK:
+ LOG_CHAN(conn, LOGL_DEBUG, "Blocking channel.\n");
+ /* Uplink is used in other cell. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_BLOCKED, 0, 0);
+ /* Send UPLINK BUSY to MS. */
+ LOG_CHAN(conn, LOGL_DEBUG, "Sending UPLINK BUSY message to channel.\n");
+ gsm48_send_uplink_busy(conn->lchan);
+ break;
+ case VGCS_EV_TALKER_DET:
+ LOG_CHAN(conn, LOGL_DEBUG, "Talker detected on free channel.\n");
+ /* BTS detected a talker. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_INIT, 0, 0);
+ /* Report talker detection to call state machine. */
+ if (conn->vgcs_chan.call)
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.call->vgcs_call.fi, VGCS_EV_TALKER_DET, data);
+ break;
+ case VGCS_EV_CLEANUP:
+ LOG_CHAN(conn, LOGL_DEBUG, "SCCP connection clearing.\n");
+ /* MSC wants to terminate. */
+ osmo_fsm_inst_term(conn->vgcs_chan.fi, 0, NULL);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void vgcs_chan_fsm_active_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ uint8_t cause = (data) ? *(uint8_t *)data : 0;
+
+ switch (event) {
+ case VGCS_EV_BLOCK:
+ case VGCS_EV_REJECT:
+ LOG_CHAN(conn, LOGL_DEBUG, "Blocking/rejecting channel.\n");
+ /* Uplink is used in other cell. Waiting for channel to be established and then released. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_REL, 0, 0);
+ break;
+ case VGCS_EV_TALKER_EST:
+ LOG_CHAN(conn, LOGL_DEBUG, "Talker established uplink.\n");
+ /* Uplink has been established */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_EST, 0, 0);
+ /* Report talker establishment to call state machine. */
+ if (conn->vgcs_chan.call)
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.call->vgcs_call.fi, VGCS_EV_TALKER_EST, data);
+ break;
+ case VGCS_EV_TALKER_FAIL:
+ LOG_CHAN(conn, LOGL_NOTICE, "Uplink failed, establishment timeout.\n");
+ /* Release datalink */
+ rsl_release_request(conn->lchan, 0, RSL_REL_LOCAL_END);
+ /* fall thru */
+ case VGCS_EV_TALKER_REL:
+ LOG_CHAN(conn, LOGL_DEBUG, "Uplink is now released.\n");
+ /* Uplink establishment failed. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_FREE, 0, 0);
+ /* Report release indication to call state machine. */
+ if (conn->vgcs_chan.call)
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.call->vgcs_call.fi, VGCS_EV_TALKER_REL, &cause);
+ break;
+ case VGCS_EV_CLEANUP:
+ LOG_CHAN(conn, LOGL_DEBUG, "SCCP connection clearing.\n");
+ /* MSC wants to terminate. */
+ osmo_fsm_inst_term(conn->vgcs_chan.fi, 0, NULL);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void vgcs_chan_fsm_active_est(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ uint8_t cause = (data) ? *(uint8_t *)data : 0;
+ struct msgb *msg = data;
+
+ switch (event) {
+ case VGCS_EV_BLOCK:
+ case VGCS_EV_REJECT:
+ LOG_CHAN(conn, LOGL_DEBUG, "Blocking/rejecting channel.\n");
+ /* Uplink is used in other cell. Waiting for channel to be release. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_REL, 0, 0);
+ /* Send UPLINK RELEASE to MS. */
+ gsm48_send_uplink_release(conn->lchan, GSM48_RR_CAUSE_NORMAL);
+ /* Go into blocked or free state. */
+ uplink_released(conn);
+ break;
+ case VGCS_EV_TALKER_DATA:
+ LOG_CHAN(conn, LOGL_DEBUG, "Talker sends data on uplink.\n");
+ if (msg) {
+ struct gsm48_hdr *gh;
+ uint8_t pdisc;
+ uint8_t msg_type;
+ if (msgb_l3len(msg) < sizeof(*gh)) {
+ LOG_LCHAN(msg->lchan, LOGL_ERROR,
+ "Message too short for a GSM48 header (%u)\n", msgb_l3len(msg));
+ break;
+ }
+ gh = msgb_l3(msg);
+ pdisc = gsm48_hdr_pdisc(gh);
+ msg_type = gsm48_hdr_msg_type(gh);
+ if (pdisc == GSM48_PDISC_RR && msg_type == GSM48_MT_RR_UPLINK_RELEASE) {
+ LOG_CHAN(conn, LOGL_DEBUG, "Uplink is released by UPLINK RELEASE message.\n");
+ /* Release datalink */
+ rsl_release_request(conn->lchan, 0, RSL_REL_LOCAL_END);
+ /* Talker released the uplink. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_FREE, 0, 0);
+ /* Report talker release to call state machine. */
+ if (conn->vgcs_chan.call) {
+ cause = GSM0808_CAUSE_CALL_CONTROL;
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.call->vgcs_call.fi, VGCS_EV_TALKER_REL,
+ &cause);
+ }
+ break;
+ }
+ if (pdisc == GSM48_PDISC_RR && msg_type == GSM48_MT_RR_ASS_COMPL) {
+ LOG_CHAN(conn, LOGL_DEBUG, "Asssignment complete.\n");
+ struct gsm_subscriber_connection *cc;
+ cc = find_calling_subscr_conn(conn);
+ if (!cc) {
+ LOG_CHAN(conn, LOGL_ERROR, "No assignment requested from MSC!\n");
+ break;
+ }
+ LOG_CHAN(conn, LOGL_DEBUG, "Trigger State machine.\n");
+ osmo_fsm_inst_dispatch(cc->assignment.fi, ASSIGNMENT_EV_RR_ASSIGNMENT_COMPLETE, msg);
+ break;
+ }
+ }
+ /* Report talker data to call state machine. */
+ if (conn->vgcs_chan.call)
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.call->vgcs_call.fi, VGCS_EV_TALKER_DATA, data);
+ break;
+ case VGCS_EV_MSC_DTAP:
+ LOG_CHAN(conn, LOGL_DEBUG, "MSC sends DTAP message to talker.\n");
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MT_DTAP, data);
+ break;
+ case VGCS_EV_TALKER_FAIL:
+ LOG_CHAN(conn, LOGL_NOTICE, "Uplink failed after establishment.\n");
+ /* Release datalink */
+ rsl_release_request(conn->lchan, 0, RSL_REL_LOCAL_END);
+ /* fall thru */
+ case VGCS_EV_TALKER_REL:
+ LOG_CHAN(conn, LOGL_DEBUG, "Uplink is now released.\n");
+ /* Talker released the uplink. */
+ osmo_fsm_inst_state_chg(fi, VGCS_CHAN_ST_ACTIVE_FREE, 0, 0);
+ /* Report talker release to call state machine. */
+ if (conn->vgcs_chan.call)
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.call->vgcs_call.fi, VGCS_EV_TALKER_REL, &cause);
+ break;
+ case VGCS_EV_CLEANUP:
+ LOG_CHAN(conn, LOGL_DEBUG, "SCCP connection clearing.\n");
+ /* MSC wants to terminate. */
+ osmo_fsm_inst_term(conn->vgcs_chan.fi, 0, NULL);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void vgcs_chan_fsm_active_rel(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ switch (event) {
+ case VGCS_EV_BLOCK:
+ case VGCS_EV_REJECT:
+ LOG_CHAN(conn, LOGL_DEBUG, "Blocking/rejecting channel.\n");
+ /* Race condition: Uplink is used in other cell, we are already releasing. */
+ break;
+ case VGCS_EV_TALKER_EST:
+ LOG_CHAN(conn, LOGL_DEBUG, "Talker established uplink, releasing.\n");
+ /* Finally the talker established the connection. Send UPLINK RELEASE to MS. */
+ gsm48_send_uplink_release(conn->lchan, GSM48_RR_CAUSE_NORMAL);
+ /* Release datalink */
+ rsl_release_request(conn->lchan, 0, RSL_REL_LOCAL_END);
+ /* fall thru */
+ case VGCS_EV_TALKER_FAIL:
+ /* Release datalink */
+ rsl_release_request(conn->lchan, 0, RSL_REL_LOCAL_END);
+ /* fall thru */
+ case VGCS_EV_TALKER_REL:
+ /* Go into blocked or free state. */
+ uplink_released(conn);
+ break;
+ case VGCS_EV_CLEANUP:
+ LOG_CHAN(conn, LOGL_DEBUG, "SCCP connection clearing.\n");
+ /* MSC wants to terminate. */
+ osmo_fsm_inst_term(conn->vgcs_chan.fi, 0, NULL);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static const struct osmo_fsm_state vgcs_chan_fsm_states[] = {
+ [VGCS_CHAN_ST_NULL] = {
+ .name = "NULL",
+ .in_event_mask = S(VGCS_EV_ASSIGN_REQ),
+ .out_state_mask = S(VGCS_CHAN_ST_WAIT_EST),
+ .action = vgcs_chan_fsm_null,
+ },
+ [VGCS_CHAN_ST_WAIT_EST] = {
+ .name = "WAIT_EST",
+ .in_event_mask = S(VGCS_EV_LCHAN_ACTIVE) |
+ S(VGCS_EV_LCHAN_ERROR) |
+ S(VGCS_EV_MGW_OK) |
+ S(VGCS_EV_MGW_FAIL) |
+ S(VGCS_EV_CLEANUP) |
+ S(VGCS_EV_BLOCK) |
+ S(VGCS_EV_UNBLOCK),
+ .out_state_mask = S(VGCS_CHAN_ST_NULL) |
+ S(VGCS_CHAN_ST_ACTIVE_BLOCKED) |
+ S(VGCS_CHAN_ST_ACTIVE_FREE),
+ .action = vgcs_chan_fsm_wait_est,
+ },
+ [VGCS_CHAN_ST_ACTIVE_BLOCKED] = {
+ .name = "ACTIVE/BLOCKED",
+ .in_event_mask = S(VGCS_EV_UNBLOCK) |
+ S(VGCS_EV_TALKER_DET) |
+ S(VGCS_EV_TALKER_EST) |
+ S(VGCS_EV_CLEANUP),
+ .out_state_mask = S(VGCS_CHAN_ST_NULL) |
+ S(VGCS_CHAN_ST_ACTIVE_EST) |
+ S(VGCS_CHAN_ST_ACTIVE_FREE) |
+ S(VGCS_CHAN_ST_ACTIVE_REL),
+ .action = vgcs_chan_fsm_active_blocked,
+ },
+ [VGCS_CHAN_ST_ACTIVE_FREE] = {
+ .name = "ACTIVE/FREE",
+ .in_event_mask = S(VGCS_EV_BLOCK) |
+ S(VGCS_EV_TALKER_DET) |
+ S(VGCS_EV_CLEANUP),
+ .out_state_mask = S(VGCS_CHAN_ST_NULL) |
+ S(VGCS_CHAN_ST_ACTIVE_BLOCKED) |
+ S(VGCS_CHAN_ST_ACTIVE_INIT) |
+ S(VGCS_CHAN_ST_ACTIVE_FREE),
+ .action = vgcs_chan_fsm_active_free,
+ .onenter = vgcs_chan_fsm_enter_active_free,
+ },
+ [VGCS_CHAN_ST_ACTIVE_INIT] = {
+ .name = "ACTIVE/INIT",
+ .in_event_mask = S(VGCS_EV_BLOCK) |
+ S(VGCS_EV_REJECT) |
+ S(VGCS_EV_TALKER_EST) |
+ S(VGCS_EV_TALKER_FAIL) |
+ S(VGCS_EV_CLEANUP),
+ .out_state_mask = S(VGCS_CHAN_ST_NULL) |
+ S(VGCS_CHAN_ST_ACTIVE_EST) |
+ S(VGCS_CHAN_ST_ACTIVE_REL) |
+ S(VGCS_CHAN_ST_ACTIVE_FREE),
+ .action = vgcs_chan_fsm_active_init,
+ },
+ [VGCS_CHAN_ST_ACTIVE_EST] = {
+ .name = "ACTIVE/ESTABLISHED",
+ .in_event_mask = S(VGCS_EV_BLOCK) |
+ S(VGCS_EV_REJECT) |
+ S(VGCS_EV_TALKER_DATA) |
+ S(VGCS_EV_MSC_DTAP) |
+ S(VGCS_EV_TALKER_REL) |
+ S(VGCS_EV_TALKER_FAIL) |
+ S(VGCS_EV_CLEANUP),
+ .out_state_mask = S(VGCS_CHAN_ST_NULL) |
+ S(VGCS_CHAN_ST_ACTIVE_FREE) |
+ S(VGCS_CHAN_ST_ACTIVE_REL),
+ .action = vgcs_chan_fsm_active_est,
+ },
+ [VGCS_CHAN_ST_ACTIVE_REL] = {
+ .name = "ACTIVE/RELEASE",
+ .in_event_mask = S(VGCS_EV_BLOCK) |
+ S(VGCS_EV_REJECT) |
+ S(VGCS_EV_TALKER_EST) |
+ S(VGCS_EV_TALKER_REL) |
+ S(VGCS_EV_TALKER_FAIL) |
+ S(VGCS_EV_CLEANUP),
+ .out_state_mask = S(VGCS_CHAN_ST_NULL) |
+ S(VGCS_CHAN_ST_ACTIVE_BLOCKED) |
+ S(VGCS_CHAN_ST_ACTIVE_FREE),
+ .action = vgcs_chan_fsm_active_rel,
+ },
+};
+
+static struct osmo_fsm vgcs_chan_fsm = {
+ .name = "vgcs_chan",
+ .states = vgcs_chan_fsm_states,
+ .num_states = ARRAY_SIZE(vgcs_chan_fsm_states),
+ .log_subsys = DASCI,
+ .event_names = vgcs_fsm_event_names,
+ .cleanup = vgcs_chan_detach_and_destroy,
+};
+
+/* Handle VGCS/VBS ASSIGNMENT REQUEST message.
+ *
+ * See 3GPP TS 48.008 §3.2.1.53
+ */
+int vgcs_vbs_chan_start(struct gsm_subscriber_connection *conn, struct msgb *msg)
+{
+ int payload_length = msg->tail - msg->l4h;
+ struct tlv_parsed tp;
+ struct gsm_subscriber_connection *c;
+ struct gsm0808_group_callref *gc = &conn->vgcs_chan.gc_ie;
+ struct assignment_request req = {
+ .aoip = gscon_is_aoip(conn),
+ };
+ uint8_t cause;
+ struct gsm_bts *bts;
+ struct gsm_lchan *lchan = NULL;
+ int rc;
+ int i;
+
+ if (osmo_bssap_tlv_parse(&tp, msg->l4h + 1, payload_length - 1) < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ cause = GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS;
+ goto reject;
+ }
+
+ /* Check for mandatory IEs. */
+ if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)
+ || !TLVP_PRESENT(&tp, GSM0808_IE_ASSIGNMENT_REQUIREMENT)
+ || !TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)
+ || !TLVP_PRESENT(&tp, GSM0808_IE_GROUP_CALL_REFERENCE)) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Mandatory IE not present.\n");
+ cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
+ goto reject;
+ }
+
+ /* Decode Channel Type element. */
+ rc = gsm0808_dec_channel_type(&conn->vgcs_chan.ct, TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE),
+ TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE));
+ if (rc < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Unable to decode Channel Type.\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+
+ /* Only speech is supported. */
+ if (conn->vgcs_chan.ct.ch_indctr != GSM0808_CHAN_SPEECH) {
+ cause = GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS;
+ goto reject;
+ }
+
+ /* Decode Assignment Requirement element. */
+ rc = gsm0808_dec_assign_req(&conn->vgcs_chan.ar, TLVP_VAL(&tp, GSM0808_IE_ASSIGNMENT_REQUIREMENT),
+ TLVP_LEN(&tp, GSM0808_IE_ASSIGNMENT_REQUIREMENT));
+ if (rc < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Unable to decode Assignment Requirement.\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+
+ /* Decode Cell Identifier element. */
+ rc = gsm0808_dec_cell_id(&conn->vgcs_chan.ci, TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER),
+ TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER));
+ if (rc < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Unable to decode Cell Identifier.\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+ gsm0808_cell_id_u_name(conn->vgcs_chan.ci_str, sizeof(conn->vgcs_chan.ci_str), conn->vgcs_chan.ci.id_discr,
+ &conn->vgcs_chan.ci.id);
+
+ /* Decode Group Call Reference element. */
+ rc = gsm0808_dec_group_callref(gc, TLVP_VAL(&tp, GSM0808_IE_GROUP_CALL_REFERENCE),
+ TLVP_LEN(&tp, GSM0808_IE_GROUP_CALL_REFERENCE));
+ if (rc < 0) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Unable to decode Group Call Reference.\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+ conn->vgcs_chan.sf = gc->sf;
+ conn->vgcs_chan.call_ref = (osmo_load32be_ext_2(gc->call_ref_hi, 3) << 3) | gc->call_ref_lo;
+
+ /* Find BTS from Cell Identity. */
+ bts = gsm_bts_by_cell_id(conn->network, &conn->vgcs_chan.ci, 0);
+ if (!bts) {
+ LOG_CHAN(conn, LOGL_ERROR, "No cell found that matches the given Cell Identifier.\n");
+ cause = GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE;
+ goto reject;
+ }
+
+ /* If Cell Identity is ambiguous. */
+ if (gsm_bts_by_cell_id(conn->network, &conn->vgcs_chan.ci, 1))
+ LOG_CHAN(conn, LOGL_NOTICE, "More thant one cell found that match the given Cell Identifier.\n");
+
+ /* Decode channel related elements.
+ * This must be done after selecting the BTS, because codec selection requires relation to BTS. */
+ rc = bssmap_handle_ass_req_ct_speech(conn, bts, &tp, &conn->vgcs_chan.ct, &req, &cause);
+ if (rc < 0)
+ goto reject;
+
+ /* Store AoIP elements. */
+ osmo_strlcpy(conn->vgcs_chan.msc_rtp_addr, req.msc_rtp_addr, sizeof(conn->vgcs_chan.msc_rtp_addr));
+ conn->vgcs_chan.msc_rtp_port = req.msc_rtp_port;
+ if (TLVP_PRESENT(&tp, GSM0808_IE_CALL_ID)) {
+ /* Decode Call Identifier element. */
+ rc = gsm0808_dec_call_id(&conn->vgcs_chan.call_id, TLVP_VAL(&tp, GSM0808_IE_CALL_ID),
+ TLVP_LEN(&tp, GSM0808_IE_CALL_ID));
+ if (rc < 0) {
+ LOG_CHAN(conn, LOGL_ERROR, "Unable to decode Call Identifier.\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+ }
+
+ /* Try to allocate a new lchan in order of preference. */
+ for (i = 0; i < req.n_ch_mode_rate; i++) {
+ 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_VGCS, NULL);
+ if (!lchan)
+ continue;
+ LOG_CHAN(conn, LOGL_DEBUG, "Selected new lchan %s for mode[%d] = %s channel_rate=%d\n",
+ gsm_lchan_name(lchan), i, gsm48_chan_mode_name(req.ch_mode_rate_list[i].chan_mode),
+ req.ch_mode_rate_list[i].chan_rate);
+
+ conn->vgcs_chan.ch_mode_rate = req.ch_mode_rate_list[i];
+ break;
+ }
+ if (!lchan) {
+ LOG_CHAN(conn, LOGL_ERROR, "Requested lchan not available.\n");
+ cause = GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE;
+ goto reject;
+ }
+ conn->vgcs_chan.new_lchan = lchan;
+
+ /* Create VGCS FSM. */
+ conn->vgcs_chan.fi = osmo_fsm_inst_alloc(&vgcs_chan_fsm, conn->network, conn, LOGL_DEBUG, NULL);
+ if (!conn->vgcs_chan.fi)
+ goto reject;
+
+ /* Attach to call control instance, if a call with same callref exists. */
+ llist_for_each_entry(c, &conn->network->subscr_conns, entry) {
+ if (!c->vgcs_call.fi)
+ continue;
+ if (c->vgcs_call.sf == conn->vgcs_chan.sf
+ && c->vgcs_call.call_ref == conn->vgcs_chan.call_ref) {
+ llist_add_tail(&conn->vgcs_chan.list, &c->vgcs_call.chan_list);
+ conn->vgcs_chan.call = c;
+ break;
+ }
+ }
+ if (!conn->vgcs_chan.call) {
+ LOG_CHAN(conn, LOGL_ERROR, "A %s call with callref %s does not exist.\n",
+ (conn->vgcs_chan.sf == GSM0808_SF_VGCS) ? "VGCS" : "VBS",
+ gsm44068_group_id_string(conn->vgcs_chan.call_ref));
+ cause = GSM0808_CAUSE_VGCS_VBS_CALL_NON_EXISTENT;
+ osmo_fsm_inst_term(conn->vgcs_chan.fi, 0, NULL);
+ goto reject;
+ }
+
+ osmo_fsm_inst_dispatch(conn->vgcs_chan.fi, VGCS_EV_ASSIGN_REQ, NULL);
+ return 0;
+reject:
+ bsc_tx_vgcs_vbs_assignment_fail(conn, cause);
+ return -EINVAL;
+}
+
+/* Return lchan of group call that exists in the same BTS. */
+struct gsm_lchan *vgcs_vbs_find_lchan(struct gsm_bts *bts, struct gsm0808_group_callref *gc)
+{
+ struct gsm_subscriber_connection *call = NULL, *c;
+ struct gsm_lchan *lchan = NULL;
+ uint32_t call_ref = (osmo_load32be_ext_2(gc->call_ref_hi, 3) << 3) | gc->call_ref_lo;
+
+ /* Find group call. */
+ llist_for_each_entry(c, &bts->network->subscr_conns, entry) {
+ if (!c->vgcs_call.fi)
+ continue;
+ if (c->vgcs_call.sf == gc->sf
+ && c->vgcs_call.call_ref == call_ref) {
+ call = c;
+ break;
+ }
+ }
+ if (!call) {
+ LOGP(DASCI, LOGL_ERROR, "Cannot assign to channel, %s channel with callref %s does not exist.\n",
+ (gc->sf == GSM0808_SF_VGCS) ? "VGCS" : "VBS", gsm44068_group_id_string(call_ref));
+ return NULL;
+ }
+
+ /* Find channel in same BTS. */
+ llist_for_each_entry(c, &call->vgcs_call.chan_list, vgcs_chan.list) {
+ if (c->lchan && c->lchan->ts->trx->bts == bts)
+ lchan = c->lchan;
+ }
+ if (!call) {
+ LOGP(DASCI, LOGL_ERROR, "Cannot assign to channel, caller's BTS has no %s channel with callref %s.\n",
+ (gc->sf == GSM0808_SF_VGCS) ? "VGCS" : "VBS", gsm44068_group_id_string(call_ref));
+ return NULL;
+ }
+
+ return lchan;
+}
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
index e585e0df8..ece993faa 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)
@@ -26,17 +28,17 @@ bin_PROGRAMS = \
isdnsync \
meas_json \
$(NULL)
-if HAVE_SQLITE3
+if BUILD_MEAS_UDP2DB
bin_PROGRAMS += \
osmo-meas-udp2db \
$(NULL)
-if HAVE_PCAP
+endif
+if BUILD_MEAS_PCAP2DB
bin_PROGRAMS += \
osmo-meas-pcap2db \
$(NULL)
endif
-endif
-if HAVE_LIBCDK
+if BUILD_MEAS_VIS
bin_PROGRAMS += \
meas_vis \
$(NULL)
@@ -47,11 +49,7 @@ bs11_config_SOURCES = \
$(NULL)
bs11_config_LDADD = \
- $(top_builddir)/src/osmo-bsc/abis_nm.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) \
@@ -117,7 +115,7 @@ meas_json_SOURCES = \
$(NULL)
meas_json_LDADD = \
- $(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 11562336c..e79507642 100644
--- a/src/utils/bs11_config.c
+++ b/src/utils/bs11_config.c
@@ -39,6 +39,7 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/core/select.h>
#include <osmocom/bsc/rs232.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/abis/abis.h>
@@ -973,19 +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; }
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 6aa531a7f..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);
}
@@ -158,7 +158,7 @@ static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
int rc;
- if (what & BSC_FD_READ) {
+ if (what & OSMO_FD_READ) {
struct msgb *msg = msgb_alloc(1024, "UDP Rx");
rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg));
@@ -201,7 +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; }
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 34f8385e8..0c97d8bf6 100644
--- a/src/utils/meas_udp2db.c
+++ b/src/utils/meas_udp2db.c
@@ -1,4 +1,4 @@
-/* liesten to meas_feed on UDP and write it to sqlite3 database */
+/* listen to meas_feed on UDP and write it to sqlite3 database */
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
*
@@ -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;
}
@@ -71,7 +64,7 @@ static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
int rc;
- if (what & BSC_FD_READ) {
+ if (what & OSMO_FD_READ) {
struct msgb *msg = msgb_alloc(1024, "UDP Rx");
rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg));
diff --git a/src/utils/meas_vis.c b/src/utils/meas_vis.c
index 01be98611..c3ee2a5e2 100644
--- a/src/utils/meas_vis.c
+++ b/src/utils/meas_vis.c
@@ -125,7 +125,7 @@ static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
int rc;
- if (what & BSC_FD_READ) {
+ if (what & OSMO_FD_READ) {
struct msgb *msg = msgb_alloc(1024, "UDP Rx");
rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg));
@@ -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 9f2e0e0f0..aac237ae5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,11 +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.
@@ -32,7 +34,10 @@ EXTRA_DIST = \
$(TESTSUITE) \
vty_test_runner.py \
ctrl_test_runner.py \
- handover_cfg.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
@@ -42,15 +47,14 @@ DISTCLEANFILES = \
$(NULL)
if ENABLE_EXT_TESTS
-python-tests: $(BUILT_SOURCES)
+python-tests: $(top_builddir)/src/osmo-bsc/osmo-bsc
$(MAKE) vty-test
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
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)
+python-tests:
echo "Not running python-based tests (determined at configure-time)"
endif
@@ -60,7 +64,7 @@ VTY_TEST ?= *.vty
# To update the VTY script from current application behavior,
# pass -u to vty_script_runner.py by doing:
# make vty-test U=-u
-vty-test:
+vty-test: $(top_builddir)/src/osmo-bsc/osmo-bsc
osmo_verify_transcript_vty.py -v \
-n OsmoBSC -p 4242 \
-r "$(top_builddir)/src/osmo-bsc/osmo-bsc -c $(top_srcdir)/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg" \
diff --git a/tests/abis/Makefile.am b/tests/abis/Makefile.am
index 60054d94d..6f001914b 100644
--- a/tests/abis/Makefile.am
+++ b/tests/abis/Makefile.am
@@ -7,16 +7,20 @@ AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
+AM_LDFLAGS = -no-install
+
EXTRA_DIST = \
abis_test.ok \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
abis_test \
$(NULL)
@@ -25,9 +29,7 @@ abis_test_SOURCES = \
$(NULL)
abis_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/abis_nm.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 43934f364..10642fa09 100644
--- a/tests/abis/abis_test.c
+++ b/tests/abis/abis_test.c
@@ -26,6 +26,7 @@
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/abis_nm.h>
#include <osmocom/bsc/debug.h>
@@ -135,7 +136,7 @@ static const struct test_abis_nm_ipaccess_cgi test_abis_nm_ipaccess_cgi_data[] =
},
};
-static void test_abis_nm_ipaccess_cgi()
+static void test_abis_nm_ipaccess_cgi(void)
{
int i;
bool pass = true;
@@ -182,11 +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; }
diff --git a/tests/acc/Makefile.am b/tests/acc/Makefile.am
new file mode 100644
index 000000000..4199d4e5b
--- /dev/null
+++ b/tests/acc/Makefile.am
@@ -0,0 +1,36 @@
+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)
+
+AM_LDFLAGS = -no-install
+
+EXTRA_DIST = \
+ acc_test.ok \
+ $(NULL)
+
+check_PROGRAMS = \
+ acc_test \
+ $(NULL)
+
+acc_test_SOURCES = \
+ acc_test.c \
+ $(NULL)
+
+acc_test_LDADD = \
+ $(top_builddir)/src/osmo-bsc/libbsc.la \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOABIS_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(NULL)
diff --git a/tests/acc/acc_test.c b/tests/acc/acc_test.c
new file mode 100644
index 000000000..887e89104
--- /dev/null
+++ b/tests/acc/acc_test.c
@@ -0,0 +1,554 @@
+/*
+ * (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 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/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+#include <osmocom/gsm/gsm23003.h>
+
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/debug.h>
+
+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);
+}
+
+#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_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;
+
+ return bts;
+}
+
+#define bts_del(bts) _bts_del(bts, __func__)
+static inline void _bts_del(struct gsm_bts *bts, const char *msg)
+{
+ 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->site_mgr);
+ fprintf(stderr, "BTS deallocated OK in %s()\n", msg);
+}
+
+static void do_allowed_len_adm_loop(struct acc_mgr *acc_mgr, uint8_t jump)
+{
+ int i;
+ fprintf(stderr, "%s(%" PRIu8 ")\n", __func__, jump);
+ /* Test decreasing the administrative (VTY) max subset size */
+ for (i = 10; i >= 0; i -= jump) {
+ acc_mgr_set_len_allowed_adm(acc_mgr, i);
+ }
+ if (i != 0)
+ acc_mgr_set_len_allowed_adm(acc_mgr, 0);
+ /* Test increasing the administrative (VTY) max subset size */
+ for (i = 0; i <= 10; i += jump) {
+ acc_mgr_set_len_allowed_adm(acc_mgr, i);
+ }
+ if (i != 10)
+ acc_mgr_set_len_allowed_adm(acc_mgr, 10);
+}
+
+static void do_allowed_len_ramp_loop(struct acc_mgr *acc_mgr, uint8_t jump)
+{
+ int i;
+ fprintf(stderr, "%s(%" PRIu8 ")\n", __func__, jump);
+ /* Test decreasing the administrative (VTY) max subset size */
+ for (i = 10; i >= 0; i -= jump) {
+ acc_mgr_set_len_allowed_ramp(acc_mgr, i);
+ }
+ if (i != 0)
+ acc_mgr_set_len_allowed_ramp(acc_mgr, 0);
+ /* Test increasing the administrative (VTY) max subset size */
+ for (i = 0; i <= 10; i += jump) {
+ acc_mgr_set_len_allowed_ramp(acc_mgr, i);
+ }
+ if (i != 10)
+ acc_mgr_set_len_allowed_ramp(acc_mgr, 10);
+}
+
+static void test_acc_mgr_no_ramp(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_mgr_get_len_allowed_adm(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr_get_len_allowed_ramp(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr->rotation_time_sec == ACC_MGR_QUANTUM_DEFAULT);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask == 0x3ff);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask_count == 10);
+ OSMO_ASSERT(acc_mgr->allowed_permanent_count == 10);
+
+
+ do_allowed_len_adm_loop(acc_mgr, 1);
+ do_allowed_len_adm_loop(acc_mgr, 4);
+
+ /* Now permantenly barr some ACC */
+ fprintf(stderr, "*** Barring some ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x02;
+ bts->si_common.rach_control.t3 |= 0xa5;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ do_allowed_len_adm_loop(acc_mgr, 1);
+ do_allowed_len_adm_loop(acc_mgr, 4);
+
+ fprintf(stderr, "*** Barring ALL ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x03;
+ bts->si_common.rach_control.t3 |= 0xff;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ fprintf(stderr, "*** Barring zero ACCs ***\n");
+ bts->si_common.rach_control.t2 = 0xfc;
+ bts->si_common.rach_control.t3 = 0x00;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ bts_del(bts);
+}
+
+static void test_acc_mgr_manual_ramp(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_mgr_get_len_allowed_adm(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr_get_len_allowed_ramp(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr->rotation_time_sec == ACC_MGR_QUANTUM_DEFAULT);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask == 0x3ff);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask_count == 10);
+ OSMO_ASSERT(acc_mgr->allowed_permanent_count == 10);
+
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ /* Now permantenly barr some ACC */
+ fprintf(stderr, "*** Barring some ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x01;
+ bts->si_common.rach_control.t3 |= 0xb3;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ fprintf(stderr, "*** Barring ALL ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x03;
+ bts->si_common.rach_control.t3 |= 0xff;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ fprintf(stderr, "*** Barring zero ACCs ***\n");
+ bts->si_common.rach_control.t2 = 0xfc;
+ bts->si_common.rach_control.t3 = 0x00;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ fprintf(stderr, "*** Barring some ACCs + adm len 4 ***\n");
+ acc_mgr_set_len_allowed_adm(acc_mgr, 4);
+ bts->si_common.rach_control.t2 = 0xfd;
+ bts->si_common.rach_control.t3 = 0xb3;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ bts_del(bts);
+}
+
+static void test_acc_mgr_rotate(struct gsm_network *net, bool barr_some, unsigned int set_len)
+{
+ fprintf(stderr, "===%s(%s, %u)===\n", __func__, barr_some ? "true" : "false", set_len);
+ int i;
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_mgr_get_len_allowed_adm(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr_get_len_allowed_ramp(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr->rotation_time_sec == ACC_MGR_QUANTUM_DEFAULT);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask == 0x3ff);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask_count == 10);
+ OSMO_ASSERT(acc_mgr->allowed_permanent_count == 10);
+
+ /* Test that rotation won't go over permanently barred ACC*/
+ if (barr_some) {
+ fprintf(stderr, "*** Barring one ACC ***\n");
+ bts->si_common.rach_control.t2 |= 0x02;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+ }
+
+
+ acc_mgr_set_rotation_time(acc_mgr, 2);
+ acc_mgr_set_len_allowed_adm(acc_mgr, set_len);
+
+ for (i = 0; i < 20; i++) {
+ osmo_gettimeofday_override_time.tv_sec += 2;
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_mgr_rotate_all(struct gsm_network *net)
+{
+ int i;
+ for (i = 1; i <= 8; i++) {
+ test_acc_mgr_rotate(net, true, i);
+ test_acc_mgr_rotate(net, false, i);
+ }
+ test_acc_mgr_rotate(net, false, 9);
+}
+
+static void test_acc_ramp(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+
+ /* Set super high rotation time so it doesn't interfer here: */
+ acc_mgr_set_rotation_time(acc_mgr, 5000);
+
+ OSMO_ASSERT(acc_ramp_set_step_interval(acc_ramp, 1) == -ERANGE);
+ OSMO_ASSERT(acc_ramp_set_step_interval(acc_ramp, 50) == 0);
+ OSMO_ASSERT(acc_ramp_set_chan_load_thresholds(acc_ramp, 100, 100) == 0);
+ acc_ramp_set_step_size(acc_ramp, 1);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ for (i = 0; i < 9; i++) {
+ osmo_gettimeofday_override_time.tv_sec += 50;
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_ramp2(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+
+ /* Set super high rotation time so it doesn't interfer here: */
+ acc_mgr_set_rotation_time(acc_mgr, 5000);
+ /* Set adm len to test that ramping won't go over it */
+ acc_mgr_set_len_allowed_adm(acc_mgr, 7);
+
+ acc_ramp_set_step_size(acc_ramp, 3);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ for (i = 0; i < 3; i++) {
+ osmo_gettimeofday_override_time.tv_sec += ACC_RAMP_STEP_INTERVAL_MIN;
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_ramp3(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+
+ /* Set super high rotation time so it doesn't interfer here: */
+ acc_mgr_set_rotation_time(acc_mgr, 5000);
+ /* Test that ramping won't go over permanently barred ACC*/
+ fprintf(stderr, "*** Barring some ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x02;
+ bts->si_common.rach_control.t3 |= 0xa5;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ acc_ramp_set_step_size(acc_ramp, 1);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ for (i = 0; i < 9; i++) {
+ osmo_gettimeofday_override_time.tv_sec += ACC_RAMP_STEP_INTERVAL_MIN;
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_ramp_up_rotate(struct gsm_network *net, unsigned int chan_load, unsigned int low_threshold, unsigned int up_threshold)
+{
+ fprintf(stderr, "===%s(%u, %u, %u)===\n",
+ __func__, chan_load, low_threshold, up_threshold);
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+ int n;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+
+ OSMO_ASSERT(acc_ramp_set_step_interval(acc_ramp, 250) == 0);
+ acc_mgr_set_rotation_time(acc_mgr, 100);
+ /* Test that ramping + rotation won't go over permanently barred ACC*/
+ fprintf(stderr, "*** Barring one ACC ***\n");
+ bts->si_common.rach_control.t2 |= 0x02;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ OSMO_ASSERT(acc_ramp_set_step_size(acc_ramp, 1) == 0);
+ OSMO_ASSERT(acc_ramp_set_chan_load_thresholds(acc_ramp, low_threshold, up_threshold) == 0);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ bts->chan_load_avg = chan_load; /*set % channel load */
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ n = 5;
+ while (true) {
+ OSMO_ASSERT(osmo_timer_pending(&acc_ramp->step_timer));
+ if (osmo_timer_pending(&acc_mgr->rotate_timer)) {
+ if ((osmo_gettimeofday_override_time.tv_sec + 50) % 250 == 0)
+ osmo_gettimeofday_override_time.tv_sec += 50;
+ else
+ osmo_gettimeofday_override_time.tv_sec += 100;
+ } else {
+ /* Once ramping is done, adm level is big enough and hence
+ * rotation is not needed and will be disabled. Run
+ * ramping a bit more and we are then done */
+ osmo_gettimeofday_override_time.tv_sec -= osmo_gettimeofday_override_time.tv_sec % 250;
+ osmo_gettimeofday_override_time.tv_sec += 250;
+ if (n-- == 0)
+ break;
+ }
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_ramp_updown_rotate(struct gsm_network *net, unsigned int low_threshold, unsigned int up_threshold,
+ unsigned int min_load, unsigned int max_load, unsigned load_step)
+{
+ fprintf(stderr, "===%s(%u, %u, %u, %u, %u)===\n",
+ __func__, low_threshold, up_threshold,
+ min_load, max_load, load_step);
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+ int i;
+ char buf[256];
+ bool up = true;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+
+ OSMO_ASSERT(acc_ramp_set_step_interval(acc_ramp, 250) == 0);
+ acc_mgr_set_rotation_time(acc_mgr, 100);
+ /* Test that ramping + rotation won't go over permanently barred ACC*/
+ fprintf(stderr, "*** Barring one ACC ***\n");
+ bts->si_common.rach_control.t2 |= 0x02;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ OSMO_ASSERT(acc_ramp_set_step_size(acc_ramp, 1) == 0);
+ OSMO_ASSERT(acc_ramp_set_chan_load_thresholds(acc_ramp, low_threshold, up_threshold) == 0);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ bts->chan_load_avg = min_load; /* set % channel load */
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ /* 50 ev loop iterations */
+ for (i = 0; i < 50; i++) {
+ OSMO_ASSERT(osmo_timer_pending(&acc_ramp->step_timer));
+ if (osmo_timer_pending(&acc_mgr->rotate_timer)) {
+ if ((osmo_gettimeofday_override_time.tv_sec + 50) % 250 == 0)
+ osmo_gettimeofday_override_time.tv_sec += 50;
+ else
+ osmo_gettimeofday_override_time.tv_sec += 100;
+ } else {
+ /* Once ramping is done, adm level is big enough and hence
+ * rotation is not needed and will be disabled. */
+ osmo_gettimeofday_override_time.tv_sec -= osmo_gettimeofday_override_time.tv_sec % 250;
+ osmo_gettimeofday_override_time.tv_sec += 250;
+ }
+ snprintf(buf, sizeof(buf), "select(%d): chan_load_avg=%" PRIu8, i, bts->chan_load_avg);
+ clock_debug(buf);
+ osmo_select_main(0);
+
+ if (up) {
+ bts->chan_load_avg += load_step;
+ if (bts->chan_load_avg >= max_load)
+ up = false;
+ if (bts->chan_load_avg > max_load)
+ bts->chan_load_avg = max_load;
+ } else {
+ bts->chan_load_avg = (uint8_t)OSMO_MAX((int)(bts->chan_load_avg - load_step), 0);
+ if (bts->chan_load_avg <= min_load)
+ up = true;
+ if (bts->chan_load_avg < min_load)
+ bts->chan_load_avg = max_load;
+ }
+ }
+
+ bts_del(bts);
+}
+
+static const struct log_info_cat log_categories[] = {
+ [DRSL] = {
+ .name = "DRSL",
+ .description = "A-bis Radio Signalling Link (RSL)",
+ .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_gettimeofday_override_time = (struct timeval) {0, 0};
+
+ 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, 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) {
+ fprintf(stderr, "Network init failure.\n");
+ return EXIT_FAILURE;
+ }
+
+ test_acc_mgr_no_ramp(net);
+ test_acc_mgr_manual_ramp(net);
+ test_acc_mgr_rotate_all(net);
+ test_acc_ramp(net);
+ test_acc_ramp2(net);
+ test_acc_ramp3(net);
+ test_acc_ramp_up_rotate(net, 0, 100, 100);
+ test_acc_ramp_up_rotate(net, 0, 20, 50);
+ test_acc_ramp_up_rotate(net, 70, 80, 90);
+ test_acc_ramp_updown_rotate(net, 80, 90, 0, 100, 15);
+ test_acc_ramp_updown_rotate(net, 30, 50, 10, 100, 15);
+ test_acc_ramp_updown_rotate(net, 50, 49, 0, 100, 10);
+ test_acc_ramp_updown_rotate(net, 30, 80, 30, 80, 5);
+
+ return EXIT_SUCCESS;
+}
+
+/* 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};
+
+ acc_mgr_apply_acc(&bts->acc_mgr, &rach_control);
+ fprintf(stderr, "%s(): t2=0x%02" PRIx8 " t3=0x%02" PRIx8 ", allowed:%s%s%s%s%s%s%s%s%s%s\n",
+ __func__, rach_control.t2, rach_control.t3,
+ rach_control.t3 & (1 << 0) ? "" : " 0",
+ rach_control.t3 & (1 << 1) ? "" : " 1",
+ rach_control.t3 & (1 << 2) ? "" : " 2",
+ rach_control.t3 & (1 << 3) ? "" : " 3",
+ rach_control.t3 & (1 << 4) ? "" : " 4",
+ rach_control.t3 & (1 << 5) ? "" : " 5",
+ rach_control.t3 & (1 << 6) ? "" : " 6",
+ rach_control.t3 & (1 << 7) ? "" : " 7",
+ rach_control.t2 & (1 << 0) ? "" : " 8",
+ rach_control.t2 & (1 << 1) ? "" : " 9"
+ );
+}
+
+/* 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; }
+
+/* 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/acc/acc_test.ok b/tests/acc/acc_test.ok
new file mode 100644
index 000000000..866502e81
--- /dev/null
+++ b/tests/acc/acc_test.ok
@@ -0,0 +1,2318 @@
+===test_acc_mgr_no_ramp===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_no_ramp()
+do_allowed_len_adm_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3fe (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x01, allowed: 1 2 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3fe -> 0x3fc (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x03, allowed: 2 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3fc -> 0x3f8 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07, allowed: 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f8 -> 0x3f0 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f, allowed: 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x3e0 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1f, allowed: 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3e0 -> 0x3c0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f, allowed: 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3c0 -> 0x380 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7f, allowed: 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x380 -> 0x300 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff, allowed: 8 9
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x200 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff, allowed: 9
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=10, adm_len=0, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+(bts=0) ACC: update ACC allowed active subset 0x001 -> 0x003 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+(bts=0) ACC: update ACC allowed active subset 0x003 -> 0x007 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8, allowed: 0 1 2
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x01f (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0, allowed: 0 1 2 3 4 5
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8 9
+do_allowed_len_adm_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f0 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f, allowed: 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x300 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff, allowed: 8 9
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x000 (active_len=0, ramp_len=10, adm_len=0, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x0ff (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8 9
+*** Barring some ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5, allowed: 1 3 4 6 8
+do_allowed_len_adm_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=9, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=8, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=7, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=6, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x158 (active_len=4, ramp_len=10, adm_len=4, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xa7, allowed: 3 4 6 8
+(bts=0) ACC: update ACC allowed active subset 0x158 -> 0x150 (active_len=3, ramp_len=10, adm_len=3, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xaf, allowed: 4 6 8
+(bts=0) ACC: update ACC allowed active subset 0x150 -> 0x140 (active_len=2, ramp_len=10, adm_len=2, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xbf, allowed: 6 8
+(bts=0) ACC: update ACC allowed active subset 0x140 -> 0x100 (active_len=1, ramp_len=10, adm_len=1, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+(bts=0) ACC: update ACC allowed active subset 0x100 -> 0x000 (active_len=0, ramp_len=10, adm_len=0, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x002 (active_len=1, ramp_len=10, adm_len=1, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+(bts=0) ACC: update ACC allowed active subset 0x002 -> 0x00a (active_len=2, ramp_len=10, adm_len=2, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf5, allowed: 1 3
+(bts=0) ACC: update ACC allowed active subset 0x00a -> 0x01a (active_len=3, ramp_len=10, adm_len=3, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe5, allowed: 1 3 4
+(bts=0) ACC: update ACC allowed active subset 0x01a -> 0x05a (active_len=4, ramp_len=10, adm_len=4, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xa5, allowed: 1 3 4 6
+(bts=0) ACC: update ACC allowed active subset 0x05a -> 0x15a (active_len=5, ramp_len=10, adm_len=5, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5, allowed: 1 3 4 6 8
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=6, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=7, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=8, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=9, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+do_allowed_len_adm_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=6, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x140 (active_len=2, ramp_len=10, adm_len=2, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xbf, allowed: 6 8
+(bts=0) ACC: update ACC allowed active subset 0x140 -> 0x000 (active_len=0, ramp_len=10, adm_len=0, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x05a (active_len=4, ramp_len=10, adm_len=4, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xa5, allowed: 1 3 4 6
+(bts=0) ACC: update ACC allowed active subset 0x05a -> 0x15a (active_len=5, ramp_len=10, adm_len=8, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5, allowed: 1 3 4 6 8
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+*** Barring ALL ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=10, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+*** Barring zero ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8 9
+BTS deallocated OK in test_acc_mgr_no_ramp()
+===test_acc_mgr_manual_ramp===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_manual_ramp()
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3fe (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x01, allowed: 1 2 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3fe -> 0x3fc (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x03, allowed: 2 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3fc -> 0x3f8 (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07, allowed: 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f8 -> 0x3f0 (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f, allowed: 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x3e0 (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1f, allowed: 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3e0 -> 0x3c0 (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f, allowed: 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3c0 -> 0x380 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7f, allowed: 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x380 -> 0x300 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff, allowed: 8 9
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x200 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff, allowed: 9
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+(bts=0) ACC: update ACC allowed active subset 0x001 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+(bts=0) ACC: update ACC allowed active subset 0x003 -> 0x007 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8, allowed: 0 1 2
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0, allowed: 0 1 2 3 4 5
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8 9
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f0 (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f, allowed: 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x300 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff, allowed: 8 9
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8 9
+*** Barring some ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x24c (active_len=4, ramp_len=10, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3, allowed: 2 3 6 9
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=9, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=8, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=7, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=6, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=5, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x248 (active_len=3, ramp_len=3, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xb7, allowed: 3 6 9
+(bts=0) ACC: update ACC allowed active subset 0x248 -> 0x240 (active_len=2, ramp_len=2, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xbf, allowed: 6 9
+(bts=0) ACC: update ACC allowed active subset 0x240 -> 0x200 (active_len=1, ramp_len=1, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff, allowed: 9
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+(bts=0) ACC: update ACC allowed active subset 0x00c -> 0x04c (active_len=3, ramp_len=3, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xb3, allowed: 2 3 6
+(bts=0) ACC: update ACC allowed active subset 0x04c -> 0x24c (active_len=4, ramp_len=4, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3, allowed: 2 3 6 9
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=5, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=6, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=7, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=8, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=9, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=10, adm_len=10, perm_len=4, rotation=off)
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=6, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x240 (active_len=2, ramp_len=2, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xbf, allowed: 6 9
+(bts=0) ACC: update ACC allowed active subset 0x240 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x24c (active_len=4, ramp_len=4, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3, allowed: 2 3 6 9
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=8, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=10, adm_len=10, perm_len=4, rotation=off)
+*** Barring ALL ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=10, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=9, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=8, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=7, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=6, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=5, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=4, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=3, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=2, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=1, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=1, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=2, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=3, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=4, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=5, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=6, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=7, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=8, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=9, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=10, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=6, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=2, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=4, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=8, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=10, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+*** Barring zero ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8 9
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3fe (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x01, allowed: 1 2 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3fe -> 0x3fc (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x03, allowed: 2 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3fc -> 0x3f8 (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07, allowed: 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f8 -> 0x3f0 (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f, allowed: 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x3e0 (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1f, allowed: 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3e0 -> 0x3c0 (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f, allowed: 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3c0 -> 0x380 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7f, allowed: 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x380 -> 0x300 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff, allowed: 8 9
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x200 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff, allowed: 9
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+(bts=0) ACC: update ACC allowed active subset 0x001 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+(bts=0) ACC: update ACC allowed active subset 0x003 -> 0x007 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8, allowed: 0 1 2
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0, allowed: 0 1 2 3 4 5
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8 9
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f0 (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f, allowed: 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x300 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff, allowed: 8 9
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8 9
+*** Barring some ACCs + adm len 4 ***
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3c0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f, allowed: 6 7 8 9
+(bts=0) ACC: New ACC allowed subset 0x24c (active_len=4, ramp_len=10, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3, allowed: 2 3 6 9
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x248 (active_len=3, ramp_len=3, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xb7, allowed: 3 6 9
+(bts=0) ACC: update ACC allowed active subset 0x248 -> 0x240 (active_len=2, ramp_len=2, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xbf, allowed: 6 9
+(bts=0) ACC: update ACC allowed active subset 0x240 -> 0x200 (active_len=1, ramp_len=1, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff, allowed: 9
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=0, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x004 (active_len=1, ramp_len=1, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+(bts=0) ACC: update ACC allowed active subset 0x00c -> 0x04c (active_len=3, ramp_len=3, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xb3, allowed: 2 3 6
+(bts=0) ACC: update ACC allowed active subset 0x04c -> 0x24c (active_len=4, ramp_len=4, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3, allowed: 2 3 6 9
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x240 (active_len=2, ramp_len=2, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xbf, allowed: 6 9
+(bts=0) ACC: update ACC allowed active subset 0x240 -> 0x000 (active_len=0, ramp_len=0, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x24c (active_len=4, ramp_len=4, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3, allowed: 2 3 6 9
+BTS deallocated OK in test_acc_mgr_manual_ramp()
+===test_acc_mgr_rotate(true, 1)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x100 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x100 -> 0x001 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x004 -> 0x008 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf7, allowed: 3
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x008 -> 0x010 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xef, allowed: 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x010 -> 0x020 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xdf, allowed: 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x020 -> 0x040 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xbf, allowed: 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x040 -> 0x080 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x7f, allowed: 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x080 -> 0x100 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x100 -> 0x001 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x004 -> 0x008 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf7, allowed: 3
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x008 -> 0x010 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xef, allowed: 4
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x010 -> 0x020 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xdf, allowed: 5
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x020 -> 0x040 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xbf, allowed: 6
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x040 -> 0x080 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x7f, allowed: 7
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x080 -> 0x100 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x100 -> 0x001 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=10, adm_len=1, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(false, 1)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x200 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff, allowed: 9
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x200 -> 0x001 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x004 -> 0x008 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf7, allowed: 3
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x008 -> 0x010 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xef, allowed: 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x010 -> 0x020 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xdf, allowed: 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x020 -> 0x040 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xbf, allowed: 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x040 -> 0x080 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x7f, allowed: 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x080 -> 0x100 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x100 -> 0x200 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff, allowed: 9
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x200 -> 0x001 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x004 -> 0x008 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf7, allowed: 3
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x008 -> 0x010 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xef, allowed: 4
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x010 -> 0x020 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xdf, allowed: 5
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x020 -> 0x040 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xbf, allowed: 6
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x040 -> 0x080 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x7f, allowed: 7
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x080 -> 0x100 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x100 -> 0x200 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff, allowed: 9
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(true, 2)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x180 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7f, allowed: 7 8
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x180 -> 0x101 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfe, allowed: 0 8
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x101 -> 0x003 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x003 -> 0x006 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf9, allowed: 1 2
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x006 -> 0x00c (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x030 -> 0x060 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x9f, allowed: 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x060 -> 0x0c0 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3f, allowed: 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0c0 -> 0x180 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7f, allowed: 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x180 -> 0x101 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfe, allowed: 0 8
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x101 -> 0x003 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x003 -> 0x006 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf9, allowed: 1 2
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x006 -> 0x00c (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x030 -> 0x060 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x9f, allowed: 5 6
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x060 -> 0x0c0 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3f, allowed: 6 7
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0c0 -> 0x180 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7f, allowed: 7 8
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x180 -> 0x101 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfe, allowed: 0 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x101 -> 0x003 (active_len=2, ramp_len=10, adm_len=2, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(false, 2)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x300 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff, allowed: 8 9
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x300 -> 0x201 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xfe, allowed: 0 9
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x201 -> 0x003 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x003 -> 0x006 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf9, allowed: 1 2
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x006 -> 0x00c (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x030 -> 0x060 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x9f, allowed: 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x060 -> 0x0c0 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3f, allowed: 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0c0 -> 0x180 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7f, allowed: 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x180 -> 0x300 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff, allowed: 8 9
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x300 -> 0x201 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xfe, allowed: 0 9
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x201 -> 0x003 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x003 -> 0x006 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf9, allowed: 1 2
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x006 -> 0x00c (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x030 -> 0x060 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x9f, allowed: 5 6
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x060 -> 0x0c0 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3f, allowed: 6 7
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0c0 -> 0x180 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7f, allowed: 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x180 -> 0x300 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff, allowed: 8 9
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(true, 3)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1c0 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c0 -> 0x181 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7e, allowed: 0 7 8
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x181 -> 0x103 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfc, allowed: 0 1 8
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x103 -> 0x007 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8, allowed: 0 1 2
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x007 -> 0x00e (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf1, allowed: 1 2 3
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00e -> 0x01c (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe3, allowed: 2 3 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01c -> 0x038 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc7, allowed: 3 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x038 -> 0x070 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f, allowed: 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x070 -> 0x0e0 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f, allowed: 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c0 -> 0x181 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7e, allowed: 0 7 8
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x181 -> 0x103 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfc, allowed: 0 1 8
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x103 -> 0x007 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8, allowed: 0 1 2
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x007 -> 0x00e (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf1, allowed: 1 2 3
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00e -> 0x01c (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe3, allowed: 2 3 4
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01c -> 0x038 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc7, allowed: 3 4 5
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x038 -> 0x070 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f, allowed: 4 5 6
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x070 -> 0x0e0 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f, allowed: 5 6 7
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c0 -> 0x181 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7e, allowed: 0 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x181 -> 0x103 (active_len=3, ramp_len=10, adm_len=3, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfc, allowed: 0 1 8
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(false, 3)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x380 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7f, allowed: 7 8 9
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x380 -> 0x301 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xfe, allowed: 0 8 9
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x301 -> 0x203 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xfc, allowed: 0 1 9
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x203 -> 0x007 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8, allowed: 0 1 2
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x007 -> 0x00e (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf1, allowed: 1 2 3
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00e -> 0x01c (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe3, allowed: 2 3 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01c -> 0x038 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc7, allowed: 3 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x038 -> 0x070 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f, allowed: 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x070 -> 0x0e0 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f, allowed: 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c0 -> 0x380 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7f, allowed: 7 8 9
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x380 -> 0x301 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xfe, allowed: 0 8 9
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x301 -> 0x203 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xfc, allowed: 0 1 9
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x203 -> 0x007 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8, allowed: 0 1 2
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x007 -> 0x00e (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf1, allowed: 1 2 3
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00e -> 0x01c (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe3, allowed: 2 3 4
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01c -> 0x038 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc7, allowed: 3 4 5
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x038 -> 0x070 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f, allowed: 4 5 6
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x070 -> 0x0e0 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f, allowed: 5 6 7
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c0 -> 0x380 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7f, allowed: 7 8 9
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(true, 4)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1e0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1f, allowed: 5 6 7 8
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e0 -> 0x1c1 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e, allowed: 0 6 7 8
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x183 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7c, allowed: 0 1 7 8
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x183 -> 0x107 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf8, allowed: 0 1 2 8
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x107 -> 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00f -> 0x01e (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe1, allowed: 1 2 3 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01e -> 0x03c (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc3, allowed: 2 3 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03c -> 0x078 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x87, allowed: 3 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x078 -> 0x0f0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x0f, allowed: 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f0 -> 0x1e0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1f, allowed: 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e0 -> 0x1c1 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e, allowed: 0 6 7 8
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x183 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7c, allowed: 0 1 7 8
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x183 -> 0x107 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf8, allowed: 0 1 2 8
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x107 -> 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00f -> 0x01e (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe1, allowed: 1 2 3 4
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01e -> 0x03c (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc3, allowed: 2 3 4 5
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03c -> 0x078 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x87, allowed: 3 4 5 6
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x078 -> 0x0f0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x0f, allowed: 4 5 6 7
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f0 -> 0x1e0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1f, allowed: 5 6 7 8
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e0 -> 0x1c1 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e, allowed: 0 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x183 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7c, allowed: 0 1 7 8
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(false, 4)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3c0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f, allowed: 6 7 8 9
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3c0 -> 0x381 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7e, allowed: 0 7 8 9
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x381 -> 0x303 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xfc, allowed: 0 1 8 9
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x303 -> 0x207 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xf8, allowed: 0 1 2 9
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x207 -> 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00f -> 0x01e (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe1, allowed: 1 2 3 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01e -> 0x03c (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc3, allowed: 2 3 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03c -> 0x078 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x87, allowed: 3 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x078 -> 0x0f0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x0f, allowed: 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f0 -> 0x1e0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1f, allowed: 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e0 -> 0x3c0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f, allowed: 6 7 8 9
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3c0 -> 0x381 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7e, allowed: 0 7 8 9
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x381 -> 0x303 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xfc, allowed: 0 1 8 9
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x303 -> 0x207 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xf8, allowed: 0 1 2 9
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x207 -> 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00f -> 0x01e (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe1, allowed: 1 2 3 4
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01e -> 0x03c (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc3, allowed: 2 3 4 5
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03c -> 0x078 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x87, allowed: 3 4 5 6
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x078 -> 0x0f0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x0f, allowed: 4 5 6 7
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f0 -> 0x1e0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1f, allowed: 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e0 -> 0x3c0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f, allowed: 6 7 8 9
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(true, 5)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1f0 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0f, allowed: 4 5 6 7 8
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f0 -> 0x1e1 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1e, allowed: 0 5 6 7 8
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e1 -> 0x1c3 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3c, allowed: 0 1 6 7 8
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c3 -> 0x187 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x78, allowed: 0 1 2 7 8
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x187 -> 0x10f (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf0, allowed: 0 1 2 3 8
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x10f -> 0x01f (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01f -> 0x03e (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc1, allowed: 1 2 3 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03e -> 0x07c (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x83, allowed: 2 3 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07c -> 0x0f8 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x07, allowed: 3 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f8 -> 0x1f0 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0f, allowed: 4 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f0 -> 0x1e1 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1e, allowed: 0 5 6 7 8
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e1 -> 0x1c3 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3c, allowed: 0 1 6 7 8
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c3 -> 0x187 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x78, allowed: 0 1 2 7 8
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x187 -> 0x10f (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf0, allowed: 0 1 2 3 8
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x10f -> 0x01f (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01f -> 0x03e (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc1, allowed: 1 2 3 4 5
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03e -> 0x07c (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x83, allowed: 2 3 4 5 6
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07c -> 0x0f8 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x07, allowed: 3 4 5 6 7
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f8 -> 0x1f0 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0f, allowed: 4 5 6 7 8
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f0 -> 0x1e1 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1e, allowed: 0 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e1 -> 0x1c3 (active_len=5, ramp_len=10, adm_len=5, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3c, allowed: 0 1 6 7 8
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(false, 5)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3e0 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1f, allowed: 5 6 7 8 9
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3e0 -> 0x3c1 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3e, allowed: 0 6 7 8 9
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3c1 -> 0x383 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7c, allowed: 0 1 7 8 9
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x383 -> 0x307 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xf8, allowed: 0 1 2 8 9
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x307 -> 0x20f (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xf0, allowed: 0 1 2 3 9
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x20f -> 0x01f (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01f -> 0x03e (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc1, allowed: 1 2 3 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03e -> 0x07c (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x83, allowed: 2 3 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07c -> 0x0f8 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x07, allowed: 3 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f8 -> 0x1f0 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0f, allowed: 4 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f0 -> 0x3e0 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1f, allowed: 5 6 7 8 9
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3e0 -> 0x3c1 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3e, allowed: 0 6 7 8 9
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3c1 -> 0x383 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7c, allowed: 0 1 7 8 9
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x383 -> 0x307 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xf8, allowed: 0 1 2 8 9
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x307 -> 0x20f (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xf0, allowed: 0 1 2 3 9
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x20f -> 0x01f (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01f -> 0x03e (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc1, allowed: 1 2 3 4 5
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03e -> 0x07c (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x83, allowed: 2 3 4 5 6
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07c -> 0x0f8 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x07, allowed: 3 4 5 6 7
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f8 -> 0x1f0 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0f, allowed: 4 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f0 -> 0x3e0 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1f, allowed: 5 6 7 8 9
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(true, 6)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1f8 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x07, allowed: 3 4 5 6 7 8
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f8 -> 0x1f1 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0e, allowed: 0 4 5 6 7 8
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f1 -> 0x1e3 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1c, allowed: 0 1 5 6 7 8
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e3 -> 0x1c7 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x38, allowed: 0 1 2 6 7 8
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c7 -> 0x18f (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x70, allowed: 0 1 2 3 7 8
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x18f -> 0x11f (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xe0, allowed: 0 1 2 3 4 8
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x11f -> 0x03f (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0, allowed: 0 1 2 3 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03f -> 0x07e (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x81, allowed: 1 2 3 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07e -> 0x0fc (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x03, allowed: 2 3 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fc -> 0x1f8 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x07, allowed: 3 4 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f8 -> 0x1f1 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0e, allowed: 0 4 5 6 7 8
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f1 -> 0x1e3 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1c, allowed: 0 1 5 6 7 8
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e3 -> 0x1c7 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x38, allowed: 0 1 2 6 7 8
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c7 -> 0x18f (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x70, allowed: 0 1 2 3 7 8
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x18f -> 0x11f (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xe0, allowed: 0 1 2 3 4 8
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x11f -> 0x03f (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0, allowed: 0 1 2 3 4 5
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03f -> 0x07e (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x81, allowed: 1 2 3 4 5 6
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07e -> 0x0fc (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x03, allowed: 2 3 4 5 6 7
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fc -> 0x1f8 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x07, allowed: 3 4 5 6 7 8
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f8 -> 0x1f1 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0e, allowed: 0 4 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f1 -> 0x1e3 (active_len=6, ramp_len=10, adm_len=6, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1c, allowed: 0 1 5 6 7 8
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(false, 6)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f0 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f, allowed: 4 5 6 7 8 9
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f0 -> 0x3e1 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1e, allowed: 0 5 6 7 8 9
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3e1 -> 0x3c3 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3c, allowed: 0 1 6 7 8 9
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3c3 -> 0x387 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x78, allowed: 0 1 2 7 8 9
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x387 -> 0x30f (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xf0, allowed: 0 1 2 3 8 9
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x30f -> 0x21f (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xe0, allowed: 0 1 2 3 4 9
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x21f -> 0x03f (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0, allowed: 0 1 2 3 4 5
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03f -> 0x07e (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x81, allowed: 1 2 3 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07e -> 0x0fc (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x03, allowed: 2 3 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fc -> 0x1f8 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x07, allowed: 3 4 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f8 -> 0x3f0 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f, allowed: 4 5 6 7 8 9
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f0 -> 0x3e1 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1e, allowed: 0 5 6 7 8 9
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3e1 -> 0x3c3 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3c, allowed: 0 1 6 7 8 9
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3c3 -> 0x387 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x78, allowed: 0 1 2 7 8 9
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x387 -> 0x30f (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xf0, allowed: 0 1 2 3 8 9
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x30f -> 0x21f (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xe0, allowed: 0 1 2 3 4 9
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x21f -> 0x03f (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0, allowed: 0 1 2 3 4 5
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03f -> 0x07e (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x81, allowed: 1 2 3 4 5 6
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07e -> 0x0fc (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x03, allowed: 2 3 4 5 6 7
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fc -> 0x1f8 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x07, allowed: 3 4 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f8 -> 0x3f0 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f, allowed: 4 5 6 7 8 9
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(true, 7)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1fc (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x03, allowed: 2 3 4 5 6 7 8
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fc -> 0x1f9 (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x06, allowed: 0 3 4 5 6 7 8
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f9 -> 0x1f3 (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0c, allowed: 0 1 4 5 6 7 8
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f3 -> 0x1e7 (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x18, allowed: 0 1 2 5 6 7 8
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e7 -> 0x1cf (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x30, allowed: 0 1 2 3 6 7 8
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1cf -> 0x19f (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x60, allowed: 0 1 2 3 4 7 8
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x19f -> 0x13f (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xc0, allowed: 0 1 2 3 4 5 8
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x13f -> 0x07f (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07f -> 0x0fe (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x01, allowed: 1 2 3 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fe -> 0x1fc (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x03, allowed: 2 3 4 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fc -> 0x1f9 (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x06, allowed: 0 3 4 5 6 7 8
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f9 -> 0x1f3 (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0c, allowed: 0 1 4 5 6 7 8
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f3 -> 0x1e7 (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x18, allowed: 0 1 2 5 6 7 8
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e7 -> 0x1cf (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x30, allowed: 0 1 2 3 6 7 8
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1cf -> 0x19f (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x60, allowed: 0 1 2 3 4 7 8
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x19f -> 0x13f (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xc0, allowed: 0 1 2 3 4 5 8
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x13f -> 0x07f (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07f -> 0x0fe (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x01, allowed: 1 2 3 4 5 6 7
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fe -> 0x1fc (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x03, allowed: 2 3 4 5 6 7 8
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fc -> 0x1f9 (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x06, allowed: 0 3 4 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f9 -> 0x1f3 (active_len=7, ramp_len=10, adm_len=7, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0c, allowed: 0 1 4 5 6 7 8
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(false, 7)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f8 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07, allowed: 3 4 5 6 7 8 9
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f8 -> 0x3f1 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0e, allowed: 0 4 5 6 7 8 9
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f1 -> 0x3e3 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1c, allowed: 0 1 5 6 7 8 9
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3e3 -> 0x3c7 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x38, allowed: 0 1 2 6 7 8 9
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3c7 -> 0x38f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x70, allowed: 0 1 2 3 7 8 9
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x38f -> 0x31f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xe0, allowed: 0 1 2 3 4 8 9
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x31f -> 0x23f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xc0, allowed: 0 1 2 3 4 5 9
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x23f -> 0x07f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07f -> 0x0fe (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x01, allowed: 1 2 3 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fe -> 0x1fc (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x03, allowed: 2 3 4 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fc -> 0x3f8 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07, allowed: 3 4 5 6 7 8 9
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f8 -> 0x3f1 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0e, allowed: 0 4 5 6 7 8 9
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f1 -> 0x3e3 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1c, allowed: 0 1 5 6 7 8 9
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3e3 -> 0x3c7 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x38, allowed: 0 1 2 6 7 8 9
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3c7 -> 0x38f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x70, allowed: 0 1 2 3 7 8 9
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x38f -> 0x31f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xe0, allowed: 0 1 2 3 4 8 9
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x31f -> 0x23f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xc0, allowed: 0 1 2 3 4 5 9
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x23f -> 0x07f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07f -> 0x0fe (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x01, allowed: 1 2 3 4 5 6 7
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fe -> 0x1fc (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x03, allowed: 2 3 4 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fc -> 0x3f8 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07, allowed: 3 4 5 6 7 8 9
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(true, 8)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1fe (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x01, allowed: 1 2 3 4 5 6 7 8
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fe -> 0x1fd (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x02, allowed: 0 2 3 4 5 6 7 8
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fd -> 0x1fb (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x04, allowed: 0 1 3 4 5 6 7 8
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fb -> 0x1f7 (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x08, allowed: 0 1 2 4 5 6 7 8
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f7 -> 0x1ef (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x10, allowed: 0 1 2 3 5 6 7 8
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1ef -> 0x1df (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x20, allowed: 0 1 2 3 4 6 7 8
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1df -> 0x1bf (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x40, allowed: 0 1 2 3 4 5 7 8
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1bf -> 0x17f (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x80, allowed: 0 1 2 3 4 5 6 8
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x17f -> 0x0ff (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0ff -> 0x1fe (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x01, allowed: 1 2 3 4 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fe -> 0x1fd (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x02, allowed: 0 2 3 4 5 6 7 8
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fd -> 0x1fb (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x04, allowed: 0 1 3 4 5 6 7 8
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fb -> 0x1f7 (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x08, allowed: 0 1 2 4 5 6 7 8
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f7 -> 0x1ef (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x10, allowed: 0 1 2 3 5 6 7 8
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1ef -> 0x1df (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x20, allowed: 0 1 2 3 4 6 7 8
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1df -> 0x1bf (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x40, allowed: 0 1 2 3 4 5 7 8
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1bf -> 0x17f (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x80, allowed: 0 1 2 3 4 5 6 8
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x17f -> 0x0ff (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0ff -> 0x1fe (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x01, allowed: 1 2 3 4 5 6 7 8
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fe -> 0x1fd (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x02, allowed: 0 2 3 4 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fd -> 0x1fb (active_len=8, ramp_len=10, adm_len=8, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x04, allowed: 0 1 3 4 5 6 7 8
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(false, 8)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3fc (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x03, allowed: 2 3 4 5 6 7 8 9
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3fc -> 0x3f9 (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x06, allowed: 0 3 4 5 6 7 8 9
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f9 -> 0x3f3 (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0c, allowed: 0 1 4 5 6 7 8 9
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f3 -> 0x3e7 (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x18, allowed: 0 1 2 5 6 7 8 9
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3e7 -> 0x3cf (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x30, allowed: 0 1 2 3 6 7 8 9
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3cf -> 0x39f (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x60, allowed: 0 1 2 3 4 7 8 9
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x39f -> 0x33f (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xc0, allowed: 0 1 2 3 4 5 8 9
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x33f -> 0x27f (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0x80, allowed: 0 1 2 3 4 5 6 9
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x27f -> 0x0ff (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0ff -> 0x1fe (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x01, allowed: 1 2 3 4 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fe -> 0x3fc (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x03, allowed: 2 3 4 5 6 7 8 9
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3fc -> 0x3f9 (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x06, allowed: 0 3 4 5 6 7 8 9
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f9 -> 0x3f3 (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0c, allowed: 0 1 4 5 6 7 8 9
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f3 -> 0x3e7 (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x18, allowed: 0 1 2 5 6 7 8 9
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3e7 -> 0x3cf (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x30, allowed: 0 1 2 3 6 7 8 9
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3cf -> 0x39f (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x60, allowed: 0 1 2 3 4 7 8 9
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x39f -> 0x33f (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xc0, allowed: 0 1 2 3 4 5 8 9
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x33f -> 0x27f (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0x80, allowed: 0 1 2 3 4 5 6 9
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x27f -> 0x0ff (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0ff -> 0x1fe (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x01, allowed: 1 2 3 4 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fe -> 0x3fc (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x03, allowed: 2 3 4 5 6 7 8 9
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_mgr_rotate(false, 9)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3fe (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x01, allowed: 1 2 3 4 5 6 7 8 9
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3fe -> 0x3fd (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x02, allowed: 0 2 3 4 5 6 7 8 9
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3fd -> 0x3fb (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x04, allowed: 0 1 3 4 5 6 7 8 9
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3fb -> 0x3f7 (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x08, allowed: 0 1 2 4 5 6 7 8 9
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f7 -> 0x3ef (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x10, allowed: 0 1 2 3 5 6 7 8 9
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3ef -> 0x3df (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x20, allowed: 0 1 2 3 4 6 7 8 9
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3df -> 0x3bf (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x40, allowed: 0 1 2 3 4 5 7 8 9
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3bf -> 0x37f (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x80, allowed: 0 1 2 3 4 5 6 8 9
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x37f -> 0x2ff (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0x00, allowed: 0 1 2 3 4 5 6 7 9
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x2ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1ff -> 0x3fe (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x01, allowed: 1 2 3 4 5 6 7 8 9
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3fe -> 0x3fd (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x02, allowed: 0 2 3 4 5 6 7 8 9
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3fd -> 0x3fb (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x04, allowed: 0 1 3 4 5 6 7 8 9
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3fb -> 0x3f7 (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x08, allowed: 0 1 2 4 5 6 7 8 9
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3f7 -> 0x3ef (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x10, allowed: 0 1 2 3 5 6 7 8 9
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3ef -> 0x3df (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x20, allowed: 0 1 2 3 4 6 7 8 9
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3df -> 0x3bf (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x40, allowed: 0 1 2 3 4 5 7 8 9
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x3bf -> 0x37f (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x80, allowed: 0 1 2 3 4 5 6 8 9
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x37f -> 0x2ff (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0x00, allowed: 0 1 2 3 4 5 6 7 9
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x2ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1ff -> 0x3fe (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x01, allowed: 1 2 3 4 5 6 7 8 9
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_ramp===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={50.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x001 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+sys={100.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x003 -> 0x007 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8, allowed: 0 1 2
+sys={150.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+sys={200.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+sys={250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0, allowed: 0 1 2 3 4 5
+sys={300.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+sys={350.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+sys={400.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+sys={450.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8 9
+BTS deallocated OK in test_acc_ramp()
+===test_acc_ramp2===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp2()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f8 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07, allowed: 3 4 5 6 7 8 9
+(bts=0) ACC: update ACC allowed active subset 0x3f8 -> 0x000 (active_len=0, ramp_len=0, adm_len=7, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x007 (active_len=3, ramp_len=3, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8, allowed: 0 1 2
+sys={5.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x03f (active_len=6, ramp_len=6, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0, allowed: 0 1 2 3 4 5
+sys={10.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=7, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+sys={15.000000}: select()
+BTS deallocated OK in test_acc_ramp2()
+===test_acc_ramp3===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp3()
+*** Barring some ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5, allowed: 1 3 4 6 8
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={5.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x002 -> 0x00a (active_len=2, ramp_len=2, adm_len=10, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf5, allowed: 1 3
+sys={10.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x00a -> 0x01a (active_len=3, ramp_len=3, adm_len=10, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe5, allowed: 1 3 4
+sys={15.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x01a -> 0x05a (active_len=4, ramp_len=4, adm_len=10, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xa5, allowed: 1 3 4 6
+sys={20.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x05a -> 0x15a (active_len=5, ramp_len=5, adm_len=10, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5, allowed: 1 3 4 6 8
+sys={25.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=6, adm_len=10, perm_len=5, rotation=off)
+sys={30.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=7, adm_len=10, perm_len=5, rotation=off)
+sys={35.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=8, adm_len=10, perm_len=5, rotation=off)
+sys={40.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=9, adm_len=10, perm_len=5, rotation=off)
+sys={45.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+BTS deallocated OK in test_acc_ramp3()
+===test_acc_ramp_up_rotate(0, 100, 100)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_up_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={100.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={200.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={350.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={450.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={500.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x030 -> 0x070 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f, allowed: 4 5 6
+sys={600.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x070 -> 0x0e0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f, allowed: 5 6 7
+sys={700.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={750.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1c0 -> 0x1c1 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e, allowed: 0 6 7 8
+sys={850.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x183 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7c, allowed: 0 1 7 8
+sys={950.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x183 -> 0x107 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf8, allowed: 0 1 2 8
+sys={1000.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x107 -> 0x10f (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf0, allowed: 0 1 2 3 8
+sys={1100.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x10f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+sys={1200.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01f -> 0x03e (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc1, allowed: 1 2 3 4 5
+sys={1250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x03e -> 0x07e (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x81, allowed: 1 2 3 4 5 6
+sys={1350.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07e -> 0x0fc (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x03, allowed: 2 3 4 5 6 7
+sys={1450.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fc -> 0x1f8 (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x07, allowed: 3 4 5 6 7 8
+sys={1500.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1f8 -> 0x1f9 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x06, allowed: 0 3 4 5 6 7 8
+sys={1600.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f9 -> 0x1f3 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0c, allowed: 0 1 4 5 6 7 8
+sys={1700.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f3 -> 0x1e7 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x18, allowed: 0 1 2 5 6 7 8
+sys={1750.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1e7 -> 0x1ef (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x10, allowed: 0 1 2 3 5 6 7 8
+sys={1850.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1ef -> 0x1df (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x20, allowed: 0 1 2 3 4 6 7 8
+sys={1950.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1df -> 0x1bf (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x40, allowed: 0 1 2 3 4 5 7 8
+sys={2000.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1bf -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+sys={2250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+sys={2500.000000}: select()
+sys={2750.000000}: select()
+sys={3000.000000}: select()
+sys={3250.000000}: select()
+BTS deallocated OK in test_acc_ramp_up_rotate()
+===test_acc_ramp_up_rotate(0, 20, 50)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_up_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={100.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={200.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={350.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={450.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={500.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x030 -> 0x070 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f, allowed: 4 5 6
+sys={600.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x070 -> 0x0e0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f, allowed: 5 6 7
+sys={700.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={750.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1c0 -> 0x1c1 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e, allowed: 0 6 7 8
+sys={850.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x183 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7c, allowed: 0 1 7 8
+sys={950.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x183 -> 0x107 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf8, allowed: 0 1 2 8
+sys={1000.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x107 -> 0x10f (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf0, allowed: 0 1 2 3 8
+sys={1100.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x10f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+sys={1200.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01f -> 0x03e (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc1, allowed: 1 2 3 4 5
+sys={1250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x03e -> 0x07e (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x81, allowed: 1 2 3 4 5 6
+sys={1350.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07e -> 0x0fc (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x03, allowed: 2 3 4 5 6 7
+sys={1450.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fc -> 0x1f8 (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x07, allowed: 3 4 5 6 7 8
+sys={1500.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1f8 -> 0x1f9 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x06, allowed: 0 3 4 5 6 7 8
+sys={1600.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f9 -> 0x1f3 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0c, allowed: 0 1 4 5 6 7 8
+sys={1700.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f3 -> 0x1e7 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x18, allowed: 0 1 2 5 6 7 8
+sys={1750.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1e7 -> 0x1ef (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x10, allowed: 0 1 2 3 5 6 7 8
+sys={1850.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1ef -> 0x1df (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x20, allowed: 0 1 2 3 4 6 7 8
+sys={1950.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1df -> 0x1bf (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x40, allowed: 0 1 2 3 4 5 7 8
+sys={2000.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1bf -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+sys={2250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+sys={2500.000000}: select()
+sys={2750.000000}: select()
+sys={3000.000000}: select()
+sys={3250.000000}: select()
+BTS deallocated OK in test_acc_ramp_up_rotate()
+===test_acc_ramp_up_rotate(70, 80, 90)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_up_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={100.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={200.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={350.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={450.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={500.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x030 -> 0x070 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f, allowed: 4 5 6
+sys={600.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x070 -> 0x0e0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f, allowed: 5 6 7
+sys={700.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={750.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1c0 -> 0x1c1 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e, allowed: 0 6 7 8
+sys={850.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x183 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7c, allowed: 0 1 7 8
+sys={950.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x183 -> 0x107 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf8, allowed: 0 1 2 8
+sys={1000.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x107 -> 0x10f (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf0, allowed: 0 1 2 3 8
+sys={1100.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x10f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0, allowed: 0 1 2 3 4
+sys={1200.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01f -> 0x03e (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc1, allowed: 1 2 3 4 5
+sys={1250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x03e -> 0x07e (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x81, allowed: 1 2 3 4 5 6
+sys={1350.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07e -> 0x0fc (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x03, allowed: 2 3 4 5 6 7
+sys={1450.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0fc -> 0x1f8 (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x07, allowed: 3 4 5 6 7 8
+sys={1500.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1f8 -> 0x1f9 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x06, allowed: 0 3 4 5 6 7 8
+sys={1600.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f9 -> 0x1f3 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0c, allowed: 0 1 4 5 6 7 8
+sys={1700.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f3 -> 0x1e7 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x18, allowed: 0 1 2 5 6 7 8
+sys={1750.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1e7 -> 0x1ef (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x10, allowed: 0 1 2 3 5 6 7 8
+sys={1850.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1ef -> 0x1df (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x20, allowed: 0 1 2 3 4 6 7 8
+sys={1950.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1df -> 0x1bf (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x40, allowed: 0 1 2 3 4 5 7 8
+sys={2000.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1bf -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+sys={2250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+sys={2500.000000}: select()
+sys={2750.000000}: select()
+sys={3000.000000}: select()
+sys={3250.000000}: select()
+BTS deallocated OK in test_acc_ramp_up_rotate()
+===test_acc_ramp_updown_rotate(80, 90, 0, 100, 15)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_updown_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={100.000000}: select(0): chan_load_avg=0
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={200.000000}: select(1): chan_load_avg=15
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={250.000000}: select(2): chan_load_avg=30
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={350.000000}: select(3): chan_load_avg=45
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={450.000000}: select(4): chan_load_avg=60
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={500.000000}: select(5): chan_load_avg=75
+(bts=0) ACC: update ACC allowed active subset 0x030 -> 0x070 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f, allowed: 4 5 6
+sys={600.000000}: select(6): chan_load_avg=90
+(bts=0) ACC: rotate ACC allowed active subset 0x070 -> 0x0e0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f, allowed: 5 6 7
+sys={700.000000}: select(7): chan_load_avg=100
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={750.000000}: select(8): chan_load_avg=85
+sys={850.000000}: select(9): chan_load_avg=70
+(bts=0) ACC: rotate ACC allowed active subset 0x1c0 -> 0x181 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7e, allowed: 0 7 8
+sys={950.000000}: select(10): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x181 -> 0x103 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfc, allowed: 0 1 8
+sys={1000.000000}: select(11): chan_load_avg=40
+(bts=0) ACC: update ACC allowed active subset 0x103 -> 0x107 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf8, allowed: 0 1 2 8
+sys={1100.000000}: select(12): chan_load_avg=25
+(bts=0) ACC: rotate ACC allowed active subset 0x107 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+sys={1200.000000}: select(13): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x00f -> 0x01e (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe1, allowed: 1 2 3 4
+sys={1250.000000}: select(14): chan_load_avg=0
+(bts=0) ACC: update ACC allowed active subset 0x01e -> 0x03e (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc1, allowed: 1 2 3 4 5
+sys={1350.000000}: select(15): chan_load_avg=15
+(bts=0) ACC: rotate ACC allowed active subset 0x03e -> 0x07c (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x83, allowed: 2 3 4 5 6
+sys={1450.000000}: select(16): chan_load_avg=30
+(bts=0) ACC: rotate ACC allowed active subset 0x07c -> 0x0f8 (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x07, allowed: 3 4 5 6 7
+sys={1500.000000}: select(17): chan_load_avg=45
+(bts=0) ACC: update ACC allowed active subset 0x0f8 -> 0x1f8 (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x07, allowed: 3 4 5 6 7 8
+sys={1600.000000}: select(18): chan_load_avg=60
+(bts=0) ACC: rotate ACC allowed active subset 0x1f8 -> 0x1f1 (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x0e, allowed: 0 4 5 6 7 8
+sys={1700.000000}: select(19): chan_load_avg=75
+(bts=0) ACC: rotate ACC allowed active subset 0x1f1 -> 0x1e3 (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1c, allowed: 0 1 5 6 7 8
+sys={1750.000000}: select(20): chan_load_avg=90
+sys={1850.000000}: select(21): chan_load_avg=100
+(bts=0) ACC: rotate ACC allowed active subset 0x1e3 -> 0x1c7 (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x38, allowed: 0 1 2 6 7 8
+sys={1950.000000}: select(22): chan_load_avg=85
+(bts=0) ACC: rotate ACC allowed active subset 0x1c7 -> 0x18f (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x70, allowed: 0 1 2 3 7 8
+sys={2000.000000}: select(23): chan_load_avg=70
+(bts=0) ACC: update ACC allowed active subset 0x18f -> 0x19f (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x60, allowed: 0 1 2 3 4 7 8
+sys={2100.000000}: select(24): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x19f -> 0x13f (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xc0, allowed: 0 1 2 3 4 5 8
+sys={2200.000000}: select(25): chan_load_avg=40
+(bts=0) ACC: rotate ACC allowed active subset 0x13f -> 0x07f (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80, allowed: 0 1 2 3 4 5 6
+sys={2250.000000}: select(26): chan_load_avg=25
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00, allowed: 0 1 2 3 4 5 6 7
+sys={2350.000000}: select(27): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x0ff -> 0x1fe (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x01, allowed: 1 2 3 4 5 6 7 8
+sys={2450.000000}: select(28): chan_load_avg=0
+(bts=0) ACC: rotate ACC allowed active subset 0x1fe -> 0x1fd (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x02, allowed: 0 2 3 4 5 6 7 8
+sys={2500.000000}: select(29): chan_load_avg=15
+(bts=0) ACC: update ACC allowed active subset 0x1fd -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+sys={2750.000000}: select(30): chan_load_avg=30
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+sys={3000.000000}: select(31): chan_load_avg=45
+sys={3250.000000}: select(32): chan_load_avg=60
+sys={3500.000000}: select(33): chan_load_avg=75
+sys={3750.000000}: select(34): chan_load_avg=90
+sys={4000.000000}: select(35): chan_load_avg=100
+sys={4250.000000}: select(36): chan_load_avg=85
+sys={4500.000000}: select(37): chan_load_avg=70
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+sys={4750.000000}: select(38): chan_load_avg=55
+sys={5000.000000}: select(39): chan_load_avg=40
+sys={5250.000000}: select(40): chan_load_avg=25
+sys={5500.000000}: select(41): chan_load_avg=10
+sys={5750.000000}: select(42): chan_load_avg=0
+sys={6000.000000}: select(43): chan_load_avg=15
+sys={6250.000000}: select(44): chan_load_avg=30
+sys={6500.000000}: select(45): chan_load_avg=45
+sys={6750.000000}: select(46): chan_load_avg=60
+sys={7000.000000}: select(47): chan_load_avg=75
+sys={7250.000000}: select(48): chan_load_avg=90
+sys={7500.000000}: select(49): chan_load_avg=100
+BTS deallocated OK in test_acc_ramp_updown_rotate()
+===test_acc_ramp_updown_rotate(30, 50, 10, 100, 15)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_updown_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={100.000000}: select(0): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={200.000000}: select(1): chan_load_avg=25
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={250.000000}: select(2): chan_load_avg=40
+sys={350.000000}: select(3): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x004 -> 0x008 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf7, allowed: 3
+sys={450.000000}: select(4): chan_load_avg=70
+(bts=0) ACC: rotate ACC allowed active subset 0x008 -> 0x010 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xef, allowed: 4
+sys={500.000000}: select(5): chan_load_avg=85
+sys={600.000000}: select(6): chan_load_avg=100
+(bts=0) ACC: rotate ACC allowed active subset 0x010 -> 0x020 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xdf, allowed: 5
+sys={700.000000}: select(7): chan_load_avg=85
+(bts=0) ACC: rotate ACC allowed active subset 0x020 -> 0x040 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xbf, allowed: 6
+sys={750.000000}: select(8): chan_load_avg=70
+sys={850.000000}: select(9): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x040 -> 0x080 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x7f, allowed: 7
+sys={950.000000}: select(10): chan_load_avg=40
+(bts=0) ACC: rotate ACC allowed active subset 0x080 -> 0x100 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={1000.000000}: select(11): chan_load_avg=25
+(bts=0) ACC: update ACC allowed active subset 0x100 -> 0x101 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfe, allowed: 0 8
+sys={1100.000000}: select(12): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x101 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+sys={1200.000000}: select(13): chan_load_avg=25
+(bts=0) ACC: rotate ACC allowed active subset 0x003 -> 0x006 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf9, allowed: 1 2
+sys={1250.000000}: select(14): chan_load_avg=40
+sys={1350.000000}: select(15): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x006 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={1450.000000}: select(16): chan_load_avg=70
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={1500.000000}: select(17): chan_load_avg=85
+(bts=0) ACC: update ACC allowed active subset 0x018 -> 0x010 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xef, allowed: 4
+sys={1600.000000}: select(18): chan_load_avg=100
+(bts=0) ACC: rotate ACC allowed active subset 0x010 -> 0x020 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xdf, allowed: 5
+sys={1700.000000}: select(19): chan_load_avg=85
+(bts=0) ACC: rotate ACC allowed active subset 0x020 -> 0x040 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xbf, allowed: 6
+sys={1750.000000}: select(20): chan_load_avg=70
+sys={1850.000000}: select(21): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x040 -> 0x080 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x7f, allowed: 7
+sys={1950.000000}: select(22): chan_load_avg=40
+(bts=0) ACC: rotate ACC allowed active subset 0x080 -> 0x100 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={2000.000000}: select(23): chan_load_avg=25
+(bts=0) ACC: update ACC allowed active subset 0x100 -> 0x101 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfe, allowed: 0 8
+sys={2100.000000}: select(24): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x101 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+sys={2200.000000}: select(25): chan_load_avg=25
+(bts=0) ACC: rotate ACC allowed active subset 0x003 -> 0x006 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf9, allowed: 1 2
+sys={2250.000000}: select(26): chan_load_avg=40
+sys={2350.000000}: select(27): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x006 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={2450.000000}: select(28): chan_load_avg=70
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={2500.000000}: select(29): chan_load_avg=85
+(bts=0) ACC: update ACC allowed active subset 0x018 -> 0x010 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xef, allowed: 4
+sys={2600.000000}: select(30): chan_load_avg=100
+(bts=0) ACC: rotate ACC allowed active subset 0x010 -> 0x020 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xdf, allowed: 5
+sys={2700.000000}: select(31): chan_load_avg=85
+(bts=0) ACC: rotate ACC allowed active subset 0x020 -> 0x040 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xbf, allowed: 6
+sys={2750.000000}: select(32): chan_load_avg=70
+sys={2850.000000}: select(33): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x040 -> 0x080 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x7f, allowed: 7
+sys={2950.000000}: select(34): chan_load_avg=40
+(bts=0) ACC: rotate ACC allowed active subset 0x080 -> 0x100 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={3000.000000}: select(35): chan_load_avg=25
+(bts=0) ACC: update ACC allowed active subset 0x100 -> 0x101 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfe, allowed: 0 8
+sys={3100.000000}: select(36): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x101 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+sys={3200.000000}: select(37): chan_load_avg=25
+(bts=0) ACC: rotate ACC allowed active subset 0x003 -> 0x006 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf9, allowed: 1 2
+sys={3250.000000}: select(38): chan_load_avg=40
+sys={3350.000000}: select(39): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x006 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={3450.000000}: select(40): chan_load_avg=70
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={3500.000000}: select(41): chan_load_avg=85
+(bts=0) ACC: update ACC allowed active subset 0x018 -> 0x010 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xef, allowed: 4
+sys={3600.000000}: select(42): chan_load_avg=100
+(bts=0) ACC: rotate ACC allowed active subset 0x010 -> 0x020 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xdf, allowed: 5
+sys={3700.000000}: select(43): chan_load_avg=85
+(bts=0) ACC: rotate ACC allowed active subset 0x020 -> 0x040 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xbf, allowed: 6
+sys={3750.000000}: select(44): chan_load_avg=70
+sys={3850.000000}: select(45): chan_load_avg=55
+(bts=0) ACC: rotate ACC allowed active subset 0x040 -> 0x080 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x7f, allowed: 7
+sys={3950.000000}: select(46): chan_load_avg=40
+(bts=0) ACC: rotate ACC allowed active subset 0x080 -> 0x100 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={4000.000000}: select(47): chan_load_avg=25
+(bts=0) ACC: update ACC allowed active subset 0x100 -> 0x101 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfe, allowed: 0 8
+sys={4100.000000}: select(48): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x101 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc, allowed: 0 1
+sys={4200.000000}: select(49): chan_load_avg=25
+(bts=0) ACC: rotate ACC allowed active subset 0x003 -> 0x006 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf9, allowed: 1 2
+BTS deallocated OK in test_acc_ramp_updown_rotate()
+===test_acc_ramp_updown_rotate(50, 49, 0, 100, 10)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_updown_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={100.000000}: select(0): chan_load_avg=0
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={200.000000}: select(1): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={250.000000}: select(2): chan_load_avg=20
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3, allowed: 2 3
+sys={350.000000}: select(3): chan_load_avg=30
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={450.000000}: select(4): chan_load_avg=40
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={500.000000}: select(5): chan_load_avg=50
+(bts=0) ACC: update ACC allowed active subset 0x030 -> 0x020 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xdf, allowed: 5
+sys={600.000000}: select(6): chan_load_avg=60
+(bts=0) ACC: rotate ACC allowed active subset 0x020 -> 0x040 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xbf, allowed: 6
+sys={700.000000}: select(7): chan_load_avg=70
+(bts=0) ACC: rotate ACC allowed active subset 0x040 -> 0x080 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x7f, allowed: 7
+sys={750.000000}: select(8): chan_load_avg=80
+sys={850.000000}: select(9): chan_load_avg=90
+(bts=0) ACC: rotate ACC allowed active subset 0x080 -> 0x100 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={950.000000}: select(10): chan_load_avg=100
+(bts=0) ACC: rotate ACC allowed active subset 0x100 -> 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={1000.000000}: select(11): chan_load_avg=90
+sys={1100.000000}: select(12): chan_load_avg=80
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={1200.000000}: select(13): chan_load_avg=70
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={1250.000000}: select(14): chan_load_avg=60
+sys={1350.000000}: select(15): chan_load_avg=50
+(bts=0) ACC: rotate ACC allowed active subset 0x004 -> 0x008 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf7, allowed: 3
+sys={1450.000000}: select(16): chan_load_avg=40
+(bts=0) ACC: rotate ACC allowed active subset 0x008 -> 0x010 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xef, allowed: 4
+sys={1500.000000}: select(17): chan_load_avg=30
+(bts=0) ACC: update ACC allowed active subset 0x010 -> 0x030 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={1600.000000}: select(18): chan_load_avg=20
+(bts=0) ACC: rotate ACC allowed active subset 0x030 -> 0x060 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x9f, allowed: 5 6
+sys={1700.000000}: select(19): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x060 -> 0x0c0 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3f, allowed: 6 7
+sys={1750.000000}: select(20): chan_load_avg=0
+(bts=0) ACC: update ACC allowed active subset 0x0c0 -> 0x1c0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={1850.000000}: select(21): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x1c0 -> 0x181 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7e, allowed: 0 7 8
+sys={1950.000000}: select(22): chan_load_avg=20
+(bts=0) ACC: rotate ACC allowed active subset 0x181 -> 0x103 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xfc, allowed: 0 1 8
+sys={2000.000000}: select(23): chan_load_avg=30
+(bts=0) ACC: update ACC allowed active subset 0x103 -> 0x107 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf8, allowed: 0 1 2 8
+sys={2100.000000}: select(24): chan_load_avg=40
+(bts=0) ACC: rotate ACC allowed active subset 0x107 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+sys={2200.000000}: select(25): chan_load_avg=50
+(bts=0) ACC: rotate ACC allowed active subset 0x00f -> 0x01e (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe1, allowed: 1 2 3 4
+sys={2250.000000}: select(26): chan_load_avg=60
+(bts=0) ACC: update ACC allowed active subset 0x01e -> 0x01c (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe3, allowed: 2 3 4
+sys={2350.000000}: select(27): chan_load_avg=70
+(bts=0) ACC: rotate ACC allowed active subset 0x01c -> 0x038 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc7, allowed: 3 4 5
+sys={2450.000000}: select(28): chan_load_avg=80
+(bts=0) ACC: rotate ACC allowed active subset 0x038 -> 0x070 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f, allowed: 4 5 6
+sys={2500.000000}: select(29): chan_load_avg=90
+(bts=0) ACC: update ACC allowed active subset 0x070 -> 0x060 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x9f, allowed: 5 6
+sys={2600.000000}: select(30): chan_load_avg=100
+(bts=0) ACC: rotate ACC allowed active subset 0x060 -> 0x0c0 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3f, allowed: 6 7
+sys={2700.000000}: select(31): chan_load_avg=90
+(bts=0) ACC: rotate ACC allowed active subset 0x0c0 -> 0x180 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7f, allowed: 7 8
+sys={2750.000000}: select(32): chan_load_avg=80
+(bts=0) ACC: update ACC allowed active subset 0x180 -> 0x100 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff, allowed: 8
+sys={2850.000000}: select(33): chan_load_avg=70
+(bts=0) ACC: rotate ACC allowed active subset 0x100 -> 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe, allowed: 0
+sys={2950.000000}: select(34): chan_load_avg=60
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd, allowed: 1
+sys={3000.000000}: select(35): chan_load_avg=50
+sys={3100.000000}: select(36): chan_load_avg=40
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb, allowed: 2
+sys={3200.000000}: select(37): chan_load_avg=30
+(bts=0) ACC: rotate ACC allowed active subset 0x004 -> 0x008 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf7, allowed: 3
+sys={3250.000000}: select(38): chan_load_avg=20
+(bts=0) ACC: update ACC allowed active subset 0x008 -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7, allowed: 3 4
+sys={3350.000000}: select(39): chan_load_avg=10
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf, allowed: 4 5
+sys={3450.000000}: select(40): chan_load_avg=0
+(bts=0) ACC: rotate ACC allowed active subset 0x030 -> 0x060 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x9f, allowed: 5 6
+sys={3500.000000}: select(41): chan_load_avg=10
+(bts=0) ACC: update ACC allowed active subset 0x060 -> 0x0e0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f, allowed: 5 6 7
+sys={3600.000000}: select(42): chan_load_avg=20
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f, allowed: 6 7 8
+sys={3700.000000}: select(43): chan_load_avg=30
+(bts=0) ACC: rotate ACC allowed active subset 0x1c0 -> 0x181 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7e, allowed: 0 7 8
+sys={3750.000000}: select(44): chan_load_avg=40
+(bts=0) ACC: update ACC allowed active subset 0x181 -> 0x183 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x7c, allowed: 0 1 7 8
+sys={3850.000000}: select(45): chan_load_avg=50
+(bts=0) ACC: rotate ACC allowed active subset 0x183 -> 0x107 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xf8, allowed: 0 1 2 8
+sys={3950.000000}: select(46): chan_load_avg=60
+(bts=0) ACC: rotate ACC allowed active subset 0x107 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0, allowed: 0 1 2 3
+sys={4000.000000}: select(47): chan_load_avg=70
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x00e (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf1, allowed: 1 2 3
+sys={4100.000000}: select(48): chan_load_avg=80
+(bts=0) ACC: rotate ACC allowed active subset 0x00e -> 0x01c (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe3, allowed: 2 3 4
+sys={4200.000000}: select(49): chan_load_avg=90
+(bts=0) ACC: rotate ACC allowed active subset 0x01c -> 0x038 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc7, allowed: 3 4 5
+BTS deallocated OK in test_acc_ramp_updown_rotate()
+===test_acc_ramp_updown_rotate(30, 80, 30, 80, 5)===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_updown_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00, allowed: 0 1 2 3 4 5 6 7 8
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff, allowed:
+sys={250.000000}: select(0): chan_load_avg=30
+sys={500.000000}: select(1): chan_load_avg=35
+sys={750.000000}: select(2): chan_load_avg=40
+sys={1000.000000}: select(3): chan_load_avg=45
+sys={1250.000000}: select(4): chan_load_avg=50
+sys={1500.000000}: select(5): chan_load_avg=55
+sys={1750.000000}: select(6): chan_load_avg=60
+sys={2000.000000}: select(7): chan_load_avg=65
+sys={2250.000000}: select(8): chan_load_avg=70
+sys={2500.000000}: select(9): chan_load_avg=75
+sys={2750.000000}: select(10): chan_load_avg=80
+sys={3000.000000}: select(11): chan_load_avg=75
+sys={3250.000000}: select(12): chan_load_avg=70
+sys={3500.000000}: select(13): chan_load_avg=65
+sys={3750.000000}: select(14): chan_load_avg=60
+sys={4000.000000}: select(15): chan_load_avg=55
+sys={4250.000000}: select(16): chan_load_avg=50
+sys={4500.000000}: select(17): chan_load_avg=45
+sys={4750.000000}: select(18): chan_load_avg=40
+sys={5000.000000}: select(19): chan_load_avg=35
+sys={5250.000000}: select(20): chan_load_avg=30
+sys={5500.000000}: select(21): chan_load_avg=35
+sys={5750.000000}: select(22): chan_load_avg=40
+sys={6000.000000}: select(23): chan_load_avg=45
+sys={6250.000000}: select(24): chan_load_avg=50
+sys={6500.000000}: select(25): chan_load_avg=55
+sys={6750.000000}: select(26): chan_load_avg=60
+sys={7000.000000}: select(27): chan_load_avg=65
+sys={7250.000000}: select(28): chan_load_avg=70
+sys={7500.000000}: select(29): chan_load_avg=75
+sys={7750.000000}: select(30): chan_load_avg=80
+sys={8000.000000}: select(31): chan_load_avg=75
+sys={8250.000000}: select(32): chan_load_avg=70
+sys={8500.000000}: select(33): chan_load_avg=65
+sys={8750.000000}: select(34): chan_load_avg=60
+sys={9000.000000}: select(35): chan_load_avg=55
+sys={9250.000000}: select(36): chan_load_avg=50
+sys={9500.000000}: select(37): chan_load_avg=45
+sys={9750.000000}: select(38): chan_load_avg=40
+sys={10000.000000}: select(39): chan_load_avg=35
+sys={10250.000000}: select(40): chan_load_avg=30
+sys={10500.000000}: select(41): chan_load_avg=35
+sys={10750.000000}: select(42): chan_load_avg=40
+sys={11000.000000}: select(43): chan_load_avg=45
+sys={11250.000000}: select(44): chan_load_avg=50
+sys={11500.000000}: select(45): chan_load_avg=55
+sys={11750.000000}: select(46): chan_load_avg=60
+sys={12000.000000}: select(47): chan_load_avg=65
+sys={12250.000000}: select(48): chan_load_avg=70
+sys={12500.000000}: select(49): chan_load_avg=75
+BTS deallocated OK in test_acc_ramp_updown_rotate()
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 b301f9e9c..be9690c6e 100644
--- a/tests/bsc/Makefile.am
+++ b/tests/bsc/Makefile.am
@@ -8,23 +8,24 @@ AM_CFLAGS = \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOLEGACYMGCP_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(LIBOSMOMGCPCLIENT_CFLAGS) \
- $(LIBOSMOSIGTRAN_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
+ -no-install \
$(NULL)
EXTRA_DIST = \
bsc_test.ok \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
bsc_test \
$(NULL)
@@ -33,20 +34,17 @@ bsc_test_SOURCES = \
$(NULL)
bsc_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/abis_nm.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/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 164f78fe5..dda10c3c8 100644
--- a/tests/bsc/bsc_test.c
+++ b/tests/bsc/bsc_test.c
@@ -29,7 +29,14 @@
#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>
#include <osmocom/core/backtrace.h>
#include <osmocom/core/talloc.h>
@@ -124,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 */
@@ -175,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.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[] = {
@@ -213,30 +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, int dlci) {}
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) {}
-int bsc_compl_l3(struct gsm_subscriber_connection *conn, 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; }
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..1ab803901
--- /dev/null
+++ b/tests/bts_features.vty
@@ -0,0 +1,58 @@
+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
+
+OsmoBSC(config-net)# exit
+OsmoBSC(config)# exit
+
+OsmoBSC# ### test ncc-permitted
+OsmoBSC# show running-config
+... !ncc-permitted
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)# ncc-permitted 3 2
+% NCCs must be listed in order
+OsmoBSC(config-net-bts)# ncc-permitted 2 2
+% NCCs must be unique
+OsmoBSC(config-net-bts)# ncc-permitted 2 3 4 5 6 7 8
+OsmoBSC(config-net-bts)# exit
+OsmoBSC(config-net)# exit
+OsmoBSC(config)# exit
+OsmoBSC# show running-config
+...
+ ncc-permitted 2 3 4 5 6 7 8
+...
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)# ncc-permitted all
+OsmoBSC(config-net-bts)# exit
+OsmoBSC(config-net)# exit
+OsmoBSC(config)# exit
+OsmoBSC# show running-config
+... !ncc-permitted
diff --git a/tests/cbc.vty b/tests/cbc.vty
new file mode 100644
index 000000000..a15de5993
--- /dev/null
+++ b/tests/cbc.vty
@@ -0,0 +1,275 @@
+OsmoBSC> ### CBSP link config
+
+OsmoBSC> list
+...
+ show cbc
+...
+
+OsmoBSC> enable
+OsmoBSC# list
+...
+ show cbc
+...
+
+OsmoBSC# show running-config
+... !cbc
+
+OsmoBSC# show cbc
+CBSP link is disabled
+
+OsmoBSC# configure terminal
+OsmoBSC(config)# cbc
+
+OsmoBSC(config-cbc)# list
+...
+ mode (server|client|disabled)
+ server
+ client
+
+OsmoBSC(config-cbc)# ?
+...
+ mode Set OsmoBSC as CBSP server or client
+ server Configure OsmoBSC's CBSP server role
+ client Configure OsmoBSC's CBSP client role
+
+OsmoBSC(config-cbc)# mode ?
+ server CBSP Server: listen for inbound TCP connections from a remote Cell Broadcast Centre
+ client CBSP Client: establish outbound TCP connection to a remote Cell Broadcast Centre
+ disabled Disable CBSP link
+
+OsmoBSC(config-cbc)# server
+OsmoBSC(config-cbc-server)# list
+...
+ local-ip (A.B.C.D|X:X::X:X)
+ local-port <1-65535>
+
+OsmoBSC(config-cbc-server)# ?
+...
+ local-ip Set IP Address to listen on for inbound CBSP from a Cell Broadcast Centre
+ local-port Set TCP port to listen on for inbound CBSP from a Cell Broadcast Centre
+
+OsmoBSC(config-cbc-server)# local-ip ?
+ A.B.C.D IPv4 address
+ X:X::X:X IPv6 address
+OsmoBSC(config-cbc-server)# local-port ?
+ <1-65535> CBSP port number (Default: 48049)
+
+OsmoBSC(config-cbc-server)# local-ip 1.2.3.4
+OsmoBSC(config-cbc-server)# local-port 12345
+OsmoBSC(config-cbc-server)# show running-config
+...
+cbc
+ mode disabled
+ server
+ local-ip 1.2.3.4
+ local-port 12345
+...
+
+OsmoBSC(config-cbc-server)# local-port 48049
+
+OsmoBSC(config-cbc-server)# show running-config
+...
+cbc
+ mode disabled
+ server
+ local-ip 1.2.3.4
+... !local-port
+
+OsmoBSC(config-cbc-server)# local-ip ::1
+OsmoBSC(config-cbc-server)# show running-config
+...
+cbc
+ mode disabled
+ server
+ local-ip ::1
+...
+
+OsmoBSC(config-cbc-server)# do show cbc
+CBSP link is disabled
+
+OsmoBSC(config-cbc-server)# exit
+
+OsmoBSC(config-cbc)# client
+OsmoBSC(config-cbc-client)# list
+...
+ remote-ip (A.B.C.D|X:X::X:X)
+ remote-port <1-65535>
+ local-ip (A.B.C.D|X:X::X:X)
+ local-port <1-65535>
+ no local-ip
+ no local-port
+
+OsmoBSC(config-cbc-client)# ?
+...
+ remote-ip Set IP Address of the Cell Broadcast Centre, to establish CBSP link to
+ remote-port Set TCP port of the Cell Broadcast Centre, to establish CBSP link to
+ local-ip Set local bind address for the outbound CBSP link to the Cell Broadcast Centre
+ local-port Set local bind port for the outbound CBSP link to the Cell Broadcast Centre
+ no Negate a command or set its defaults
+
+OsmoBSC(config-cbc-client)# remote-ip ?
+ A.B.C.D IPv4 address
+ X:X::X:X IPv6 address
+OsmoBSC(config-cbc-client)# remote-port ?
+ <1-65535> CBSP port number (Default: 48049)
+
+OsmoBSC(config-cbc-client)# no ?
+ local-ip Remove local IP address bind config for the CBSP client mode
+ local-port Remove local TCP port bind config for the CBSP client mode
+
+OsmoBSC(config-cbc-client)# remote-ip 1.2.3.4
+OsmoBSC(config-cbc-client)# remote-port 12345
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1.2.3.4
+ remote-port 12345
+...
+
+OsmoBSC(config-cbc-client)# remote-port 48049
+
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1.2.3.4
+... !remote-port
+
+OsmoBSC(config-cbc-client)# remote-ip 1:2:3:4::5
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1:2:3:4::5
+...
+
+OsmoBSC(config-cbc-client)# local-ip 1.2.3.4
+
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1:2:3:4::5
+ local-ip 1.2.3.4
+... !local-port
+
+OsmoBSC(config-cbc-client)# local-port 12345
+
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1:2:3:4::5
+ local-ip 1.2.3.4
+ local-port 12345
+...
+
+OsmoBSC(config-cbc-client)# no local-ip
+
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1:2:3:4::5
+ local-port 12345
+... !local
+
+OsmoBSC(config-cbc-client)# no local-port
+
+OsmoBSC(config-cbc-client)# show running-config
+...
+cbc
+...
+ client
+ remote-ip 1:2:3:4::5
+... !local
+
+OsmoBSC(config-cbc-client)# do show cbc
+CBSP link is disabled
+
+OsmoBSC(config-cbc-client)# exit
+
+OsmoBSC(config-cbc)# mode server
+OsmoBSC(config-cbc)# do show cbc
+OsmoBSC is configured as CBSP Server on [::1]:48049
+CBSP Server Connection: Disconnected
+
+OsmoBSC(config-cbc)# mode client
+OsmoBSC(config-cbc)# do show cbc
+OsmoBSC is configured as CBSP Client to remote CBC at [1:2:3:4::5]:48049
+CBSP Client Connection: Disconnected
+
+OsmoBSC(config-cbc)# mode disabled
+OsmoBSC(config-cbc)# do show cbc
+CBSP link is disabled
+
+
+OsmoBSC(config-cbc)# # TEST DEPRECATED COMMANDS
+
+OsmoBSC(config-cbc)# remote-ip 1.2.3.4
+% cbc/remote-ip config is deprecated, instead use cbc/client/remote-ip and cbc/ mode
+OsmoBSC(config-cbc)# remote-port 1234
+% cbc/remote-port config is deprecated, instead use cbc/client/remote-port
+OsmoBSC(config-cbc)# do show cbc
+OsmoBSC is configured as CBSP Client to remote CBC at 1.2.3.4:1234
+CBSP Client Connection: Disconnected
+OsmoBSC(config-cbc)# show running-config
+...
+cbc
+ mode client
+...
+ client
+ remote-ip 1.2.3.4
+ remote-port 1234
+...
+
+OsmoBSC(config-cbc)# no remote-ip
+% cbc/remote-ip config is deprecated, instead use cbc/client/remote-ip and cbc/mode
+OsmoBSC(config-cbc)# do show cbc
+CBSP link is disabled
+OsmoBSC(config-cbc)# show running-config
+...
+cbc
+ mode disabled
+...
+ client
+ remote-ip 1.2.3.4
+ remote-port 1234
+...
+
+OsmoBSC(config-cbc)# listen-ip 127.0.0.2
+% cbc/listen-ip config is deprecated, instead use cbc/server/local-ip
+OsmoBSC(config-cbc)# do show cbc
+CBSP link is disabled
+OsmoBSC(config-cbc)# listen-port 48049
+% cbc/listen-port config is deprecated, instead use cbc/server/local-port and cbc/mode
+OsmoBSC(config-cbc)# do show cbc
+OsmoBSC is configured as CBSP Server on 127.0.0.2:48049
+CBSP Server Connection: Disconnected
+OsmoBSC(config-cbc)# show running-config
+...
+cbc
+ mode server
+ server
+ local-ip 127.0.0.2
+ client
+ remote-ip 1.2.3.4
+ remote-port 1234
+...
+
+OsmoBSC(config-cbc)# no listen-port
+% cbc/listen-port config is deprecated, instead use cbc/server/local-port and cbc/mode
+OsmoBSC(config-cbc)# do show cbc
+CBSP link is disabled
+OsmoBSC(config-cbc)# show running-config
+...
+cbc
+ mode disabled
+...
diff --git a/tests/codec_pref/Makefile.am b/tests/codec_pref/Makefile.am
index e000252da..5e77f0e69 100644
--- a/tests/codec_pref/Makefile.am
+++ b/tests/codec_pref/Makefile.am
@@ -8,17 +8,17 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(NULL)
-AM_LDFLAGS = \
- $(NULL)
+AM_LDFLAGS = -no-install
EXTRA_DIST = \
codec_pref_test.ok \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
codec_pref_test \
$(NULL)
@@ -27,7 +27,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 f163f35a6..920352c54 100644
--- a/tests/codec_pref/codec_pref_test.c
+++ b/tests/codec_pref/codec_pref_test.c
@@ -20,6 +20,7 @@
*/
#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/osmo_bsc.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/core/application.h>
@@ -29,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
@@ -214,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;
}
@@ -393,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");
@@ -425,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);
@@ -435,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 */
@@ -451,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);
@@ -461,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 */
@@ -477,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);
@@ -487,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 */
@@ -503,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);
@@ -513,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 */
@@ -528,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);
@@ -571,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 */
@@ -586,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);
@@ -629,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 */
@@ -643,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");
@@ -659,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");
}
@@ -675,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);
@@ -685,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 e59827313..780842634 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]
@@ -64,7 +63,9 @@ class TestCtrlBase(unittest.TestCase):
def tearDown(self):
self.disconnect()
- osmoutil.end_proc(self.proc)
+ rc = osmoutil.end_proc(self.proc)
+ if rc is not None and rc != 0:
+ raise Exception("Process returned %d" % rc)
def disconnect(self):
if not (self.sock is None):
@@ -234,8 +235,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"""
@@ -258,12 +259,12 @@ class TestCtrlBSC(TestCtrlBase):
self.assertEqual(r['mtype'], 'SET_REPLY')
self.assertEqual(r['var'], 'bts.0.trx.0.max-power-reduction')
self.assertEqual(r['value'], '22')
-
+
r = self.do_get('bts.0.trx.0.max-power-reduction')
self.assertEqual(r['mtype'], 'GET_REPLY')
self.assertEqual(r['var'], 'bts.0.trx.0.max-power-reduction')
self.assertEqual(r['value'], '22')
-
+
r = self.do_set('bts.0.trx.0.max-power-reduction', '1')
self.assertEqual(r['mtype'], 'ERROR')
self.assertEqual(r['error'], 'Value must be even')
@@ -292,7 +293,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 +322,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 +490,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 +831,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/early_ia.vty b/tests/early_ia.vty
new file mode 100644
index 000000000..4ba6b3aee
--- /dev/null
+++ b/tests/early_ia.vty
@@ -0,0 +1,24 @@
+OsmoBSC> enable
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+
+OsmoBSC(config-net-bts)# ### Check that first setting early-IA and then enabling frequency hopping throws an error
+OsmoBSC(config-net-bts)# immediate-assignment pre-chan-ack
+OsmoBSC(config-net-bts)# trx 0
+OsmoBSC(config-net-bts-trx)# timeslot 2
+OsmoBSC(config-net-bts-trx-ts)# hopping enabled 1
+% ERROR: 'hopping enabled 1' works only with 'immediate-assignment post-chan-ack'
+OsmoBSC(config-net-bts-trx-ts)# hopping enabled 0
+OsmoBSC(config-net-bts-trx-ts)# exit
+OsmoBSC(config-net-bts-trx)# exit
+OsmoBSC(config-net-bts)# immediate-assignment post-chan-ack
+
+OsmoBSC(config-net-bts)# ### Check that first enabling frequency hopping and then setting early-IA throws an error
+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)# immediate-assignment pre-chan-ack
+% ERROR: 'hopping enabled 1' works only with 'immediate-assignment post-chan-ack', see timeslot 0-0-2-NONE
diff --git a/tests/gprs_bvci_default.vty b/tests/gprs_bvci_default.vty
deleted file mode 100644
index 4bd95ce39..000000000
--- a/tests/gprs_bvci_default.vty
+++ /dev/null
@@ -1,13 +0,0 @@
-OsmoBSC> enable
-OsmoBSC# configure terminal
-OsmoBSC(config)# network
-OsmoBSC(config-net)# bts 0
-OsmoBSC(config-net-bts)# gprs mode gprs
-OsmoBSC(config-net-bts)# exit
-
-OsmoBSC(config-net)# show running-config
-...
- bts 0
-...
- gprs cell bvci 2
-...
diff --git a/tests/gprs_params.vty b/tests/gprs_params.vty
new file mode 100644
index 000000000..0f69a8cfb
--- /dev/null
+++ b/tests/gprs_params.vty
@@ -0,0 +1,113 @@
+OsmoBSC> enable
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+OsmoBSC(config-net)# bts 0
+
+OsmoBSC(config-net-bts)# ### GPRS is disabled by default
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ gprs mode none
+...
+
+OsmoBSC(config-net-bts)# ### Default [E]GPRS params
+OsmoBSC(config-net-bts)# gprs mode gprs
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ gprs mode gprs
+ gprs routing area 0
+ gprs network-control-order nc0
+ gprs power-control alpha 0
+ gprs cell bvci 2
+ gprs cell timer blocking-timer 3
+ gprs cell timer blocking-retries 3
+ gprs cell timer unblocking-retries 3
+ gprs cell timer reset-timer 3
+ gprs cell timer reset-retries 3
+ gprs cell timer suspend-timer 10
+ gprs cell timer suspend-retries 3
+ gprs cell timer resume-timer 10
+ gprs cell timer resume-retries 3
+ gprs cell timer capability-update-timer 10
+ gprs cell timer capability-update-retries 3
+ gprs nsei 0
+ gprs ns timer tns-block 3
+ gprs ns timer tns-block-retries 3
+ gprs ns timer tns-reset 3
+ gprs ns timer tns-reset-retries 3
+ gprs ns timer tns-test 30
+ gprs ns timer tns-alive 3
+ gprs ns timer tns-alive-retries 10
+ no gprs nsvc 0
+ no gprs nsvc 1
+...
+
+
+OsmoBSC(config-net-bts)# ### NSVC sub-command syntax
+OsmoBSC(config-net-bts)# gprs nsvc?
+ nsvc Network Service Virtual Connection (NS-VC)
+OsmoBSC(config-net-bts)# gprs nsvc ?
+ <0-1> NSVC Logical Number
+OsmoBSC(config-net-bts)# gprs nsvc 0 ?
+ nsvci NS Virtual Connection Identifier
+ local GPRS NS Local UDP Port
+ remote GPRS NS Remote UDP Port
+OsmoBSC(config-net-bts)# gprs nsvc 0 nsvci ?
+ <0-65535> GPRS NS VC Identifier
+OsmoBSC(config-net-bts)# gprs nsvc 0 local ?
+ udp GPRS NS Local UDP Port
+OsmoBSC(config-net-bts)# gprs nsvc 0 local udp ?
+ port GPRS NS Local UDP Port
+OsmoBSC(config-net-bts)# gprs nsvc 0 local udp port ?
+ <0-65535> GPRS NS Local UDP Port Number
+OsmoBSC(config-net-bts)# gprs nsvc 0 remote ?
+ udp GPRS NS Remote UDP Port
+ ip GPRS NS Remote IP Address
+OsmoBSC(config-net-bts)# gprs nsvc 0 remote udp ?
+ port GPRS NS Remote UDP Port
+OsmoBSC(config-net-bts)# gprs nsvc 0 remote udp port ?
+ <0-65535> GPRS NS Remote UDP Port Number
+OsmoBSC(config-net-bts)# gprs nsvc 0 remote ip ?
+ A.B.C.D GPRS NS Remote IPv4 Address
+ X:X::X:X GPRS NS Remote IPv6 Address
+
+OsmoBSC(config-net-bts)# ### NSVC sub-command params
+OsmoBSC(config-net-bts)# gprs nsvc 0 nsvci 4242
+OsmoBSC(config-net-bts)# gprs nsvc 1 nsvci 2424
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ gprs nsvc 0 nsvci 4242
+ gprs nsvc 0 local udp port 0
+ gprs nsvc 1 nsvci 2424
+ gprs nsvc 1 local udp port 0
+...
+OsmoBSC(config-net-bts)# gprs nsvc 1 local udp port 23023
+OsmoBSC(config-net-bts)# gprs nsvc 1 remote udp port 23032
+OsmoBSC(config-net-bts)# gprs nsvc 1 remote ip 1.2.3.4
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ gprs nsvc 0 nsvci 4242
+ gprs nsvc 0 local udp port 0
+ gprs nsvc 1 nsvci 2424
+ gprs nsvc 1 local udp port 23023
+ gprs nsvc 1 remote ip 1.2.3.4
+ gprs nsvc 1 remote udp port 23032
+...
+
+OsmoBSC(config-net-bts)# ### Disable secondary NSVC
+OsmoBSC(config-net-bts)# no gprs nsvc 1
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+...
+ gprs nsvc 0 nsvci 4242
+ gprs nsvc 0 local udp port 0
+ no gprs nsvc 1
+...
diff --git a/tests/gsm0408/Makefile.am b/tests/gsm0408/Makefile.am
index aff7c7dd4..e13f6877d 100644
--- a/tests/gsm0408/Makefile.am
+++ b/tests/gsm0408/Makefile.am
@@ -7,10 +7,15 @@ AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(NULL)
-noinst_PROGRAMS = \
+AM_LDFLAGS = -no-install
+
+check_PROGRAMS = \
gsm0408_test \
$(NULL)
@@ -23,14 +28,9 @@ gsm0408_test_SOURCES = \
$(NULL)
gsm0408_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/gsm_04_08_rr.o \
- $(top_builddir)/src/osmo-bsc/arfcn_range_encode.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 5ff9491e9..a39f8c2f5 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -26,9 +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>
@@ -57,8 +58,6 @@
__FILE__, __LINE__, (int) res, # cmp, (int) wanted); \
}
-
-
static inline void gen(struct gsm_bts *bts, const char *s)
{
int r;
@@ -122,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);
@@ -137,10 +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);
+ 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);
}
@@ -269,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);
@@ -738,7 +405,7 @@ static const struct test_gsm48_ra_id_by_bts test_gsm48_ra_id_by_bts_data[] = {
},
};
-static void test_gsm48_ra_id_by_bts()
+static void test_gsm48_ra_id_by_bts(void)
{
int i;
bool pass = true;
@@ -773,12 +440,12 @@ static void test_gsm48_ra_id_by_bts()
OSMO_ASSERT(pass);
}
-static void test_gsm48_multirate_config()
+static void test_gsm48_multirate_config(void)
{
- 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));
@@ -802,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[] = {
@@ -895,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);
@@ -913,58 +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_subscriber_connection *conn, 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; }
diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok
index 7e054f48e..619a29712 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...
@@ -135,97 +79,95 @@ generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 18 3
BTS deallocated OK in test_si2q_mu()
BTS allocation OK in test_si2q_long()
Testing SYSINFO_TYPE_2quater combined EARFCN & UARFCN generation:
-generating SI2quater for 17 EARFCNs and 1 UARFCNs...
-generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 0c 1a 10 9c 00 02 cc 1e 09 07 82 39 76 56 0b
+generating SI2quater for 16 EARFCNs and 1 UARFCNs...
+generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 0c 1a 10 9c 00 02 cc 1e 09 07 84 d7 2e ca c1
+generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 87 00 00 b3 07 83 d8 3c 2e c2 0f f5 cb b2 b0 2b 2b
+generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 87 00 00 b3 08 40 c8 42 0d c2 10 a9 cb b2 b0 2b 2b
+generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 87 00 00 b3 08 53 d8 42 a6 42 15 6d cb b2 b0 2b 2b
+generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 87 00 00 b3 08 56 a8 42 4e c2 12 b1 cb b2 b0 2b 2b
+generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 87 00 00 b3 08 4b b8 42 65 39 76 56 0b 2b 2b 2b 2b
+generating SI2quater for 16 EARFCNs and 2 UARFCNs...
+generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 14 4d e7 00 46 00 01 66 0f 04 72 ec ac 0b 2b
generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 87 00 00 b3 07 84 d8 3c 1e c1 e1 75 cb b2 b0 2b 2b
generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 87 00 00 b3 08 3f d8 42 06 42 10 6d cb b2 b0 2b 2b
generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 87 00 00 b3 08 42 a8 42 9e c2 15 31 cb b2 b0 2b 2b
generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 87 00 00 b3 08 55 b8 42 b5 42 12 75 cb b2 b0 2b 2b
generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 87 00 00 b3 08 4a c8 42 5d c2 13 29 cb b2 b0 2b 2b
-generating SI2quater for 17 EARFCNs and 2 UARFCNs...
-generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 14 4d e7 00 46 00 01 66 0f 04 72 ec ac 0b 2b
+generating SI2quater for 16 EARFCNs and 3 UARFCNs...
+generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 1c 4d e7 03 04 87 00 00 b3 07 82 39 76 56 0b
+generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 87 00 00 b3 07 84 d8 3c 1e c1 e1 75 cb b2 b0 2b 2b
+generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 87 00 00 b3 08 3f d8 42 06 42 10 6d cb b2 b0 2b 2b
+generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 87 00 00 b3 08 42 a8 42 9e c2 15 31 cb b2 b0 2b 2b
+generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 87 00 00 b3 08 55 b8 42 b5 42 12 75 cb b2 b0 2b 2b
+generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 87 00 00 b3 08 4a c8 42 5d c2 13 29 cb b2 b0 2b 2b
+generating SI2quater for 16 EARFCNs and 4 UARFCNs...
+generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 24 59 fa 26 73 84 87 00 00 b2 e5 d9 58 2b 2b
generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e1 36 0f 07 ae 5d 95 83 2b 2b
generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 85 d8 41 fe c2 10 31 cb b2 b0 2b 2b
generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 41 b8 42 15 42 14 f5 cb b2 b0 2b 2b
generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 54 c8 42 ad c2 15 a9 cb b2 b0 2b 2b
generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 49 d8 42 56 42 12 ed cb b2 b0 2b 2b
generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4c a7 2e ca c1 2b 2b 2b 2b 2b 2b 2b
-generating SI2quater for 17 EARFCNs and 3 UARFCNs...
-generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 1c 4d e7 03 04 87 00 00 b3 07 82 39 76 56 0b
+generating SI2quater for 16 EARFCNs and 5 UARFCNs...
+generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 2c 59 fa 30 73 f6 04 87 00 00 b2 e5 d9 58 2b
generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e1 36 0f 07 ae 5d 95 83 2b 2b
generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 85 d8 41 fe c2 10 31 cb b2 b0 2b 2b
generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 41 b8 42 15 42 14 f5 cb b2 b0 2b 2b
generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 54 c8 42 ad c2 15 a9 cb b2 b0 2b 2b
generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 49 d8 42 56 42 12 ed cb b2 b0 2b 2b
generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4c a7 2e ca c1 2b 2b 2b 2b 2b 2b 2b
-generating SI2quater for 17 EARFCNs and 4 UARFCNs...
-generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 24 59 fa 26 73 84 87 00 00 b2 e5 d9 58 2b 2b
-generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e0 90 78 4d 72 ec ac 0b 2b 2b
-generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 83 d8 3c 2e c2 0f f5 cb b2 b0 2b 2b
-generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 40 c8 42 0d c2 10 a9 cb b2 b0 2b 2b
-generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 53 d8 42 a6 42 15 6d cb b2 b0 2b 2b
-generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 56 a8 42 4e c2 12 b1 cb b2 b0 2b 2b
-generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 42 65 39 76 56 0b 2b 2b 2b 2b
-generating SI2quater for 17 EARFCNs and 5 UARFCNs...
-generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 2c 59 fa 30 73 f6 04 87 00 00 b2 e5 d9 58 2b
-generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e0 90 78 4d 72 ec ac 0b 2b 2b
-generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 83 d8 3c 2e c2 0f f5 cb b2 b0 2b 2b
-generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 40 c8 42 0d c2 10 a9 cb b2 b0 2b 2b
-generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 53 d8 42 a6 42 15 6d cb b2 b0 2b 2b
-generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 56 a8 42 4e c2 12 b1 cb b2 b0 2b 2b
-generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 42 65 39 76 56 0b 2b 2b 2b 2b
-generating SI2quater for 17 EARFCNs and 6 UARFCNs...
+generating SI2quater for 16 EARFCNs and 6 UARFCNs...
generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 34 f1 ae 15 f3 f4 83 03 2b 2b 2b 2b 2b 2b 2b
-generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e0 90 78 4d 72 ec ac 0b 2b 2b
-generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 83 d8 3c 2e c2 0f f5 cb b2 b0 2b 2b
-generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 40 c8 42 0d c2 10 a9 cb b2 b0 2b 2b
-generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 53 d8 42 a6 42 15 6d cb b2 b0 2b 2b
-generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 56 a8 42 4e c2 12 b1 cb b2 b0 2b 2b
-generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 42 65 39 76 56 0b 2b 2b 2b 2b
-generating SI2quater for 17 EARFCNs and 7 UARFCNs...
+generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e1 36 0f 07 ae 5d 95 83 2b 2b
+generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 85 d8 41 fe c2 10 31 cb b2 b0 2b 2b
+generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 41 b8 42 15 42 14 f5 cb b2 b0 2b 2b
+generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 54 c8 42 ad c2 15 a9 cb b2 b0 2b 2b
+generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 49 d8 42 56 42 12 ed cb b2 b0 2b 2b
+generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4c a7 2e ca c1 2b 2b 2b 2b 2b 2b 2b
+generating SI2quater for 16 EARFCNs and 7 UARFCNs...
generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 3c f1 ae 15 f3 f4 83 01 83 2b 2b 2b 2b 2b 2b
-generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e0 90 78 4d 72 ec ac 0b 2b 2b
-generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 83 d8 3c 2e c2 0f f5 cb b2 b0 2b 2b
-generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 40 c8 42 0d c2 10 a9 cb b2 b0 2b 2b
-generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 53 d8 42 a6 42 15 6d cb b2 b0 2b 2b
-generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 56 a8 42 4e c2 12 b1 cb b2 b0 2b 2b
-generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 42 65 39 76 56 0b 2b 2b 2b 2b
-generating SI2quater for 17 EARFCNs and 8 UARFCNs...
+generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e1 36 0f 07 ae 5d 95 83 2b 2b
+generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 85 d8 41 fe c2 10 31 cb b2 b0 2b 2b
+generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 41 b8 42 15 42 14 f5 cb b2 b0 2b 2b
+generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 54 c8 42 ad c2 15 a9 cb b2 b0 2b 2b
+generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 49 d8 42 56 42 12 ed cb b2 b0 2b 2b
+generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4c a7 2e ca c1 2b 2b 2b 2b 2b 2b 2b
+generating SI2quater for 16 EARFCNs and 8 UARFCNs...
generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 45 19 a0 0d 7d 7e a6 19 e7 0b 2b 2b 2b 2b 2b
-generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e0 90 78 4d 72 ec ac 0b 2b 2b
-generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 83 d8 3c 2e c2 0f f5 cb b2 b0 2b 2b
-generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 40 c8 42 0d c2 10 a9 cb b2 b0 2b 2b
-generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 53 d8 42 a6 42 15 6d cb b2 b0 2b 2b
-generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 56 a8 42 4e c2 12 b1 cb b2 b0 2b 2b
-generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 42 65 39 76 56 0b 2b 2b 2b 2b
-generating SI2quater for 17 EARFCNs and 9 UARFCNs...
+generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e1 36 0f 07 ae 5d 95 83 2b 2b
+generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 85 d8 41 fe c2 10 31 cb b2 b0 2b 2b
+generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 41 b8 42 15 42 14 f5 cb b2 b0 2b 2b
+generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 54 c8 42 ad c2 15 a9 cb b2 b0 2b 2b
+generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 49 d8 42 56 42 12 ed cb b2 b0 2b 2b
+generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4c a7 2e ca c1 2b 2b 2b 2b 2b 2b 2b
+generating SI2quater for 16 EARFCNs and 9 UARFCNs...
generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 4d 19 a0 26 fd 66 a6 03 e7 fa 0b 2b 2b 2b 2b
-generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e0 90 78 4d 72 ec ac 0b 2b 2b
-generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 83 d8 3c 2e c2 0f f5 cb b2 b0 2b 2b
-generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 40 c8 42 0d c2 10 a9 cb b2 b0 2b 2b
-generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 53 d8 42 a6 42 15 6d cb b2 b0 2b 2b
-generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 56 a8 42 4e c2 12 b1 cb b2 b0 2b 2b
-generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 42 65 39 76 56 0b 2b 2b 2b 2b
-generating SI2quater for 17 EARFCNs and 10 UARFCNs...
+generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e1 36 0f 07 ae 5d 95 83 2b 2b
+generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 85 d8 41 fe c2 10 31 cb b2 b0 2b 2b
+generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 41 b8 42 15 42 14 f5 cb b2 b0 2b 2b
+generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 54 c8 42 ad c2 15 a9 cb b2 b0 2b 2b
+generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 49 d8 42 56 42 12 ed cb b2 b0 2b 2b
+generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4c a7 2e ca c1 2b 2b 2b 2b 2b 2b 2b
+generating SI2quater for 16 EARFCNs and 10 UARFCNs...
generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 55 47 89 1e fd 7c b0 00 e7 9b b0 2b 2b 2b 2b
-generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e0 90 78 4d 72 ec ac 0b 2b 2b
-generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 83 d8 3c 2e c2 0f f5 cb b2 b0 2b 2b
-generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 40 c8 42 0d c2 10 a9 cb b2 b0 2b 2b
-generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 53 d8 42 a6 42 15 6d cb b2 b0 2b 2b
-generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 56 a8 42 4e c2 12 b1 cb b2 b0 2b 2b
-generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 42 65 39 76 56 0b 2b 2b 2b 2b
-generating SI2quater for 17 EARFCNs and 11 UARFCNs...
+generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e1 36 0f 07 ae 5d 95 83 2b 2b
+generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 85 d8 41 fe c2 10 31 cb b2 b0 2b 2b
+generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 41 b8 42 15 42 14 f5 cb b2 b0 2b 2b
+generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 54 c8 42 ad c2 15 a9 cb b2 b0 2b 2b
+generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 49 d8 42 56 42 12 ed cb b2 b0 2b 2b
+generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4c a7 2e ca c1 2b 2b 2b 2b 2b 2b 2b
+generating SI2quater for 16 EARFCNs and 11 UARFCNs...
generated valid SI2quater [00/06]: [23] 59 06 07 40 c0 25 0f 70 5d 47 89 1e fd 7c b0 01 67 9b b3 f8 2b 2b 2b
-generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e0 90 78 4d 72 ec ac 0b 2b 2b
-generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 83 d8 3c 2e c2 0f f5 cb b2 b0 2b 2b
-generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 40 c8 42 0d c2 10 a9 cb b2 b0 2b 2b
-generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 53 d8 42 a6 42 15 6d cb b2 b0 2b 2b
-generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 56 a8 42 4e c2 12 b1 cb b2 b0 2b 2b
-generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4b b8 42 65 39 76 56 0b 2b 2b 2b 2b
+generated valid SI2quater [01/06]: [23] 59 06 07 42 c0 04 87 00 00 b3 07 82 41 e1 36 0f 07 ae 5d 95 83 2b 2b
+generated valid SI2quater [02/06]: [23] 59 06 07 44 c0 04 87 00 00 b3 07 85 d8 41 fe c2 10 31 cb b2 b0 2b 2b
+generated valid SI2quater [03/06]: [23] 59 06 07 46 c0 04 87 00 00 b3 08 41 b8 42 15 42 14 f5 cb b2 b0 2b 2b
+generated valid SI2quater [04/06]: [23] 59 06 07 48 c0 04 87 00 00 b3 08 54 c8 42 ad c2 15 a9 cb b2 b0 2b 2b
+generated valid SI2quater [05/06]: [23] 59 06 07 4a c0 04 87 00 00 b3 08 49 d8 42 56 42 12 ed cb b2 b0 2b 2b
+generated valid SI2quater [06/06]: [23] 59 06 07 4c c0 04 87 00 00 b3 08 4c a7 2e ca c1 2b 2b 2b 2b 2b 2b 2b
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 +181,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 736b44456..0ff387b70 100644
--- a/tests/handover/Makefile.am
+++ b/tests/handover/Makefile.am
@@ -18,17 +18,17 @@ AM_CFLAGS = \
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
+ -no-install \
$(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 = \
@@ -37,60 +37,16 @@ handover_test_SOURCES = \
handover_test_LDFLAGS = \
-Wl,--wrap=abis_rsl_sendmsg \
+ -Wl,--wrap=bsc_cipher_mode_compl \
+ -Wl,--wrap=bsc_cm_update \
+ -Wl,--wrap=bsc_compl_l3 \
+ -Wl,--wrap=bsc_dtap \
+ -Wl,--wrap=bsc_sapi_n_reject \
-Wl,--wrap=osmo_mgcpc_ep_ci_request \
$(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_ramp.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_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/libbsc.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCTRL_LIBS) \
@@ -101,17 +57,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 1a756cda3..174e9e141 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>
@@ -47,10 +50,15 @@
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/handover_fsm.h>
#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. */
@@ -75,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(void)
+{
+ 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;
@@ -103,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);
@@ -121,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;
@@ -193,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;
@@ -242,41 +426,47 @@ void create_conn(struct gsm_lchan *lchan)
/* Make up a new IMSI for this test, for logging the subscriber */
next_imsi ++;
snprintf(imsi, sizeof(imsi), "%06u", next_imsi);
- lchan->conn->bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers, 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_A_CONN_REQ, NULL);
+ 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);
}
@@ -291,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);
+ }
-static int parse_chan_act(struct gsm_lchan *lchan, uint8_t *data)
+ return lchan_act(lchan, full_rate, codec);
+}
+
+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)
{
@@ -358,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;
@@ -381,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;
@@ -420,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;
@@ -436,886 +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 more 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_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,
-};
+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;
+}
+
+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(void)
+{
+ 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] = {
@@ -1388,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);
@@ -1452,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() {}
@@ -1767,23 +1738,47 @@ 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, int dlci) {}
-void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) {}
-int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel)
+
+void __real_bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, uint8_t dlci, enum gsm0808_cause cause);
+void __wrap_bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, uint8_t dlci, enum gsm0808_cause cause) {}
+
+void __real_bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_a5_n);
+void __wrap_bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_a5_n) {}
+
+int __real_bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel);
+int __wrap_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_cm_update(struct gsm_subscriber_connection *conn,
+
+void __real_bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg);
+void __wrap_bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) {}
+
+void __real_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 __wrap_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..08c7f71bf
--- /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
+"foo" is not a valid codec version
+
+OsmoBSC(config-msc)# codec-list fr10
+"fr10" is not a valid codec version
+
+OsmoBSC(config-msc)# codec-list hr10
+"hr10" is not a valid codec version
+
+OsmoBSC(config-msc)# codec-list FR1
+"FR1" is not a valid codec version
+
+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-list': 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 aa7045e49..b45e9b557 100644
--- a/tests/nanobts_omlattr/Makefile.am
+++ b/tests/nanobts_omlattr/Makefile.am
@@ -8,9 +8,13 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(NULL)
-noinst_PROGRAMS = \
+AM_LDFLAGS = -no-install
+
+check_PROGRAMS = \
nanobts_omlattr_test \
$(NULL)
@@ -23,9 +27,7 @@ nanobts_omlattr_test_SOURCES = \
$(NULL)
nanobts_omlattr_test_LDADD = \
- $(top_builddir)/src/osmo-bsc/abis_nm.o \
- $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.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 a3ba8e006..9ef90ae08 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.c
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.c
@@ -22,161 +22,97 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
+#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>
#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");
+
+ bts->gprs.cell.mo.ipaccess.obj_version = 30;
- 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");
@@ -192,9 +128,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" },
{}
};
@@ -221,27 +159,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,
@@ -249,7 +204,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;
@@ -258,20 +213,21 @@ int main(int argc, char **argv)
0x02, 0x00, 0x02, 0xa3, 0x00, 0x09, 0x14, 0x05, 0x05, 0xa0,
0x05, 0x0a, 0x04, 0x08,
0x0f, 0xa8, 0x00, 0x02, 0x0f, 0x00, 0xa9, 0x00, 0x05, 0x00,
- 0xfa, 0x00, 0xfa, 0x02
+ 0xfa, 0x00, 0xfa, 0x02, 0xac, 0x00, 0x01, 0x06,
};
- /* Parameters needed to test nanobts_attr_nscv_get() */
- bts->gprs.nsvc[0].nsvci = 0x65;
- bts->gprs.nsvc[0].remote_port = 0x59d8;
- bts->gprs.nsvc[0].remote_ip = 0x0a090165;
- bts->gprs.nsvc[0].local_port = 0x5a3c;
+ /* 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->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;
@@ -279,11 +235,29 @@ 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->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,
+ /* 1b type, 1b padding, 2b local port, 2b remote port */
+ 0x29, 0x00, 0x5a, 0x3c, 0x59, 0xe2,
+ /* 128bit / 16b ipv6 address */
+ 0xfd, 0x00, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+ 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+ };
+ test_nanobts_gen_set_nsvc_attr(bts, attr_nscv6_expected);
+
printf("Done\n");
talloc_free(bts);
@@ -302,20 +276,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; }
diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.ok b/tests/nanobts_omlattr/nanobts_omlattr_test.ok
index ef46cf951..e2f109ef3 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.ok
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.ok
@@ -1,26 +1,31 @@
-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()...
-result= 9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02
-expected=9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02
+Testing nanobts_gen_set_cell_attr()...
+result= 9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02ac000106
+expected=9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02ac000106
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_gen_set_nsvc_attr()...
+result= 9f00020065fd001629005a3c59e2fd005678901234567890123456789012
+expected=9f00020065fd001629005a3c59e2fd005678901234567890123456789012
+ok.
+
Done
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..690b9f36d 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,143 @@ 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
+ vgcs-vbs Channel allocation for VGCS/VBS
+
+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 mode vgcs-vbs ?
+ ascending Allocate Timeslots and Transceivers in ascending order
+ descending Allocate Timeslots and Transceivers in descending order
+
+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..7fb7ff196
--- /dev/null
+++ b/tests/paging/Makefile.am
@@ -0,0 +1,39 @@
+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 \
+ -no-install \
+ $(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/si2quater_neighbor_list.vty b/tests/si2quater_neighbor_list.vty
new file mode 100644
index 000000000..0aa1c231a
--- /dev/null
+++ b/tests/si2quater_neighbor_list.vty
@@ -0,0 +1,169 @@
+OsmoBSC> enable
+OsmoBSC# configure terminal
+OsmoBSC(config)# network
+
+OsmoBSC(config-net)# ## No neighbor EARFCNs by default
+OsmoBSC(config-net)# show running-config
+... !si2quater neighbor-list
+
+
+OsmoBSC(config-net)# bts 0
+OsmoBSC(config-net-bts)# si2quater?
+ si2quater SI2quater Neighbor List
+OsmoBSC(config-net-bts)# si2quater ?
+ neighbor-list SI2quater Neighbor List
+OsmoBSC(config-net-bts)# si2quater neighbor-list ?
+ add Add to manual SI2quater neighbor list
+ del Delete from SI2quater manual neighbor list
+
+OsmoBSC(config-net-bts)# si2quater neighbor-list add ?
+ earfcn EARFCN of neighbor
+ uarfcn UARFCN of neighbor
+OsmoBSC(config-net-bts)# si2quater neighbor-list del ?
+ earfcn EARFCN of neighbor
+ uarfcn UARFCN of neighbor
+
+
+OsmoBSC(config-net-bts)# ### Delete command syntax
+OsmoBSC(config-net-bts)# si2quater neighbor-list del earfcn ?
+ <0-65535> EARFCN
+OsmoBSC(config-net-bts)# si2quater neighbor-list del uarfcn ?
+ <0-16383> UARFCN
+
+
+OsmoBSC(config-net-bts)# ### EARFCN add command syntax
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn ?
+ <0-65535> EARFCN of neighbor
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 0 thresh-hi ?
+ <0-31> threshold high bits
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 0 thresh-hi 0 thresh-lo ?
+ <0-32> threshold low bits (32 means NA)
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 0 thresh-hi 0 thresh-lo 32 prio ?
+ <0-8> priority (8 means NA)
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 0 thresh-hi 0 thresh-lo 32 prio 8 qrxlv ?
+ <0-32> QRXLEVMIN (32 means NA)
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 0 thresh-hi 0 thresh-lo 32 prio 8 qrxlv 32 meas ?
+ <0-8> measurement bandwidth (8 means NA)
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 0 thresh-hi 0 thresh-lo 32 prio 8 qrxlv 32 meas 8 ?
+ <cr>
+
+
+OsmoBSC(config-net-bts)# ### UARFCN add command syntax
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn ?
+ <0-16383> UARFCN of neighbor
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 0 ?
+ <0-511> scrambling code
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 0 0 ?
+ <0-1> diversity bit
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 0 0 0 ?
+ <cr>
+
+
+OsmoBSC(config-net-bts)# ### EARFCN add/del command tests
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 0 thresh-hi 0 thresh-lo 32 prio 8 qrxlv 32 meas 8
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 65535 thresh-hi 0 thresh-lo 32 prio 8 qrxlv 32 meas 8
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+... !si2quater neighbor-list
+ si2quater neighbor-list add earfcn 0 thresh-hi 0 thresh-lo 32 prio 8 qrxlv 32 meas 8
+ si2quater neighbor-list add earfcn 65535 thresh-hi 0 thresh-lo 32 prio 8 qrxlv 32 meas 8
+... !si2quater neighbor-list
+
+OsmoBSC(config-net-bts)# si2quater neighbor-list del earfcn 33
+% Unable to delete arfcn 33: No such file or directory
+
+OsmoBSC(config-net-bts)# si2quater neighbor-list del earfcn 65535
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+... !si2quater neighbor-list
+ si2quater neighbor-list add earfcn 0 thresh-hi 0 thresh-lo 32 prio 8 qrxlv 32 meas 8
+... !si2quater neighbor-list
+
+
+OsmoBSC(config-net-bts)# ### EARFCN add command: different params
+OsmoBSC(config-net-bts)# ### FIXME: osmo-bsc does not support different thresh/prio/qrxlv
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 300 thresh-hi 7 thresh-lo 5 prio 5 qrxlv 6 meas 5
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+... !si2quater neighbor-list
+ si2quater neighbor-list add earfcn 0 thresh-hi 7 thresh-lo 5 prio 5 qrxlv 6 meas 8
+ si2quater neighbor-list add earfcn 300 thresh-hi 7 thresh-lo 5 prio 5 qrxlv 6 meas 5
+... !si2quater neighbor-list
+
+
+OsmoBSC(config-net-bts)# ### EARFCN add command: EARFCN already exists
+OsmoBSC(config-net-bts)# si2quater neighbor-list add earfcn 300 thresh-hi 7 thresh-lo 5 prio 5 qrxlv 6 meas 2
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+... !si2quater neighbor-list
+ si2quater neighbor-list add earfcn 0 thresh-hi 7 thresh-lo 5 prio 5 qrxlv 6 meas 8
+ si2quater neighbor-list add earfcn 300 thresh-hi 7 thresh-lo 5 prio 5 qrxlv 6 meas 2
+... !si2quater neighbor-list
+
+
+OsmoBSC(config-net-bts)# ### Remove remaining EARFCNs
+OsmoBSC(config-net-bts)# si2quater neighbor-list del earfcn 0
+OsmoBSC(config-net-bts)# si2quater neighbor-list del earfcn 300
+OsmoBSC(config-net-bts)# show running-config
+... !si2quater neighbor-list
+
+
+OsmoBSC(config-net-bts)# ### UARFCN add/del command tests
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 1 256 0
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 111 211 1
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 111 511 1
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 16383 0 0
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+... !si2quater neighbor-list
+ si2quater neighbor-list add uarfcn 1 256 0
+ si2quater neighbor-list add uarfcn 111 511 1
+ si2quater neighbor-list add uarfcn 111 211 1
+ si2quater neighbor-list add uarfcn 16383 0 0
+... !si2quater neighbor-list
+
+OsmoBSC(config-net-bts)# si2quater neighbor-list del uarfcn 1
+% Command incomplete.
+OsmoBSC(config-net-bts)# si2quater neighbor-list del uarfcn 33 256
+% Unable to delete uarfcn: pair not found
+OsmoBSC(config-net-bts)# si2quater neighbor-list del uarfcn 1 255
+% Unable to delete uarfcn: pair not found
+
+OsmoBSC(config-net-bts)# si2quater neighbor-list del uarfcn 1 256
+OsmoBSC(config-net-bts)# si2quater neighbor-list del uarfcn 16383 0
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+... !si2quater neighbor-list
+ si2quater neighbor-list add uarfcn 111 511 1
+ si2quater neighbor-list add uarfcn 111 211 1
+... !si2quater neighbor-list
+
+
+OsmoBSC(config-net-bts)# ### UARFCN add command: UARFCN already exists
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 111 511 1
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 111 511 0
+OsmoBSC(config-net-bts)# show running-config
+...
+ bts 0
+... !si2quater neighbor-list
+ si2quater neighbor-list add uarfcn 111 511 0
+ si2quater neighbor-list add uarfcn 111 211 1
+... !si2quater neighbor-list
+
+
+OsmoBSC(config-net-bts)# ### FIXME: UARFCN 0 cannot be added
+OsmoBSC(config-net-bts)# si2quater neighbor-list add uarfcn 0 1 1
+% Warning: not enough space in SI2quater for a given UARFCN (0, 1)
+
+
+OsmoBSC(config-net-bts)# ### Remove remaining UARFCNs
+OsmoBSC(config-net-bts)# si2quater neighbor-list del uarfcn 111 511
+OsmoBSC(config-net-bts)# si2quater neighbor-list del uarfcn 111 211
+OsmoBSC(config-net-bts)# show running-config
+... !si2quater neighbor-list
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..634ea59bf 100644
--- a/tests/subscr/Makefile.am
+++ b/tests/subscr/Makefile.am
@@ -9,12 +9,15 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(LIBSMPP34_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
+ -no-install \
$(NULL)
EXTRA_DIST = \
@@ -22,7 +25,7 @@ EXTRA_DIST = \
bsc_subscr_test.err \
$(NULL)
-noinst_PROGRAMS = \
+check_PROGRAMS = \
bsc_subscr_test \
$(NULL)
@@ -31,7 +34,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 3c94b8662..37a61649c 100644
--- a/tests/subscr/bsc_subscr_test.c
+++ b/tests/subscr/bsc_subscr_test.c
@@ -29,7 +29,7 @@
#include <stdlib.h>
#include <inttypes.h>
-struct llist_head *bsc_subscribers;
+struct bsc_subscr_store *bsc_subscribers;
#define VERBOSE_ASSERT(val, expect_op, fmt) \
do { \
@@ -37,16 +37,18 @@ struct llist_head *bsc_subscribers;
OSMO_ASSERT((val) expect_op); \
} while (0);
+#define BSUB_USE "test"
+
static void assert_bsc_subscr(const struct bsc_subscr *bsub, const char *imsi)
{
struct bsc_subscr *sfound;
OSMO_ASSERT(bsub);
OSMO_ASSERT(strcmp(bsub->imsi, imsi) == 0);
- sfound = bsc_subscr_find_by_imsi(bsc_subscribers, imsi);
+ sfound = bsc_subscr_find_by_imsi(bsc_subscribers, imsi, BSUB_USE);
OSMO_ASSERT(sfound == bsub);
- bsc_subscr_put(sfound);
+ bsc_subscr_put(sfound, BSUB_USE);
}
static void test_bsc_subscr(void)
@@ -59,25 +61,26 @@ static void test_bsc_subscr(void)
printf("Test BSC subscriber allocation and deletion\n");
/* Check for emptiness */
- VERBOSE_ASSERT(llist_count(bsc_subscribers), == 0, "%d");
- OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1) == NULL);
- OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2) == NULL);
- OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi3) == NULL);
+ VERBOSE_ASSERT(llist_count(&bsc_subscribers->bsub_list), == 0, "%d");
+ OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1, BSUB_USE) == NULL);
+ OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2, BSUB_USE) == NULL);
+ OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi3, BSUB_USE) == NULL);
/* Allocate entry 1 */
- s1 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi1);
- VERBOSE_ASSERT(llist_count(bsc_subscribers), == 1, "%d");
+ s1 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi1, BSUB_USE);
+ VERBOSE_ASSERT(llist_count(&bsc_subscribers->bsub_list), == 1, "%d");
assert_bsc_subscr(s1, imsi1);
- VERBOSE_ASSERT(llist_count(bsc_subscribers), == 1, "%d");
- OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2) == NULL);
+ VERBOSE_ASSERT(llist_count(&bsc_subscribers->bsub_list), == 1, "%d");
+ OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2, BSUB_USE) == NULL);
/* Allocate entry 2 */
- s2 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi2);
- VERBOSE_ASSERT(llist_count(bsc_subscribers), == 2, "%d");
+ s2 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi2, BSUB_USE);
+ bsc_subscr_set_tmsi(s2, 0x73517351);
+ VERBOSE_ASSERT(llist_count(&bsc_subscribers->bsub_list), == 2, "%d");
/* Allocate entry 3 */
- s3 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi3);
- VERBOSE_ASSERT(llist_count(bsc_subscribers), == 3, "%d");
+ s3 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi3, BSUB_USE);
+ VERBOSE_ASSERT(llist_count(&bsc_subscribers->bsub_list), == 3, "%d");
/* Check entries */
assert_bsc_subscr(s1, imsi1);
@@ -85,29 +88,29 @@ static void test_bsc_subscr(void)
assert_bsc_subscr(s3, imsi3);
/* Free entry 1 */
- bsc_subscr_put(s1);
+ bsc_subscr_put(s1, BSUB_USE);
s1 = NULL;
- VERBOSE_ASSERT(llist_count(bsc_subscribers), == 2, "%d");
- OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1) == NULL);
+ VERBOSE_ASSERT(llist_count(&bsc_subscribers->bsub_list), == 2, "%d");
+ OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1, BSUB_USE) == NULL);
assert_bsc_subscr(s2, imsi2);
assert_bsc_subscr(s3, imsi3);
/* Free entry 2 */
- bsc_subscr_put(s2);
+ bsc_subscr_put(s2, BSUB_USE);
s2 = NULL;
- VERBOSE_ASSERT(llist_count(bsc_subscribers), == 1, "%d");
- OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1) == NULL);
- OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2) == NULL);
+ VERBOSE_ASSERT(llist_count(&bsc_subscribers->bsub_list), == 1, "%d");
+ OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1, BSUB_USE) == NULL);
+ OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2, BSUB_USE) == NULL);
assert_bsc_subscr(s3, imsi3);
/* Free entry 3 */
- bsc_subscr_put(s3);
+ bsc_subscr_put(s3, BSUB_USE);
s3 = NULL;
- VERBOSE_ASSERT(llist_count(bsc_subscribers), == 0, "%d");
- OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi3) == NULL);
+ VERBOSE_ASSERT(llist_count(&bsc_subscribers->bsub_list), == 0, "%d");
+ OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi3, BSUB_USE) == NULL);
- OSMO_ASSERT(llist_empty(bsc_subscribers));
+ OSMO_ASSERT(llist_empty(&bsc_subscribers->bsub_list));
}
static const struct log_info_cat log_categories[] = {
@@ -123,18 +126,18 @@ static const struct log_info log_info = {
.num_cat = ARRAY_SIZE(log_categories),
};
-int main()
+int main(int argc, char **argv)
{
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);
- INIT_LLIST_HEAD(bsc_subscribers);
+ bsc_subscribers = bsc_subscr_store_alloc(ctx);
test_bsc_subscr();
diff --git a/tests/subscr/bsc_subscr_test.err b/tests/subscr/bsc_subscr_test.err
index afc8bf778..c41d3ca2a 100644
--- a/tests/subscr/bsc_subscr_test.err
+++ b/tests/subscr/bsc_subscr_test.err
@@ -1,20 +1,20 @@
-DREF BSC subscr IMSI:1234567890 usage increases to: 1
-DREF BSC subscr IMSI:1234567890 usage increases to: 2
-DREF BSC subscr IMSI:1234567890 usage decreases to: 1
-DREF BSC subscr IMSI:9876543210 usage increases to: 1
-DREF BSC subscr IMSI:5656565656 usage increases to: 1
-DREF BSC subscr IMSI:1234567890 usage increases to: 2
-DREF BSC subscr IMSI:1234567890 usage decreases to: 1
-DREF BSC subscr IMSI:9876543210 usage increases to: 2
-DREF BSC subscr IMSI:9876543210 usage decreases to: 1
-DREF BSC subscr IMSI:5656565656 usage increases to: 2
-DREF BSC subscr IMSI:5656565656 usage decreases to: 1
-DREF BSC subscr IMSI:1234567890 usage decreases to: 0
-DREF BSC subscr IMSI:9876543210 usage increases to: 2
-DREF BSC subscr IMSI:9876543210 usage decreases to: 1
-DREF BSC subscr IMSI:5656565656 usage increases to: 2
-DREF BSC subscr IMSI:5656565656 usage decreases to: 1
-DREF BSC subscr IMSI:9876543210 usage decreases to: 0
-DREF BSC subscr IMSI:5656565656 usage increases to: 2
-DREF BSC subscr IMSI:5656565656 usage decreases to: 1
-DREF BSC subscr IMSI:5656565656 usage decreases to: 0
+DREF subscr-IMSI-1234567890: + test: now used by 1 (test)
+DREF subscr-IMSI-1234567890: + test: now used by 2 (2*test)
+DREF subscr-IMSI-1234567890: - test: now used by 1 (test)
+DREF subscr-IMSI-9876543210: + test: now used by 1 (test)
+DREF subscr-IMSI-5656565656: + test: now used by 1 (test)
+DREF subscr-IMSI-1234567890: + test: now used by 2 (2*test)
+DREF subscr-IMSI-1234567890: - test: now used by 1 (test)
+DREF subscr-IMSI-9876543210-TMSI-0x73517351: + test: now used by 2 (2*test)
+DREF subscr-IMSI-9876543210-TMSI-0x73517351: - test: now used by 1 (test)
+DREF subscr-IMSI-5656565656: + test: now used by 2 (2*test)
+DREF subscr-IMSI-5656565656: - test: now used by 1 (test)
+DREF subscr-IMSI-1234567890: - test: now used by 0 (-)
+DREF subscr-IMSI-9876543210-TMSI-0x73517351: + test: now used by 2 (2*test)
+DREF subscr-IMSI-9876543210-TMSI-0x73517351: - test: now used by 1 (test)
+DREF subscr-IMSI-5656565656: + test: now used by 2 (2*test)
+DREF subscr-IMSI-5656565656: - test: now used by 1 (test)
+DREF subscr-IMSI-9876543210-TMSI-0x73517351: - test: now used by 0 (-)
+DREF subscr-IMSI-5656565656: + test: now used by 2 (2*test)
+DREF subscr-IMSI-5656565656: - test: now used by 1 (test)
+DREF subscr-IMSI-5656565656: - test: now used by 0 (-)
diff --git a/tests/subscr/bsc_subscr_test.ok b/tests/subscr/bsc_subscr_test.ok
index 0f6a8be01..b82b09625 100644
--- a/tests/subscr/bsc_subscr_test.ok
+++ b/tests/subscr/bsc_subscr_test.ok
@@ -1,11 +1,11 @@
Testing BSC subscriber core code.
Test BSC subscriber allocation and deletion
-llist_count(bsc_subscribers) == 0
-llist_count(bsc_subscribers) == 1
-llist_count(bsc_subscribers) == 1
-llist_count(bsc_subscribers) == 2
-llist_count(bsc_subscribers) == 3
-llist_count(bsc_subscribers) == 2
-llist_count(bsc_subscribers) == 1
-llist_count(bsc_subscribers) == 0
+llist_count(&bsc_subscribers->bsub_list) == 0
+llist_count(&bsc_subscribers->bsub_list) == 1
+llist_count(&bsc_subscribers->bsub_list) == 1
+llist_count(&bsc_subscribers->bsub_list) == 2
+llist_count(&bsc_subscribers->bsub_list) == 3
+llist_count(&bsc_subscribers->bsub_list) == 2
+llist_count(&bsc_subscribers->bsub_list) == 1
+llist_count(&bsc_subscribers->bsub_list) == 0
Done
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 1a190dda3..270d10c85 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -1,25 +1,18 @@
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
AT_CHECK([$abs_top_builddir/tests/abis/abis_test], [], [expout], [ignore])
AT_CLEANUP
+AT_SETUP([acc])
+AT_KEYWORDS([acc])
+cat $abs_srcdir/acc/acc_test.ok > experr
+AT_CHECK([$abs_top_builddir/tests/acc/acc_test], [], [ignore], [experr])
+AT_CLEANUP
+
AT_SETUP([bsc])
AT_KEYWORDS([bsc])
cat $abs_srcdir/bsc/bsc_test.ok > expout
@@ -32,189 +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_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 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([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 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([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 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([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 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([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
new file mode 100644
index 000000000..9b1843927
--- /dev/null
+++ b/tests/timer.vty
@@ -0,0 +1,172 @@
+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)
+net: T101 = 10 s inter-BSC/MSC Handover incoming, BSSMAP HO Request to HO Accept (default: 10 s)
+net: T3101 = 3 s RR Immediate Assignment (default: 3 s)
+net: T3103 = 5 s Handover (default: 5 s)
+net: T3105 = 100 ms Physical Information (default: 100 ms, range: [1 .. inf])
+net: T3107 = 5 s (unused) (default: 5 s)
+net: T3109 = 5 s RSL SACCH deactivation (default: 5 s)
+net: T3111 = 2 s Wait time before RSL RF Channel Release (default: 2 s)
+net: T3113 = 7 s Paging (default: 7 s)
+net: T3115 = 10 s (unused) (default: 10 s)
+net: T3117 = 10 s (unused) (default: 10 s)
+net: T3119 = 10 s (unused) (default: 10 s)
+net: T3122 = 10 s Wait time after RR Immediate Assignment Reject (default: 10 s)
+net: T3141 = 10 s (unused) (default: 10 s)
+net: T3212 = 5 Periodic Location Update timer, sent to MS (1 = 6 minutes) (default: 5)
+net: X4 = 60 s After Clear Request, wait for MSC to Clear Command (sanity) (default: 60 s)
+net: X5 = 5 s Timeout to switch dynamic timeslot PCHAN modes (default: 5 s)
+net: X6 = 5 s Timeout for RSL Channel Activate ACK after sending RSL Channel Activate (default: 5 s)
+net: X7 = 5 s Timeout for RSL IPA CRCX ACK after sending RSL IPA CRCX (default: 5 s)
+net: X8 = 5 s Timeout for RSL IPA MDCX ACK after sending RSL IPA MDCX (default: 5 s)
+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: X28 = 30 s Interval at which to try to recover a BORKEN lchan (default: 30 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)
+
+OsmoBSC# show timer T3111
+T3111 = 2 s Wait time before RSL RF Channel Release (default: 2 s)
+
+OsmoBSC# # specifically test legacy timers that are moved to X timers
+OsmoBSC# show timer T993111
+% Legacy: timer T993111 is now X3111
+X3111 = 4 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
+OsmoBSC# show timer T993210
+% Legacy: timer T993210 is now X3210
+X3210 = 20 s After L3 Complete, wait for MSC to confirm (default: 20 s)
+OsmoBSC# show timer T999
+% Legacy: timer T999 is now X4
+X4 = 60 s After Clear Request, wait for MSC to Clear Command (sanity) (default: 60 s)
+
+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)
+net: T101 = 10 s inter-BSC/MSC Handover incoming, BSSMAP HO Request to HO Accept (default: 10 s)
+net: T3101 = 3 s RR Immediate Assignment (default: 3 s)
+net: T3103 = 5 s Handover (default: 5 s)
+net: T3105 = 100 ms Physical Information (default: 100 ms, range: [1 .. inf])
+net: T3107 = 5 s (unused) (default: 5 s)
+net: T3109 = 5 s RSL SACCH deactivation (default: 5 s)
+net: T3111 = 2 s Wait time before RSL RF Channel Release (default: 2 s)
+net: T3113 = 7 s Paging (default: 7 s)
+net: T3115 = 10 s (unused) (default: 10 s)
+net: T3117 = 10 s (unused) (default: 10 s)
+net: T3119 = 10 s (unused) (default: 10 s)
+net: T3122 = 10 s Wait time after RR Immediate Assignment Reject (default: 10 s)
+net: T3141 = 10 s (unused) (default: 10 s)
+net: T3212 = 5 Periodic Location Update timer, sent to MS (1 = 6 minutes) (default: 5)
+net: X4 = 60 s After Clear Request, wait for MSC to Clear Command (sanity) (default: 60 s)
+net: X5 = 5 s Timeout to switch dynamic timeslot PCHAN modes (default: 5 s)
+net: X6 = 5 s Timeout for RSL Channel Activate ACK after sending RSL Channel Activate (default: 5 s)
+net: X7 = 5 s Timeout for RSL IPA CRCX ACK after sending RSL IPA CRCX (default: 5 s)
+net: X8 = 5 s Timeout for RSL IPA MDCX ACK after sending RSL IPA MDCX (default: 5 s)
+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: X28 = 30 s Interval at which to try to recover a BORKEN lchan (default: 30 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)
+
+OsmoBSC(config-net)# # Using the legacy 'timer Txxx' still works:
+OsmoBSC(config-net)# timer T3111
+T3111 = 2 s Wait time before RSL RF Channel Release (default: 2 s)
+OsmoBSC(config-net)# timer T3111 23
+OsmoBSC(config-net)# timer T3111
+T3111 = 23 s Wait time before RSL RF Channel Release (default: 2 s)
+OsmoBSC(config-net)# timer T3111 default
+OsmoBSC(config-net)# timer T3111
+T3111 = 2 s Wait time before RSL RF Channel Release (default: 2 s)
+
+OsmoBSC(config-net)# # Using the new timer group commands also works:
+OsmoBSC(config-net)# timer net T3111
+net: T3111 = 2 s Wait time before RSL RF Channel Release (default: 2 s)
+OsmoBSC(config-net)# timer net T3111 42
+OsmoBSC(config-net)# timer net T3111
+net: T3111 = 42 s Wait time before RSL RF Channel Release (default: 2 s)
+OsmoBSC(config-net)# timer net T3111 default
+OsmoBSC(config-net)# timer net T3111
+net: T3111 = 2 s Wait time before RSL RF Channel Release (default: 2 s)
+OsmoBSC(config-net)# timer mgw
+mgw: X2427 = 5 s timeout for MGCP response from MGW (default: 5 s)
+OsmoBSC(config-net)# timer mgw X2427
+mgw: X2427 = 5 s timeout for MGCP response from MGW (default: 5 s)
+OsmoBSC(config-net)# timer mgw X2427 42
+OsmoBSC(config-net)# timer mgw X2427
+mgw: X2427 = 42 s timeout for MGCP response from MGW (default: 5 s)
+OsmoBSC(config-net)# timer mgw X2427 default
+OsmoBSC(config-net)# timer mgw X2427
+mgw: X2427 = 5 s timeout for MGCP response from MGW (default: 5 s)
+
+OsmoBSC(config-net)# # specifically test legacy timers that are moved to X timers
+OsmoBSC(config-net)# timer T993111
+% Legacy: timer T993111 is now X3111
+X3111 = 4 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
+OsmoBSC(config-net)# timer T993111 1
+% Legacy: timer T993111 is now X3111
+OsmoBSC(config-net)# timer T993111
+% Legacy: timer T993111 is now X3111
+X3111 = 1 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
+OsmoBSC(config-net)# timer T993111 default
+% Legacy: timer T993111 is now X3111
+OsmoBSC(config-net)# timer T993111
+% Legacy: timer T993111 is now X3111
+X3111 = 4 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
+
+OsmoBSC(config-net)# timer T993210
+% Legacy: timer T993210 is now X3210
+X3210 = 20 s After L3 Complete, wait for MSC to confirm (default: 20 s)
+OsmoBSC(config-net)# timer T993210 2
+% Legacy: timer T993210 is now X3210
+OsmoBSC(config-net)# timer T993210
+% Legacy: timer T993210 is now X3210
+X3210 = 2 s After L3 Complete, wait for MSC to confirm (default: 20 s)
+OsmoBSC(config-net)# timer T993210 default
+% Legacy: timer T993210 is now X3210
+OsmoBSC(config-net)# timer T993210
+% Legacy: timer T993210 is now X3210
+X3210 = 20 s After L3 Complete, wait for MSC to confirm (default: 20 s)
+
+OsmoBSC(config-net)# timer T999
+% Legacy: timer T999 is now X4
+X4 = 60 s After Clear Request, wait for MSC to Clear Command (sanity) (default: 60 s)
+OsmoBSC(config-net)# timer T999 3
+% Legacy: timer T999 is now X4
+OsmoBSC(config-net)# timer T999
+% Legacy: timer T999 is now X4
+X4 = 3 s After Clear Request, wait for MSC to Clear Command (sanity) (default: 60 s)
+OsmoBSC(config-net)# timer T999 default
+% Legacy: timer T999 is now X4
+OsmoBSC(config-net)# timer T999
+% Legacy: timer T999 is now X4
+X4 = 60 s After Clear Request, wait for MSC to Clear Command (sanity) (default: 60 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..50bf6a0f5 100755
--- a/tests/vty_test_runner.py
+++ b/tests/vty_test_runner.py
@@ -65,7 +65,9 @@ class TestVTYBase(unittest.TestCase):
if self.vty:
self.vty._close_socket()
self.vty = None
- osmoutil.end_proc(self.proc)
+ rc = osmoutil.end_proc(self.proc)
+ if rc is not None and rc != 0:
+ raise Exception("Process returned %d" % rc)
class TestVTYGenericBSC(TestVTYBase):
@@ -172,21 +174,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")):